xref: /petsc/src/tao/bound/impls/bncg/bncg.c (revision 7494f0b1f7799db628534ba94149e25a20dbd59a)
1ac9112b8SAlp Dener #include <petsctaolinesearch.h>
2414d97d3SAlp Dener #include <../src/tao/bound/impls/bncg/bncg.h> /*I "petsctao.h" I*/
350b47da0SAdam Denchfield #include <petscksp.h>
4ac9112b8SAlp Dener 
5c8bcdf1eSAdam Denchfield #define CG_GradientDescent      0
6c8bcdf1eSAdam Denchfield #define CG_HestenesStiefel      1
7c8bcdf1eSAdam Denchfield #define CG_FletcherReeves       2
850b47da0SAdam Denchfield #define CG_PolakRibierePolyak   3
9c8bcdf1eSAdam Denchfield #define CG_PolakRibierePlus     4
10c8bcdf1eSAdam Denchfield #define CG_DaiYuan              5
11c8bcdf1eSAdam Denchfield #define CG_HagerZhang           6
12c8bcdf1eSAdam Denchfield #define CG_DaiKou               7
13c8bcdf1eSAdam Denchfield #define CG_KouDai               8
14c8bcdf1eSAdam Denchfield #define CG_SSML_BFGS            9
15c8bcdf1eSAdam Denchfield #define CG_SSML_DFP             10
16c8bcdf1eSAdam Denchfield #define CG_SSML_BROYDEN         11
17484c7b14SAdam Denchfield #define CG_PCGradientDescent    12
18484c7b14SAdam Denchfield #define CGTypes                 13
19ac9112b8SAlp Dener 
20484c7b14SAdam Denchfield static const char *CG_Table[64] = {"gd", "hs", "fr", "pr", "prp", "dy", "hz", "dk", "kd", "ssml_bfgs", "ssml_dfp", "ssml_brdn", "pcgd"};
21ac9112b8SAlp Dener 
2261be54a6SAlp Dener #define CG_AS_NONE       0
2361be54a6SAlp Dener #define CG_AS_BERTSEKAS  1
2461be54a6SAlp Dener #define CG_AS_SIZE       2
25ac9112b8SAlp Dener 
2661be54a6SAlp Dener static const char *CG_AS_TYPE[64] = {"none", "bertsekas"};
27ac9112b8SAlp Dener 
2861be54a6SAlp Dener PetscErrorCode TaoBNCGEstimateActiveSet(Tao tao, PetscInt asType)
2961be54a6SAlp Dener {
3061be54a6SAlp Dener   TAO_BNCG                     *cg = (TAO_BNCG *)tao->data;
3161be54a6SAlp Dener 
3261be54a6SAlp Dener   PetscFunctionBegin;
339566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->inactive_old));
3461be54a6SAlp Dener   if (cg->inactive_idx) {
359566063dSJacob Faibussowitsch     PetscCall(ISDuplicate(cg->inactive_idx, &cg->inactive_old));
369566063dSJacob Faibussowitsch     PetscCall(ISCopy(cg->inactive_idx, cg->inactive_old));
3761be54a6SAlp Dener   }
3861be54a6SAlp Dener   switch (asType) {
3961be54a6SAlp Dener   case CG_AS_NONE:
409566063dSJacob Faibussowitsch     PetscCall(ISDestroy(&cg->inactive_idx));
419566063dSJacob Faibussowitsch     PetscCall(VecWhichInactive(tao->XL, tao->solution, cg->unprojected_gradient, tao->XU, PETSC_TRUE, &cg->inactive_idx));
429566063dSJacob Faibussowitsch     PetscCall(ISDestroy(&cg->active_idx));
439566063dSJacob Faibussowitsch     PetscCall(ISComplementVec(cg->inactive_idx, tao->solution, &cg->active_idx));
4461be54a6SAlp Dener     break;
4561be54a6SAlp Dener 
4661be54a6SAlp Dener   case CG_AS_BERTSEKAS:
4761be54a6SAlp Dener     /* Use gradient descent to estimate the active set */
489566063dSJacob Faibussowitsch     PetscCall(VecCopy(cg->unprojected_gradient, cg->W));
499566063dSJacob Faibussowitsch     PetscCall(VecScale(cg->W, -1.0));
50d0609cedSBarry Smith     PetscCall(TaoEstimateActiveBounds(tao->solution, tao->XL, tao->XU, cg->unprojected_gradient, cg->W, cg->work, cg->as_step, &cg->as_tol,
51d0609cedSBarry Smith                                       &cg->active_lower, &cg->active_upper, &cg->active_fixed, &cg->active_idx, &cg->inactive_idx));
52c4b75bccSAlp Dener     break;
5361be54a6SAlp Dener   default:
5461be54a6SAlp Dener     break;
5561be54a6SAlp Dener   }
5661be54a6SAlp Dener   PetscFunctionReturn(0);
5761be54a6SAlp Dener }
5861be54a6SAlp Dener 
59a1318120SAlp Dener PetscErrorCode TaoBNCGBoundStep(Tao tao, PetscInt asType, Vec step)
6061be54a6SAlp Dener {
6161be54a6SAlp Dener   TAO_BNCG                     *cg = (TAO_BNCG *)tao->data;
6261be54a6SAlp Dener 
6361be54a6SAlp Dener   PetscFunctionBegin;
64a1318120SAlp Dener   switch (asType) {
6561be54a6SAlp Dener   case CG_AS_NONE:
669566063dSJacob Faibussowitsch     PetscCall(VecISSet(step, cg->active_idx, 0.0));
6761be54a6SAlp Dener     break;
6861be54a6SAlp Dener 
6961be54a6SAlp Dener   case CG_AS_BERTSEKAS:
709566063dSJacob Faibussowitsch     PetscCall(TaoBoundStep(tao->solution, tao->XL, tao->XU, cg->active_lower, cg->active_upper, cg->active_fixed, 1.0, step));
7161be54a6SAlp Dener     break;
7261be54a6SAlp Dener 
7361be54a6SAlp Dener   default:
7461be54a6SAlp Dener     break;
7561be54a6SAlp Dener   }
7661be54a6SAlp Dener   PetscFunctionReturn(0);
7761be54a6SAlp Dener }
7861be54a6SAlp Dener 
79ac9112b8SAlp Dener static PetscErrorCode TaoSolve_BNCG(Tao tao)
80ac9112b8SAlp Dener {
81ac9112b8SAlp Dener   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
82484c7b14SAdam Denchfield   PetscReal                    step=1.0,gnorm,gnorm2, resnorm;
83c4b75bccSAlp Dener   PetscInt                     nDiff;
84ac9112b8SAlp Dener 
85ac9112b8SAlp Dener   PetscFunctionBegin;
86ac9112b8SAlp Dener   /*   Project the current point onto the feasible set */
879566063dSJacob Faibussowitsch   PetscCall(TaoComputeVariableBounds(tao));
889566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchSetVariableBounds(tao->linesearch,tao->XL,tao->XU));
89ac9112b8SAlp Dener 
90c8bcdf1eSAdam Denchfield   /* Project the initial point onto the feasible region */
919566063dSJacob Faibussowitsch   PetscCall(TaoBoundSolution(tao->solution, tao->XL,tao->XU, 0.0, &nDiff, tao->solution));
92484c7b14SAdam Denchfield 
93414d97d3SAlp Dener   if (nDiff > 0 || !tao->recycle) {
949566063dSJacob Faibussowitsch     PetscCall(TaoComputeObjectiveAndGradient(tao, tao->solution, &cg->f, cg->unprojected_gradient));
95484c7b14SAdam Denchfield   }
969566063dSJacob Faibussowitsch   PetscCall(VecNorm(cg->unprojected_gradient,NORM_2,&gnorm));
973c859ba3SBarry Smith   PetscCheck(!PetscIsInfOrNanReal(cg->f) && !PetscIsInfOrNanReal(gnorm),PetscObjectComm((PetscObject)tao),PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
98ac9112b8SAlp Dener 
9961be54a6SAlp Dener   /* Estimate the active set and compute the projected gradient */
1009566063dSJacob Faibussowitsch   PetscCall(TaoBNCGEstimateActiveSet(tao, cg->as_type));
10161be54a6SAlp Dener 
102ac9112b8SAlp Dener   /* Project the gradient and calculate the norm */
1039566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, tao->gradient));
1049566063dSJacob Faibussowitsch   PetscCall(VecISSet(tao->gradient, cg->active_idx, 0.0));
1059566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->gradient,NORM_2,&gnorm));
106ac9112b8SAlp Dener   gnorm2 = gnorm*gnorm;
107ac9112b8SAlp Dener 
108c8bcdf1eSAdam Denchfield   /* Initialize counters */
109e031d6f5SAlp Dener   tao->niter = 0;
11050b47da0SAdam Denchfield   cg->ls_fails = cg->descent_error = 0;
111c8bcdf1eSAdam Denchfield   cg->resets = -1;
112484c7b14SAdam Denchfield   cg->skipped_updates = cg->pure_gd_steps = 0;
113c8bcdf1eSAdam Denchfield   cg->iter_quad = 0;
114c8bcdf1eSAdam Denchfield 
115c8bcdf1eSAdam Denchfield   /* Convergence test at the starting point. */
116ac9112b8SAlp Dener   tao->reason = TAO_CONTINUE_ITERATING;
117484c7b14SAdam Denchfield 
1189566063dSJacob Faibussowitsch   PetscCall(VecFischer(tao->solution, cg->unprojected_gradient, tao->XL, tao->XU, cg->W));
1199566063dSJacob Faibussowitsch   PetscCall(VecNorm(cg->W, NORM_2, &resnorm));
1203c859ba3SBarry Smith   PetscCheck(!PetscIsInfOrNanReal(resnorm),PetscObjectComm((PetscObject)tao),PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
1219566063dSJacob Faibussowitsch   PetscCall(TaoLogConvergenceHistory(tao, cg->f, resnorm, 0.0, tao->ksp_its));
1229566063dSJacob Faibussowitsch   PetscCall(TaoMonitor(tao, tao->niter, cg->f, resnorm, 0.0, step));
1239566063dSJacob Faibussowitsch   PetscCall((*tao->ops->convergencetest)(tao,tao->cnvP));
124ac9112b8SAlp Dener   if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
125484c7b14SAdam Denchfield   /* Calculate initial direction. */
126414d97d3SAlp Dener   if (!tao->recycle) {
127484c7b14SAdam Denchfield     /* We are not recycling a solution/history from a past TaoSolve */
1289566063dSJacob Faibussowitsch     PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
129ac9112b8SAlp Dener   }
130c8bcdf1eSAdam Denchfield   /* Initial gradient descent step. Scaling by 1.0 also does a decent job for some problems. */
131c8bcdf1eSAdam Denchfield   while (1) {
132e1e80dc8SAlp Dener     /* Call general purpose update function */
133e1e80dc8SAlp Dener     if (tao->ops->update) {
1349566063dSJacob Faibussowitsch       PetscCall((*tao->ops->update)(tao, tao->niter, tao->user_update));
135*7494f0b1SStefano Zampini       PetscCall(TaoComputeObjectiveAndGradient(tao, tao->solution, &cg->f, cg->unprojected_gradient));
136e1e80dc8SAlp Dener     }
1379566063dSJacob Faibussowitsch     PetscCall(TaoBNCGConductIteration(tao, gnorm));
138c8bcdf1eSAdam Denchfield     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
139ac9112b8SAlp Dener   }
140ac9112b8SAlp Dener }
141ac9112b8SAlp Dener 
142ac9112b8SAlp Dener static PetscErrorCode TaoSetUp_BNCG(Tao tao)
143ac9112b8SAlp Dener {
144ac9112b8SAlp Dener   TAO_BNCG         *cg = (TAO_BNCG*)tao->data;
145ac9112b8SAlp Dener 
146ac9112b8SAlp Dener   PetscFunctionBegin;
147c4b75bccSAlp Dener   if (!tao->gradient) {
1489566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&tao->gradient));
149c4b75bccSAlp Dener   }
150c4b75bccSAlp Dener   if (!tao->stepdirection) {
1519566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&tao->stepdirection));
152c4b75bccSAlp Dener   }
153c4b75bccSAlp Dener   if (!cg->W) {
1549566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->W));
155c4b75bccSAlp Dener   }
156c4b75bccSAlp Dener   if (!cg->work) {
1579566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->work));
158c4b75bccSAlp Dener   }
159c8bcdf1eSAdam Denchfield   if (!cg->sk) {
1609566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->sk));
161c8bcdf1eSAdam Denchfield   }
162c8bcdf1eSAdam Denchfield   if (!cg->yk) {
1639566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->yk));
164c8bcdf1eSAdam Denchfield   }
165c4b75bccSAlp Dener   if (!cg->X_old) {
1669566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->X_old));
167c4b75bccSAlp Dener   }
168c4b75bccSAlp Dener   if (!cg->G_old) {
1699566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->G_old));
170c8bcdf1eSAdam Denchfield   }
171c8bcdf1eSAdam Denchfield   if (cg->diag_scaling) {
1729566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->d_work));
1739566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->y_work));
1749566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->g_work));
175c4b75bccSAlp Dener   }
176c4b75bccSAlp Dener   if (!cg->unprojected_gradient) {
1779566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->unprojected_gradient));
178c4b75bccSAlp Dener   }
179c4b75bccSAlp Dener   if (!cg->unprojected_gradient_old) {
1809566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->unprojected_gradient_old));
181c4b75bccSAlp Dener   }
1829566063dSJacob Faibussowitsch   PetscCall(MatLMVMAllocate(cg->B, cg->sk, cg->yk));
183484c7b14SAdam Denchfield   if (cg->pc) {
1849566063dSJacob Faibussowitsch     PetscCall(MatLMVMSetJ0(cg->B, cg->pc));
185484c7b14SAdam Denchfield   }
186ac9112b8SAlp Dener   PetscFunctionReturn(0);
187ac9112b8SAlp Dener }
188ac9112b8SAlp Dener 
189ac9112b8SAlp Dener static PetscErrorCode TaoDestroy_BNCG(Tao tao)
190ac9112b8SAlp Dener {
191ac9112b8SAlp Dener   TAO_BNCG       *cg = (TAO_BNCG*) tao->data;
192ac9112b8SAlp Dener 
193ac9112b8SAlp Dener   PetscFunctionBegin;
194ac9112b8SAlp Dener   if (tao->setupcalled) {
1959566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->W));
1969566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->work));
1979566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->X_old));
1989566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->G_old));
1999566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->unprojected_gradient));
2009566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->unprojected_gradient_old));
2019566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->g_work));
2029566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->d_work));
2039566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->y_work));
2049566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->sk));
2059566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->yk));
206ac9112b8SAlp Dener   }
2079566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_lower));
2089566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_upper));
2099566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_fixed));
2109566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_idx));
2119566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->inactive_idx));
2129566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->inactive_old));
2139566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->new_inactives));
2149566063dSJacob Faibussowitsch   PetscCall(MatDestroy(&cg->B));
215484c7b14SAdam Denchfield   if (cg->pc) {
2169566063dSJacob Faibussowitsch     PetscCall(MatDestroy(&cg->pc));
217484c7b14SAdam Denchfield   }
2189566063dSJacob Faibussowitsch   PetscCall(PetscFree(tao->data));
219ac9112b8SAlp Dener   PetscFunctionReturn(0);
220ac9112b8SAlp Dener }
221ac9112b8SAlp Dener 
222ac9112b8SAlp Dener static PetscErrorCode TaoSetFromOptions_BNCG(PetscOptionItems *PetscOptionsObject,Tao tao)
223ac9112b8SAlp Dener {
224ac9112b8SAlp Dener   TAO_BNCG       *cg = (TAO_BNCG*)tao->data;
225ac9112b8SAlp Dener 
226ac9112b8SAlp Dener   PetscFunctionBegin;
227d0609cedSBarry Smith   PetscOptionsHeadBegin(PetscOptionsObject,"Nonlinear Conjugate Gradient method for unconstrained optimization");
2289566063dSJacob Faibussowitsch   PetscCall(PetscOptionsEList("-tao_bncg_type","cg formula", "", CG_Table, CGTypes, CG_Table[cg->cg_type], &cg->cg_type,NULL));
2298ebe3e4eSStefano Zampini   if (cg->cg_type != CG_SSML_BFGS) cg->alpha = -1.0; /* Setting defaults for non-BFGS methods. User can change it below. */
230484c7b14SAdam Denchfield   if (CG_GradientDescent == cg->cg_type) {
231484c7b14SAdam Denchfield     cg->cg_type = CG_PCGradientDescent;
232484c7b14SAdam Denchfield     /* Set scaling equal to none or, at best, scalar scaling. */
233484c7b14SAdam Denchfield     cg->unscaled_restart = PETSC_TRUE;
234484c7b14SAdam Denchfield     cg->diag_scaling = PETSC_FALSE;
235484c7b14SAdam Denchfield   }
2369566063dSJacob Faibussowitsch   PetscCall(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));
2379566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_hz_eta","(developer) cutoff tolerance for HZ", "", cg->hz_eta,&cg->hz_eta,NULL));
2389566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_eps","(developer) cutoff value for restarts", "", cg->epsilon,&cg->epsilon,NULL));
2399566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_dk_eta","(developer) cutoff tolerance for DK", "", cg->dk_eta,&cg->dk_eta,NULL));
2409566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_xi","(developer) Parameter in the KD method", "", cg->xi,&cg->xi,NULL));
2419566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_theta", "(developer) update parameter for the Broyden method", "", cg->theta, &cg->theta, NULL));
2429566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_hz_theta", "(developer) parameter for the HZ (2006) method", "", cg->hz_theta, &cg->hz_theta, NULL));
2439566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_alpha","(developer) parameter for the scalar scaling","",cg->alpha,&cg->alpha,NULL));
2449566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_bfgs_scale", "(developer) update parameter for bfgs/brdn CG methods", "", cg->bfgs_scale, &cg->bfgs_scale, NULL));
2459566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_dfp_scale", "(developer) update parameter for bfgs/brdn CG methods", "", cg->dfp_scale, &cg->dfp_scale, NULL));
2469566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_diag_scaling","Enable diagonal Broyden-like preconditioning","",cg->diag_scaling,&cg->diag_scaling,NULL));
2479566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_dynamic_restart","(developer) use dynamic restarts as in HZ, DK, KD","",cg->use_dynamic_restart,&cg->use_dynamic_restart,NULL));
2489566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_unscaled_restart","(developer) use unscaled gradient restarts","",cg->unscaled_restart,&cg->unscaled_restart,NULL));
2499566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_zeta", "(developer) Free parameter for the Kou-Dai method", "", cg->zeta, &cg->zeta, NULL));
2509566063dSJacob Faibussowitsch   PetscCall(PetscOptionsInt("-tao_bncg_min_quad", "(developer) Number of iterations with approximate quadratic behavior needed for restart", "", cg->min_quad, &cg->min_quad, NULL));
2519566063dSJacob Faibussowitsch   PetscCall(PetscOptionsInt("-tao_bncg_min_restart_num", "(developer) Number of iterations between restarts (times dimension)", "", cg->min_restart_num, &cg->min_restart_num, NULL));
2529566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_spaced_restart","(developer) Enable regular steepest descent restarting every fixed number of iterations","",cg->spaced_restart,&cg->spaced_restart,NULL));
2539566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_no_scaling","Disable all scaling except in restarts","",cg->no_scaling,&cg->no_scaling,NULL));
254484c7b14SAdam Denchfield   if (cg->no_scaling) {
255484c7b14SAdam Denchfield     cg->diag_scaling = PETSC_FALSE;
256484c7b14SAdam Denchfield     cg->alpha = -1.0;
257484c7b14SAdam Denchfield   }
258b474139fSKarl Rupp   if (cg->alpha == -1.0 && cg->cg_type == CG_KouDai && !cg->diag_scaling) { /* Some more default options that appear to be good. */
259484c7b14SAdam Denchfield     cg->neg_xi = PETSC_TRUE;
260484c7b14SAdam Denchfield   }
2619566063dSJacob Faibussowitsch   PetscCall(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));
2629566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_as_tol", "(developer) initial tolerance used when estimating actively bounded variables","",cg->as_tol,&cg->as_tol,NULL));
2639566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_as_step", "(developer) step length used when estimating actively bounded variables","",cg->as_step,&cg->as_step,NULL));
2649566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_delta_min", "(developer) minimum scaling factor used for scaled gradient restarts","",cg->delta_min,&cg->delta_min,NULL));
2659566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_delta_max", "(developer) maximum scaling factor used for scaled gradient restarts","",cg->delta_max,&cg->delta_max,NULL));
26650b47da0SAdam Denchfield 
267d0609cedSBarry Smith   PetscOptionsHeadEnd();
2689566063dSJacob Faibussowitsch   PetscCall(MatSetOptionsPrefix(cg->B, ((PetscObject)tao)->prefix));
2699566063dSJacob Faibussowitsch   PetscCall(MatAppendOptionsPrefix(cg->B, "tao_bncg_"));
2709566063dSJacob Faibussowitsch   PetscCall(MatSetFromOptions(cg->B));
271ac9112b8SAlp Dener   PetscFunctionReturn(0);
272ac9112b8SAlp Dener }
273ac9112b8SAlp Dener 
274ac9112b8SAlp Dener static PetscErrorCode TaoView_BNCG(Tao tao, PetscViewer viewer)
275ac9112b8SAlp Dener {
276ac9112b8SAlp Dener   PetscBool      isascii;
277ac9112b8SAlp Dener   TAO_BNCG       *cg = (TAO_BNCG*)tao->data;
278ac9112b8SAlp Dener 
279ac9112b8SAlp Dener   PetscFunctionBegin;
2809566063dSJacob Faibussowitsch   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
281ac9112b8SAlp Dener   if (isascii) {
2829566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPushTab(viewer));
2839566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "CG Type: %s\n", CG_Table[cg->cg_type]));
28463a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Skipped Stepdirection Updates: %" PetscInt_FMT "\n", cg->skipped_updates));
28563a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Scaled gradient steps: %" PetscInt_FMT "\n", cg->resets));
28663a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Pure gradient steps: %" PetscInt_FMT "\n", cg->pure_gd_steps));
28763a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Not a descent direction: %" PetscInt_FMT "\n", cg->descent_error));
28863a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Line search fails: %" PetscInt_FMT "\n", cg->ls_fails));
289484c7b14SAdam Denchfield     if (cg->diag_scaling) {
2909566063dSJacob Faibussowitsch       PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
291484c7b14SAdam Denchfield       if (isascii) {
2929566063dSJacob Faibussowitsch         PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_INFO));
2939566063dSJacob Faibussowitsch         PetscCall(MatView(cg->B, viewer));
2949566063dSJacob Faibussowitsch         PetscCall(PetscViewerPopFormat(viewer));
295484c7b14SAdam Denchfield       }
296484c7b14SAdam Denchfield     }
2979566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPopTab(viewer));
298ac9112b8SAlp Dener   }
299ac9112b8SAlp Dener   PetscFunctionReturn(0);
300ac9112b8SAlp Dener }
301ac9112b8SAlp Dener 
302c8bcdf1eSAdam Denchfield PetscErrorCode TaoBNCGComputeScalarScaling(PetscReal yty, PetscReal yts, PetscReal sts, PetscReal *scale, PetscReal alpha)
303c8bcdf1eSAdam Denchfield {
304c8bcdf1eSAdam Denchfield   PetscReal a, b, c, sig1, sig2;
305c8bcdf1eSAdam Denchfield 
306c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
307c8bcdf1eSAdam Denchfield   *scale = 0.0;
3088ebe3e4eSStefano Zampini   if (1.0 == alpha) *scale = yts/yty;
3098ebe3e4eSStefano Zampini   else if (0.0 == alpha) *scale = sts/yts;
31050b47da0SAdam Denchfield   else if (-1.0 == alpha) *scale = 1.0;
311c8bcdf1eSAdam Denchfield   else {
312c8bcdf1eSAdam Denchfield     a = yty;
313c8bcdf1eSAdam Denchfield     b = yts;
314c8bcdf1eSAdam Denchfield     c = sts;
315c8bcdf1eSAdam Denchfield     a *= alpha;
316c8bcdf1eSAdam Denchfield     b *= -(2.0*alpha - 1.0);
317c8bcdf1eSAdam Denchfield     c *= alpha - 1.0;
318c8bcdf1eSAdam Denchfield     sig1 = (-b + PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
319c8bcdf1eSAdam Denchfield     sig2 = (-b - PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
320c8bcdf1eSAdam Denchfield     /* accept the positive root as the scalar */
3218ebe3e4eSStefano Zampini     if (sig1 > 0.0) *scale = sig1;
3228ebe3e4eSStefano Zampini     else if (sig2 > 0.0) *scale = sig2;
3238ebe3e4eSStefano Zampini     else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_CONV_FAILED, "Cannot find positive scalar");
324c8bcdf1eSAdam Denchfield   }
325c8bcdf1eSAdam Denchfield   PetscFunctionReturn(0);
326c8bcdf1eSAdam Denchfield }
327c8bcdf1eSAdam Denchfield 
328ac9112b8SAlp Dener /*MC
329ac9112b8SAlp Dener   TAOBNCG - Bound-constrained Nonlinear Conjugate Gradient method.
330ac9112b8SAlp Dener 
331ac9112b8SAlp Dener   Options Database Keys:
33250b47da0SAdam Denchfield + -tao_bncg_recycle - enable recycling the latest calculated gradient vector in subsequent TaoSolve() calls (currently disabled)
333c4b75bccSAlp Dener . -tao_bncg_eta <r> - restart tolerance
33461be54a6SAlp Dener . -tao_bncg_type <taocg_type> - cg formula
335c4b75bccSAlp Dener . -tao_bncg_as_type <none,bertsekas> - active set estimation method
336c4b75bccSAlp Dener . -tao_bncg_as_tol <r> - tolerance used in Bertsekas active-set estimation
337c4b75bccSAlp Dener . -tao_bncg_as_step <r> - trial step length used in Bertsekas active-set estimation
33850b47da0SAdam Denchfield . -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.
33950b47da0SAdam Denchfield . -tao_bncg_theta <r> - convex combination parameter for the Broyden method
34050b47da0SAdam Denchfield . -tao_bncg_hz_eta <r> - cutoff tolerance for the beta term in the HZ, DK methods
34150b47da0SAdam Denchfield . -tao_bncg_dk_eta <r> - cutoff tolerance for the beta term in the HZ, DK methods
34250b47da0SAdam Denchfield . -tao_bncg_xi <r> - Multiplicative constant of the gamma term in the KD method
34350b47da0SAdam Denchfield . -tao_bncg_hz_theta <r> - Multiplicative constant of the theta term for the HZ method
34450b47da0SAdam Denchfield . -tao_bncg_bfgs_scale <r> - Scaling parameter of the bfgs contribution to the scalar Broyden method
34550b47da0SAdam Denchfield . -tao_bncg_dfp_scale <r> - Scaling parameter of the dfp contribution to the scalar Broyden method
34650b47da0SAdam Denchfield . -tao_bncg_diag_scaling <b> - Whether or not to use diagonal initialization/preconditioning for the CG methods. Default True.
34750b47da0SAdam Denchfield . -tao_bncg_dynamic_restart <b> - use dynamic restart strategy in the HZ, DK, KD methods
34850b47da0SAdam Denchfield . -tao_bncg_unscaled_restart <b> - whether or not to scale the gradient when doing gradient descent restarts
34950b47da0SAdam Denchfield . -tao_bncg_zeta <r> - Scaling parameter in the KD method
350484c7b14SAdam Denchfield . -tao_bncg_delta_min <r> - Minimum bound for rescaling during restarted gradient descent steps
351484c7b14SAdam Denchfield . -tao_bncg_delta_max <r> - Maximum bound for rescaling during restarted gradient descent steps
35250b47da0SAdam Denchfield . -tao_bncg_min_quad <i> - Number of quadratic-like steps in a row necessary to do a dynamic restart
35350b47da0SAdam Denchfield . -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
35450b47da0SAdam Denchfield . -tao_bncg_spaced_restart <b> - whether or not to do gradient descent steps every x*n iterations
355484c7b14SAdam Denchfield . -tao_bncg_no_scaling <b> - If true, eliminates all scaling, including defaults.
3563850be85SAlp Dener - -tao_bncg_neg_xi <b> - Whether or not to use negative xi in the KD method under certain conditions
357ac9112b8SAlp Dener 
358ac9112b8SAlp Dener   Notes:
359ac9112b8SAlp Dener     CG formulas are:
3603850be85SAlp Dener + "gd" - Gradient Descent
3613850be85SAlp Dener . "fr" - Fletcher-Reeves
3623850be85SAlp Dener . "pr" - Polak-Ribiere-Polyak
3633850be85SAlp Dener . "prp" - Polak-Ribiere-Plus
3643850be85SAlp Dener . "hs" - Hestenes-Steifel
3653850be85SAlp Dener . "dy" - Dai-Yuan
3663850be85SAlp Dener . "ssml_bfgs" - Self-Scaling Memoryless BFGS
3673850be85SAlp Dener . "ssml_dfp"  - Self-Scaling Memoryless DFP
3683850be85SAlp Dener . "ssml_brdn" - Self-Scaling Memoryless Broyden
3693850be85SAlp Dener . "hz" - Hager-Zhang (CG_DESCENT 5.3)
3703850be85SAlp Dener . "dk" - Dai-Kou (2013)
3713850be85SAlp Dener - "kd" - Kou-Dai (2015)
3729abc5736SPatrick Sanan 
373ac9112b8SAlp Dener   Level: beginner
374ac9112b8SAlp Dener M*/
375ac9112b8SAlp Dener 
376ac9112b8SAlp Dener PETSC_EXTERN PetscErrorCode TaoCreate_BNCG(Tao tao)
377ac9112b8SAlp Dener {
378ac9112b8SAlp Dener   TAO_BNCG       *cg;
379ac9112b8SAlp Dener   const char     *morethuente_type = TAOLINESEARCHMT;
380ac9112b8SAlp Dener 
381ac9112b8SAlp Dener   PetscFunctionBegin;
382ac9112b8SAlp Dener   tao->ops->setup = TaoSetUp_BNCG;
383ac9112b8SAlp Dener   tao->ops->solve = TaoSolve_BNCG;
384ac9112b8SAlp Dener   tao->ops->view = TaoView_BNCG;
385ac9112b8SAlp Dener   tao->ops->setfromoptions = TaoSetFromOptions_BNCG;
386ac9112b8SAlp Dener   tao->ops->destroy = TaoDestroy_BNCG;
387ac9112b8SAlp Dener 
388ac9112b8SAlp Dener   /* Override default settings (unless already changed) */
389ac9112b8SAlp Dener   if (!tao->max_it_changed) tao->max_it = 2000;
390ac9112b8SAlp Dener   if (!tao->max_funcs_changed) tao->max_funcs = 4000;
391ac9112b8SAlp Dener 
392ac9112b8SAlp Dener   /*  Note: nondefault values should be used for nonlinear conjugate gradient  */
393ac9112b8SAlp Dener   /*  method.  In particular, gtol should be less that 0.5; the value used in  */
394ac9112b8SAlp Dener   /*  Nocedal and Wright is 0.10.  We use the default values for the  */
395ac9112b8SAlp Dener   /*  linesearch because it seems to work better. */
3969566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchCreate(((PetscObject)tao)->comm, &tao->linesearch));
3979566063dSJacob Faibussowitsch   PetscCall(PetscObjectIncrementTabLevel((PetscObject)tao->linesearch, (PetscObject)tao, 1));
3989566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchSetType(tao->linesearch, morethuente_type));
3999566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchUseTaoRoutines(tao->linesearch, tao));
400ac9112b8SAlp Dener 
4019566063dSJacob Faibussowitsch   PetscCall(PetscNewLog(tao,&cg));
402ac9112b8SAlp Dener   tao->data = (void*)cg;
4039566063dSJacob Faibussowitsch   PetscCall(KSPInitializePackage());
4049566063dSJacob Faibussowitsch   PetscCall(MatCreate(PetscObjectComm((PetscObject)tao), &cg->B));
4059566063dSJacob Faibussowitsch   PetscCall(PetscObjectIncrementTabLevel((PetscObject)cg->B, (PetscObject)tao, 1));
4069566063dSJacob Faibussowitsch   PetscCall(MatSetType(cg->B, MATLMVMDIAGBROYDEN));
40750b47da0SAdam Denchfield 
408484c7b14SAdam Denchfield   cg->pc = NULL;
409484c7b14SAdam Denchfield 
41050b47da0SAdam Denchfield   cg->dk_eta = 0.5;
41150b47da0SAdam Denchfield   cg->hz_eta = 0.4;
412c8bcdf1eSAdam Denchfield   cg->dynamic_restart = PETSC_FALSE;
413c8bcdf1eSAdam Denchfield   cg->unscaled_restart = PETSC_FALSE;
414484c7b14SAdam Denchfield   cg->no_scaling = PETSC_FALSE;
415484c7b14SAdam Denchfield   cg->delta_min = 1e-7;
416484c7b14SAdam Denchfield   cg->delta_max = 100;
417c8bcdf1eSAdam Denchfield   cg->theta = 1.0;
418c8bcdf1eSAdam Denchfield   cg->hz_theta = 1.0;
419c8bcdf1eSAdam Denchfield   cg->dfp_scale = 1.0;
420c8bcdf1eSAdam Denchfield   cg->bfgs_scale = 1.0;
42150b47da0SAdam Denchfield   cg->zeta = 0.1;
42250b47da0SAdam Denchfield   cg->min_quad = 6;
423c8bcdf1eSAdam Denchfield   cg->min_restart_num = 6; /* As in CG_DESCENT and KD2015*/
424c8bcdf1eSAdam Denchfield   cg->xi = 1.0;
42550b47da0SAdam Denchfield   cg->neg_xi = PETSC_TRUE;
426c8bcdf1eSAdam Denchfield   cg->spaced_restart = PETSC_FALSE;
427c8bcdf1eSAdam Denchfield   cg->tol_quad = 1e-8;
42861be54a6SAlp Dener   cg->as_step = 0.001;
42961be54a6SAlp Dener   cg->as_tol = 0.001;
43050b47da0SAdam Denchfield   cg->eps_23 = PetscPowReal(PETSC_MACHINE_EPSILON, 2.0/3.0); /* Just a little tighter*/
43161be54a6SAlp Dener   cg->as_type = CG_AS_BERTSEKAS;
432c8bcdf1eSAdam Denchfield   cg->cg_type = CG_SSML_BFGS;
433c8bcdf1eSAdam Denchfield   cg->alpha = 1.0;
434c8bcdf1eSAdam Denchfield   cg->diag_scaling = PETSC_TRUE;
435c8bcdf1eSAdam Denchfield   PetscFunctionReturn(0);
436c8bcdf1eSAdam Denchfield }
437c8bcdf1eSAdam Denchfield 
438c8bcdf1eSAdam Denchfield PetscErrorCode TaoBNCGResetUpdate(Tao tao, PetscReal gnormsq)
439c8bcdf1eSAdam Denchfield {
440c8bcdf1eSAdam Denchfield    TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
441c8bcdf1eSAdam Denchfield    PetscReal         scaling;
442c8bcdf1eSAdam Denchfield 
443c8bcdf1eSAdam Denchfield    PetscFunctionBegin;
444c8bcdf1eSAdam Denchfield    ++cg->resets;
445c8bcdf1eSAdam Denchfield    scaling = 2.0 * PetscMax(1.0, PetscAbsScalar(cg->f)) / PetscMax(gnormsq, cg->eps_23);
446484c7b14SAdam Denchfield    scaling = PetscMin(cg->delta_max, PetscMax(cg->delta_min, scaling));
447484c7b14SAdam Denchfield    if (cg->unscaled_restart) {
448484c7b14SAdam Denchfield      scaling = 1.0;
449484c7b14SAdam Denchfield      ++cg->pure_gd_steps;
450484c7b14SAdam Denchfield    }
4519566063dSJacob Faibussowitsch    PetscCall(VecAXPBY(tao->stepdirection, -scaling, 0.0, tao->gradient));
452c8bcdf1eSAdam Denchfield    /* Also want to reset our diagonal scaling with each restart */
453c8bcdf1eSAdam Denchfield    if (cg->diag_scaling) {
4549566063dSJacob Faibussowitsch      PetscCall(MatLMVMReset(cg->B, PETSC_FALSE));
455c8bcdf1eSAdam Denchfield    }
456c8bcdf1eSAdam Denchfield    PetscFunctionReturn(0);
457c8bcdf1eSAdam Denchfield  }
458c8bcdf1eSAdam Denchfield 
459c8bcdf1eSAdam Denchfield PetscErrorCode TaoBNCGCheckDynamicRestart(Tao tao, PetscReal stepsize, PetscReal gd, PetscReal gd_old, PetscBool *dynrestart, PetscReal fold)
460c8bcdf1eSAdam Denchfield {
461c8bcdf1eSAdam Denchfield    TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
462c8bcdf1eSAdam Denchfield    PetscReal         quadinterp;
463c8bcdf1eSAdam Denchfield 
464c8bcdf1eSAdam Denchfield    PetscFunctionBegin;
46550b47da0SAdam Denchfield    if (cg->f < cg->min_quad/10) {
46650b47da0SAdam Denchfield      *dynrestart = PETSC_FALSE;
46750b47da0SAdam Denchfield      PetscFunctionReturn(0);
46850b47da0SAdam Denchfield    } /* just skip this since this strategy doesn't work well for functions near zero */
469484c7b14SAdam Denchfield    quadinterp = 2.0*(cg->f - fold)/(stepsize*(gd + gd_old));
47050b47da0SAdam Denchfield    if (PetscAbs(quadinterp - 1.0) < cg->tol_quad) ++cg->iter_quad;
471c8bcdf1eSAdam Denchfield    else {
472c8bcdf1eSAdam Denchfield      cg->iter_quad = 0;
473c8bcdf1eSAdam Denchfield      *dynrestart = PETSC_FALSE;
474c8bcdf1eSAdam Denchfield    }
475c8bcdf1eSAdam Denchfield    if (cg->iter_quad >= cg->min_quad) {
476c8bcdf1eSAdam Denchfield      cg->iter_quad = 0;
477c8bcdf1eSAdam Denchfield      *dynrestart = PETSC_TRUE;
478c8bcdf1eSAdam Denchfield    }
479c8bcdf1eSAdam Denchfield    PetscFunctionReturn(0);
480c8bcdf1eSAdam Denchfield  }
481c8bcdf1eSAdam Denchfield 
4828ca2df50S PETSC_INTERN PetscErrorCode TaoBNCGStepDirectionUpdate(Tao tao, PetscReal gnorm2, PetscReal step, PetscReal fold, PetscReal gnorm2_old, PetscReal dnorm, PetscBool pcgd_fallback)
48350b47da0SAdam Denchfield {
484c8bcdf1eSAdam Denchfield   TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
48550b47da0SAdam Denchfield   PetscReal         gamma = 1.0, tau_k, beta;
486484c7b14SAdam Denchfield   PetscReal         tmp = 1.0, ynorm, ynorm2 = 1.0, snorm = 1.0, dk_yk=1.0, gd;
48750b47da0SAdam Denchfield   PetscReal         gkp1_yk, gd_old, tau_bfgs, tau_dfp, gkp1D_yk, gtDg;
488c8bcdf1eSAdam Denchfield   PetscInt          dim;
489484c7b14SAdam Denchfield   PetscBool         cg_restart = PETSC_FALSE;
490c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
491c8bcdf1eSAdam Denchfield 
49250b47da0SAdam Denchfield   /* Local curvature check to see if we need to restart */
493414d97d3SAlp Dener   if (tao->niter >= 1 || tao->recycle) {
4949566063dSJacob Faibussowitsch     PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
4959566063dSJacob Faibussowitsch     PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
496c8bcdf1eSAdam Denchfield     ynorm2 = ynorm*ynorm;
4979566063dSJacob Faibussowitsch     PetscCall(VecDot(cg->yk, tao->stepdirection, &dk_yk));
498484c7b14SAdam Denchfield     if (step*dnorm < PETSC_MACHINE_EPSILON || step*dk_yk < PETSC_MACHINE_EPSILON) {
499e2570530SAlp Dener       cg_restart = PETSC_TRUE;
500484c7b14SAdam Denchfield       ++cg->skipped_updates;
501484c7b14SAdam Denchfield     }
50250b47da0SAdam Denchfield     if (cg->spaced_restart) {
5039566063dSJacob Faibussowitsch       PetscCall(VecGetSize(tao->gradient, &dim));
504e2570530SAlp Dener       if (tao->niter % (dim*cg->min_restart_num)) cg_restart = PETSC_TRUE;
50550b47da0SAdam Denchfield     }
50650b47da0SAdam Denchfield   }
50750b47da0SAdam Denchfield   /* If the user wants regular restarts, do it every 6n iterations, where n=dimension */
50850b47da0SAdam Denchfield   if (cg->spaced_restart) {
5099566063dSJacob Faibussowitsch     PetscCall(VecGetSize(tao->gradient, &dim));
510e2570530SAlp Dener     if (0 == tao->niter % (6*dim)) cg_restart = PETSC_TRUE;
51150b47da0SAdam Denchfield   }
51250b47da0SAdam Denchfield   /* Compute the diagonal scaling vector if applicable */
51350b47da0SAdam Denchfield   if (cg->diag_scaling) {
5149566063dSJacob Faibussowitsch     PetscCall(MatLMVMUpdate(cg->B, tao->solution, tao->gradient));
51550b47da0SAdam Denchfield   }
51650b47da0SAdam Denchfield 
517484c7b14SAdam Denchfield   /* A note on diagonal scaling (to be added to paper):
518484c7b14SAdam Denchfield    For the FR, PR, PRP, and DY methods, the diagonally scaled versions
519484c7b14SAdam Denchfield    must be derived as a preconditioned CG method rather than as
520484c7b14SAdam Denchfield    a Hessian initialization like in the Broyden methods. */
52150b47da0SAdam Denchfield 
522484c7b14SAdam Denchfield   /* In that case, one writes the objective function as
523484c7b14SAdam Denchfield    f(x) \equiv f(Ay). Gradient evaluations yield g(x_k) = A g(Ay_k) = A g(x_k).
524484c7b14SAdam Denchfield    Furthermore, the direction d_k \equiv (x_k - x_{k-1})/step according to
525484c7b14SAdam Denchfield    HZ (2006) becomes A^{-1} d_k, such that d_k^T g_k remains the
526484c7b14SAdam Denchfield    same under preconditioning. Note that A is diagonal, such that A^T = A. */
52750b47da0SAdam Denchfield 
528484c7b14SAdam Denchfield   /* This yields questions like what the dot product d_k^T y_k
529484c7b14SAdam Denchfield    should look like. HZ mistakenly treats that as the same under
530484c7b14SAdam Denchfield    preconditioning, but that is not necessarily true. */
53150b47da0SAdam Denchfield 
532484c7b14SAdam Denchfield   /* Observe y_k \equiv g_k - g_{k-1}, and under the P.C. transformation,
533484c7b14SAdam Denchfield    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}),
534484c7b14SAdam Denchfield    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
535484c7b14SAdam Denchfield    NOT the same if our preconditioning matrix is updated between iterations.
536484c7b14SAdam Denchfield    This same issue is found when considering dot products of the form g_{k+1}^T y_k. */
53750b47da0SAdam Denchfield 
53850b47da0SAdam Denchfield   /* Compute CG step direction */
53950b47da0SAdam Denchfield   if (cg_restart) {
5409566063dSJacob Faibussowitsch     PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
541484c7b14SAdam Denchfield   } else if (pcgd_fallback) {
542484c7b14SAdam Denchfield     /* Just like preconditioned CG */
5439566063dSJacob Faibussowitsch     PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5449566063dSJacob Faibussowitsch     PetscCall(VecAXPBY(tao->stepdirection, -1.0, 0.0, cg->g_work));
54550b47da0SAdam Denchfield   } else if (ynorm2 > PETSC_MACHINE_EPSILON) {
54650b47da0SAdam Denchfield     switch (cg->cg_type) {
547484c7b14SAdam Denchfield     case CG_PCGradientDescent:
54850b47da0SAdam Denchfield       if (!cg->diag_scaling) {
549484c7b14SAdam Denchfield         if (!cg->no_scaling) {
55050b47da0SAdam Denchfield         cg->sts = step*step*dnorm*dnorm;
5519566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->sts, &tau_k, cg->alpha));
552484c7b14SAdam Denchfield         } else {
553484c7b14SAdam Denchfield           tau_k = 1.0;
554484c7b14SAdam Denchfield           ++cg->pure_gd_steps;
555484c7b14SAdam Denchfield         }
5569566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, 0.0, tao->gradient));
55750b47da0SAdam Denchfield       } else {
5589566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5599566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, 0.0, cg->g_work));
56050b47da0SAdam Denchfield       }
56150b47da0SAdam Denchfield       break;
562484c7b14SAdam Denchfield 
56350b47da0SAdam Denchfield     case CG_HestenesStiefel:
56450b47da0SAdam Denchfield       /* Classic Hestenes-Stiefel method, modified with scalar and diagonal preconditioning. */
56550b47da0SAdam Denchfield       if (!cg->diag_scaling) {
56650b47da0SAdam Denchfield         cg->sts = step*step*dnorm*dnorm;
5679566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
5689566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->sts, &tau_k, cg->alpha));
56950b47da0SAdam Denchfield         beta = tau_k*gkp1_yk/dk_yk;
5709566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
57150b47da0SAdam Denchfield       } else {
5729566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5739566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
57450b47da0SAdam Denchfield         beta = gkp1_yk/dk_yk;
5759566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
57650b47da0SAdam Denchfield       }
577c8bcdf1eSAdam Denchfield       break;
578484c7b14SAdam Denchfield 
579c8bcdf1eSAdam Denchfield     case CG_FletcherReeves:
5809566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
5819566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
5829566063dSJacob Faibussowitsch       PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
58350b47da0SAdam Denchfield       ynorm2 = ynorm*ynorm;
5849566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->yk, tao->stepdirection, &dk_yk));
58550b47da0SAdam Denchfield       if (!cg->diag_scaling) {
5869566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, step*step*dnorm*dnorm, &tau_k, cg->alpha));
58750b47da0SAdam Denchfield         beta = tau_k*gnorm2/gnorm2_old;
5889566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
58950b47da0SAdam Denchfield       } else {
5909566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old)); /* Before it's updated */
5919566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5929566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->gradient, cg->g_work, &tmp));
59350b47da0SAdam Denchfield         beta = tmp/gnorm2_old;
5949566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
59550b47da0SAdam Denchfield       }
596c8bcdf1eSAdam Denchfield       break;
597484c7b14SAdam Denchfield 
59850b47da0SAdam Denchfield     case CG_PolakRibierePolyak:
59950b47da0SAdam Denchfield       snorm = step*dnorm;
60050b47da0SAdam Denchfield       if (!cg->diag_scaling) {
6019566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
6029566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
6039566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, snorm*snorm, &tau_k, cg->alpha));
60450b47da0SAdam Denchfield         beta = tau_k*gkp1_yk/gnorm2_old;
6059566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
60650b47da0SAdam Denchfield       } else {
6079566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old));
6089566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6099566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
61050b47da0SAdam Denchfield         beta = gkp1_yk/gnorm2_old;
6119566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
61250b47da0SAdam Denchfield       }
613c8bcdf1eSAdam Denchfield       break;
614484c7b14SAdam Denchfield 
615c8bcdf1eSAdam Denchfield     case CG_PolakRibierePlus:
6169566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
6179566063dSJacob Faibussowitsch       PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
61850b47da0SAdam Denchfield       ynorm2 = ynorm*ynorm;
61950b47da0SAdam Denchfield       if (!cg->diag_scaling) {
6209566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
6219566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
6229566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, snorm*snorm, &tau_k, cg->alpha));
62350b47da0SAdam Denchfield         beta = tau_k*gkp1_yk/gnorm2_old;
62450b47da0SAdam Denchfield         beta = PetscMax(beta, 0.0);
6259566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
62650b47da0SAdam Denchfield       } else {
6279566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old)); /* Old gtDg */
6289566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6299566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
63050b47da0SAdam Denchfield         beta = gkp1_yk/gnorm2_old;
63150b47da0SAdam Denchfield         beta = PetscMax(beta, 0.0);
6329566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
63350b47da0SAdam Denchfield       }
634c8bcdf1eSAdam Denchfield       break;
635484c7b14SAdam Denchfield 
636484c7b14SAdam Denchfield     case CG_DaiYuan:
637484c7b14SAdam Denchfield       /* Dai, Yu-Hong, and Yaxiang Yuan. "A nonlinear conjugate gradient method with a strong global convergence property."
638484c7b14SAdam Denchfield          SIAM Journal on optimization 10, no. 1 (1999): 177-182. */
63950b47da0SAdam Denchfield       if (!cg->diag_scaling) {
6409566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, tao->gradient, &gd));
6419566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
6429566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->yts, &tau_k, cg->alpha));
64350b47da0SAdam Denchfield         beta = tau_k*gnorm2/(gd - gd_old);
6449566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
64550b47da0SAdam Denchfield       } else {
6469566063dSJacob Faibussowitsch         PetscCall(MatMult(cg->B, tao->stepdirection, cg->d_work));
6479566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6489566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, tao->gradient, &gtDg));
6499566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, cg->G_old, &gd_old));
6509566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->d_work, cg->g_work, &dk_yk));
65150b47da0SAdam Denchfield         dk_yk = dk_yk - gd_old;
65250b47da0SAdam Denchfield         beta = gtDg/dk_yk;
6539566063dSJacob Faibussowitsch         PetscCall(VecScale(cg->d_work, beta));
6549566063dSJacob Faibussowitsch         PetscCall(VecWAXPY(tao->stepdirection, -1.0, cg->g_work, cg->d_work));
65550b47da0SAdam Denchfield       }
656c8bcdf1eSAdam Denchfield       break;
657484c7b14SAdam Denchfield 
658c8bcdf1eSAdam Denchfield     case CG_HagerZhang:
659484c7b14SAdam Denchfield       /* Hager, William W., and Hongchao Zhang. "Algorithm 851: CG_DESCENT, a conjugate gradient method with guaranteed descent."
660484c7b14SAdam Denchfield          ACM Transactions on Mathematical Software (TOMS) 32, no. 1 (2006): 113-137. */
6619566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
6629566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
6639566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
66450b47da0SAdam Denchfield       snorm = dnorm*step;
66550b47da0SAdam Denchfield       cg->yts = step*dk_yk;
666c8bcdf1eSAdam Denchfield       if (cg->use_dynamic_restart) {
6679566063dSJacob Faibussowitsch         PetscCall(TaoBNCGCheckDynamicRestart(tao, step, gd, gd_old, &cg->dynamic_restart, fold));
668c8bcdf1eSAdam Denchfield       }
66950b47da0SAdam Denchfield       if (cg->dynamic_restart) {
6709566063dSJacob Faibussowitsch         PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
671c8bcdf1eSAdam Denchfield       } else {
672c8bcdf1eSAdam Denchfield         if (!cg->diag_scaling) {
6739566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
6749566063dSJacob Faibussowitsch           PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha));
675c8bcdf1eSAdam Denchfield           /* Supplying cg->alpha = -1.0 will give the CG_DESCENT 5.3 special case of tau_k = 1.0 */
676c8bcdf1eSAdam Denchfield           tmp = gd/dk_yk;
677c8bcdf1eSAdam Denchfield           beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk));
678c8bcdf1eSAdam Denchfield           /* Bound beta as in CG_DESCENT 5.3, as implemented, with the third comparison from DK 2013 */
67950b47da0SAdam Denchfield           beta = PetscMax(PetscMax(beta, cg->hz_eta*tau_k*gd_old/(dnorm*dnorm)), cg->dk_eta*tau_k*gd/(dnorm*dnorm));
680c8bcdf1eSAdam Denchfield           /* d <- -t*g + beta*t*d */
6819566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
682c8bcdf1eSAdam Denchfield         } else {
683c8bcdf1eSAdam Denchfield           /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
684c8bcdf1eSAdam Denchfield           cg->yty = ynorm2;
685c8bcdf1eSAdam Denchfield           cg->sts = snorm*snorm;
68650b47da0SAdam Denchfield           /* Apply the diagonal scaling to all my vectors */
6879566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6889566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
6899566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->stepdirection, cg->d_work));
690c8bcdf1eSAdam Denchfield           /* Construct the constant ytDgkp1 */
6919566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
692c8bcdf1eSAdam Denchfield           /* Construct the constant for scaling Dkyk in the update */
693c8bcdf1eSAdam Denchfield           tmp = gd/dk_yk;
6949566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
695c8bcdf1eSAdam Denchfield           tau_k = -tau_k*gd/(dk_yk*dk_yk);
696c8bcdf1eSAdam Denchfield           /* beta is the constant which adds the dk contribution */
697484c7b14SAdam Denchfield           beta = gkp1_yk/dk_yk + cg->hz_theta*tau_k; /* HZ; (1.15) from DK 2013 */
698c8bcdf1eSAdam Denchfield           /* From HZ2013, modified to account for diagonal scaling*/
6999566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->G_old, cg->d_work, &gd_old));
7009566063dSJacob Faibussowitsch           PetscCall(VecDot(tao->stepdirection, cg->g_work, &gd));
70150b47da0SAdam Denchfield           beta = PetscMax(PetscMax(beta, cg->hz_eta*gd_old/(dnorm*dnorm)), cg->dk_eta*gd/(dnorm*dnorm));
702c8bcdf1eSAdam Denchfield           /* Do the update */
7039566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
70450b47da0SAdam Denchfield         }
70550b47da0SAdam Denchfield       }
706c8bcdf1eSAdam Denchfield       break;
707484c7b14SAdam Denchfield 
708c8bcdf1eSAdam Denchfield     case CG_DaiKou:
709484c7b14SAdam Denchfield       /* Dai, Yu-Hong, and Cai-Xia Kou. "A nonlinear conjugate gradient algorithm with an optimal property and an improved Wolfe line search."
710484c7b14SAdam Denchfield          SIAM Journal on Optimization 23, no. 1 (2013): 296-320. */
7119566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
7129566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
7139566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
71450b47da0SAdam Denchfield       snorm = step*dnorm;
71550b47da0SAdam Denchfield       cg->yts = dk_yk*step;
716c8bcdf1eSAdam Denchfield       if (!cg->diag_scaling) {
7179566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
7189566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha));
719c8bcdf1eSAdam Denchfield         /* Use cg->alpha = -1.0 to get tau_k = 1.0 as in CG_DESCENT 5.3 */
720c8bcdf1eSAdam Denchfield         tmp = gd/dk_yk;
72150b47da0SAdam Denchfield         beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk) + gd/(dnorm*dnorm)) - step*gd/dk_yk;
72250b47da0SAdam Denchfield         beta = PetscMax(PetscMax(beta, cg->hz_eta*tau_k*gd_old/(dnorm*dnorm)), cg->dk_eta*tau_k*gd/(dnorm*dnorm));
723c8bcdf1eSAdam Denchfield         /* d <- -t*g + beta*t*d */
7249566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, 0.0, beta, tao->gradient, cg->yk));
725c8bcdf1eSAdam Denchfield       } else {
726c8bcdf1eSAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
727c8bcdf1eSAdam Denchfield         cg->yty = ynorm2;
728c8bcdf1eSAdam Denchfield         cg->sts = snorm*snorm;
7299566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
7309566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
7319566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->stepdirection, cg->d_work));
732c8bcdf1eSAdam Denchfield         /* Construct the constant ytDgkp1 */
7339566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
7349566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
735c8bcdf1eSAdam Denchfield         tau_k = tau_k*gd/(dk_yk*dk_yk);
736c8bcdf1eSAdam Denchfield         tmp = gd/dk_yk;
737c8bcdf1eSAdam Denchfield         /* beta is the constant which adds the dk contribution */
738484c7b14SAdam Denchfield         beta = gkp1_yk/dk_yk - step*tmp - tau_k;
739c8bcdf1eSAdam Denchfield         /* Update this for the last term in beta */
7409566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, tao->stepdirection, &dk_yk));
741c8bcdf1eSAdam Denchfield         beta += tmp*dk_yk/(dnorm*dnorm); /* projection of y_work onto dk */
7429566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, cg->g_work, &gd));
7439566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->d_work, &gd_old));
74450b47da0SAdam Denchfield         beta = PetscMax(PetscMax(beta, cg->hz_eta*gd_old/(dnorm*dnorm)), cg->dk_eta*gd/(dnorm*dnorm));
745c8bcdf1eSAdam Denchfield         /* Do the update */
7469566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
74750b47da0SAdam Denchfield       }
748c8bcdf1eSAdam Denchfield       break;
749484c7b14SAdam Denchfield 
750c8bcdf1eSAdam Denchfield     case CG_KouDai:
751110fc3b0SBarry Smith       /* Kou, Cai-Xia, and Yu-Hong Dai. "A modified self-scaling memoryless Broyden-Fletcher-Goldfarb-Shanno method for unconstrained optimization."
752484c7b14SAdam Denchfield          Journal of Optimization Theory and Applications 165, no. 1 (2015): 209-224. */
7539566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
7549566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
7559566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
75650b47da0SAdam Denchfield       snorm = step*dnorm;
75750b47da0SAdam Denchfield       cg->yts = dk_yk*step;
758c8bcdf1eSAdam Denchfield       if (cg->use_dynamic_restart) {
7599566063dSJacob Faibussowitsch         PetscCall(TaoBNCGCheckDynamicRestart(tao, step, gd, gd_old, &cg->dynamic_restart, fold));
760c8bcdf1eSAdam Denchfield       }
76150b47da0SAdam Denchfield       if (cg->dynamic_restart) {
7629566063dSJacob Faibussowitsch         PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
763c8bcdf1eSAdam Denchfield       } else {
764c8bcdf1eSAdam Denchfield         if (!cg->diag_scaling) {
7659566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
7669566063dSJacob Faibussowitsch           PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha));
767c8bcdf1eSAdam Denchfield           beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk)) - step*gd/dk_yk;
768c8bcdf1eSAdam Denchfield           if (beta < cg->zeta*tau_k*gd/(dnorm*dnorm)) /* 0.1 is KD's zeta parameter */
769c8bcdf1eSAdam Denchfield           {
770c8bcdf1eSAdam Denchfield             beta = cg->zeta*tau_k*gd/(dnorm*dnorm);
771c8bcdf1eSAdam Denchfield             gamma = 0.0;
772c8bcdf1eSAdam Denchfield           } else {
773c8bcdf1eSAdam Denchfield             if (gkp1_yk < 0 && cg->neg_xi) gamma = -1.0*gd/dk_yk;
774484c7b14SAdam Denchfield             /* This seems to be very effective when there's no tau_k scaling.
775484c7b14SAdam Denchfield                This guarantees a large descent step every iteration, going through DK 2015 Lemma 3.1's proof but allowing for negative xi */
77650b47da0SAdam Denchfield             else {
77750b47da0SAdam Denchfield               gamma = cg->xi*gd/dk_yk;
77850b47da0SAdam Denchfield             }
779c8bcdf1eSAdam Denchfield           }
780c8bcdf1eSAdam Denchfield           /* d <- -t*g + beta*t*d + t*tmp*yk */
7819566063dSJacob Faibussowitsch           PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, gamma*tau_k, beta, tao->gradient, cg->yk));
782c8bcdf1eSAdam Denchfield         } else {
783c8bcdf1eSAdam Denchfield           /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
784c8bcdf1eSAdam Denchfield           cg->yty = ynorm2;
785c8bcdf1eSAdam Denchfield           cg->sts = snorm*snorm;
7869566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
7879566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
788c8bcdf1eSAdam Denchfield           /* Construct the constant ytDgkp1 */
7899566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->g_work, &gkp1D_yk));
790c8bcdf1eSAdam Denchfield           /* Construct the constant for scaling Dkyk in the update */
791c8bcdf1eSAdam Denchfield           gamma = gd/dk_yk;
792c8bcdf1eSAdam Denchfield           /* tau_k = -ytDy/(ytd)^2 * gd */
7939566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
794c8bcdf1eSAdam Denchfield           tau_k = tau_k*gd/(dk_yk*dk_yk);
795c8bcdf1eSAdam Denchfield           /* beta is the constant which adds the d_k contribution */
796c8bcdf1eSAdam Denchfield           beta = gkp1D_yk/dk_yk - step*gamma - tau_k;
797c8bcdf1eSAdam Denchfield           /* Here is the requisite check */
7989566063dSJacob Faibussowitsch           PetscCall(VecDot(tao->stepdirection, cg->g_work, &tmp));
799c8bcdf1eSAdam Denchfield           if (cg->neg_xi) {
800c8bcdf1eSAdam Denchfield             /* modified KD implementation */
801c8bcdf1eSAdam Denchfield             if (gkp1D_yk/dk_yk < 0) gamma = -1.0*gd/dk_yk;
80250b47da0SAdam Denchfield             else {
80350b47da0SAdam Denchfield               gamma = cg->xi*gd/dk_yk;
80450b47da0SAdam Denchfield             }
805c8bcdf1eSAdam Denchfield             if (beta < cg->zeta*tmp/(dnorm*dnorm)) {
806c8bcdf1eSAdam Denchfield               beta = cg->zeta*tmp/(dnorm*dnorm);
807c8bcdf1eSAdam Denchfield               gamma = 0.0;
808c8bcdf1eSAdam Denchfield             }
809c8bcdf1eSAdam Denchfield           } else { /* original KD 2015 implementation */
810c8bcdf1eSAdam Denchfield             if (beta < cg->zeta*tmp/(dnorm*dnorm)) {
811c8bcdf1eSAdam Denchfield               beta = cg->zeta*tmp/(dnorm*dnorm);
812c8bcdf1eSAdam Denchfield               gamma = 0.0;
813c8bcdf1eSAdam Denchfield             } else {
814c8bcdf1eSAdam Denchfield               gamma = cg->xi*gd/dk_yk;
815c8bcdf1eSAdam Denchfield             }
816c8bcdf1eSAdam Denchfield           }
817c8bcdf1eSAdam Denchfield           /* Do the update in two steps */
8189566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
8199566063dSJacob Faibussowitsch           PetscCall(VecAXPY(tao->stepdirection, gamma, cg->y_work));
82050b47da0SAdam Denchfield         }
82150b47da0SAdam Denchfield       }
822c8bcdf1eSAdam Denchfield       break;
823484c7b14SAdam Denchfield 
824484c7b14SAdam Denchfield     case CG_SSML_BFGS:
825484c7b14SAdam Denchfield       /* Perry, J. M. "A class of conjugate gradient algorithms with a two-step variable-metric memory."
826484c7b14SAdam Denchfield          Discussion Papers 269 (1977). */
8279566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
8289566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
829484c7b14SAdam Denchfield       snorm = step*dnorm;
830484c7b14SAdam Denchfield       cg->yts = dk_yk*step;
831484c7b14SAdam Denchfield       cg->yty = ynorm2;
832484c7b14SAdam Denchfield       cg->sts = snorm*snorm;
833484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
8349566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
8359566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, cg->yts, cg->sts, &tau_k, cg->alpha));
836484c7b14SAdam Denchfield         tmp = gd/dk_yk;
837484c7b14SAdam Denchfield         beta = tau_k*(gkp1_yk/dk_yk - cg->yty*gd/(dk_yk*dk_yk)) - step*tmp;
838484c7b14SAdam Denchfield         /* d <- -t*g + beta*t*d + t*tmp*yk */
8399566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp*tau_k, beta, tao->gradient, cg->yk));
840484c7b14SAdam Denchfield       } else {
841484c7b14SAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
8429566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
8439566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
844484c7b14SAdam Denchfield         /* compute scalar gamma */
8459566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
8469566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
847484c7b14SAdam Denchfield         gamma = gd/dk_yk;
848484c7b14SAdam Denchfield         /* Compute scalar beta */
849484c7b14SAdam Denchfield         beta = (gkp1_yk/dk_yk - gd*tmp/(dk_yk*dk_yk)) - step*gd/dk_yk;
850484c7b14SAdam Denchfield         /* Compute stepdirection d_kp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
8519566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
852484c7b14SAdam Denchfield       }
853484c7b14SAdam Denchfield       break;
854484c7b14SAdam Denchfield 
855484c7b14SAdam Denchfield     case CG_SSML_DFP:
8569566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
8579566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
858484c7b14SAdam Denchfield       snorm = step*dnorm;
859484c7b14SAdam Denchfield       cg->yts = dk_yk*step;
860484c7b14SAdam Denchfield       cg->yty = ynorm2;
861484c7b14SAdam Denchfield       cg->sts = snorm*snorm;
862484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
863484c7b14SAdam Denchfield         /* Instead of a regular convex combination, we will solve a quadratic formula. */
8649566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, cg->yts, cg->sts, &tau_k, cg->alpha));
8659566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
866484c7b14SAdam Denchfield         tau_k = cg->dfp_scale*tau_k;
867484c7b14SAdam Denchfield         tmp = tau_k*gkp1_yk/cg->yty;
868484c7b14SAdam Denchfield         beta = -step*gd/dk_yk;
869484c7b14SAdam Denchfield         /* d <- -t*g + beta*d + tmp*yk */
8709566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp, beta, tao->gradient, cg->yk));
871484c7b14SAdam Denchfield       } else {
872484c7b14SAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless DFP step */
8739566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
8749566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
875484c7b14SAdam Denchfield         /* compute scalar gamma */
8769566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
8779566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
878484c7b14SAdam Denchfield         gamma = (gkp1_yk/tmp);
879484c7b14SAdam Denchfield         /* Compute scalar beta */
880484c7b14SAdam Denchfield         beta = -step*gd/dk_yk;
881484c7b14SAdam Denchfield         /* Compute stepdirection d_kp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
8829566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
883484c7b14SAdam Denchfield       }
884484c7b14SAdam Denchfield       break;
885484c7b14SAdam Denchfield 
886484c7b14SAdam Denchfield     case CG_SSML_BROYDEN:
8879566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
8889566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
889484c7b14SAdam Denchfield       snorm = step*dnorm;
890484c7b14SAdam Denchfield       cg->yts = step*dk_yk;
891484c7b14SAdam Denchfield       cg->yty = ynorm2;
892484c7b14SAdam Denchfield       cg->sts = snorm*snorm;
893484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
894484c7b14SAdam Denchfield         /* Instead of a regular convex combination, we will solve a quadratic formula. */
8959566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, step*dk_yk, snorm*snorm, &tau_bfgs, cg->bfgs_scale));
8969566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, step*dk_yk, snorm*snorm, &tau_dfp, cg->dfp_scale));
8979566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
898484c7b14SAdam Denchfield         tau_k = cg->theta*tau_bfgs + (1.0-cg->theta)*tau_dfp;
899484c7b14SAdam Denchfield         /* If bfgs_scale = 1.0, it should reproduce the bfgs tau_bfgs. If bfgs_scale = 0.0,
900484c7b14SAdam Denchfield            it should reproduce the tau_dfp scaling. Same with dfp_scale.   */
901484c7b14SAdam Denchfield         tmp = cg->theta*tau_bfgs*gd/dk_yk + (1-cg->theta)*tau_dfp*gkp1_yk/cg->yty;
902484c7b14SAdam Denchfield         beta = cg->theta*tau_bfgs*(gkp1_yk/dk_yk - cg->yty*gd/(dk_yk*dk_yk)) - step*gd/dk_yk;
903484c7b14SAdam Denchfield         /* d <- -t*g + beta*d + tmp*yk */
9049566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp, beta, tao->gradient, cg->yk));
905484c7b14SAdam Denchfield       } else {
906484c7b14SAdam Denchfield         /* We have diagonal scaling enabled */
9079566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
9089566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
909484c7b14SAdam Denchfield         /* compute scalar gamma */
9109566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
9119566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
912484c7b14SAdam Denchfield         gamma = cg->theta*gd/dk_yk + (1-cg->theta)*(gkp1_yk/tmp);
913484c7b14SAdam Denchfield         /* Compute scalar beta */
914484c7b14SAdam Denchfield         beta = cg->theta*(gkp1_yk/dk_yk - gd*tmp/(dk_yk*dk_yk)) - step*gd/dk_yk;
915484c7b14SAdam Denchfield         /* Compute stepdirection dkp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
9169566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
917484c7b14SAdam Denchfield       }
918484c7b14SAdam Denchfield       break;
919484c7b14SAdam Denchfield 
920c8bcdf1eSAdam Denchfield     default:
921c8bcdf1eSAdam Denchfield       break;
922484c7b14SAdam Denchfield 
923c8bcdf1eSAdam Denchfield     }
924c8bcdf1eSAdam Denchfield   }
925c8bcdf1eSAdam Denchfield   PetscFunctionReturn(0);
926c8bcdf1eSAdam Denchfield }
927c8bcdf1eSAdam Denchfield 
928c8bcdf1eSAdam Denchfield PETSC_INTERN PetscErrorCode TaoBNCGConductIteration(Tao tao, PetscReal gnorm)
929c8bcdf1eSAdam Denchfield {
930c8bcdf1eSAdam Denchfield   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
931c8bcdf1eSAdam Denchfield   TaoLineSearchConvergedReason ls_status = TAOLINESEARCH_CONTINUE_ITERATING;
9328ca2df50S   PetscReal                    step=1.0,gnorm2,gd,dnorm=0.0;
933c8bcdf1eSAdam Denchfield   PetscReal                    gnorm2_old,f_old,resnorm, gnorm_old;
934c624ebd3SAlp Dener   PetscBool                    pcgd_fallback = PETSC_FALSE;
935c8bcdf1eSAdam Denchfield 
936c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
937c8bcdf1eSAdam Denchfield   /* We are now going to perform a line search along the direction. */
938c8bcdf1eSAdam Denchfield   /* Store solution and gradient info before it changes */
9399566063dSJacob Faibussowitsch   PetscCall(VecCopy(tao->solution, cg->X_old));
9409566063dSJacob Faibussowitsch   PetscCall(VecCopy(tao->gradient, cg->G_old));
9419566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, cg->unprojected_gradient_old));
942c8bcdf1eSAdam Denchfield 
943c8bcdf1eSAdam Denchfield   gnorm_old = gnorm;
944c8bcdf1eSAdam Denchfield   gnorm2_old = gnorm_old*gnorm_old;
945c8bcdf1eSAdam Denchfield   f_old = cg->f;
946484c7b14SAdam Denchfield   /* Perform bounded line search. If we are recycling a solution from a previous */
947484c7b14SAdam Denchfield   /* TaoSolve, then we want to immediately skip to calculating a new direction rather than performing a linesearch */
948414d97d3SAlp Dener   if (!(tao->recycle && 0 == tao->niter)) {
949484c7b14SAdam Denchfield     /* Above logic: the below code happens every iteration, except for the first iteration of a recycled TaoSolve */
9509566063dSJacob Faibussowitsch     PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
9519566063dSJacob Faibussowitsch     PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
9529566063dSJacob Faibussowitsch     PetscCall(TaoAddLineSearchCounts(tao));
953c8bcdf1eSAdam Denchfield 
954c8bcdf1eSAdam Denchfield     /*  Check linesearch failure */
955c8bcdf1eSAdam Denchfield     if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
956c8bcdf1eSAdam Denchfield       ++cg->ls_fails;
957c624ebd3SAlp Dener       if (cg->cg_type == CG_GradientDescent) {
958c8bcdf1eSAdam Denchfield         /* Nothing left to do but fail out of the optimization */
959c8bcdf1eSAdam Denchfield         step = 0.0;
960c8bcdf1eSAdam Denchfield         tao->reason = TAO_DIVERGED_LS_FAILURE;
961c8bcdf1eSAdam Denchfield       } else {
962484c7b14SAdam Denchfield         /* Restore previous point, perform preconditioned GD and regular GD steps at the last good point */
9639566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->X_old, tao->solution));
9649566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->G_old, tao->gradient));
9659566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->unprojected_gradient_old, cg->unprojected_gradient));
966c8bcdf1eSAdam Denchfield         gnorm = gnorm_old;
967c8bcdf1eSAdam Denchfield         gnorm2 = gnorm2_old;
968c8bcdf1eSAdam Denchfield         cg->f = f_old;
969c8bcdf1eSAdam Denchfield 
970484c7b14SAdam Denchfield         /* Fall back on preconditioned CG (so long as you're not already using it) */
971484c7b14SAdam Denchfield         if (cg->cg_type != CG_PCGradientDescent && cg->diag_scaling) {
972e2570530SAlp Dener           pcgd_fallback = PETSC_TRUE;
9739566063dSJacob Faibussowitsch           PetscCall(TaoBNCGStepDirectionUpdate(tao, gnorm2, step, f_old, gnorm2_old, dnorm, pcgd_fallback));
974484c7b14SAdam Denchfield 
9759566063dSJacob Faibussowitsch           PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
9769566063dSJacob Faibussowitsch           PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
977c8bcdf1eSAdam Denchfield 
9789566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
9799566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
9809566063dSJacob Faibussowitsch           PetscCall(TaoAddLineSearchCounts(tao));
981c8bcdf1eSAdam Denchfield 
982484c7b14SAdam Denchfield           pcgd_fallback = PETSC_FALSE;
983484c7b14SAdam Denchfield           if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
984484c7b14SAdam Denchfield             /* Going to perform a regular gradient descent step. */
985484c7b14SAdam Denchfield             ++cg->ls_fails;
986484c7b14SAdam Denchfield             step = 0.0;
987484c7b14SAdam Denchfield           }
988484c7b14SAdam Denchfield         }
989484c7b14SAdam Denchfield         /* Fall back on the scaled gradient step */
990484c7b14SAdam Denchfield         if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
991484c7b14SAdam Denchfield           ++cg->ls_fails;
9929566063dSJacob Faibussowitsch           PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
9939566063dSJacob Faibussowitsch           PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
9949566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
9959566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
9969566063dSJacob Faibussowitsch           PetscCall(TaoAddLineSearchCounts(tao));
997484c7b14SAdam Denchfield         }
998484c7b14SAdam Denchfield 
999c8bcdf1eSAdam Denchfield         if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
1000c8bcdf1eSAdam Denchfield           /* Nothing left to do but fail out of the optimization */
100150b47da0SAdam Denchfield           ++cg->ls_fails;
1002c8bcdf1eSAdam Denchfield           step = 0.0;
1003c8bcdf1eSAdam Denchfield           tao->reason = TAO_DIVERGED_LS_FAILURE;
1004484c7b14SAdam Denchfield         } else {
1005484c7b14SAdam Denchfield           /* One of the fallbacks worked. Set them both back equal to false. */
1006484c7b14SAdam Denchfield           pcgd_fallback = PETSC_FALSE;
1007c8bcdf1eSAdam Denchfield         }
1008c8bcdf1eSAdam Denchfield       }
1009c8bcdf1eSAdam Denchfield     }
1010c8bcdf1eSAdam Denchfield     /* Convergence test for line search failure */
1011c8bcdf1eSAdam Denchfield     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
1012c8bcdf1eSAdam Denchfield 
1013c8bcdf1eSAdam Denchfield     /* Standard convergence test */
10149566063dSJacob Faibussowitsch     PetscCall(VecFischer(tao->solution, cg->unprojected_gradient, tao->XL, tao->XU, cg->W));
10159566063dSJacob Faibussowitsch     PetscCall(VecNorm(cg->W, NORM_2, &resnorm));
10163c859ba3SBarry Smith     PetscCheck(!PetscIsInfOrNanReal(resnorm),PetscObjectComm((PetscObject)tao),PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
10179566063dSJacob Faibussowitsch     PetscCall(TaoLogConvergenceHistory(tao, cg->f, resnorm, 0.0, tao->ksp_its));
10189566063dSJacob Faibussowitsch     PetscCall(TaoMonitor(tao, tao->niter, cg->f, resnorm, 0.0, step));
10199566063dSJacob Faibussowitsch     PetscCall((*tao->ops->convergencetest)(tao,tao->cnvP));
1020c8bcdf1eSAdam Denchfield     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
1021484c7b14SAdam Denchfield   }
1022c8bcdf1eSAdam Denchfield   /* Assert we have an updated step and we need at least one more iteration. */
1023c8bcdf1eSAdam Denchfield   /* Calculate the next direction */
1024c8bcdf1eSAdam Denchfield   /* Estimate the active set at the new solution */
10259566063dSJacob Faibussowitsch   PetscCall(TaoBNCGEstimateActiveSet(tao, cg->as_type));
1026c8bcdf1eSAdam Denchfield   /* Compute the projected gradient and its norm */
10279566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, tao->gradient));
10289566063dSJacob Faibussowitsch   PetscCall(VecISSet(tao->gradient, cg->active_idx, 0.0));
10299566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->gradient,NORM_2,&gnorm));
1030c8bcdf1eSAdam Denchfield   gnorm2 = gnorm*gnorm;
1031c8bcdf1eSAdam Denchfield 
1032484c7b14SAdam Denchfield   /* Calculate some quantities used in the StepDirectionUpdate. */
10339566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->stepdirection, NORM_2, &dnorm));
1034484c7b14SAdam Denchfield   /* Update the step direction. */
10359566063dSJacob Faibussowitsch   PetscCall(TaoBNCGStepDirectionUpdate(tao, gnorm2, step, f_old, gnorm2_old, dnorm, pcgd_fallback));
1036484c7b14SAdam Denchfield   ++tao->niter;
10379566063dSJacob Faibussowitsch   PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
1038c8bcdf1eSAdam Denchfield 
1039c8bcdf1eSAdam Denchfield   if (cg->cg_type != CG_GradientDescent) {
1040c8bcdf1eSAdam Denchfield     /* Figure out which previously active variables became inactive this iteration */
10419566063dSJacob Faibussowitsch     PetscCall(ISDestroy(&cg->new_inactives));
1042c8bcdf1eSAdam Denchfield     if (cg->inactive_idx && cg->inactive_old) {
10439566063dSJacob Faibussowitsch       PetscCall(ISDifference(cg->inactive_idx, cg->inactive_old, &cg->new_inactives));
1044c8bcdf1eSAdam Denchfield     }
1045c8bcdf1eSAdam Denchfield     /* Selectively reset the CG step those freshly inactive variables */
1046c8bcdf1eSAdam Denchfield     if (cg->new_inactives) {
10479566063dSJacob Faibussowitsch       PetscCall(VecGetSubVector(tao->stepdirection, cg->new_inactives, &cg->inactive_step));
10489566063dSJacob Faibussowitsch       PetscCall(VecGetSubVector(cg->unprojected_gradient, cg->new_inactives, &cg->inactive_grad));
10499566063dSJacob Faibussowitsch       PetscCall(VecCopy(cg->inactive_grad, cg->inactive_step));
10509566063dSJacob Faibussowitsch       PetscCall(VecScale(cg->inactive_step, -1.0));
10519566063dSJacob Faibussowitsch       PetscCall(VecRestoreSubVector(tao->stepdirection, cg->new_inactives, &cg->inactive_step));
10529566063dSJacob Faibussowitsch       PetscCall(VecRestoreSubVector(cg->unprojected_gradient, cg->new_inactives, &cg->inactive_grad));
1053c8bcdf1eSAdam Denchfield     }
1054c8bcdf1eSAdam Denchfield     /* Verify that this is a descent direction */
10559566063dSJacob Faibussowitsch     PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
10569566063dSJacob Faibussowitsch     PetscCall(VecNorm(tao->stepdirection, NORM_2, &dnorm));
105750b47da0SAdam Denchfield     if (PetscIsInfOrNanReal(gd) || (gd/(dnorm*dnorm) <= -1e10 || gd/(dnorm*dnorm) >= -1e-10)) {
1058c8bcdf1eSAdam Denchfield       /* Not a descent direction, so we reset back to projected gradient descent */
10599566063dSJacob Faibussowitsch       PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
10609566063dSJacob Faibussowitsch       PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
1061c8bcdf1eSAdam Denchfield       ++cg->descent_error;
1062c8bcdf1eSAdam Denchfield     } else {
1063c8bcdf1eSAdam Denchfield     }
1064c8bcdf1eSAdam Denchfield   }
1065ac9112b8SAlp Dener   PetscFunctionReturn(0);
1066ac9112b8SAlp Dener }
1067484c7b14SAdam Denchfield 
1068484c7b14SAdam Denchfield PetscErrorCode TaoBNCGSetH0(Tao tao, Mat H0)
1069484c7b14SAdam Denchfield {
1070484c7b14SAdam Denchfield   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
1071484c7b14SAdam Denchfield 
1072484c7b14SAdam Denchfield   PetscFunctionBegin;
10739566063dSJacob Faibussowitsch   PetscCall(PetscObjectReference((PetscObject)H0));
1074484c7b14SAdam Denchfield   cg->pc = H0;
1075484c7b14SAdam Denchfield   PetscFunctionReturn(0);
1076484c7b14SAdam Denchfield }
1077