Date: 2013-07-02T14:49:31+00:00

Actual source code: qn.c
petsc-3.4.2 2013-07-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[] =        {"NONE","SHANNO","LINESEARCH","JACOBIAN","SNESQNScaleType","SNES_QN_SCALING_",0};
      6: const char *const SNESQNRestartTypes[] =      {"NONE","POWELL","PERIODIC","SNESQNRestartType","SNES_QN_RESTART_",0};
      7: const char *const SNESQNTypes[] =             {"LBFGS","BROYDEN","BADBROYDEN","SNESQNType","SNES_QN_RESTART_",0};

      9: typedef enum {SNES_QN_LBFGS      = 0,
     10:               SNES_QN_BROYDEN    = 1,
     11:               SNES_QN_BADBROYDEN = 2
     12:              } SNESQNType;

     14: typedef struct {
     15:   Vec               *U;                   /* Stored past states (vary from method to method) */
     16:   Vec               *V;                   /* Stored past states (vary from method to method) */
     17:   PetscInt          m;                    /* The number of kept previous steps */
     18:   PetscScalar       *alpha, *beta;
     19:   PetscScalar       *dXtdF, *dFtdX, *YtdX;
     20:   PetscBool         singlereduction;      /* Aggregated reduction implementation */
     21:   PetscScalar       *dXdFmat;             /* A matrix of values for dX_i dot dF_j */
     22:   PetscViewer       monitor;
     23:   PetscReal         powell_gamma;         /* Powell angle restart condition */
     24:   PetscReal         powell_downhill;      /* Powell descent restart condition */
     25:   PetscReal         scaling;              /* scaling of H0 */
     26:   SNESQNType        type;                 /* the type of quasi-newton method used */
     27:   SNESQNScaleType   scale_type;           /* the type of scaling used */
     28:   SNESQNRestartType restart_type;         /* determine the frequency and type of restart conditions */
     29: } SNES_QN;

     33: PetscErrorCode SNESQNApply_Broyden(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold, Vec D, Vec Dold)
     34: {
     35:   PetscErrorCode     ierr;
     36:   SNES_QN            *qn = (SNES_QN*)snes->data;
     37:   Vec                W   = snes->work[3];
     38:   Vec                *U  = qn->U;
     39:   Vec                *V  = qn->V;
     40:   KSPConvergedReason kspreason;
     41:   PetscInt           k,i,lits;
     42:   PetscInt           m = qn->m;
     43:   PetscScalar        gdot;
     44:   PetscInt           l = m;

     47:   if (it < m) l = it;
     48:   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
     49:     KSPSolve(snes->ksp,D,W);
     50:     KSPGetConvergedReason(snes->ksp,&kspreason);
     51:     if (kspreason < 0) {
     52:       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
     53:         PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
     54:         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
     55:         return(0);
     56:       }
     57:     }
     58:     KSPGetIterationNumber(snes->ksp,&lits);
     59:     snes->linear_its += lits;
     60:     VecCopy(W,Y);
     61:   } else {
     62:     VecCopy(D,Y);
     63:     VecScale(Y,qn->scaling);
     64:   }

     66:   /* inward recursion starting at the first update and working forward */
     67:   if (it > 1) {
     68:     for (i = 0; i < l-1; i++) {
     69:       k    = (it+i-l)%l;
     70:       VecDot(U[k],Y,&gdot);
     71:       VecAXPY(Y,gdot,V[k]);
     72:       if (qn->monitor) {
     73:         PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
     74:         PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));
     75:         PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
     76:       }
     77:     }
     78:   }
     79:   if (it < m) l = it;

     81:   /* set W to be the last step, post-linesearch */
     82:   VecCopy(Xold,W);
     83:   VecAXPY(W,-1.0,X);

     85:   if (l > 0) {
     86:     k    = (it-1)%l;
     87:     VecCopy(W,U[k]);
     88:     VecAXPY(W,-1.0,Y);
     89:     VecDot(U[k],W,&gdot);
     90:     VecCopy(Y,V[k]);
     91:     VecScale(V[k],1.0/gdot);
     92:     VecDot(U[k],Y,&gdot);
     93:     VecAXPY(Y,gdot,V[k]);
     94:     if (qn->monitor) {
     95:       PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
     96:       PetscViewerASCIIPrintf(qn->monitor, "update: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));
     97:       PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
     98:     }
     99:   }
    100:   return(0);
    101: }

    105: PetscErrorCode SNESQNApply_BadBroyden(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold)
    106: {
    108:   SNES_QN        *qn = (SNES_QN*)snes->data;
    109:   Vec            W   = snes->work[3];
    110:   Vec            *U  = qn->U;
    111:   Vec            *T  = qn->V;

    113:   /* ksp thing for jacobian scaling */
    114:   KSPConvergedReason kspreason;
    115:   PetscInt           k, i, lits;
    116:   PetscInt           m = qn->m;
    117:   PetscScalar        gdot;
    118:   PetscInt           l = m;

    121:   if (it < m) l = it;
    122:   VecCopy(D,Y);
    123:   if (l > 0) {
    124:     k    = (it-1)%l;
    125:     VecCopy(Dold,U[k]);
    126:     VecAXPY(U[k],-1.0,D);
    127:     VecDot(U[k],U[k],&gdot);
    128:     VecCopy(D,T[k]);
    129:     VecScale(T[k],1.0/gdot);
    130:     VecDot(U[k],Y,&gdot);
    131:     VecAXPY(Y,gdot,T[k]);
    132:     if (qn->monitor) {
    133:       PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
    134:       PetscViewerASCIIPrintf(qn->monitor, "update: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));
    135:       PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
    136:     }
    137:   }

    139:   /* inward recursion starting at the first update and working forward */
    140:   if (it > 1) {
    141:     for (i = 0; i < l-1; i++) {
    142:       k    = (it+i-l)%l;
    143:       VecDot(U[k],Y,&gdot);
    144:       VecAXPY(Y,gdot,T[k]);
    145:       if (qn->monitor) {
    146:         PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
    147:         PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));
    148:         PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
    149:       }
    150:     }
    151:   }

    153:   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
    154:     KSPSolve(snes->ksp,Y,W);
    155:     KSPGetConvergedReason(snes->ksp,&kspreason);
    156:     if (kspreason < 0) {
    157:       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
    158:         PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
    159:         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
    160:         return(0);
    161:       }
    162:     }
    163:     KSPGetIterationNumber(snes->ksp,&lits);
    164:     snes->linear_its += lits;
    165:     VecCopy(W,Y);
    166:   } else {
    167:     VecScale(Y,qn->scaling);
    168:   }
    169:   return(0);
    170: }

    174: PetscErrorCode SNESQNApply_LBFGS(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold)
    175: {
    177:   SNES_QN        *qn    = (SNES_QN*)snes->data;
    178:   Vec            W      = snes->work[3];
    179:   Vec            *dX    = qn->U;
    180:   Vec            *dF    = qn->V;
    181:   PetscScalar    *alpha = qn->alpha;
    182:   PetscScalar    *beta  = qn->beta;
    183:   PetscScalar    *dXtdF = qn->dXtdF;
    184:   PetscScalar    *dFtdX = qn->dFtdX;
    185:   PetscScalar    *YtdX  = qn->YtdX;

    187:   /* ksp thing for jacobian scaling */
    188:   KSPConvergedReason kspreason;
    189:   PetscInt           k,i,j,g,lits;
    190:   PetscInt           m = qn->m;
    191:   PetscScalar        t;
    192:   PetscInt           l = m;

    195:   if (it < m) l = it;
    196:   if (it > 0) {
    197:     k    = (it - 1) % l;
    198:     VecCopy(D, dF[k]);
    199:     VecAXPY(dF[k], -1.0, Dold);
    200:     VecCopy(X, dX[k]);
    201:     VecAXPY(dX[k], -1.0, Xold);
    202:     if (qn->singlereduction) {
    203:       VecMDot(dF[k], l, dX, dXtdF);
    204:       VecMDot(dX[k], l, dF, dFtdX);
    205:       for (j = 0; j < l; j++) {
    206:         H(k, j) = dFtdX[j];
    207:         H(j, k) = dXtdF[j];
    208:       }
    209:       /* copy back over to make the computation of alpha and beta easier */
    210:       for (j = 0; j < l; j++) dXtdF[j] = H(j, j);
    211:     } else {
    212:       VecDot(dX[k], dF[k], &dXtdF[k]);
    213:     }
    214:     if (qn->scale_type == SNES_QN_SCALE_SHANNO) {
    215:       PetscReal dFtdF;
    216:       VecDotRealPart(dF[k],dF[k],&dFtdF);
    217:       qn->scaling = PetscRealPart(dXtdF[k])/dFtdF;
    218:     } else if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) {
    219:       SNESLineSearchGetLambda(snes->linesearch,&qn->scaling);
    220:     }
    221:   }

    223:   VecCopy(D,Y);

    225:   if (qn->singlereduction) {
    226:     VecMDot(Y,l,dX,YtdX);
    227:   }
    228:   /* outward recursion starting at iteration k's update and working back */
    229:   for (i=0; i<l; i++) {
    230:     k = (it-i-1)%l;
    231:     if (qn->singlereduction) {
    232:       /* construct t = dX[k] dot Y as Y_0 dot dX[k] + sum(-alpha[j]dX[k]dF[j]) */
    233:       t = YtdX[k];
    234:       for (j=0; j<i; j++) {
    235:         g  = (it-j-1)%l;
    236:         t += -alpha[g]*H(g, k);
    237:       }
    238:       alpha[k] = t/H(k,k);
    239:     } else {
    240:       VecDot(dX[k],Y,&t);
    241:       alpha[k] = t/dXtdF[k];
    242:     }
    243:     if (qn->monitor) {
    244:       PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
    245:       PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha:        %14.12e\n", it, k, PetscRealPart(alpha[k]));
    246:       PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
    247:     }
    248:     VecAXPY(Y,-alpha[k],dF[k]);
    249:   }

    251:   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
    252:     KSPSolve(snes->ksp,Y,W);
    253:     KSPGetConvergedReason(snes->ksp,&kspreason);
    254:     if (kspreason < 0) {
    255:       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
    256:         PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
    257:         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
    258:         return(0);
    259:       }
    260:     }
    261:     KSPGetIterationNumber(snes->ksp,&lits);
    262:     snes->linear_its += lits;
    263:     VecCopy(W, Y);
    264:   } else {
    265:     VecScale(Y, qn->scaling);
    266:   }
    267:   if (qn->singlereduction) {
    268:     VecMDot(Y,l,dF,YtdX);
    269:   }
    270:   /* inward recursion starting at the first update and working forward */
    271:   for (i = 0; i < l; i++) {
    272:     k = (it + i - l) % l;
    273:     if (qn->singlereduction) {
    274:       t = YtdX[k];
    275:       for (j = 0; j < i; j++) {
    276:         g  = (it + j - l) % l;
    277:         t += (alpha[g] - beta[g])*H(k, g);
    278:       }
    279:       beta[k] = t / H(k, k);
    280:     } else {
    281:       VecDot(dF[k], Y, &t);
    282:       beta[k] = t / dXtdF[k];
    283:     }
    284:     VecAXPY(Y, (alpha[k] - beta[k]), dX[k]);
    285:     if (qn->monitor) {
    286:       PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
    287:       PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha - beta: %14.12e\n", it, k, PetscRealPart(alpha[k] - beta[k]));
    288:       PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
    289:     }
    290:   }
    291:   return(0);
    292: }

    296: static PetscErrorCode SNESSolve_QN(SNES snes)
    297: {
    298:   PetscErrorCode      ierr;
    299:   SNES_QN             *qn = (SNES_QN*) snes->data;
    300:   Vec                 X,Xold;
    301:   Vec                 F,B;
    302:   Vec                 Y,FPC,D,Dold;
    303:   SNESConvergedReason reason;
    304:   PetscInt            i, i_r;
    305:   PetscReal           fnorm,xnorm,ynorm,gnorm;
    306:   PetscBool           lssucceed,powell,periodic;
    307:   PetscScalar         DolddotD,DolddotDold,DdotD,YdotD;
    308:   MatStructure        flg = DIFFERENT_NONZERO_PATTERN;

    310:   /* basically just a regular newton's method except for the application of the jacobian */

    313:   F = snes->vec_func;                   /* residual vector */
    314:   Y = snes->vec_sol_update;             /* search direction generated by J^-1D*/
    315:   B = snes->vec_rhs;

    317:   X    = snes->vec_sol;                 /* solution vector */
    318:   Xold = snes->work[0];

    320:   /* directions generated by the preconditioned problem with F_pre = F or x - M(x, b) */
    321:   D    = snes->work[1];
    322:   Dold = snes->work[2];

    324:   snes->reason = SNES_CONVERGED_ITERATING;

    326:   PetscObjectAMSTakeAccess((PetscObject)snes);
    327:   snes->iter = 0;
    328:   snes->norm = 0.;
    329:   PetscObjectAMSGrantAccess((PetscObject)snes);
    330:   if (!snes->vec_func_init_set) {
    331:     SNESComputeFunction(snes,X,F);
    332:     if (snes->domainerror) {
    333:       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
    334:       return(0);
    335:     }
    336:   } else snes->vec_func_init_set = PETSC_FALSE;

    338:   if (!snes->norm_init_set) {
    339:     VecNorm(F, NORM_2, &fnorm); /* fnorm <- ||F||  */
    340:     if (PetscIsInfOrNanReal(fnorm)) {
    341:       snes->reason = SNES_DIVERGED_FNORM_NAN;
    342:       return(0);
    343:     }
    344:   } else {
    345:     fnorm               = snes->norm_init;
    346:     snes->norm_init_set = PETSC_FALSE;
    347:   }

    349:   PetscObjectAMSTakeAccess((PetscObject)snes);
    350:   snes->norm = fnorm;
    351:   PetscObjectAMSGrantAccess((PetscObject)snes);
    352:   SNESLogConvergenceHistory(snes,fnorm,0);
    353:   SNESMonitor(snes,0,fnorm);

    355:   /* set parameter for default relative tolerance convergence test */
    356:   snes->ttol = fnorm*snes->rtol;
    357:   /* test convergence */
    358:   (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);
    359:   if (snes->reason) return(0);

    361:   /* composed solve */
    362:   if (snes->pc && snes->pcside == PC_RIGHT) {
    363:     SNESSetInitialFunction(snes->pc, F);
    364:     SNESSetInitialFunctionNorm(snes->pc, fnorm);

    366:     PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,B,0);
    367:     SNESSolve(snes->pc, B, X);
    368:     PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,B,0);

    370:     SNESGetConvergedReason(snes->pc,&reason);
    371:     if (reason < 0 && (reason != SNES_DIVERGED_MAX_IT)) {
    372:       snes->reason = SNES_DIVERGED_INNER;
    373:       return(0);
    374:     }
    375:     SNESGetFunction(snes->pc, &FPC, NULL, NULL);
    376:     VecCopy(FPC, F);
    377:     SNESGetFunctionNorm(snes->pc, &fnorm);
    378:     VecCopy(F, Y);
    379:   } else {
    380:     VecCopy(F, Y);
    381:   }
    382:   VecCopy(Y, D);

    384:   /* scale the initial update */
    385:   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
    386:     SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
    387:     KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);
    388:   }

    390:   for (i = 0, i_r = 0; i < snes->max_its; i++, i_r++) {
    391:     switch (qn->type) {
    392:     case SNES_QN_BADBROYDEN:
    393:       SNESQNApply_BadBroyden(snes,i_r,Y,X,Xold,D,Dold);
    394:       break;
    395:     case SNES_QN_BROYDEN:
    396:       SNESQNApply_Broyden(snes,i_r,Y,X,Xold,D,Dold);
    397:       break;
    398:     case SNES_QN_LBFGS:
    399:       SNESQNApply_LBFGS(snes,i_r,Y,X,Xold,D,Dold);
    400:       break;
    401:     }
    402:     /* line search for lambda */
    403:     ynorm = 1; gnorm = fnorm;
    404:     VecCopy(D, Dold);
    405:     VecCopy(X, Xold);
    406:     SNESLineSearchApply(snes->linesearch, X, F, &fnorm, Y);
    407:     if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
    408:     if (snes->domainerror) {
    409:       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
    410:       return(0);
    411:     }
    412:     SNESLineSearchGetSuccess(snes->linesearch, &lssucceed);
    413:     if (!lssucceed) {
    414:       if (++snes->numFailures >= snes->maxFailures) {
    415:         snes->reason = SNES_DIVERGED_LINE_SEARCH;
    416:         break;
    417:       }
    418:     }
    419:     SNESLineSearchGetNorms(snes->linesearch, &xnorm, &fnorm, &ynorm);
    420:     if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) {
    421:       SNESLineSearchGetLambda(snes->linesearch, &qn->scaling);
    422:     }

    424:     /* convergence monitoring */
    425:     PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",(double)fnorm,(double)gnorm,(double)ynorm,(int)lssucceed);

    427:     SNESSetIterationNumber(snes, i+1);
    428:     SNESSetFunctionNorm(snes, fnorm);

    430:     SNESLogConvergenceHistory(snes,snes->norm,snes->iter);
    431:     SNESMonitor(snes,snes->iter,snes->norm);
    432:     /* set parameter for default relative tolerance convergence test */
    433:     (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);
    434:     if (snes->reason) return(0);

    436:     if (snes->pc && snes->pcside == PC_RIGHT) {
    437:       SNESSetInitialFunction(snes->pc, F);
    438:       SNESSetInitialFunctionNorm(snes->pc, fnorm);
    439:       PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,B,0);
    440:       SNESSolve(snes->pc, B, X);
    441:       PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,B,0);
    442:       SNESGetConvergedReason(snes->pc,&reason);
    443:       if (reason < 0 && (reason != SNES_DIVERGED_MAX_IT)) {
    444:         snes->reason = SNES_DIVERGED_INNER;
    445:         return(0);
    446:       }
    447:       SNESGetFunction(snes->pc, &FPC, NULL, NULL);
    448:       VecCopy(FPC, F);
    449:       SNESGetFunctionNorm(snes->pc, &fnorm);
    450:       VecCopy(F, D);
    451:     } else {
    452:       VecCopy(F, D);
    453:     }

    455:     powell = PETSC_FALSE;
    456:     if (qn->restart_type == SNES_QN_RESTART_POWELL) {
    457:       /* check restart by Powell's Criterion: |F^T H_0 Fold| > 0.2 * |Fold^T H_0 Fold| */
    458:       VecDotBegin(Dold, Dold, &DolddotDold);
    459:       VecDotBegin(Dold, D, &DolddotD);
    460:       VecDotBegin(D, D, &DdotD);
    461:       VecDotBegin(Y, D, &YdotD);
    462:       VecDotEnd(Dold, Dold, &DolddotDold);
    463:       VecDotEnd(Dold, D, &DolddotD);
    464:       VecDotEnd(D, D, &DdotD);
    465:       VecDotEnd(Y, D, &YdotD);
    466:       if (PetscAbs(PetscRealPart(DolddotD)) > qn->powell_gamma*PetscAbs(PetscRealPart(DolddotDold))) powell = PETSC_TRUE;
    467:     }
    468:     periodic = PETSC_FALSE;
    469:     if (qn->restart_type == SNES_QN_RESTART_PERIODIC) {
    470:       if (i_r>qn->m-1) periodic = PETSC_TRUE;
    471:     }
    472:     /* restart if either powell or periodic restart is satisfied. */
    473:     if (powell || periodic) {
    474:       if (qn->monitor) {
    475:         PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);
    476:         PetscViewerASCIIPrintf(qn->monitor, "restart! |%14.12e| > %4.2f*|%14.12e| or i_r = %d\n", PetscRealPart(DolddotD), qn->powell_gamma, PetscRealPart(DolddotDold), i_r);
    477:         PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);
    478:       }
    479:       i_r = -1;
    480:       /* general purpose update */
    481:       if (snes->ops->update) {
    482:         (*snes->ops->update)(snes, snes->iter);
    483:       }
    484:       if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
    485:         SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
    486:         KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);
    487:       }
    488:     }
    489:     /* general purpose update */
    490:     if (snes->ops->update) {
    491:       (*snes->ops->update)(snes, snes->iter);
    492:     }
    493:   }
    494:   if (i == snes->max_its) {
    495:     PetscInfo1(snes, "Maximum number of iterations has been reached: %D\n", snes->max_its);
    496:     if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
    497:   }
    498:   return(0);
    499: }

    503: static PetscErrorCode SNESSetUp_QN(SNES snes)
    504: {
    505:   SNES_QN        *qn = (SNES_QN*)snes->data;

    509:   VecDuplicateVecs(snes->vec_sol, qn->m, &qn->U);
    510:   VecDuplicateVecs(snes->vec_sol, qn->m, &qn->V);
    511:   PetscMalloc3(qn->m, PetscScalar, &qn->alpha,
    512:                       qn->m, PetscScalar, &qn->beta,
    513:                       qn->m, PetscScalar, &qn->dXtdF);

    515:   if (qn->singlereduction) {
    516:     PetscMalloc3(qn->m*qn->m, PetscScalar, &qn->dXdFmat,
    517:                         qn->m, PetscScalar, &qn->dFtdX,
    518:                         qn->m, PetscScalar, &qn->YtdX);
    519:   }
    520:   SNESSetWorkVecs(snes,4);

    522:   /* set up the line search */
    523:   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
    524:     SNESSetUpMatrices(snes);
    525:   }
    526:   return(0);
    527: }

    531: static PetscErrorCode SNESReset_QN(SNES snes)
    532: {
    534:   SNES_QN        *qn;

    537:   if (snes->data) {
    538:     qn = (SNES_QN*)snes->data;
    539:     if (qn->U) {
    540:       VecDestroyVecs(qn->m, &qn->U);
    541:     }
    542:     if (qn->V) {
    543:       VecDestroyVecs(qn->m, &qn->V);
    544:     }
    545:     if (qn->singlereduction) {
    546:       PetscFree3(qn->dXdFmat, qn->dFtdX, qn->YtdX);
    547:     }
    548:     PetscFree3(qn->alpha, qn->beta, qn->dXtdF);
    549:   }
    550:   return(0);
    551: }

    555: static PetscErrorCode SNESDestroy_QN(SNES snes)
    556: {

    560:   SNESReset_QN(snes);
    561:   PetscFree(snes->data);
    562:   PetscObjectComposeFunction((PetscObject)snes,"",NULL);
    563:   return(0);
    564: }

    568: static PetscErrorCode SNESSetFromOptions_QN(SNES snes)
    569: {

    571:   PetscErrorCode    ierr;
    572:   SNES_QN           *qn    = (SNES_QN*)snes->data;
    573:   PetscBool         monflg = PETSC_FALSE,flg;
    574:   SNESLineSearch    linesearch;
    575:   SNESQNRestartType rtype = qn->restart_type;
    576:   SNESQNScaleType   stype = qn->scale_type;

    579:   PetscOptionsHead("SNES QN options");
    580:   PetscOptionsInt("-snes_qn_m","Number of past states saved for L-BFGS methods","SNESQN",qn->m,&qn->m,NULL);
    581:   PetscOptionsReal("-snes_qn_powell_gamma","Powell angle tolerance",          "SNESQN", qn->powell_gamma, &qn->powell_gamma, NULL);
    582:   PetscOptionsReal("-snes_qn_powell_downhill","Powell descent tolerance",        "SNESQN", qn->powell_downhill, &qn->powell_downhill, NULL);
    583:   PetscOptionsBool("-snes_qn_monitor",         "Monitor for the QN methods",      "SNESQN", monflg, &monflg, NULL);
    584:   PetscOptionsBool("-snes_qn_single_reduction", "Aggregate reductions",           "SNESQN", qn->singlereduction, &qn->singlereduction, NULL);
    585:   PetscOptionsEnum("-snes_qn_scale_type","Scaling type","SNESQNSetScaleType",SNESQNScaleTypes,(PetscEnum)stype,(PetscEnum*)&stype,&flg);
    586:   if (flg) SNESQNSetScaleType(snes,stype);

    588:   PetscOptionsEnum("-snes_qn_restart_type","Restart type","SNESQNSetRestartType",SNESQNRestartTypes,(PetscEnum)rtype,(PetscEnum*)&rtype,&flg);
    589:   if (flg) SNESQNSetRestartType(snes,rtype);

    591:   PetscOptionsEnum("-snes_qn_type","Quasi-Newton update type","",SNESQNTypes,
    592:                           (PetscEnum)qn->type,(PetscEnum*)&qn->type,NULL);
    593:   PetscOptionsTail();
    594:   if (!snes->linesearch) {
    595:     SNESGetLineSearch(snes, &linesearch);
    596:     SNESLineSearchSetType(linesearch, SNESLINESEARCHCP);
    597:   }
    598:   if (monflg) {
    599:     qn->monitor = PETSC_VIEWER_STDOUT_(PetscObjectComm((PetscObject)snes));
    600:   }
    601:   return(0);
    602: }

    607: /*@
    608:     SNESQNSetRestartType - Sets the restart type for SNESQN.

    610:     Logically Collective on SNES

    612:     Input Parameters:
    613: +   snes - the iterative context
    614: -   rtype - restart type

    616:     Options Database:
    617: +   -snes_qn_restart_type<powell,periodic,none> - set the restart type
    618: -   -snes_qn_restart[10] - sets the number of iterations before restart for periodic

    620:     Level: intermediate

    622:     SNESQNRestartTypes:
    623: +   SNES_QN_RESTART_NONE - never restart
    624: .   SNES_QN_RESTART_POWELL - restart based upon descent criteria
    625: -   SNES_QN_RESTART_PERIODIC - restart after a fixed number of iterations

    627:     Notes:
    628:     The default line search used is the L2 line search and it requires two additional function evaluations.

    630: .keywords: SNES, SNESQN, restart, type, set SNESLineSearch
    631: @*/
    632: PetscErrorCode SNESQNSetRestartType(SNES snes, SNESQNRestartType rtype)
    633: {

    638:   PetscTryMethod(snes,"SNESQNSetRestartType_C",(SNES,SNESQNRestartType),(snes,rtype));
    639:   return(0);
    640: }

    644: /*@
    645:     SNESQNSetScaleType - Sets the scaling type for the inner inverse jacobian in SNESQN.

    647:     Logically Collective on SNES

    649:     Input Parameters:
    650: +   snes - the iterative context
    651: -   stype - scale type

    653:     Options Database:
    654: .   -snes_qn_scale_type<shanno,none,linesearch,jacobian>

    656:     Level: intermediate

    658:     SNESQNSelectTypes:
    659: +   SNES_QN_SCALE_NONE - don't scale the problem
    660: .   SNES_QN_SCALE_SHANNO - use shanno scaling
    661: .   SNES_QN_SCALE_LINESEARCH - scale based upon line search lambda
    662: -   SNES_QN_SCALE_JACOBIAN - scale by inverting a previously computed Jacobian.

    664: .keywords: SNES, SNESQN, scaling, type, set SNESLineSearch
    665: @*/

    667: PetscErrorCode SNESQNSetScaleType(SNES snes, SNESQNScaleType stype)
    668: {

    673:   PetscTryMethod(snes,"SNESQNSetScaleType_C",(SNES,SNESQNScaleType),(snes,stype));
    674:   return(0);
    675: }

    679: PetscErrorCode SNESQNSetScaleType_QN(SNES snes, SNESQNScaleType stype)
    680: {
    681:   SNES_QN *qn = (SNES_QN*)snes->data;

    684:   qn->scale_type = stype;
    685:   return(0);
    686: }

    690: PetscErrorCode SNESQNSetRestartType_QN(SNES snes, SNESQNRestartType rtype)
    691: {
    692:   SNES_QN *qn = (SNES_QN*)snes->data;

    695:   qn->restart_type = rtype;
    696:   return(0);
    697: }

    699: /* -------------------------------------------------------------------------- */
    700: /*MC
    701:       SNESQN - Limited-Memory Quasi-Newton methods for the solution of nonlinear systems.

    703:       Options Database:

    705: +     -snes_qn_m - Number of past states saved for the L-Broyden methods.
    706: .     -snes_qn_powell_angle - Angle condition for restart.
    707: .     -snes_qn_powell_descent - Descent condition for restart.
    708: .     -snes_linesearch_type <cp, l2, basic> - Type of line search.
    709: -     -snes_qn_monitor - Monitors the quasi-newton jacobian.

    711:       Notes: This implements the L-BFGS, Broyden, and "Bad" Broyden algorithms for the solution of F(x) = b using
    712:       previous change in F(x) and x to form the approximate inverse Jacobian using a series of multiplicative rank-one
    713:       updates.

    715:       When using a nonlinear preconditioner, one has two options as to how the preconditioner is applied.  The first of
    716:       these options, sequential, uses the preconditioner to generate a new solution and function and uses those at this
    717:       iteration as the current iteration's values when constructing the approximate jacobian.  The second, composed,
    718:       perturbs the problem the jacobian represents to be P(x, b) - x = 0, where P(x, b) is the preconditioner.

    720:       References:

    722:       L-Broyden Methods: a generalization of the L-BFGS method to the limited memory Broyden family, M. B. Reed,
    723:       International Journal of Computer Mathematics, vol. 86, 2009.

    725:       Experiments with Quasi-Newton Methods in Solving Stiff ODE Systems, Peter N. Brown, Alan C. Hindmarsh, Homer F. Walker
    726:       SIAM J. Sci. Stat. Comput. Vol 6(2), April 1985.

    729:       Level: beginner

    731: .seealso:  SNESCreate(), SNES, SNESSetType(), SNESNEWTONLS, SNESNEWTONTR

    733: M*/
    736: PETSC_EXTERN PetscErrorCode SNESCreate_QN(SNES snes)
    737: {
    739:   SNES_QN        *qn;

    742:   snes->ops->setup          = SNESSetUp_QN;
    743:   snes->ops->solve          = SNESSolve_QN;
    744:   snes->ops->destroy        = SNESDestroy_QN;
    745:   snes->ops->setfromoptions = SNESSetFromOptions_QN;
    746:   snes->ops->view           = 0;
    747:   snes->ops->reset          = SNESReset_QN;

    749:   snes->usespc  = PETSC_TRUE;
    750:   snes->usesksp = PETSC_FALSE;

    752:   if (!snes->tolerancesset) {
    753:     snes->max_funcs = 30000;
    754:     snes->max_its   = 10000;
    755:   }

    757:   PetscNewLog(snes,SNES_QN,&qn);
    758:   snes->data          = (void*) qn;
    759:   qn->m               = 10;
    760:   qn->scaling         = 1.0;
    761:   qn->U               = NULL;
    762:   qn->V               = NULL;
    763:   qn->dXtdF           = NULL;
    764:   qn->dFtdX           = NULL;
    765:   qn->dXdFmat         = NULL;
    766:   qn->monitor         = NULL;
    767:   qn->singlereduction = PETSC_FALSE;
    768:   qn->powell_gamma    = 0.9999;
    769:   qn->scale_type      = SNES_QN_SCALE_SHANNO;
    770:   qn->restart_type    = SNES_QN_RESTART_POWELL;
    771:   qn->type            = SNES_QN_LBFGS;

    773:   PetscObjectComposeFunction((PetscObject)snes,"SNESQNSetScaleType_C",SNESQNSetScaleType_QN);
    774:   PetscObjectComposeFunction((PetscObject)snes,"SNESQNSetRestartType_C",SNESQNSetRestartType_QN);
    775:   return(0);
    776: }