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