Actual source code: qn.c

petsc-dev 2014-02-02
Report Typos and Errors
  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: }