Actual source code: qn.c
petsc-dev 2014-02-02
1: #include <petsc-private/snesimpl.h> /*I "petscsnes.h" I*/
3: #define H(i,j) qn->dXdFmat[i*qn->m + j]
5: const char *const SNESQNScaleTypes[] = {"DEFAULT","NONE","SHANNO","LINESEARCH","JACOBIAN","SNESQNScaleType","SNES_QN_SCALING_",0};
6: const char *const SNESQNRestartTypes[] = {"DEFAULT","NONE","POWELL","PERIODIC","SNESQNRestartType","SNES_QN_RESTART_",0};
7: const char *const SNESQNTypes[] = {"LBFGS","BROYDEN","BADBROYDEN","SNESQNType","SNES_QN_",0};
9: typedef struct {
10: Vec *U; /* Stored past states (vary from method to method) */
11: Vec *V; /* Stored past states (vary from method to method) */
12: PetscInt m; /* The number of kept previous steps */
13: PetscReal *lambda; /* The line search history of the method */
14: PetscReal *norm; /* norms of the steps */
15: PetscScalar *alpha, *beta;
16: PetscScalar *dXtdF, *dFtdX, *YtdX;
17: PetscBool singlereduction; /* Aggregated reduction implementation */
18: PetscScalar *dXdFmat; /* A matrix of values for dX_i dot dF_j */
19: PetscViewer monitor;
20: PetscReal powell_gamma; /* Powell angle restart condition */
21: PetscReal powell_downhill; /* Powell descent restart condition */
22: PetscReal scaling; /* scaling of H0 */
23: SNESQNType type; /* the type of quasi-newton method used */
24: SNESQNScaleType scale_type; /* the type of scaling used */
25: SNESQNRestartType restart_type; /* determine the frequency and type of restart conditions */
26: } SNES_QN;
30: PetscErrorCode SNESQNApply_Broyden(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D)
31: {
32: PetscErrorCode ierr;
33: SNES_QN *qn = (SNES_QN*)snes->data;
34: Vec W = snes->work[3];
35: Vec *U = qn->U;
36: KSPConvergedReason kspreason;
37: PetscInt m = qn->m;
38: PetscInt k,i,j,lits,l = m;
39: PetscReal unorm,a,b;
40: PetscReal *lambda=qn->lambda;
41: PetscScalar gdot;
42: PetscReal udot;
45: if (it < m) l = it;
46: if (it > 0) {
47: k = (it-1)%l;
48: SNESLineSearchGetLambda(snes->linesearch,&lambda[k]);
49: VecCopy(Xold,U[k]);
50: VecAXPY(U[k],-1.0,X);
51: if (qn->monitor) {
52: PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
53: PetscViewerASCIIPrintf(qn->monitor, "scaling vector %d of %d by lambda: %14.12e \n",k,l,lambda[k]);
54: PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
55: }
56: }
57: if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
58: KSPSolve(snes->ksp,D,W);
59: KSPGetConvergedReason(snes->ksp,&kspreason);
60: if (kspreason < 0) {
61: if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
62: PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
63: snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
64: return(0);
65: }
66: }
67: KSPGetIterationNumber(snes->ksp,&lits);
68: snes->linear_its += lits;
69: VecCopy(W,Y);
70: } else {
71: VecCopy(D,Y);
72: VecScale(Y,qn->scaling);
73: }
75: /* inward recursion starting at the first update and working forward */
76: for (i = 0; i < l-1; i++) {
77: j = (it+i-l)%l;
78: k = (it+i-l+1)%l;
79: VecNorm(U[j],NORM_2,&unorm);
80: VecDot(U[j],Y,&gdot);
81: unorm *= unorm;
82: udot = PetscRealPart(gdot);
83: a = (lambda[j]/lambda[k]);
84: b = -(1.-lambda[j]);
85: a *= udot/unorm;
86: b *= udot/unorm;
87: VecAXPBYPCZ(Y,a,b,1.,U[k],U[j]);
89: if (qn->monitor) {
90: PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
91: PetscViewerASCIIPrintf(qn->monitor, "using vector %d and %d, gdot: %14.12e\n",k,j,PetscRealPart(gdot));
92: PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
93: }
94: }
95: if (l > 0) {
96: k = (it-1)%l;
97: VecDot(U[k],Y,&gdot);
98: VecNorm(U[k],NORM_2,&unorm);
99: unorm *= unorm;
100: udot = PetscRealPart(gdot);
101: a = unorm/(unorm-lambda[k]*udot);
102: b = -(1.-lambda[k])*udot/(unorm-lambda[k]*udot);
103: if (qn->monitor) {
104: PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
105: PetscViewerASCIIPrintf(qn->monitor, "using vector %d: a: %14.12e b: %14.12e \n",k,a,b);
106: PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
107: }
108: VecAXPBY(Y,b,a,U[k]);
109: }
110: l = m;
111: if (it+1<m)l=it+1;
112: k = it%l;
113: if (qn->monitor) {
114: PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
115: PetscViewerASCIIPrintf(qn->monitor, "setting vector %d of %d\n",k,l);
116: PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
117: }
118: return(0);
119: }
123: PetscErrorCode SNESQNApply_BadBroyden(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold)
124: {
126: SNES_QN *qn = (SNES_QN*)snes->data;
127: Vec W = snes->work[3];
128: Vec *U = qn->U;
129: Vec *T = qn->V;
131: /* ksp thing for jacobian scaling */
132: KSPConvergedReason kspreason;
133: PetscInt h,k,j,i,lits;
134: PetscInt m = qn->m;
135: PetscScalar gdot,udot;
136: PetscInt l = m;
139: if (it < m) l = it;
140: VecCopy(D,Y);
141: if (l > 0) {
142: k = (it-1)%l;
143: SNESLineSearchGetLambda(snes->linesearch,&qn->lambda[k]);
144: VecCopy(Dold,U[k]);
145: VecAXPY(U[k],-1.0,D);
146: VecCopy(Xold,T[k]);
147: VecAXPY(T[k],-1.0,X);
148: }
150: if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
151: KSPSolve(snes->ksp,Y,W);
152: KSPGetConvergedReason(snes->ksp,&kspreason);
153: if (kspreason < 0) {
154: if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
155: PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
156: snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
157: return(0);
158: }
159: }
160: KSPGetIterationNumber(snes->ksp,&lits);
161: snes->linear_its += lits;
162: VecCopy(W,Y);
163: } else {
164: VecScale(Y,qn->scaling);
165: }
167: /* inward recursion starting at the first update and working forward */
168: if (l > 0) {
169: for (i = 0; i < l-1; i++) {
170: j = (it+i-l)%l;
171: k = (it+i-l+1)%l;
172: h = (it-1)%l;
173: VecDotBegin(U[j],U[h],&gdot);
174: VecDotBegin(U[j],U[j],&udot);
175: VecDotEnd(U[j],U[h],&gdot);
176: VecDotEnd(U[j],U[j],&udot);
177: VecAXPY(Y,PetscRealPart(gdot)/PetscRealPart(udot),T[k]);
178: VecAXPY(Y,-(1.-qn->lambda[k])*PetscRealPart(gdot)/PetscRealPart(udot),T[j]);
179: if (qn->monitor) {
180: PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
181: PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));
182: PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
183: }
184: }
185: }
186: return(0);
187: }
191: PetscErrorCode SNESQNApply_LBFGS(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold)
192: {
194: SNES_QN *qn = (SNES_QN*)snes->data;
195: Vec W = snes->work[3];
196: Vec *dX = qn->U;
197: Vec *dF = qn->V;
198: PetscScalar *alpha = qn->alpha;
199: PetscScalar *beta = qn->beta;
200: PetscScalar *dXtdF = qn->dXtdF;
201: PetscScalar *dFtdX = qn->dFtdX;
202: PetscScalar *YtdX = qn->YtdX;
204: /* ksp thing for jacobian scaling */
205: KSPConvergedReason kspreason;
206: PetscInt k,i,j,g,lits;
207: PetscInt m = qn->m;
208: PetscScalar t;
209: PetscInt l = m;
212: if (it < m) l = it;
213: VecCopy(D,Y);
214: if (it > 0) {
215: k = (it - 1) % l;
216: VecCopy(D,dF[k]);
217: VecAXPY(dF[k], -1.0, Dold);
218: VecCopy(X, dX[k]);
219: VecAXPY(dX[k], -1.0, Xold);
220: if (qn->singlereduction) {
221: VecMDotBegin(dF[k],l,dX,dXtdF);
222: VecMDotBegin(dX[k],l,dF,dFtdX);
223: VecMDotBegin(Y,l,dX,YtdX);
224: VecMDotEnd(dF[k],l,dX,dXtdF);
225: VecMDotEnd(dX[k],l,dF,dFtdX);
226: VecMDotEnd(Y,l,dX,YtdX);
227: for (j = 0; j < l; j++) {
228: H(k, j) = dFtdX[j];
229: H(j, k) = dXtdF[j];
230: }
231: /* copy back over to make the computation of alpha and beta easier */
232: for (j = 0; j < l; j++) dXtdF[j] = H(j, j);
233: } else {
234: VecDot(dX[k], dF[k], &dXtdF[k]);
235: }
236: if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) {
237: SNESLineSearchGetLambda(snes->linesearch,&qn->scaling);
238: }
239: }
241: /* outward recursion starting at iteration k's update and working back */
242: for (i=0; i<l; i++) {
243: k = (it-i-1)%l;
244: if (qn->singlereduction) {
245: /* construct t = dX[k] dot Y as Y_0 dot dX[k] + sum(-alpha[j]dX[k]dF[j]) */
246: t = YtdX[k];
247: for (j=0; j<i; j++) {
248: g = (it-j-1)%l;
249: t -= alpha[g]*H(k, g);
250: }
251: alpha[k] = t/H(k,k);
252: } else {
253: VecDot(dX[k],Y,&t);
254: alpha[k] = t/dXtdF[k];
255: }
256: if (qn->monitor) {
257: PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
258: PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha: %14.12e\n", it, k, PetscRealPart(alpha[k]));
259: PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
260: }
261: VecAXPY(Y,-alpha[k],dF[k]);
262: }
264: if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
265: KSPSolve(snes->ksp,Y,W);
266: KSPGetConvergedReason(snes->ksp,&kspreason);
267: if (kspreason < 0) {
268: if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
269: PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
270: snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
271: return(0);
272: }
273: }
274: KSPGetIterationNumber(snes->ksp,&lits);
275: snes->linear_its += lits;
276: VecCopy(W, Y);
277: } else {
278: VecScale(Y, qn->scaling);
279: }
280: if (qn->singlereduction) {
281: VecMDot(Y,l,dF,YtdX);
282: }
283: /* inward recursion starting at the first update and working forward */
284: for (i = 0; i < l; i++) {
285: k = (it + i - l) % l;
286: if (qn->singlereduction) {
287: t = YtdX[k];
288: for (j = 0; j < i; j++) {
289: g = (it + j - l) % l;
290: t += (alpha[g] - beta[g])*H(g, k);
291: }
292: beta[k] = t / H(k, k);
293: } else {
294: VecDot(dF[k], Y, &t);
295: beta[k] = t / dXtdF[k];
296: }
297: VecAXPY(Y, (alpha[k] - beta[k]), dX[k]);
298: if (qn->monitor) {
299: PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
300: PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha - beta: %14.12e\n", it, k, PetscRealPart(alpha[k] - beta[k]));
301: PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
302: }
303: }
304: return(0);
305: }
309: static PetscErrorCode SNESSolve_QN(SNES snes)
310: {
311: PetscErrorCode ierr;
312: SNES_QN *qn = (SNES_QN*) snes->data;
313: Vec X,Xold;
314: Vec F,W;
315: Vec Y,D,Dold;
316: PetscInt i, i_r;
317: PetscReal fnorm,xnorm,ynorm,gnorm;
318: PetscBool lssucceed,powell,periodic;
319: PetscScalar DolddotD,DolddotDold;
320: MatStructure flg = DIFFERENT_NONZERO_PATTERN;
321: SNESConvergedReason reason;
323: /* basically just a regular newton's method except for the application of the jacobian */
326: F = snes->vec_func; /* residual vector */
327: Y = snes->vec_sol_update; /* search direction generated by J^-1D*/
328: W = snes->work[3];
329: X = snes->vec_sol; /* solution vector */
330: Xold = snes->work[0];
332: /* directions generated by the preconditioned problem with F_pre = F or x - M(x, b) */
333: D = snes->work[1];
334: Dold = snes->work[2];
336: snes->reason = SNES_CONVERGED_ITERATING;
338: PetscObjectSAWsTakeAccess((PetscObject)snes);
339: snes->iter = 0;
340: snes->norm = 0.;
341: PetscObjectSAWsGrantAccess((PetscObject)snes);
343: if (snes->pc && snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_PRECONDITIONED) {
344: SNESApplyPC(snes,X,NULL,NULL,F);
345: SNESGetConvergedReason(snes->pc,&reason);
346: if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
347: snes->reason = SNES_DIVERGED_INNER;
348: return(0);
349: }
350: VecNorm(F,NORM_2,&fnorm);
351: } else {
352: if (!snes->vec_func_init_set) {
353: SNESComputeFunction(snes,X,F);
354: if (snes->domainerror) {
355: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
356: return(0);
357: }
358: } else snes->vec_func_init_set = PETSC_FALSE;
360: VecNorm(F,NORM_2,&fnorm);
361: if (PetscIsInfOrNanReal(fnorm)) {
362: snes->reason = SNES_DIVERGED_FNORM_NAN;
363: return(0);
364: }
365: }
366: if (snes->pc && snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
367: SNESApplyPC(snes,X,F,&fnorm,D);
368: SNESGetConvergedReason(snes->pc,&reason);
369: if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
370: snes->reason = SNES_DIVERGED_INNER;
371: return(0);
372: }
373: } else {
374: VecCopy(F,D);
375: }
377: PetscObjectSAWsTakeAccess((PetscObject)snes);
378: snes->norm = fnorm;
379: PetscObjectSAWsGrantAccess((PetscObject)snes);
380: SNESLogConvergenceHistory(snes,fnorm,0);
381: SNESMonitor(snes,0,fnorm);
383: /* test convergence */
384: (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);
385: if (snes->reason) return(0);
387: if (snes->pc && snes->pcside == PC_RIGHT) {
388: PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,0,0);
389: SNESSolve(snes->pc,snes->vec_rhs,X);
390: PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,0,0);
391: SNESGetConvergedReason(snes->pc,&reason);
392: if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
393: snes->reason = SNES_DIVERGED_INNER;
394: return(0);
395: }
396: SNESGetPCFunction(snes,F,&fnorm);
397: VecCopy(F,D);
398: }
400: /* scale the initial update */
401: if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
402: SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
403: KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);
404: }
406: for (i = 0, i_r = 0; i < snes->max_its; i++, i_r++) {
407: if (qn->scale_type == SNES_QN_SCALE_SHANNO && i_r > 0) {
408: PetscScalar ff,xf;
409: VecCopy(Dold,Y);
410: VecCopy(Xold,W);
411: VecAXPY(Y,-1.0,D);
412: VecAXPY(W,-1.0,X);
413: VecDotBegin(Y,Y,&ff);
414: VecDotBegin(W,Y,&xf);
415: VecDotEnd(Y,Y,&ff);
416: VecDotEnd(W,Y,&xf);
417: qn->scaling = PetscRealPart(xf)/PetscRealPart(ff);
418: }
419: switch (qn->type) {
420: case SNES_QN_BADBROYDEN:
421: SNESQNApply_BadBroyden(snes,i_r,Y,X,Xold,D,Dold);
422: break;
423: case SNES_QN_BROYDEN:
424: SNESQNApply_Broyden(snes,i_r,Y,X,Xold,D);
425: break;
426: case SNES_QN_LBFGS:
427: SNESQNApply_LBFGS(snes,i_r,Y,X,Xold,D,Dold);
428: break;
429: }
430: /* line search for lambda */
431: ynorm = 1; gnorm = fnorm;
432: VecCopy(D, Dold);
433: VecCopy(X, Xold);
434: SNESLineSearchApply(snes->linesearch, X, F, &fnorm, Y);
435: if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
436: if (snes->domainerror) {
437: snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
438: return(0);
439: }
440: SNESLineSearchGetSuccess(snes->linesearch, &lssucceed);
441: if (!lssucceed) {
442: if (++snes->numFailures >= snes->maxFailures) {
443: snes->reason = SNES_DIVERGED_LINE_SEARCH;
444: break;
445: }
446: }
447: SNESLineSearchGetNorms(snes->linesearch, &xnorm, &fnorm, &ynorm);
448: if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) {
449: SNESLineSearchGetLambda(snes->linesearch, &qn->scaling);
450: }
452: /* convergence monitoring */
453: PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",(double)fnorm,(double)gnorm,(double)ynorm,(int)lssucceed);
455: if (snes->pc && snes->pcside == PC_RIGHT) {
456: PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,0,0);
457: SNESSolve(snes->pc,snes->vec_rhs,X);
458: PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,0,0);
459: SNESGetConvergedReason(snes->pc,&reason);
460: if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
461: snes->reason = SNES_DIVERGED_INNER;
462: return(0);
463: }
464: SNESGetPCFunction(snes,F,&fnorm);
465: }
467: SNESSetIterationNumber(snes, i+1);
468: SNESSetFunctionNorm(snes, fnorm);
470: SNESLogConvergenceHistory(snes,snes->norm,snes->iter);
471: SNESMonitor(snes,snes->iter,snes->norm);
472: /* set parameter for default relative tolerance convergence test */
473: (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);
474: if (snes->reason) return(0);
475: if (snes->pc && snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
476: SNESApplyPC(snes,X,F,&fnorm,D);
477: SNESGetConvergedReason(snes->pc,&reason);
478: if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
479: snes->reason = SNES_DIVERGED_INNER;
480: return(0);
481: }
482: } else {
483: VecCopy(F, D);
484: }
485: powell = PETSC_FALSE;
486: if (qn->restart_type == SNES_QN_RESTART_POWELL) {
487: /* check restart by Powell's Criterion: |F^T H_0 Fold| > 0.2 * |Fold^T H_0 Fold| */
488: if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
489: MatMult(snes->jacobian_pre,Dold,W);
490: } else {
491: VecCopy(Dold,W);
492: }
493: VecDotBegin(W, Dold, &DolddotDold);
494: VecDotBegin(W, D, &DolddotD);
495: VecDotEnd(W, Dold, &DolddotDold);
496: VecDotEnd(W, D, &DolddotD);
497: if (PetscAbs(PetscRealPart(DolddotD)) > qn->powell_gamma*PetscAbs(PetscRealPart(DolddotDold))) powell = PETSC_TRUE;
498: }
499: periodic = PETSC_FALSE;
500: if (qn->restart_type == SNES_QN_RESTART_PERIODIC) {
501: if (i_r>qn->m-1) periodic = PETSC_TRUE;
502: }
503: /* restart if either powell or periodic restart is satisfied. */
504: if (powell || periodic) {
505: if (qn->monitor) {
506: PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
507: PetscViewerASCIIPrintf(qn->monitor, "restart! |%14.12e| > %4.2f*|%14.12e| or i_r = %d\n", PetscRealPart(DolddotD), qn->powell_gamma, PetscRealPart(DolddotDold), i_r);
508: PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
509: }
510: i_r = -1;
511: /* general purpose update */
512: if (snes->ops->update) {
513: (*snes->ops->update)(snes, snes->iter);
514: }
515: if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
516: SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
517: KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);
518: }
519: }
520: /* general purpose update */
521: if (snes->ops->update) {
522: (*snes->ops->update)(snes, snes->iter);
523: }
524: }
525: if (i == snes->max_its) {
526: PetscInfo1(snes, "Maximum number of iterations has been reached: %D\n", snes->max_its);
527: if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
528: }
529: return(0);
530: }
534: static PetscErrorCode SNESSetUp_QN(SNES snes)
535: {
536: SNES_QN *qn = (SNES_QN*)snes->data;
540: VecDuplicateVecs(snes->vec_sol, qn->m, &qn->U);
541: if (qn->type != SNES_QN_BROYDEN) VecDuplicateVecs(snes->vec_sol, qn->m, &qn->V);
542: PetscMalloc4(qn->m,&qn->alpha,qn->m,&qn->beta,qn->m,&qn->dXtdF,qn->m,&qn->lambda);
544: if (qn->singlereduction) {
545: PetscMalloc3(qn->m*qn->m,&qn->dXdFmat,qn->m,&qn->dFtdX,qn->m,&qn->YtdX);
546: }
547: SNESSetWorkVecs(snes,4);
548: /* set method defaults */
549: if (qn->scale_type == SNES_QN_SCALE_DEFAULT) {
550: if (qn->type == SNES_QN_BADBROYDEN) {
551: qn->scale_type = SNES_QN_SCALE_NONE;
552: } else {
553: qn->scale_type = SNES_QN_SCALE_SHANNO;
554: }
555: }
556: if (qn->restart_type == SNES_QN_RESTART_DEFAULT) {
557: if (qn->type == SNES_QN_LBFGS) {
558: qn->restart_type = SNES_QN_RESTART_POWELL;
559: } else {
560: qn->restart_type = SNES_QN_RESTART_PERIODIC;
561: }
562: }
564: if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
565: SNESSetUpMatrices(snes);
566: }
567: if (snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_DEFAULT) {snes->functype = SNES_FUNCTION_UNPRECONDITIONED;}
568: return(0);
569: }
573: static PetscErrorCode SNESReset_QN(SNES snes)
574: {
576: SNES_QN *qn;
579: if (snes->data) {
580: qn = (SNES_QN*)snes->data;
581: if (qn->U) {
582: VecDestroyVecs(qn->m, &qn->U);
583: }
584: if (qn->V) {
585: VecDestroyVecs(qn->m, &qn->V);
586: }
587: if (qn->singlereduction) {
588: PetscFree3(qn->dXdFmat, qn->dFtdX, qn->YtdX);
589: }
590: PetscFree4(qn->alpha,qn->beta,qn->dXtdF,qn->lambda);
591: }
592: return(0);
593: }
597: static PetscErrorCode SNESDestroy_QN(SNES snes)
598: {
602: SNESReset_QN(snes);
603: PetscFree(snes->data);
604: PetscObjectComposeFunction((PetscObject)snes,"",NULL);
605: return(0);
606: }
610: static PetscErrorCode SNESSetFromOptions_QN(SNES snes)
611: {
613: PetscErrorCode ierr;
614: SNES_QN *qn = (SNES_QN*)snes->data;
615: PetscBool monflg = PETSC_FALSE,flg;
616: SNESLineSearch linesearch;
617: SNESQNRestartType rtype = qn->restart_type;
618: SNESQNScaleType stype = qn->scale_type;
619: SNESQNType qtype = qn->type;
622: PetscOptionsHead("SNES QN options");
623: PetscOptionsInt("-snes_qn_m","Number of past states saved for L-BFGS methods","SNESQN",qn->m,&qn->m,NULL);
624: PetscOptionsReal("-snes_qn_powell_gamma","Powell angle tolerance", "SNESQN", qn->powell_gamma, &qn->powell_gamma, NULL);
625: PetscOptionsReal("-snes_qn_powell_downhill","Powell descent tolerance", "SNESQN", qn->powell_downhill, &qn->powell_downhill, NULL);
626: PetscOptionsBool("-snes_qn_monitor", "Monitor for the QN methods", "SNESQN", monflg, &monflg, NULL);
627: PetscOptionsBool("-snes_qn_single_reduction", "Aggregate reductions", "SNESQN", qn->singlereduction, &qn->singlereduction, NULL);
628: PetscOptionsEnum("-snes_qn_scale_type","Scaling type","SNESQNSetScaleType",SNESQNScaleTypes,(PetscEnum)stype,(PetscEnum*)&stype,&flg);
629: if (flg) SNESQNSetScaleType(snes,stype);
631: PetscOptionsEnum("-snes_qn_restart_type","Restart type","SNESQNSetRestartType",SNESQNRestartTypes,(PetscEnum)rtype,(PetscEnum*)&rtype,&flg);
632: if (flg) SNESQNSetRestartType(snes,rtype);
634: PetscOptionsEnum("-snes_qn_type","Quasi-Newton update type","",SNESQNTypes,
635: (PetscEnum)qtype,(PetscEnum*)&qtype,&flg);
636: if (flg) {SNESQNSetType(snes,qtype);}
637: PetscOptionsTail();
638: if (!snes->linesearch) {
639: SNESGetLineSearch(snes, &linesearch);
640: if (qn->type == SNES_QN_LBFGS) {
641: SNESLineSearchSetType(linesearch, SNESLINESEARCHCP);
642: } else if (qn->type == SNES_QN_BROYDEN) {
643: SNESLineSearchSetType(linesearch, SNESLINESEARCHBASIC);
644: } else {
645: SNESLineSearchSetType(linesearch, SNESLINESEARCHL2);
646: }
647: }
648: if (monflg) {
649: qn->monitor = PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)snes));
650: }
651: return(0);
652: }
656: static PetscErrorCode SNESView_QN(SNES snes, PetscViewer viewer)
657: {
658: SNES_QN *qn = (SNES_QN*)snes->data;
659: PetscBool iascii;
663: PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii);
664: if (iascii) {
665: PetscViewerASCIIPrintf(viewer," QN type is %s, restart type is %s, scale type is %s\n",SNESQNTypes[qn->type],SNESQNRestartTypes[qn->restart_type],SNESQNScaleTypes[qn->scale_type]);
666: PetscViewerASCIIPrintf(viewer," Stored subspace size: %d\n", qn->m);
667: if (qn->singlereduction) {
668: PetscViewerASCIIPrintf(viewer," Using the single reduction variant.\n");
669: }
670: }
671: return(0);
672: }
676: /*@
677: SNESQNSetRestartType - Sets the restart type for SNESQN.
679: Logically Collective on SNES
681: Input Parameters:
682: + snes - the iterative context
683: - rtype - restart type
685: Options Database:
686: + -snes_qn_restart_type<powell,periodic,none> - set the restart type
687: - -snes_qn_restart[10] - sets the number of iterations before restart for periodic
689: Level: intermediate
691: SNESQNRestartTypes:
692: + SNES_QN_RESTART_NONE - never restart
693: . SNES_QN_RESTART_POWELL - restart based upon descent criteria
694: - SNES_QN_RESTART_PERIODIC - restart after a fixed number of iterations
696: Notes:
697: The default line search used is the L2 line search and it requires two additional function evaluations.
699: .keywords: SNES, SNESQN, restart, type, set SNESLineSearch
700: @*/
701: PetscErrorCode SNESQNSetRestartType(SNES snes, SNESQNRestartType rtype)
702: {
707: PetscTryMethod(snes,"SNESQNSetRestartType_C",(SNES,SNESQNRestartType),(snes,rtype));
708: return(0);
709: }
713: /*@
714: SNESQNSetScaleType - Sets the scaling type for the inner inverse jacobian in SNESQN.
716: Logically Collective on SNES
718: Input Parameters:
719: + snes - the iterative context
720: - stype - scale type
722: Options Database:
723: . -snes_qn_scale_type<shanno,none,linesearch,jacobian>
725: Level: intermediate
727: SNESQNSelectTypes:
728: + SNES_QN_SCALE_NONE - don't scale the problem
729: . SNES_QN_SCALE_SHANNO - use shanno scaling
730: . SNES_QN_SCALE_LINESEARCH - scale based upon line search lambda
731: - SNES_QN_SCALE_JACOBIAN - scale by inverting a previously computed Jacobian.
733: .keywords: SNES, SNESQN, scaling, type, set SNESLineSearch
734: @*/
736: PetscErrorCode SNESQNSetScaleType(SNES snes, SNESQNScaleType stype)
737: {
742: PetscTryMethod(snes,"SNESQNSetScaleType_C",(SNES,SNESQNScaleType),(snes,stype));
743: return(0);
744: }
748: PetscErrorCode SNESQNSetScaleType_QN(SNES snes, SNESQNScaleType stype)
749: {
750: SNES_QN *qn = (SNES_QN*)snes->data;
753: qn->scale_type = stype;
754: return(0);
755: }
759: PetscErrorCode SNESQNSetRestartType_QN(SNES snes, SNESQNRestartType rtype)
760: {
761: SNES_QN *qn = (SNES_QN*)snes->data;
764: qn->restart_type = rtype;
765: return(0);
766: }
770: /*@
771: SNESQNSetType - Sets the quasi-Newton variant to be used in SNESQN.
773: Logically Collective on SNES
775: Input Parameters:
776: + snes - the iterative context
777: - qtype - variant type
779: Options Database:
780: . -snes_qn_scale_type<lbfgs,broyden,badbroyden>
782: Level: beginner
784: SNESQNTypes:
785: + SNES_QN_LBFGS - LBFGS variant
786: . SNES_QN_BROYDEN - Broyden variant
787: - SNES_QN_BADBROYDEN - Bad Broyden variant
789: .keywords: SNES, SNESQN, type, set
790: @*/
792: PetscErrorCode SNESQNSetType(SNES snes, SNESQNType qtype)
793: {
798: PetscTryMethod(snes,"SNESQNSetType_C",(SNES,SNESQNType),(snes,qtype));
799: return(0);
800: }
804: PetscErrorCode SNESQNSetType_QN(SNES snes, SNESQNType qtype)
805: {
806: SNES_QN *qn = (SNES_QN*)snes->data;
809: qn->type = qtype;
810: return(0);
811: }
813: /* -------------------------------------------------------------------------- */
814: /*MC
815: SNESQN - Limited-Memory Quasi-Newton methods for the solution of nonlinear systems.
817: Options Database:
819: + -snes_qn_m - Number of past states saved for the L-Broyden methods.
820: . -snes_qn_powell_angle - Angle condition for restart.
821: . -snes_qn_powell_descent - Descent condition for restart.
822: . -snes_linesearch_type <cp, l2, basic> - Type of line search.
823: - -snes_qn_monitor - Monitors the quasi-newton jacobian.
825: Notes: This implements the L-BFGS, Broyden, and "Bad" Broyden algorithms for the solution of F(x) = b using
826: previous change in F(x) and x to form the approximate inverse Jacobian using a series of multiplicative rank-one
827: updates.
829: When using a nonlinear preconditioner, one has two options as to how the preconditioner is applied. The first of
830: these options, sequential, uses the preconditioner to generate a new solution and function and uses those at this
831: iteration as the current iteration's values when constructing the approximate jacobian. The second, composed,
832: perturbs the problem the jacobian represents to be P(x, b) - x = 0, where P(x, b) is the preconditioner.
834: References:
836: Kelley, C.T., Iterative Methods for Linear and Nonlinear Equations, Chapter 8, SIAM, 1995.
838: R. Byrd, J. Nocedal, R. Schnabel, Representations of Quasi-Newton Matrices and their use in Limited Memory Methods,
839: Technical Report, Northwestern University, June 1992.
841: Peter N. Brown, Alan C. Hindmarsh, Homer F. Walker, Experiments with Quasi-Newton Methods in Solving Stiff ODE
842: Systems, SIAM J. Sci. Stat. Comput. Vol 6(2), April 1985.
845: Level: beginner
847: .seealso: SNESCreate(), SNES, SNESSetType(), SNESNEWTONLS, SNESNEWTONTR
849: M*/
852: PETSC_EXTERN PetscErrorCode SNESCreate_QN(SNES snes)
853: {
855: SNES_QN *qn;
858: snes->ops->setup = SNESSetUp_QN;
859: snes->ops->solve = SNESSolve_QN;
860: snes->ops->destroy = SNESDestroy_QN;
861: snes->ops->setfromoptions = SNESSetFromOptions_QN;
862: snes->ops->view = SNESView_QN;
863: snes->ops->reset = SNESReset_QN;
865: snes->pcside = PC_LEFT;
867: snes->usespc = PETSC_TRUE;
868: snes->usesksp = PETSC_FALSE;
870: if (!snes->tolerancesset) {
871: snes->max_funcs = 30000;
872: snes->max_its = 10000;
873: }
875: PetscNewLog(snes,&qn);
876: snes->data = (void*) qn;
877: qn->m = 10;
878: qn->scaling = 1.0;
879: qn->U = NULL;
880: qn->V = NULL;
881: qn->dXtdF = NULL;
882: qn->dFtdX = NULL;
883: qn->dXdFmat = NULL;
884: qn->monitor = NULL;
885: qn->singlereduction = PETSC_TRUE;
886: qn->powell_gamma = 0.9999;
887: qn->scale_type = SNES_QN_SCALE_DEFAULT;
888: qn->restart_type = SNES_QN_RESTART_DEFAULT;
889: qn->type = SNES_QN_LBFGS;
891: PetscObjectComposeFunction((PetscObject)snes,"SNESQNSetScaleType_C",SNESQNSetScaleType_QN);
892: PetscObjectComposeFunction((PetscObject)snes,"SNESQNSetRestartType_C",SNESQNSetRestartType_QN);
893: PetscObjectComposeFunction((PetscObject)snes,"SNESQNSetType_C",SNESQNSetType_QN);
894: return(0);
895: }