Actual source code: bncg.c
petsc-3.10.5 2019-03-28
Report Typos and Errors

      1:  #include <petsctaolinesearch.h>
      2:  #include <../src/tao/bound/impls/bncg/bncg.h>
      3:  #include <petscksp.h>

      5: #define CG_GradientDescent      0
      6: #define CG_HestenesStiefel      1
      7: #define CG_FletcherReeves       2
      8: #define CG_PolakRibierePolyak   3
      9: #define CG_PolakRibierePlus     4
     10: #define CG_DaiYuan              5
     11: #define CG_HagerZhang           6
     12: #define CG_DaiKou               7
     13: #define CG_KouDai               8
     14: #define CG_SSML_BFGS            9
     15: #define CG_SSML_DFP             10
     16: #define CG_SSML_BROYDEN         11
     17: #define CG_PCGradientDescent    12
     18: #define CGTypes                 13

     20: static const char *CG_Table[64] = {"gd", "hs", "fr", "pr", "prp", "dy", "hz", "dk", "kd", "ssml_bfgs", "ssml_dfp", "ssml_brdn", "pcgd"};

     22: #define CG_AS_NONE       0
     23: #define CG_AS_BERTSEKAS  1
     24: #define CG_AS_SIZE       2

     26: static const char *CG_AS_TYPE[64] = {"none", "bertsekas"};

     28: PetscErrorCode TaoBNCGSetRecycleFlag(Tao tao, PetscBool recycle)
     29: {
     30:   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;

     33:   cg->recycle = recycle;
     34:   return(0);
     35: }

     37: PetscErrorCode TaoBNCGEstimateActiveSet(Tao tao, PetscInt asType)
     38: {
     39:   PetscErrorCode               ierr;
     40:   TAO_BNCG                     *cg = (TAO_BNCG *)tao->data;

     43:   ISDestroy(&cg->inactive_old);
     44:   if (cg->inactive_idx) {
     45:     ISDuplicate(cg->inactive_idx, &cg->inactive_old);
     46:     ISCopy(cg->inactive_idx, cg->inactive_old);
     47:   }
     48:   switch (asType) {
     49:   case CG_AS_NONE:
     50:     ISDestroy(&cg->inactive_idx);
     51:     VecWhichInactive(tao->XL, tao->solution, cg->unprojected_gradient, tao->XU, PETSC_TRUE, &cg->inactive_idx);
     52:     ISDestroy(&cg->active_idx);
     53:     ISComplementVec(cg->inactive_idx, tao->solution, &cg->active_idx);
     54:     break;

     56:   case CG_AS_BERTSEKAS:
     57:     /* Use gradient descent to estimate the active set */
     58:     VecCopy(cg->unprojected_gradient, cg->W);
     59:     VecScale(cg->W, -1.0);
     60:     TaoEstimateActiveBounds(tao->solution, tao->XL, tao->XU, cg->unprojected_gradient, cg->W, cg->work, cg->as_step, &cg->as_tol,
     61:                                    &cg->active_lower, &cg->active_upper, &cg->active_fixed, &cg->active_idx, &cg->inactive_idx);
     62:     break;

     64:   default:
     65:     break;
     66:   }
     67:   return(0);
     68: }

     70: PetscErrorCode TaoBNCGBoundStep(Tao tao, PetscInt asType, Vec step)
     71: {
     72:   PetscErrorCode               ierr;
     73:   TAO_BNCG                     *cg = (TAO_BNCG *)tao->data;

     76:   switch (asType) {
     77:   case CG_AS_NONE:
     78:     VecISSet(step, cg->active_idx, 0.0);
     79:     break;

     81:   case CG_AS_BERTSEKAS:
     82:     TaoBoundStep(tao->solution, tao->XL, tao->XU, cg->active_lower, cg->active_upper, cg->active_fixed, 1.0, step);
     83:     break;

     85:   default:
     86:     break;
     87:   }
     88:   return(0);
     89: }

     91: static PetscErrorCode TaoSolve_BNCG(Tao tao)
     92: {
     93:   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
     94:   PetscErrorCode               ierr;
     95:   PetscReal                    step=1.0,gnorm,gnorm2, resnorm;
     96:   PetscInt                     nDiff;

     99:   /*   Project the current point onto the feasible set */
    100:   TaoComputeVariableBounds(tao);
    101:   TaoLineSearchSetVariableBounds(tao->linesearch,tao->XL,tao->XU);

    103:   /* Project the initial point onto the feasible region */
    104:   TaoBoundSolution(tao->solution, tao->XL,tao->XU, 0.0, &nDiff, tao->solution);

    106:   if (nDiff > 0 || !cg->recycle){
    107:     TaoComputeObjectiveAndGradient(tao, tao->solution, &cg->f, cg->unprojected_gradient);
    108:   }
    109:   VecNorm(cg->unprojected_gradient,NORM_2,&gnorm);
    110:   if (PetscIsInfOrNanReal(cg->f) || PetscIsInfOrNanReal(gnorm)) SETERRQ(PETSC_COMM_SELF,1, "User provided compute function generated Inf or NaN");

    112:   /* Estimate the active set and compute the projected gradient */
    113:   TaoBNCGEstimateActiveSet(tao, cg->as_type);

    115:   /* Project the gradient and calculate the norm */
    116:   VecCopy(cg->unprojected_gradient, tao->gradient);
    117:   VecISSet(tao->gradient, cg->active_idx, 0.0);
    118:   VecNorm(tao->gradient,NORM_2,&gnorm);
    119:   gnorm2 = gnorm*gnorm;

    121:   /* Initialize counters */
    122:   tao->niter = 0;
    123:   cg->ls_fails = cg->descent_error = 0;
    124:   cg->resets = -1;
    125:   cg->skipped_updates = cg->pure_gd_steps = 0;
    126:   cg->iter_quad = 0;

    128:   /* Convergence test at the starting point. */
    129:   tao->reason = TAO_CONTINUE_ITERATING;

    131:   VecFischer(tao->solution, cg->unprojected_gradient, tao->XL, tao->XU, cg->W);
    132:   VecNorm(cg->W, NORM_2, &resnorm);
    133:   if (PetscIsInfOrNanReal(resnorm)) SETERRQ(PETSC_COMM_SELF,1, "User provided compute function generated Inf or NaN");
    134:   TaoLogConvergenceHistory(tao, cg->f, resnorm, 0.0, tao->ksp_its);
    135:   TaoMonitor(tao, tao->niter, cg->f, resnorm, 0.0, step);
    136:   (*tao->ops->convergencetest)(tao,tao->cnvP);
    137:   if (tao->reason != TAO_CONTINUE_ITERATING) return(0);
    138:   /* Calculate initial direction. */
    139:   if (!cg->recycle) {
    140:     /* We are not recycling a solution/history from a past TaoSolve */
    141:     TaoBNCGResetUpdate(tao, gnorm2);
    142:   }
    143:   /* Initial gradient descent step. Scaling by 1.0 also does a decent job for some problems. */
    144:   while(1) {
    145:     TaoBNCGConductIteration(tao, gnorm);
    146:     if (tao->reason != TAO_CONTINUE_ITERATING) return(0);
    147:   }
    148:   return(0);
    149: }

    151: static PetscErrorCode TaoSetUp_BNCG(Tao tao)
    152: {
    153:   TAO_BNCG         *cg = (TAO_BNCG*)tao->data;

    157:   if (!tao->gradient) {
    158:     VecDuplicate(tao->solution,&tao->gradient);
    159:   }
    160:   if (!tao->stepdirection) {
    161:     VecDuplicate(tao->solution,&tao->stepdirection);
    162:   }
    163:   if (!cg->W) {
    164:     VecDuplicate(tao->solution,&cg->W);
    165:   }
    166:   if (!cg->work) {
    167:     VecDuplicate(tao->solution,&cg->work);
    168:   }
    169:   if (!cg->sk) {
    170:     VecDuplicate(tao->solution,&cg->sk);
    171:   }
    172:   if (!cg->yk) {
    173:     VecDuplicate(tao->gradient,&cg->yk);
    174:   }
    175:   if (!cg->X_old) {
    176:     VecDuplicate(tao->solution,&cg->X_old);
    177:   }
    178:   if (!cg->G_old) {
    179:     VecDuplicate(tao->gradient,&cg->G_old);
    180:   }
    181:   if (cg->diag_scaling){
    182:     VecDuplicate(tao->solution,&cg->d_work);
    183:     VecDuplicate(tao->solution,&cg->y_work);
    184:     VecDuplicate(tao->solution,&cg->g_work);
    185:   }
    186:   if (!cg->unprojected_gradient) {
    187:     VecDuplicate(tao->gradient,&cg->unprojected_gradient);
    188:   }
    189:   if (!cg->unprojected_gradient_old) {
    190:     VecDuplicate(tao->gradient,&cg->unprojected_gradient_old);
    191:   }
    192:   MatLMVMAllocate(cg->B, cg->sk, cg->yk);
    193:   if (cg->pc){
    194:     MatLMVMSetJ0(cg->B, cg->pc);
    195:   }
    196:   return(0);
    197: }

    199: static PetscErrorCode TaoDestroy_BNCG(Tao tao)
    200: {
    201:   TAO_BNCG       *cg = (TAO_BNCG*) tao->data;

    205:   if (tao->setupcalled) {
    206:     VecDestroy(&cg->W);
    207:     VecDestroy(&cg->work);
    208:     VecDestroy(&cg->X_old);
    209:     VecDestroy(&cg->G_old);
    210:     VecDestroy(&cg->unprojected_gradient);
    211:     VecDestroy(&cg->unprojected_gradient_old);
    212:     VecDestroy(&cg->g_work);
    213:     VecDestroy(&cg->d_work);
    214:     VecDestroy(&cg->y_work);
    215:     VecDestroy(&cg->sk);
    216:     VecDestroy(&cg->yk);
    217:   }
    218:   ISDestroy(&cg->active_lower);
    219:   ISDestroy(&cg->active_upper);
    220:   ISDestroy(&cg->active_fixed);
    221:   ISDestroy(&cg->active_idx);
    222:   ISDestroy(&cg->inactive_idx);
    223:   ISDestroy(&cg->inactive_old);
    224:   ISDestroy(&cg->new_inactives);
    225:   MatDestroy(&cg->B);
    226:   if (cg->pc) {
    227:     MatDestroy(&cg->pc);
    228:   }
    229:   PetscFree(tao->data);
    230:   return(0);
    231: }

    233: static PetscErrorCode TaoSetFromOptions_BNCG(PetscOptionItems *PetscOptionsObject,Tao tao)
    234: {
    235:     TAO_BNCG       *cg = (TAO_BNCG*)tao->data;

    239:     TaoLineSearchSetFromOptions(tao->linesearch);
    240:     PetscOptionsHead(PetscOptionsObject,"Nonlinear Conjugate Gradient method for unconstrained optimization");
    241:     PetscOptionsEList("-tao_bncg_type","cg formula", "", CG_Table, CGTypes, CG_Table[cg->cg_type], &cg->cg_type,NULL);
    242:     if (cg->cg_type != CG_SSML_BFGS){
    243:       cg->alpha = -1.0; /* Setting defaults for non-BFGS methods. User can change it below. */
    244:     }
    245:     if (CG_GradientDescent == cg->cg_type){
    246:       cg->cg_type = CG_PCGradientDescent;
    247:       /* Set scaling equal to none or, at best, scalar scaling. */
    248:       cg->unscaled_restart = PETSC_TRUE;
    249:       cg->diag_scaling = PETSC_FALSE;
    250:     }
    251:     PetscOptionsEList("-tao_bncg_as_type","active set estimation method", "", CG_AS_TYPE, CG_AS_SIZE, CG_AS_TYPE[cg->cg_type], &cg->cg_type,NULL);

    253:     PetscOptionsReal("-tao_bncg_hz_eta","(developer) cutoff tolerance for HZ", "", cg->hz_eta,&cg->hz_eta,NULL);
    254:     PetscOptionsReal("-tao_bncg_eps","(developer) cutoff value for restarts", "", cg->epsilon,&cg->epsilon,NULL);
    255:     PetscOptionsReal("-tao_bncg_dk_eta","(developer) cutoff tolerance for DK", "", cg->dk_eta,&cg->dk_eta,NULL);
    256:     PetscOptionsReal("-tao_bncg_xi","(developer) Parameter in the KD method", "", cg->xi,&cg->xi,NULL);
    257:     PetscOptionsReal("-tao_bncg_theta", "(developer) update parameter for the Broyden method", "", cg->theta, &cg->theta, NULL);
    258:     PetscOptionsReal("-tao_bncg_hz_theta", "(developer) parameter for the HZ (2006) method", "", cg->hz_theta, &cg->hz_theta, NULL);
    259:     PetscOptionsReal("-tao_bncg_alpha","(developer) parameter for the scalar scaling","",cg->alpha,&cg->alpha,NULL);
    260:     PetscOptionsReal("-tao_bncg_bfgs_scale", "(developer) update parameter for bfgs/brdn CG methods", "", cg->bfgs_scale, &cg->bfgs_scale, NULL);
    261:     PetscOptionsReal("-tao_bncg_dfp_scale", "(developer) update parameter for bfgs/brdn CG methods", "", cg->dfp_scale, &cg->dfp_scale, NULL);
    262:     PetscOptionsBool("-tao_bncg_diag_scaling","Enable diagonal Broyden-like preconditioning","",cg->diag_scaling,&cg->diag_scaling,NULL);
    263:     PetscOptionsBool("-tao_bncg_dynamic_restart","(developer) use dynamic restarts as in HZ, DK, KD","",cg->use_dynamic_restart,&cg->use_dynamic_restart,NULL);
    264:     PetscOptionsBool("-tao_bncg_unscaled_restart","(developer) use unscaled gradient restarts","",cg->unscaled_restart,&cg->unscaled_restart,NULL);
    265:     PetscOptionsReal("-tao_bncg_zeta", "(developer) Free parameter for the Kou-Dai method", "", cg->zeta, &cg->zeta, NULL);
    266:     PetscOptionsInt("-tao_bncg_min_quad", "(developer) Number of iterations with approximate quadratic behavior needed for restart", "", cg->min_quad, &cg->min_quad, NULL);
    267:     PetscOptionsInt("-tao_bncg_min_restart_num", "(developer) Number of iterations between restarts (times dimension)", "", cg->min_restart_num, &cg->min_restart_num, NULL);
    268:     PetscOptionsBool("-tao_bncg_recycle","enable recycling the existing solution, gradient, and diagonal scaling vector at the start of a new TaoSolve() call","",cg->recycle,&cg->recycle,NULL);
    269:     PetscOptionsBool("-tao_bncg_spaced_restart","(developer) Enable regular steepest descent restarting every fixed number of iterations","",cg->spaced_restart,&cg->spaced_restart,NULL);
    270:     PetscOptionsBool("-tao_bncg_no_scaling","Disable all scaling except in restarts","",cg->no_scaling,&cg->no_scaling,NULL);
    271:     if (cg->no_scaling){
    272:       cg->diag_scaling = PETSC_FALSE;
    273:       cg->alpha = -1.0;
    274:     }
    275:     if (cg->alpha == -1.0 && cg->cg_type == CG_KouDai && !cg->diag_scaling){ /* Some more default options that appear to be good. */
    276:       cg->neg_xi = PETSC_TRUE;
    277:     }
    278:     PetscOptionsBool("-tao_bncg_neg_xi","(developer) Use negative xi when it might be a smaller descent direction than necessary","",cg->neg_xi,&cg->neg_xi,NULL);
    279:     PetscOptionsReal("-tao_bncg_as_tol", "(developer) initial tolerance used when estimating actively bounded variables","",cg->as_tol,&cg->as_tol,NULL);
    280:     PetscOptionsReal("-tao_bncg_as_step", "(developer) step length used when estimating actively bounded variables","",cg->as_step,&cg->as_step,NULL);
    281:     PetscOptionsReal("-tao_bncg_delta_min", "(developer) minimum scaling factor used for scaled gradient restarts","",cg->delta_min,&cg->delta_min,NULL);
    282:     PetscOptionsReal("-tao_bncg_delta_max", "(developer) maximum scaling factor used for scaled gradient restarts","",cg->delta_max,&cg->delta_max,NULL);

    284:    PetscOptionsTail();
    285:    MatSetFromOptions(cg->B);
    286:    return(0);
    287: }

    289: static PetscErrorCode TaoView_BNCG(Tao tao, PetscViewer viewer)
    290: {
    291:   PetscBool      isascii;
    292:   TAO_BNCG       *cg = (TAO_BNCG*)tao->data;

    296:   PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii);
    297:   if (isascii) {
    298:     PetscViewerASCIIPushTab(viewer);
    299:     PetscViewerASCIIPrintf(viewer, "CG Type: %s\n", CG_Table[cg->cg_type]);
    300:     PetscViewerASCIIPrintf(viewer, "Skipped Stepdirection Updates: %i\n", cg->skipped_updates);
    301:     PetscViewerASCIIPrintf(viewer, "Scaled gradient steps: %i\n", cg->resets);
    302:     PetscViewerASCIIPrintf(viewer, "Pure gradient steps: %i\n", cg->pure_gd_steps);
    303:     PetscViewerASCIIPrintf(viewer, "Not a descent direction: %i\n", cg->descent_error);
    304:     PetscViewerASCIIPrintf(viewer, "Line search fails: %i\n", cg->ls_fails);
    305:     if (cg->diag_scaling){
    306:       PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii);
    307:       if (isascii) {
    308:         PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_INFO);
    309:         MatView(cg->B, viewer);
    310:         PetscViewerPopFormat(viewer);
    311:       }
    312:     }
    313:     PetscViewerASCIIPopTab(viewer);
    314:   }
    315:   return(0);
    316: }

    318: PetscErrorCode TaoBNCGComputeScalarScaling(PetscReal yty, PetscReal yts, PetscReal sts, PetscReal *scale, PetscReal alpha)
    319: {
    320:   PetscReal            a, b, c, sig1, sig2;

    323:   *scale = 0.0;

    325:   if (1.0 == alpha){
    326:     *scale = yts/yty;
    327:   } else if (0.0 == alpha){
    328:     *scale = sts/yts;
    329:   }
    330:   else if (-1.0 == alpha) *scale = 1.0;
    331:   else {
    332:     a = yty;
    333:     b = yts;
    334:     c = sts;
    335:     a *= alpha;
    336:     b *= -(2.0*alpha - 1.0);
    337:     c *= alpha - 1.0;
    338:     sig1 = (-b + PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
    339:     sig2 = (-b - PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
    340:     /* accept the positive root as the scalar */
    341:     if (sig1 > 0.0) {
    342:       *scale = sig1;
    343:     } else if (sig2 > 0.0) {
    344:       *scale = sig2;
    345:     } else {
    346:       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_CONV_FAILED, "Cannot find positive scalar");
    347:     }
    348:   }
    349:   return(0);
    350: }

    352: /*MC
    353:      TAOBNCG -   Bound-constrained Nonlinear Conjugate Gradient method.

    355:    Options Database Keys:
    356: +      -tao_bncg_recycle - enable recycling the latest calculated gradient vector in subsequent TaoSolve() calls (currently disabled)
    357: .      -tao_bncg_eta <r> - restart tolerance
    358: .      -tao_bncg_type <taocg_type> - cg formula
    359: .      -tao_bncg_as_type <none,bertsekas> - active set estimation method
    360: .      -tao_bncg_as_tol <r> - tolerance used in Bertsekas active-set estimation
    361: .      -tao_bncg_as_step <r> - trial step length used in Bertsekas active-set estimation
    362: .      -tao_bncg_eps <r> - cutoff used for determining whether or not we restart based on steplength each iteration, as well as determining whether or not we continue using the last stepdirection. Defaults to machine precision.
    363: .      -tao_bncg_theta <r> - convex combination parameter for the Broyden method
    364: .      -tao_bncg_hz_eta <r> - cutoff tolerance for the beta term in the HZ, DK methods
    365: .      -tao_bncg_dk_eta <r> - cutoff tolerance for the beta term in the HZ, DK methods
    366: .      -tao_bncg_xi <r> - Multiplicative constant of the gamma term in the KD method
    367: .      -tao_bncg_hz_theta <r> - Multiplicative constant of the theta term for the HZ method
    368: .      -tao_bncg_bfgs_scale <r> - Scaling parameter of the bfgs contribution to the scalar Broyden method
    369: .      -tao_bncg_dfp_scale <r> - Scaling parameter of the dfp contribution to the scalar Broyden method
    370: .      -tao_bncg_diag_scaling <b> - Whether or not to use diagonal initialization/preconditioning for the CG methods. Default True.
    371: .      -tao_bncg_dynamic_restart <b> - use dynamic restart strategy in the HZ, DK, KD methods
    372: .      -tao_bncg_unscaled_restart <b> - whether or not to scale the gradient when doing gradient descent restarts
    373: .      -tao_bncg_zeta <r> - Scaling parameter in the KD method
    374: .      -tao_bncg_delta_min <r> - Minimum bound for rescaling during restarted gradient descent steps
    375: .      -tao_bncg_delta_max <r> - Maximum bound for rescaling during restarted gradient descent steps
    376: .      -tao_bncg_min_quad <i> - Number of quadratic-like steps in a row necessary to do a dynamic restart
    377: .      -tao_bncg_min_restart_num <i> - This number, x, makes sure there is a gradient descent step every x*n iterations, where n is the dimension of the problem
    378: .      -tao_bncg_spaced_restart <b> - whether or not to do gradient descent steps every x*n iterations
    379: .      -tao_bncg_no_scaling <b> - If true, eliminates all scaling, including defaults.
    380: .      -tao_bncg_neg_xi <b> - Whether or not to use negative xi in the KD method under certain conditions

    382:   Notes:
    383:      CG formulas are:
    384:          "gd" - Gradient Descent
    385:          "fr" - Fletcher-Reeves
    386:          "pr" - Polak-Ribiere-Polyak
    387:          "prp" - Polak-Ribiere-Plus
    388:          "hs" - Hestenes-Steifel
    389:          "dy" - Dai-Yuan
    390:          "ssml_bfgs" - Self-Scaling Memoryless BFGS
    391:          "ssml_dfp"  - Self-Scaling Memoryless DFP
    392:          "ssml_brdn" - Self-Scaling Memoryless Broyden
    393:          "hz" - Hager-Zhang (CG_DESCENT 5.3)
    394:          "dk" - Dai-Kou (2013)
    395:          "kd" - Kou-Dai (2015)
    396:   Level: beginner
    397: M*/

    399: PETSC_EXTERN PetscErrorCode TaoCreate_BNCG(Tao tao)
    400: {
    401:   TAO_BNCG       *cg;
    402:   const char     *morethuente_type = TAOLINESEARCHMT;

    406:   tao->ops->setup = TaoSetUp_BNCG;
    407:   tao->ops->solve = TaoSolve_BNCG;
    408:   tao->ops->view = TaoView_BNCG;
    409:   tao->ops->setfromoptions = TaoSetFromOptions_BNCG;
    410:   tao->ops->destroy = TaoDestroy_BNCG;

    412:   /* Override default settings (unless already changed) */
    413:   if (!tao->max_it_changed) tao->max_it = 2000;
    414:   if (!tao->max_funcs_changed) tao->max_funcs = 4000;

    416:   /*  Note: nondefault values should be used for nonlinear conjugate gradient  */
    417:   /*  method.  In particular, gtol should be less that 0.5; the value used in  */
    418:   /*  Nocedal and Wright is 0.10.  We use the default values for the  */
    419:   /*  linesearch because it seems to work better. */
    420:   TaoLineSearchCreate(((PetscObject)tao)->comm, &tao->linesearch);
    421:   PetscObjectIncrementTabLevel((PetscObject)tao->linesearch, (PetscObject)tao, 1);
    422:   TaoLineSearchSetType(tao->linesearch, morethuente_type);
    423:   TaoLineSearchUseTaoRoutines(tao->linesearch, tao);
    424:   TaoLineSearchSetOptionsPrefix(tao->linesearch,tao->hdr.prefix);

    426:   PetscNewLog(tao,&cg);
    427:   tao->data = (void*)cg;
    428:   KSPInitializePackage();
    429:   MatCreate(PetscObjectComm((PetscObject)tao), &cg->B);
    430:   PetscObjectIncrementTabLevel((PetscObject)cg->B, (PetscObject)tao, 1);
    431:   MatSetOptionsPrefix(cg->B, "tao_bncg_");
    432:   MatSetType(cg->B, MATLMVMDIAGBRDN);

    434:   cg->pc = NULL;

    436:   cg->dk_eta = 0.5;
    437:   cg->hz_eta = 0.4;
    438:   cg->dynamic_restart = PETSC_FALSE;
    439:   cg->unscaled_restart = PETSC_FALSE;
    440:   cg->no_scaling = PETSC_FALSE;
    441:   cg->delta_min = 1e-7;
    442:   cg->delta_max = 100;
    443:   cg->theta = 1.0;
    444:   cg->hz_theta = 1.0;
    445:   cg->dfp_scale = 1.0;
    446:   cg->bfgs_scale = 1.0;
    447:   cg->zeta = 0.1;
    448:   cg->min_quad = 6;
    449:   cg->min_restart_num = 6; /* As in CG_DESCENT and KD2015*/
    450:   cg->xi = 1.0;
    451:   cg->neg_xi = PETSC_TRUE;
    452:   cg->spaced_restart = PETSC_FALSE;
    453:   cg->tol_quad = 1e-8;
    454:   cg->as_step = 0.001;
    455:   cg->as_tol = 0.001;
    456:   cg->eps_23 = PetscPowReal(PETSC_MACHINE_EPSILON, 2.0/3.0); /* Just a little tighter*/
    457:   cg->as_type = CG_AS_BERTSEKAS;
    458:   cg->cg_type = CG_SSML_BFGS;
    459:   cg->recycle = PETSC_FALSE;
    460:   cg->alpha = 1.0;
    461:   cg->diag_scaling = PETSC_TRUE;
    462:   return(0);
    463: }

    466: PetscErrorCode TaoBNCGResetUpdate(Tao tao, PetscReal gnormsq)
    467: {
    468:    TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
    469:    PetscErrorCode    ierr;
    470:    PetscReal         scaling;

    473:    ++cg->resets;
    474:    scaling = 2.0 * PetscMax(1.0, PetscAbsScalar(cg->f)) / PetscMax(gnormsq, cg->eps_23);
    475:    scaling = PetscMin(cg->delta_max, PetscMax(cg->delta_min, scaling));
    476:    if (cg->unscaled_restart) {
    477:      scaling = 1.0;
    478:      ++cg->pure_gd_steps;
    479:    }
    480:    VecAXPBY(tao->stepdirection, -scaling, 0.0, tao->gradient);
    481:    /* Also want to reset our diagonal scaling with each restart */
    482:    if (cg->diag_scaling) {
    483:      MatLMVMReset(cg->B, PETSC_FALSE);
    484:    }
    485:    return(0);
    486:  }

    488: PetscErrorCode TaoBNCGCheckDynamicRestart(Tao tao, PetscReal stepsize, PetscReal gd, PetscReal gd_old, PetscBool *dynrestart, PetscReal fold)
    489: {
    490:    TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
    491:    PetscReal         quadinterp;

    494:    if (cg->f < cg->min_quad/10) {
    495:      *dynrestart = PETSC_FALSE;
    496:      return(0);
    497:    } /* just skip this since this strategy doesn't work well for functions near zero */
    498:    quadinterp = 2.0*(cg->f - fold)/(stepsize*(gd + gd_old));
    499:    if (PetscAbs(quadinterp - 1.0) < cg->tol_quad) ++cg->iter_quad;
    500:    else {
    501:      cg->iter_quad = 0;
    502:      *dynrestart = PETSC_FALSE;
    503:    }
    504:    if (cg->iter_quad >= cg->min_quad) {
    505:      cg->iter_quad = 0;
    506:      *dynrestart = PETSC_TRUE;
    507:    }
    508:    return(0);
    509:  }

    511: PETSC_INTERN PetscErrorCode TaoBNCGStepDirectionUpdate(Tao tao, PetscReal gnorm2, PetscReal step, PetscReal fold, PetscReal gnorm2_old, PetscReal dnorm, PetscBool pcgd_fallback)
    512: {
    513:   TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
    514:   PetscErrorCode    ierr;
    515:   PetscReal         gamma = 1.0, tau_k, beta;
    516:   PetscReal         tmp = 1.0, ynorm, ynorm2 = 1.0, snorm = 1.0, dk_yk=1.0, gd;
    517:   PetscReal         gkp1_yk, gd_old, tau_bfgs, tau_dfp, gkp1D_yk, gtDg;
    518:   PetscInt          dim;
    519:   PetscBool         cg_restart = PETSC_FALSE;

    522:   /* Local curvature check to see if we need to restart */
    523:   if (tao->niter >= 1 || cg->recycle){
    524:     VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient);
    525:     VecNorm(cg->yk, NORM_2, &ynorm);
    526:     ynorm2 = ynorm*ynorm;
    527:     VecDot(cg->yk, tao->stepdirection, &dk_yk);
    528:     if (step*dnorm < PETSC_MACHINE_EPSILON || step*dk_yk < PETSC_MACHINE_EPSILON){
    529:       cg_restart = PETSC_TRUE;
    530:       ++cg->skipped_updates;
    531:     }
    532:     if (cg->spaced_restart) {
    533:       VecGetSize(tao->gradient, &dim);
    534:       if (tao->niter % (dim*cg->min_restart_num)) cg_restart = PETSC_TRUE;
    535:     }
    536:   }
    537:   /* If the user wants regular restarts, do it every 6n iterations, where n=dimension */
    538:   if (cg->spaced_restart){
    539:     VecGetSize(tao->gradient, &dim);
    540:     if (0 == tao->niter % (6*dim)) cg_restart = PETSC_TRUE;
    541:   }
    542:   /* Compute the diagonal scaling vector if applicable */
    543:   if (cg->diag_scaling) {
    544:     MatLMVMUpdate(cg->B, tao->solution, tao->gradient);
    545:   }

    547:   /* A note on diagonal scaling (to be added to paper):
    548:    For the FR, PR, PRP, and DY methods, the diagonally scaled versions
    549:    must be derived as a preconditioned CG method rather than as
    550:    a Hessian initialization like in the Broyden methods. */

    552:   /* In that case, one writes the objective function as
    553:    f(x) \equiv f(Ay). Gradient evaluations yield g(x_k) = A g(Ay_k) = A g(x_k).
    554:    Furthermore, the direction d_k \equiv (x_k - x_{k-1})/step according to
    555:    HZ (2006) becomes A^{-1} d_k, such that d_k^T g_k remains the
    556:    same under preconditioning. Note that A is diagonal, such that A^T = A. */

    558:   /* This yields questions like what the dot product d_k^T y_k
    559:    should look like. HZ mistakenly treats that as the same under
    560:    preconditioning, but that is not necessarily true. */

    562:   /* Observe y_k \equiv g_k - g_{k-1}, and under the P.C. transformation,
    563:    we get d_k^T y_k = (d_k^T A_k^{-T} A_k g_k - d_k^T A_k^{-T} A_{k-1} g_{k-1}),
    564:    yielding d_k^T y_k = d_k^T g_k - d_k^T (A_k^{-T} A_{k-1} g_{k-1}), which is
    565:    NOT the same if our preconditioning matrix is updated between iterations.
    566:    This same issue is found when considering dot products of the form g_{k+1}^T y_k. */

    568:   /* Compute CG step direction */
    569:   if (cg_restart) {
    570:     TaoBNCGResetUpdate(tao, gnorm2);
    571:   } else if (pcgd_fallback) {
    572:     /* Just like preconditioned CG */
    573:     MatSolve(cg->B, tao->gradient, cg->g_work);
    574:     VecAXPBY(tao->stepdirection, -1.0, 0.0, cg->g_work);
    575:   } else if (ynorm2 > PETSC_MACHINE_EPSILON) {
    576:     switch (cg->cg_type) {
    577:     case CG_PCGradientDescent:
    578:       if (!cg->diag_scaling){
    579:         if (!cg->no_scaling){
    580:         cg->sts = step*step*dnorm*dnorm;
    581:         TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->sts, &tau_k, cg->alpha);
    582:         } else {
    583:           tau_k = 1.0;
    584:           ++cg->pure_gd_steps;
    585:         }
    586:         VecAXPBY(tao->stepdirection, -tau_k, 0.0, tao->gradient);
    587:       } else {
    588:         MatSolve(cg->B, tao->gradient, cg->g_work);
    589:         VecAXPBY(tao->stepdirection, -1.0, 0.0, cg->g_work);
    590:       }
    591:       break;

    593:     case CG_HestenesStiefel:
    594:       /* Classic Hestenes-Stiefel method, modified with scalar and diagonal preconditioning. */
    595:       if (!cg->diag_scaling){
    596:         cg->sts = step*step*dnorm*dnorm;
    597:         VecDot(cg->yk, tao->gradient, &gkp1_yk);
    598:         TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->sts, &tau_k, cg->alpha);
    599:         beta = tau_k*gkp1_yk/dk_yk;
    600:         VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient);
    601:       } else {
    602:         MatSolve(cg->B, tao->gradient, cg->g_work);
    603:         VecDot(cg->yk, cg->g_work, &gkp1_yk);
    604:         beta = gkp1_yk/dk_yk;
    605:         VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work);
    606:       }
    607:       break;

    609:     case CG_FletcherReeves:
    610:       VecDot(cg->G_old, cg->G_old, &gnorm2_old);
    611:       VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient);
    612:       VecNorm(cg->yk, NORM_2, &ynorm);
    613:       ynorm2 = ynorm*ynorm;
    614:       VecDot(cg->yk, tao->stepdirection, &dk_yk);
    615:       if (!cg->diag_scaling){
    616:         TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, step*step*dnorm*dnorm, &tau_k, cg->alpha);
    617:         beta = tau_k*gnorm2/gnorm2_old;
    618:         VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient);
    619:       } else {
    620:         VecDot(cg->G_old, cg->g_work, &gnorm2_old); /* Before it's updated */
    621:         MatSolve(cg->B, tao->gradient, cg->g_work);
    622:         VecDot(tao->gradient, cg->g_work, &tmp);
    623:         beta = tmp/gnorm2_old;
    624:         VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work);
    625:       }
    626:       break;

    628:     case CG_PolakRibierePolyak:
    629:       snorm = step*dnorm;
    630:       if (!cg->diag_scaling){
    631:         VecDot(cg->G_old, cg->G_old, &gnorm2_old);
    632:         VecDot(cg->yk, tao->gradient, &gkp1_yk);
    633:         TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, snorm*snorm, &tau_k, cg->alpha);
    634:         beta = tau_k*gkp1_yk/gnorm2_old;
    635:         VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient);
    636:       } else {
    637:         VecDot(cg->G_old, cg->g_work, &gnorm2_old);
    638:         MatSolve(cg->B, tao->gradient, cg->g_work);
    639:         VecDot(cg->g_work, cg->yk, &gkp1_yk);
    640:         beta = gkp1_yk/gnorm2_old;
    641:         VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work);
    642:       }
    643:       break;

    645:     case CG_PolakRibierePlus:
    646:       VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient);
    647:       VecNorm(cg->yk, NORM_2, &ynorm);
    648:       ynorm2 = ynorm*ynorm;
    649:       if (!cg->diag_scaling){
    650:         VecDot(cg->G_old, cg->G_old, &gnorm2_old);
    651:         VecDot(cg->yk, tao->gradient, &gkp1_yk);
    652:         TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, snorm*snorm, &tau_k, cg->alpha);
    653:         beta = tau_k*gkp1_yk/gnorm2_old;
    654:         beta = PetscMax(beta, 0.0);
    655:         VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient);
    656:       } else {
    657:         VecDot(cg->G_old, cg->g_work, &gnorm2_old); /* Old gtDg */
    658:         MatSolve(cg->B, tao->gradient, cg->g_work);
    659:         VecDot(cg->g_work, cg->yk, &gkp1_yk);
    660:         beta = gkp1_yk/gnorm2_old;
    661:         beta = PetscMax(beta, 0.0);
    662:         VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work);
    663:       }
    664:       break;

    666:     case CG_DaiYuan:
    667:       /* Dai, Yu-Hong, and Yaxiang Yuan. "A nonlinear conjugate gradient method with a strong global convergence property."
    668:          SIAM Journal on optimization 10, no. 1 (1999): 177-182. */
    669:       if (!cg->diag_scaling){
    670:         VecDot(tao->stepdirection, tao->gradient, &gd);
    671:         VecDot(cg->G_old, tao->stepdirection, &gd_old);
    672:         TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->yts, &tau_k, cg->alpha);
    673:         beta = tau_k*gnorm2/(gd - gd_old);
    674:         VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient);
    675:       } else {
    676:         MatMult(cg->B, tao->stepdirection, cg->d_work);
    677:         MatSolve(cg->B, tao->gradient, cg->g_work);
    678:         VecDot(cg->g_work, tao->gradient, &gtDg);
    679:         VecDot(tao->stepdirection, cg->G_old, &gd_old);
    680:         VecDot(cg->d_work, cg->g_work, &dk_yk);
    681:         dk_yk = dk_yk - gd_old;
    682:         beta = gtDg/dk_yk;
    683:         VecScale(cg->d_work, beta);
    684:         VecWAXPY(tao->stepdirection, -1.0, cg->g_work, cg->d_work);
    685:       }
    686:       break;

    688:     case CG_HagerZhang:
    689:       /* Hager, William W., and Hongchao Zhang. "Algorithm 851: CG_DESCENT, a conjugate gradient method with guaranteed descent."
    690:          ACM Transactions on Mathematical Software (TOMS) 32, no. 1 (2006): 113-137. */
    691:       VecDot(tao->gradient, tao->stepdirection, &gd);
    692:       VecDot(cg->G_old, tao->stepdirection, &gd_old);
    693:       VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution);
    694:       snorm = dnorm*step;
    695:       cg->yts = step*dk_yk;
    696:       if (cg->use_dynamic_restart){
    697:         TaoBNCGCheckDynamicRestart(tao, step, gd, gd_old, &cg->dynamic_restart, fold);
    698:       }
    699:       if (cg->dynamic_restart){
    700:         TaoBNCGResetUpdate(tao, gnorm2);
    701:       } else {
    702:         if (!cg->diag_scaling){
    703:           VecDot(cg->yk, tao->gradient, &gkp1_yk);
    704:           TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha);
    705:           /* Supplying cg->alpha = -1.0 will give the CG_DESCENT 5.3 special case of tau_k = 1.0 */
    706:           tmp = gd/dk_yk;
    707:           beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk));
    708:           /* Bound beta as in CG_DESCENT 5.3, as implemented, with the third comparison from DK 2013 */
    709:           beta = PetscMax(PetscMax(beta, cg->hz_eta*tau_k*gd_old/(dnorm*dnorm)), cg->dk_eta*tau_k*gd/(dnorm*dnorm));
    710:           /* d <- -t*g + beta*t*d */
    711:           VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient);
    712:         } else {
    713:           /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
    714:           cg->yty = ynorm2;
    715:           cg->sts = snorm*snorm;
    716:           /* Apply the diagonal scaling to all my vectors */
    717:           MatSolve(cg->B, tao->gradient, cg->g_work);
    718:           MatSolve(cg->B, cg->yk, cg->y_work);
    719:           MatSolve(cg->B, tao->stepdirection, cg->d_work);
    720:           /* Construct the constant ytDgkp1 */
    721:           VecDot(cg->yk, cg->g_work, &gkp1_yk);
    722:           /* Construct the constant for scaling Dkyk in the update */
    723:           tmp = gd/dk_yk;
    724:           VecDot(cg->yk, cg->y_work, &tau_k);
    725:           tau_k = -tau_k*gd/(dk_yk*dk_yk);
    726:           /* beta is the constant which adds the dk contribution */
    727:           beta = gkp1_yk/dk_yk + cg->hz_theta*tau_k; /* HZ; (1.15) from DK 2013 */
    728:           /* From HZ2013, modified to account for diagonal scaling*/
    729:           VecDot(cg->G_old, cg->d_work, &gd_old);
    730:           VecDot(tao->stepdirection, cg->g_work, &gd);
    731:           beta = PetscMax(PetscMax(beta, cg->hz_eta*gd_old/(dnorm*dnorm)), cg->dk_eta*gd/(dnorm*dnorm));
    732:           /* Do the update */
    733:           VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work);
    734:         }
    735:       }
    736:       break;

    738:     case CG_DaiKou:
    739:       /* Dai, Yu-Hong, and Cai-Xia Kou. "A nonlinear conjugate gradient algorithm with an optimal property and an improved Wolfe line search."
    740:          SIAM Journal on Optimization 23, no. 1 (2013): 296-320. */
    741:       VecDot(tao->gradient, tao->stepdirection, &gd);
    742:       VecDot(cg->G_old, tao->stepdirection, &gd_old);
    743:       VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution);
    744:       snorm = step*dnorm;
    745:       cg->yts = dk_yk*step;
    746:       if (!cg->diag_scaling){
    747:         VecDot(cg->yk, tao->gradient, &gkp1_yk);
    748:         TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha);
    749:         /* Use cg->alpha = -1.0 to get tau_k = 1.0 as in CG_DESCENT 5.3 */
    750:         tmp = gd/dk_yk;
    751:         beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk) + gd/(dnorm*dnorm)) - step*gd/dk_yk;
    752:         beta = PetscMax(PetscMax(beta, cg->hz_eta*tau_k*gd_old/(dnorm*dnorm)), cg->dk_eta*tau_k*gd/(dnorm*dnorm));
    753:         /* d <- -t*g + beta*t*d */
    754:         VecAXPBYPCZ(tao->stepdirection, -tau_k, 0.0, beta, tao->gradient, cg->yk);
    755:       } else {
    756:         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
    757:         cg->yty = ynorm2;
    758:         cg->sts = snorm*snorm;
    759:         MatSolve(cg->B, tao->gradient, cg->g_work);
    760:         MatSolve(cg->B, cg->yk, cg->y_work);
    761:         MatSolve(cg->B, tao->stepdirection, cg->d_work);
    762:         /* Construct the constant ytDgkp1 */
    763:         VecDot(cg->yk, cg->g_work, &gkp1_yk);
    764:         VecDot(cg->yk, cg->y_work, &tau_k);
    765:         tau_k = tau_k*gd/(dk_yk*dk_yk);
    766:         tmp = gd/dk_yk;
    767:         /* beta is the constant which adds the dk contribution */
    768:         beta = gkp1_yk/dk_yk - step*tmp - tau_k;
    769:         /* Update this for the last term in beta */
    770:         VecDot(cg->y_work, tao->stepdirection, &dk_yk);
    771:         beta += tmp*dk_yk/(dnorm*dnorm); /* projection of y_work onto dk */
    772:         VecDot(tao->stepdirection, cg->g_work, &gd);
    773:         VecDot(cg->G_old, cg->d_work, &gd_old);
    774:         beta = PetscMax(PetscMax(beta, cg->hz_eta*gd_old/(dnorm*dnorm)), cg->dk_eta*gd/(dnorm*dnorm));
    775:         /* Do the update */
    776:         VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work);
    777:       }
    778:       break;

    780:     case CG_KouDai:
    781:       /* Kou, Cai-Xia, and Yu-Hong Dai. "A modified self-scaling memoryless Broydenâ€“Fletcherâ€“Goldfarbâ€“Shanno method for unconstrained optimization."
    782:          Journal of Optimization Theory and Applications 165, no. 1 (2015): 209-224. */
    783:       VecDot(tao->gradient, tao->stepdirection, &gd);
    784:       VecDot(cg->G_old, tao->stepdirection, &gd_old);
    785:       VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution);
    786:       snorm = step*dnorm;
    787:       cg->yts = dk_yk*step;
    788:       if (cg->use_dynamic_restart){
    789:         TaoBNCGCheckDynamicRestart(tao, step, gd, gd_old, &cg->dynamic_restart, fold);
    790:       }
    791:       if (cg->dynamic_restart){
    792:         TaoBNCGResetUpdate(tao, gnorm2);
    793:       } else {
    794:         if (!cg->diag_scaling){
    795:           VecDot(cg->yk, tao->gradient, &gkp1_yk);
    796:           TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha);
    797:           beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk)) - step*gd/dk_yk;
    798:           if (beta < cg->zeta*tau_k*gd/(dnorm*dnorm)) /* 0.1 is KD's zeta parameter */
    799:           {
    800:             beta = cg->zeta*tau_k*gd/(dnorm*dnorm);
    801:             gamma = 0.0;
    802:           } else {
    803:             if (gkp1_yk < 0 && cg->neg_xi) gamma = -1.0*gd/dk_yk;
    804:             /* This seems to be very effective when there's no tau_k scaling.
    805:                This guarantees a large descent step every iteration, going through DK 2015 Lemma 3.1's proof but allowing for negative xi */
    806:             else {
    807:               gamma = cg->xi*gd/dk_yk;
    808:             }
    809:           }
    810:           /* d <- -t*g + beta*t*d + t*tmp*yk */
    811:           VecAXPBYPCZ(tao->stepdirection, -tau_k, gamma*tau_k, beta, tao->gradient, cg->yk);
    812:         } else {
    813:           /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
    814:           cg->yty = ynorm2;
    815:           cg->sts = snorm*snorm;
    816:           MatSolve(cg->B, tao->gradient, cg->g_work);
    817:           MatSolve(cg->B, cg->yk, cg->y_work);
    818:           /* Construct the constant ytDgkp1 */
    819:           VecDot(cg->yk, cg->g_work, &gkp1D_yk);
    820:           /* Construct the constant for scaling Dkyk in the update */
    821:           gamma = gd/dk_yk;
    822:           /* tau_k = -ytDy/(ytd)^2 * gd */
    823:           VecDot(cg->yk, cg->y_work, &tau_k);
    824:           tau_k = tau_k*gd/(dk_yk*dk_yk);
    825:           /* beta is the constant which adds the d_k contribution */
    826:           beta = gkp1D_yk/dk_yk - step*gamma - tau_k;
    827:           /* Here is the requisite check */
    828:           VecDot(tao->stepdirection, cg->g_work, &tmp);
    829:           if (cg->neg_xi){
    830:             /* modified KD implementation */
    831:             if (gkp1D_yk/dk_yk < 0) gamma = -1.0*gd/dk_yk;
    832:             else {
    833:               gamma = cg->xi*gd/dk_yk;
    834:             }
    835:             if (beta < cg->zeta*tmp/(dnorm*dnorm)){
    836:               beta = cg->zeta*tmp/(dnorm*dnorm);
    837:               gamma = 0.0;
    838:             }
    839:           } else { /* original KD 2015 implementation */
    840:             if (beta < cg->zeta*tmp/(dnorm*dnorm)) {
    841:               beta = cg->zeta*tmp/(dnorm*dnorm);
    842:               gamma = 0.0;
    843:             } else {
    844:               gamma = cg->xi*gd/dk_yk;
    845:             }
    846:           }
    847:           /* Do the update in two steps */
    848:           VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work);
    849:           VecAXPY(tao->stepdirection, gamma, cg->y_work);
    850:         }
    851:       }
    852:       break;

    854:     case CG_SSML_BFGS:
    855:       /* Perry, J. M. "A class of conjugate gradient algorithms with a two-step variable-metric memory."
    856:          Discussion Papers 269 (1977). */
    857:       VecDot(tao->gradient, tao->stepdirection, &gd);
    858:       VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution);
    859:       snorm = step*dnorm;
    860:       cg->yts = dk_yk*step;
    861:       cg->yty = ynorm2;
    862:       cg->sts = snorm*snorm;
    863:       if (!cg->diag_scaling){
    864:         VecDot(cg->yk, tao->gradient, &gkp1_yk);
    865:         TaoBNCGComputeScalarScaling(cg->yty, cg->yts, cg->sts, &tau_k, cg->alpha);
    866:         tmp = gd/dk_yk;
    867:         beta = tau_k*(gkp1_yk/dk_yk - cg->yty*gd/(dk_yk*dk_yk)) - step*tmp;
    868:         /* d <- -t*g + beta*t*d + t*tmp*yk */
    869:         VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp*tau_k, beta, tao->gradient, cg->yk);
    870:       } else {
    871:         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
    872:         MatSolve(cg->B, tao->gradient, cg->g_work);
    873:         MatSolve(cg->B, cg->yk, cg->y_work);
    874:         /* compute scalar gamma */
    875:         VecDot(cg->g_work, cg->yk, &gkp1_yk);
    876:         VecDot(cg->y_work, cg->yk, &tmp);
    877:         gamma = gd/dk_yk;
    878:         /* Compute scalar beta */
    879:         beta = (gkp1_yk/dk_yk - gd*tmp/(dk_yk*dk_yk)) - step*gd/dk_yk;
    880:         /* Compute stepdirection d_kp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
    881:         VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work);
    882:       }
    883:       break;

    885:     case CG_SSML_DFP:
    886:       VecDot(tao->gradient, tao->stepdirection, &gd);
    887:       VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution);
    888:       snorm = step*dnorm;
    889:       cg->yts = dk_yk*step;
    890:       cg->yty = ynorm2;
    891:       cg->sts = snorm*snorm;
    892:       if (!cg->diag_scaling){
    893:         /* Instead of a regular convex combination, we will solve a quadratic formula. */
    894:         TaoBNCGComputeScalarScaling(cg->yty, cg->yts, cg->sts, &tau_k, cg->alpha);
    895:         VecDot(cg->yk, tao->gradient, &gkp1_yk);
    896:         tau_k = cg->dfp_scale*tau_k;
    897:         tmp = tau_k*gkp1_yk/cg->yty;
    898:         beta = -step*gd/dk_yk;
    899:         /* d <- -t*g + beta*d + tmp*yk */
    900:         VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp, beta, tao->gradient, cg->yk);
    901:       } else {
    902:         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless DFP step */
    903:         MatSolve(cg->B, tao->gradient, cg->g_work);
    904:         MatSolve(cg->B, cg->yk, cg->y_work);
    905:         /* compute scalar gamma */
    906:         VecDot(cg->g_work, cg->yk, &gkp1_yk);
    907:         VecDot(cg->y_work, cg->yk, &tmp);
    908:         gamma = (gkp1_yk/tmp);
    909:         /* Compute scalar beta */
    910:         beta = -step*gd/dk_yk;
    911:         /* Compute stepdirection d_kp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
    912:         VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work);
    913:       }
    914:       break;

    916:     case CG_SSML_BROYDEN:
    917:       VecDot(tao->gradient, tao->stepdirection, &gd);
    918:       VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution);
    919:       snorm = step*dnorm;
    920:       cg->yts = step*dk_yk;
    921:       cg->yty = ynorm2;
    922:       cg->sts = snorm*snorm;
    923:       if (!cg->diag_scaling){
    924:         /* Instead of a regular convex combination, we will solve a quadratic formula. */
    925:         TaoBNCGComputeScalarScaling(cg->yty, step*dk_yk, snorm*snorm, &tau_bfgs, cg->bfgs_scale);
    926:         TaoBNCGComputeScalarScaling(cg->yty, step*dk_yk, snorm*snorm, &tau_dfp, cg->dfp_scale);
    927:         VecDot(cg->yk, tao->gradient, &gkp1_yk);
    928:         tau_k = cg->theta*tau_bfgs + (1.0-cg->theta)*tau_dfp;
    929:         /* If bfgs_scale = 1.0, it should reproduce the bfgs tau_bfgs. If bfgs_scale = 0.0,
    930:            it should reproduce the tau_dfp scaling. Same with dfp_scale.   */
    931:         tmp = cg->theta*tau_bfgs*gd/dk_yk + (1-cg->theta)*tau_dfp*gkp1_yk/cg->yty;
    932:         beta = cg->theta*tau_bfgs*(gkp1_yk/dk_yk - cg->yty*gd/(dk_yk*dk_yk)) - step*gd/dk_yk;
    933:         /* d <- -t*g + beta*d + tmp*yk */
    934:         VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp, beta, tao->gradient, cg->yk);
    935:       } else {
    936:         /* We have diagonal scaling enabled */
    937:         MatSolve(cg->B, tao->gradient, cg->g_work);
    938:         MatSolve(cg->B, cg->yk, cg->y_work);
    939:         /* compute scalar gamma */
    940:         VecDot(cg->g_work, cg->yk, &gkp1_yk);
    941:         VecDot(cg->y_work, cg->yk, &tmp);
    942:         gamma = cg->theta*gd/dk_yk + (1-cg->theta)*(gkp1_yk/tmp);
    943:         /* Compute scalar beta */
    944:         beta = cg->theta*(gkp1_yk/dk_yk - gd*tmp/(dk_yk*dk_yk)) - step*gd/dk_yk;
    945:         /* Compute stepdirection dkp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
    946:         VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work);
    947:       }
    948:       break;

    950:     default:
    951:       break;

    953:     }
    954:   }
    955:   return(0);
    956: }

    958: PETSC_INTERN PetscErrorCode TaoBNCGConductIteration(Tao tao, PetscReal gnorm)
    959: {
    960:   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
    961:   PetscErrorCode               ierr;
    962:   TaoLineSearchConvergedReason ls_status = TAOLINESEARCH_CONTINUE_ITERATING;
    963:   PetscReal                    step=1.0,gnorm2,gd,dnorm=0.0;
    964:   PetscReal                    gnorm2_old,f_old,resnorm, gnorm_old;
    965:   PetscBool                    pcgd_fallback = PETSC_FALSE;

    968:   /* We are now going to perform a line search along the direction. */
    969:   /* Store solution and gradient info before it changes */
    970:   VecCopy(tao->solution, cg->X_old);
    971:   VecCopy(tao->gradient, cg->G_old);
    972:   VecCopy(cg->unprojected_gradient, cg->unprojected_gradient_old);

    974:   gnorm_old = gnorm;
    975:   gnorm2_old = gnorm_old*gnorm_old;
    976:   f_old = cg->f;
    977:   /* Perform bounded line search. If we are recycling a solution from a previous */
    978:   /* TaoSolve, then we want to immediately skip to calculating a new direction rather than performing a linesearch */
    979:   if (!(cg->recycle && 0 == tao->niter)){
    980:     /* Above logic: the below code happens every iteration, except for the first iteration of a recycled TaoSolve */
    981:     TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0);
    982:     TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status);
    983:     TaoAddLineSearchCounts(tao);

    985:     /*  Check linesearch failure */
    986:     if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
    987:       ++cg->ls_fails;
    988:       if (cg->cg_type == CG_GradientDescent){
    989:         /* Nothing left to do but fail out of the optimization */
    990:         step = 0.0;
    991:         tao->reason = TAO_DIVERGED_LS_FAILURE;
    992:       } else {
    993:         /* Restore previous point, perform preconditioned GD and regular GD steps at the last good point */
    994:         VecCopy(cg->X_old, tao->solution);
    995:         VecCopy(cg->G_old, tao->gradient);
    996:         VecCopy(cg->unprojected_gradient_old, cg->unprojected_gradient);
    997:         gnorm = gnorm_old;
    998:         gnorm2 = gnorm2_old;
    999:         cg->f = f_old;

    1001:         /* Fall back on preconditioned CG (so long as you're not already using it) */
    1002:         if (cg->cg_type != CG_PCGradientDescent && cg->diag_scaling){
    1003:           pcgd_fallback = PETSC_TRUE;
    1004:           TaoBNCGStepDirectionUpdate(tao, gnorm2, step, f_old, gnorm2_old, dnorm, pcgd_fallback);

    1006:           TaoBNCGResetUpdate(tao, gnorm2);
    1007:           TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection);

    1009:           TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0);
    1010:           TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status);
    1011:           TaoAddLineSearchCounts(tao);

    1013:           pcgd_fallback = PETSC_FALSE;
    1014:           if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER){
    1015:             /* Going to perform a regular gradient descent step. */
    1016:             ++cg->ls_fails;
    1017:             step = 0.0;
    1018:           }
    1019:         }
    1020:         /* Fall back on the scaled gradient step */
    1021:         if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER){
    1022:           ++cg->ls_fails;
    1023:           TaoBNCGResetUpdate(tao, gnorm2);
    1024:           TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection);
    1025:           TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0);
    1026:           TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status);
    1027:           TaoAddLineSearchCounts(tao);
    1028:         }

    1030:         if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER){
    1031:           /* Nothing left to do but fail out of the optimization */
    1032:           ++cg->ls_fails;
    1033:           step = 0.0;
    1034:           tao->reason = TAO_DIVERGED_LS_FAILURE;
    1035:         } else {
    1036:           /* One of the fallbacks worked. Set them both back equal to false. */
    1037:           pcgd_fallback = PETSC_FALSE;
    1038:         }
    1039:       }
    1040:     }
    1041:     /* Convergence test for line search failure */
    1042:     if (tao->reason != TAO_CONTINUE_ITERATING) return(0);

    1044:     /* Standard convergence test */
    1045:     VecFischer(tao->solution, cg->unprojected_gradient, tao->XL, tao->XU, cg->W);
    1046:     VecNorm(cg->W, NORM_2, &resnorm);
    1047:     if (PetscIsInfOrNanReal(resnorm)) SETERRQ(PETSC_COMM_SELF,1, "User provided compute function generated Inf or NaN");
    1048:     TaoLogConvergenceHistory(tao, cg->f, resnorm, 0.0, tao->ksp_its);
    1049:     TaoMonitor(tao, tao->niter, cg->f, resnorm, 0.0, step);
    1050:     (*tao->ops->convergencetest)(tao,tao->cnvP);
    1051:     if (tao->reason != TAO_CONTINUE_ITERATING) return(0);
    1052:   }
    1053:   /* Assert we have an updated step and we need at least one more iteration. */
    1054:   /* Calculate the next direction */
    1055:   /* Estimate the active set at the new solution */
    1056:   TaoBNCGEstimateActiveSet(tao, cg->as_type);
    1057:   /* Compute the projected gradient and its norm */
    1058:   VecCopy(cg->unprojected_gradient, tao->gradient);
    1059:   VecISSet(tao->gradient, cg->active_idx, 0.0);
    1060:   VecNorm(tao->gradient,NORM_2,&gnorm);
    1061:   gnorm2 = gnorm*gnorm;

    1063:   /* Calculate some quantities used in the StepDirectionUpdate. */
    1064:   VecNorm(tao->stepdirection, NORM_2, &dnorm);
    1065:   /* Update the step direction. */
    1066:   TaoBNCGStepDirectionUpdate(tao, gnorm2, step, f_old, gnorm2_old, dnorm, pcgd_fallback);
    1067:   ++tao->niter;
    1068:   TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection);

    1070:   if (cg->cg_type != CG_GradientDescent) {
    1071:     /* Figure out which previously active variables became inactive this iteration */
    1072:     ISDestroy(&cg->new_inactives);
    1073:     if (cg->inactive_idx && cg->inactive_old) {
    1074:       ISDifference(cg->inactive_idx, cg->inactive_old, &cg->new_inactives);
    1075:     }
    1076:     /* Selectively reset the CG step those freshly inactive variables */
    1077:     if (cg->new_inactives) {
    1078:       VecGetSubVector(tao->stepdirection, cg->new_inactives, &cg->inactive_step);
    1079:       VecGetSubVector(cg->unprojected_gradient, cg->new_inactives, &cg->inactive_grad);
    1080:       VecCopy(cg->inactive_grad, cg->inactive_step);
    1081:       VecScale(cg->inactive_step, -1.0);
    1082:       VecRestoreSubVector(tao->stepdirection, cg->new_inactives, &cg->inactive_step);
    1083:       VecRestoreSubVector(cg->unprojected_gradient, cg->new_inactives, &cg->inactive_grad);
    1084:     }
    1085:     /* Verify that this is a descent direction */
    1086:     VecDot(tao->gradient, tao->stepdirection, &gd);
    1087:     VecNorm(tao->stepdirection, NORM_2, &dnorm);
    1088:     if (PetscIsInfOrNanReal(gd) || (gd/(dnorm*dnorm) <= -1e10 || gd/(dnorm*dnorm) >= -1e-10)) {
    1089:       /* Not a descent direction, so we reset back to projected gradient descent */
    1090:       TaoBNCGResetUpdate(tao, gnorm2);
    1091:       TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection);
    1092:       ++cg->descent_error;
    1093:     } else {
    1094:     }
    1095:   }
    1096:   return(0);
    1097: }

    1099: PetscErrorCode TaoBNCGSetH0(Tao tao, Mat H0)
    1100: {
    1101:   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
    1102:   PetscErrorCode               ierr;

    1105:   PetscObjectReference((PetscObject)H0);
    1106:   cg->pc = H0;
    1107:   return(0);
    1108: }