xref: /petsc/src/tao/bound/impls/bncg/bncg.c (revision d0609ced746bc51b019815ca91d747429db24893)
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));
50*d0609cedSBarry Smith     PetscCall(TaoEstimateActiveBounds(tao->solution, tao->XL, tao->XU, cg->unprojected_gradient, cg->W, cg->work, cg->as_step, &cg->as_tol,
51*d0609cedSBarry 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));
135e1e80dc8SAlp Dener     }
1369566063dSJacob Faibussowitsch     PetscCall(TaoBNCGConductIteration(tao, gnorm));
137c8bcdf1eSAdam Denchfield     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
138ac9112b8SAlp Dener   }
139ac9112b8SAlp Dener }
140ac9112b8SAlp Dener 
141ac9112b8SAlp Dener static PetscErrorCode TaoSetUp_BNCG(Tao tao)
142ac9112b8SAlp Dener {
143ac9112b8SAlp Dener   TAO_BNCG         *cg = (TAO_BNCG*)tao->data;
144ac9112b8SAlp Dener 
145ac9112b8SAlp Dener   PetscFunctionBegin;
146c4b75bccSAlp Dener   if (!tao->gradient) {
1479566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&tao->gradient));
148c4b75bccSAlp Dener   }
149c4b75bccSAlp Dener   if (!tao->stepdirection) {
1509566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&tao->stepdirection));
151c4b75bccSAlp Dener   }
152c4b75bccSAlp Dener   if (!cg->W) {
1539566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->W));
154c4b75bccSAlp Dener   }
155c4b75bccSAlp Dener   if (!cg->work) {
1569566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->work));
157c4b75bccSAlp Dener   }
158c8bcdf1eSAdam Denchfield   if (!cg->sk) {
1599566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->sk));
160c8bcdf1eSAdam Denchfield   }
161c8bcdf1eSAdam Denchfield   if (!cg->yk) {
1629566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->yk));
163c8bcdf1eSAdam Denchfield   }
164c4b75bccSAlp Dener   if (!cg->X_old) {
1659566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->X_old));
166c4b75bccSAlp Dener   }
167c4b75bccSAlp Dener   if (!cg->G_old) {
1689566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->G_old));
169c8bcdf1eSAdam Denchfield   }
170c8bcdf1eSAdam Denchfield   if (cg->diag_scaling) {
1719566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->d_work));
1729566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->y_work));
1739566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->g_work));
174c4b75bccSAlp Dener   }
175c4b75bccSAlp Dener   if (!cg->unprojected_gradient) {
1769566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->unprojected_gradient));
177c4b75bccSAlp Dener   }
178c4b75bccSAlp Dener   if (!cg->unprojected_gradient_old) {
1799566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->unprojected_gradient_old));
180c4b75bccSAlp Dener   }
1819566063dSJacob Faibussowitsch   PetscCall(MatLMVMAllocate(cg->B, cg->sk, cg->yk));
182484c7b14SAdam Denchfield   if (cg->pc) {
1839566063dSJacob Faibussowitsch     PetscCall(MatLMVMSetJ0(cg->B, cg->pc));
184484c7b14SAdam Denchfield   }
185ac9112b8SAlp Dener   PetscFunctionReturn(0);
186ac9112b8SAlp Dener }
187ac9112b8SAlp Dener 
188ac9112b8SAlp Dener static PetscErrorCode TaoDestroy_BNCG(Tao tao)
189ac9112b8SAlp Dener {
190ac9112b8SAlp Dener   TAO_BNCG       *cg = (TAO_BNCG*) tao->data;
191ac9112b8SAlp Dener 
192ac9112b8SAlp Dener   PetscFunctionBegin;
193ac9112b8SAlp Dener   if (tao->setupcalled) {
1949566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->W));
1959566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->work));
1969566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->X_old));
1979566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->G_old));
1989566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->unprojected_gradient));
1999566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->unprojected_gradient_old));
2009566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->g_work));
2019566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->d_work));
2029566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->y_work));
2039566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->sk));
2049566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->yk));
205ac9112b8SAlp Dener   }
2069566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_lower));
2079566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_upper));
2089566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_fixed));
2099566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_idx));
2109566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->inactive_idx));
2119566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->inactive_old));
2129566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->new_inactives));
2139566063dSJacob Faibussowitsch   PetscCall(MatDestroy(&cg->B));
214484c7b14SAdam Denchfield   if (cg->pc) {
2159566063dSJacob Faibussowitsch     PetscCall(MatDestroy(&cg->pc));
216484c7b14SAdam Denchfield   }
2179566063dSJacob Faibussowitsch   PetscCall(PetscFree(tao->data));
218ac9112b8SAlp Dener   PetscFunctionReturn(0);
219ac9112b8SAlp Dener }
220ac9112b8SAlp Dener 
221ac9112b8SAlp Dener static PetscErrorCode TaoSetFromOptions_BNCG(PetscOptionItems *PetscOptionsObject,Tao tao)
222ac9112b8SAlp Dener {
223ac9112b8SAlp Dener   TAO_BNCG       *cg = (TAO_BNCG*)tao->data;
224ac9112b8SAlp Dener 
225ac9112b8SAlp Dener   PetscFunctionBegin;
226*d0609cedSBarry Smith   PetscOptionsHeadBegin(PetscOptionsObject,"Nonlinear Conjugate Gradient method for unconstrained optimization");
2279566063dSJacob Faibussowitsch   PetscCall(PetscOptionsEList("-tao_bncg_type","cg formula", "", CG_Table, CGTypes, CG_Table[cg->cg_type], &cg->cg_type,NULL));
2288ebe3e4eSStefano Zampini   if (cg->cg_type != CG_SSML_BFGS) cg->alpha = -1.0; /* Setting defaults for non-BFGS methods. User can change it below. */
229484c7b14SAdam Denchfield   if (CG_GradientDescent == cg->cg_type) {
230484c7b14SAdam Denchfield     cg->cg_type = CG_PCGradientDescent;
231484c7b14SAdam Denchfield     /* Set scaling equal to none or, at best, scalar scaling. */
232484c7b14SAdam Denchfield     cg->unscaled_restart = PETSC_TRUE;
233484c7b14SAdam Denchfield     cg->diag_scaling = PETSC_FALSE;
234484c7b14SAdam Denchfield   }
2359566063dSJacob 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));
2369566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_hz_eta","(developer) cutoff tolerance for HZ", "", cg->hz_eta,&cg->hz_eta,NULL));
2379566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_eps","(developer) cutoff value for restarts", "", cg->epsilon,&cg->epsilon,NULL));
2389566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_dk_eta","(developer) cutoff tolerance for DK", "", cg->dk_eta,&cg->dk_eta,NULL));
2399566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_xi","(developer) Parameter in the KD method", "", cg->xi,&cg->xi,NULL));
2409566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_theta", "(developer) update parameter for the Broyden method", "", cg->theta, &cg->theta, NULL));
2419566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_hz_theta", "(developer) parameter for the HZ (2006) method", "", cg->hz_theta, &cg->hz_theta, NULL));
2429566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_alpha","(developer) parameter for the scalar scaling","",cg->alpha,&cg->alpha,NULL));
2439566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_bfgs_scale", "(developer) update parameter for bfgs/brdn CG methods", "", cg->bfgs_scale, &cg->bfgs_scale, NULL));
2449566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_dfp_scale", "(developer) update parameter for bfgs/brdn CG methods", "", cg->dfp_scale, &cg->dfp_scale, NULL));
2459566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_diag_scaling","Enable diagonal Broyden-like preconditioning","",cg->diag_scaling,&cg->diag_scaling,NULL));
2469566063dSJacob 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));
2479566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_unscaled_restart","(developer) use unscaled gradient restarts","",cg->unscaled_restart,&cg->unscaled_restart,NULL));
2489566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_zeta", "(developer) Free parameter for the Kou-Dai method", "", cg->zeta, &cg->zeta, NULL));
2499566063dSJacob 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));
2509566063dSJacob 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));
2519566063dSJacob 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));
2529566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_no_scaling","Disable all scaling except in restarts","",cg->no_scaling,&cg->no_scaling,NULL));
253484c7b14SAdam Denchfield   if (cg->no_scaling) {
254484c7b14SAdam Denchfield     cg->diag_scaling = PETSC_FALSE;
255484c7b14SAdam Denchfield     cg->alpha = -1.0;
256484c7b14SAdam Denchfield   }
257b474139fSKarl Rupp   if (cg->alpha == -1.0 && cg->cg_type == CG_KouDai && !cg->diag_scaling) { /* Some more default options that appear to be good. */
258484c7b14SAdam Denchfield     cg->neg_xi = PETSC_TRUE;
259484c7b14SAdam Denchfield   }
2609566063dSJacob 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));
2619566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_as_tol", "(developer) initial tolerance used when estimating actively bounded variables","",cg->as_tol,&cg->as_tol,NULL));
2629566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_as_step", "(developer) step length used when estimating actively bounded variables","",cg->as_step,&cg->as_step,NULL));
2639566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_delta_min", "(developer) minimum scaling factor used for scaled gradient restarts","",cg->delta_min,&cg->delta_min,NULL));
2649566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_delta_max", "(developer) maximum scaling factor used for scaled gradient restarts","",cg->delta_max,&cg->delta_max,NULL));
26550b47da0SAdam Denchfield 
266*d0609cedSBarry Smith   PetscOptionsHeadEnd();
2679566063dSJacob Faibussowitsch   PetscCall(MatSetOptionsPrefix(cg->B, ((PetscObject)tao)->prefix));
2689566063dSJacob Faibussowitsch   PetscCall(MatAppendOptionsPrefix(cg->B, "tao_bncg_"));
2699566063dSJacob Faibussowitsch   PetscCall(MatSetFromOptions(cg->B));
270ac9112b8SAlp Dener   PetscFunctionReturn(0);
271ac9112b8SAlp Dener }
272ac9112b8SAlp Dener 
273ac9112b8SAlp Dener static PetscErrorCode TaoView_BNCG(Tao tao, PetscViewer viewer)
274ac9112b8SAlp Dener {
275ac9112b8SAlp Dener   PetscBool      isascii;
276ac9112b8SAlp Dener   TAO_BNCG       *cg = (TAO_BNCG*)tao->data;
277ac9112b8SAlp Dener 
278ac9112b8SAlp Dener   PetscFunctionBegin;
2799566063dSJacob Faibussowitsch   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
280ac9112b8SAlp Dener   if (isascii) {
2819566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPushTab(viewer));
2829566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "CG Type: %s\n", CG_Table[cg->cg_type]));
2839566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Skipped Stepdirection Updates: %i\n", cg->skipped_updates));
2849566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Scaled gradient steps: %i\n", cg->resets));
2859566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Pure gradient steps: %i\n", cg->pure_gd_steps));
2869566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Not a descent direction: %i\n", cg->descent_error));
2879566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Line search fails: %i\n", cg->ls_fails));
288484c7b14SAdam Denchfield     if (cg->diag_scaling) {
2899566063dSJacob Faibussowitsch       PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
290484c7b14SAdam Denchfield       if (isascii) {
2919566063dSJacob Faibussowitsch         PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_INFO));
2929566063dSJacob Faibussowitsch         PetscCall(MatView(cg->B, viewer));
2939566063dSJacob Faibussowitsch         PetscCall(PetscViewerPopFormat(viewer));
294484c7b14SAdam Denchfield       }
295484c7b14SAdam Denchfield     }
2969566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPopTab(viewer));
297ac9112b8SAlp Dener   }
298ac9112b8SAlp Dener   PetscFunctionReturn(0);
299ac9112b8SAlp Dener }
300ac9112b8SAlp Dener 
301c8bcdf1eSAdam Denchfield PetscErrorCode TaoBNCGComputeScalarScaling(PetscReal yty, PetscReal yts, PetscReal sts, PetscReal *scale, PetscReal alpha)
302c8bcdf1eSAdam Denchfield {
303c8bcdf1eSAdam Denchfield   PetscReal a, b, c, sig1, sig2;
304c8bcdf1eSAdam Denchfield 
305c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
306c8bcdf1eSAdam Denchfield   *scale = 0.0;
3078ebe3e4eSStefano Zampini   if (1.0 == alpha) *scale = yts/yty;
3088ebe3e4eSStefano Zampini   else if (0.0 == alpha) *scale = sts/yts;
30950b47da0SAdam Denchfield   else if (-1.0 == alpha) *scale = 1.0;
310c8bcdf1eSAdam Denchfield   else {
311c8bcdf1eSAdam Denchfield     a = yty;
312c8bcdf1eSAdam Denchfield     b = yts;
313c8bcdf1eSAdam Denchfield     c = sts;
314c8bcdf1eSAdam Denchfield     a *= alpha;
315c8bcdf1eSAdam Denchfield     b *= -(2.0*alpha - 1.0);
316c8bcdf1eSAdam Denchfield     c *= alpha - 1.0;
317c8bcdf1eSAdam Denchfield     sig1 = (-b + PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
318c8bcdf1eSAdam Denchfield     sig2 = (-b - PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
319c8bcdf1eSAdam Denchfield     /* accept the positive root as the scalar */
3208ebe3e4eSStefano Zampini     if (sig1 > 0.0) *scale = sig1;
3218ebe3e4eSStefano Zampini     else if (sig2 > 0.0) *scale = sig2;
3228ebe3e4eSStefano Zampini     else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_CONV_FAILED, "Cannot find positive scalar");
323c8bcdf1eSAdam Denchfield   }
324c8bcdf1eSAdam Denchfield   PetscFunctionReturn(0);
325c8bcdf1eSAdam Denchfield }
326c8bcdf1eSAdam Denchfield 
327ac9112b8SAlp Dener /*MC
328ac9112b8SAlp Dener   TAOBNCG - Bound-constrained Nonlinear Conjugate Gradient method.
329ac9112b8SAlp Dener 
330ac9112b8SAlp Dener   Options Database Keys:
33150b47da0SAdam Denchfield + -tao_bncg_recycle - enable recycling the latest calculated gradient vector in subsequent TaoSolve() calls (currently disabled)
332c4b75bccSAlp Dener . -tao_bncg_eta <r> - restart tolerance
33361be54a6SAlp Dener . -tao_bncg_type <taocg_type> - cg formula
334c4b75bccSAlp Dener . -tao_bncg_as_type <none,bertsekas> - active set estimation method
335c4b75bccSAlp Dener . -tao_bncg_as_tol <r> - tolerance used in Bertsekas active-set estimation
336c4b75bccSAlp Dener . -tao_bncg_as_step <r> - trial step length used in Bertsekas active-set estimation
33750b47da0SAdam 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.
33850b47da0SAdam Denchfield . -tao_bncg_theta <r> - convex combination parameter for the Broyden method
33950b47da0SAdam Denchfield . -tao_bncg_hz_eta <r> - cutoff tolerance for the beta term in the HZ, DK methods
34050b47da0SAdam Denchfield . -tao_bncg_dk_eta <r> - cutoff tolerance for the beta term in the HZ, DK methods
34150b47da0SAdam Denchfield . -tao_bncg_xi <r> - Multiplicative constant of the gamma term in the KD method
34250b47da0SAdam Denchfield . -tao_bncg_hz_theta <r> - Multiplicative constant of the theta term for the HZ method
34350b47da0SAdam Denchfield . -tao_bncg_bfgs_scale <r> - Scaling parameter of the bfgs contribution to the scalar Broyden method
34450b47da0SAdam Denchfield . -tao_bncg_dfp_scale <r> - Scaling parameter of the dfp contribution to the scalar Broyden method
34550b47da0SAdam Denchfield . -tao_bncg_diag_scaling <b> - Whether or not to use diagonal initialization/preconditioning for the CG methods. Default True.
34650b47da0SAdam Denchfield . -tao_bncg_dynamic_restart <b> - use dynamic restart strategy in the HZ, DK, KD methods
34750b47da0SAdam Denchfield . -tao_bncg_unscaled_restart <b> - whether or not to scale the gradient when doing gradient descent restarts
34850b47da0SAdam Denchfield . -tao_bncg_zeta <r> - Scaling parameter in the KD method
349484c7b14SAdam Denchfield . -tao_bncg_delta_min <r> - Minimum bound for rescaling during restarted gradient descent steps
350484c7b14SAdam Denchfield . -tao_bncg_delta_max <r> - Maximum bound for rescaling during restarted gradient descent steps
35150b47da0SAdam Denchfield . -tao_bncg_min_quad <i> - Number of quadratic-like steps in a row necessary to do a dynamic restart
35250b47da0SAdam 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
35350b47da0SAdam Denchfield . -tao_bncg_spaced_restart <b> - whether or not to do gradient descent steps every x*n iterations
354484c7b14SAdam Denchfield . -tao_bncg_no_scaling <b> - If true, eliminates all scaling, including defaults.
3553850be85SAlp Dener - -tao_bncg_neg_xi <b> - Whether or not to use negative xi in the KD method under certain conditions
356ac9112b8SAlp Dener 
357ac9112b8SAlp Dener   Notes:
358ac9112b8SAlp Dener     CG formulas are:
3593850be85SAlp Dener + "gd" - Gradient Descent
3603850be85SAlp Dener . "fr" - Fletcher-Reeves
3613850be85SAlp Dener . "pr" - Polak-Ribiere-Polyak
3623850be85SAlp Dener . "prp" - Polak-Ribiere-Plus
3633850be85SAlp Dener . "hs" - Hestenes-Steifel
3643850be85SAlp Dener . "dy" - Dai-Yuan
3653850be85SAlp Dener . "ssml_bfgs" - Self-Scaling Memoryless BFGS
3663850be85SAlp Dener . "ssml_dfp"  - Self-Scaling Memoryless DFP
3673850be85SAlp Dener . "ssml_brdn" - Self-Scaling Memoryless Broyden
3683850be85SAlp Dener . "hz" - Hager-Zhang (CG_DESCENT 5.3)
3693850be85SAlp Dener . "dk" - Dai-Kou (2013)
3703850be85SAlp Dener - "kd" - Kou-Dai (2015)
3719abc5736SPatrick Sanan 
372ac9112b8SAlp Dener   Level: beginner
373ac9112b8SAlp Dener M*/
374ac9112b8SAlp Dener 
375ac9112b8SAlp Dener PETSC_EXTERN PetscErrorCode TaoCreate_BNCG(Tao tao)
376ac9112b8SAlp Dener {
377ac9112b8SAlp Dener   TAO_BNCG       *cg;
378ac9112b8SAlp Dener   const char     *morethuente_type = TAOLINESEARCHMT;
379ac9112b8SAlp Dener 
380ac9112b8SAlp Dener   PetscFunctionBegin;
381ac9112b8SAlp Dener   tao->ops->setup = TaoSetUp_BNCG;
382ac9112b8SAlp Dener   tao->ops->solve = TaoSolve_BNCG;
383ac9112b8SAlp Dener   tao->ops->view = TaoView_BNCG;
384ac9112b8SAlp Dener   tao->ops->setfromoptions = TaoSetFromOptions_BNCG;
385ac9112b8SAlp Dener   tao->ops->destroy = TaoDestroy_BNCG;
386ac9112b8SAlp Dener 
387ac9112b8SAlp Dener   /* Override default settings (unless already changed) */
388ac9112b8SAlp Dener   if (!tao->max_it_changed) tao->max_it = 2000;
389ac9112b8SAlp Dener   if (!tao->max_funcs_changed) tao->max_funcs = 4000;
390ac9112b8SAlp Dener 
391ac9112b8SAlp Dener   /*  Note: nondefault values should be used for nonlinear conjugate gradient  */
392ac9112b8SAlp Dener   /*  method.  In particular, gtol should be less that 0.5; the value used in  */
393ac9112b8SAlp Dener   /*  Nocedal and Wright is 0.10.  We use the default values for the  */
394ac9112b8SAlp Dener   /*  linesearch because it seems to work better. */
3959566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchCreate(((PetscObject)tao)->comm, &tao->linesearch));
3969566063dSJacob Faibussowitsch   PetscCall(PetscObjectIncrementTabLevel((PetscObject)tao->linesearch, (PetscObject)tao, 1));
3979566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchSetType(tao->linesearch, morethuente_type));
3989566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchUseTaoRoutines(tao->linesearch, tao));
399ac9112b8SAlp Dener 
4009566063dSJacob Faibussowitsch   PetscCall(PetscNewLog(tao,&cg));
401ac9112b8SAlp Dener   tao->data = (void*)cg;
4029566063dSJacob Faibussowitsch   PetscCall(KSPInitializePackage());
4039566063dSJacob Faibussowitsch   PetscCall(MatCreate(PetscObjectComm((PetscObject)tao), &cg->B));
4049566063dSJacob Faibussowitsch   PetscCall(PetscObjectIncrementTabLevel((PetscObject)cg->B, (PetscObject)tao, 1));
4059566063dSJacob Faibussowitsch   PetscCall(MatSetType(cg->B, MATLMVMDIAGBROYDEN));
40650b47da0SAdam Denchfield 
407484c7b14SAdam Denchfield   cg->pc = NULL;
408484c7b14SAdam Denchfield 
40950b47da0SAdam Denchfield   cg->dk_eta = 0.5;
41050b47da0SAdam Denchfield   cg->hz_eta = 0.4;
411c8bcdf1eSAdam Denchfield   cg->dynamic_restart = PETSC_FALSE;
412c8bcdf1eSAdam Denchfield   cg->unscaled_restart = PETSC_FALSE;
413484c7b14SAdam Denchfield   cg->no_scaling = PETSC_FALSE;
414484c7b14SAdam Denchfield   cg->delta_min = 1e-7;
415484c7b14SAdam Denchfield   cg->delta_max = 100;
416c8bcdf1eSAdam Denchfield   cg->theta = 1.0;
417c8bcdf1eSAdam Denchfield   cg->hz_theta = 1.0;
418c8bcdf1eSAdam Denchfield   cg->dfp_scale = 1.0;
419c8bcdf1eSAdam Denchfield   cg->bfgs_scale = 1.0;
42050b47da0SAdam Denchfield   cg->zeta = 0.1;
42150b47da0SAdam Denchfield   cg->min_quad = 6;
422c8bcdf1eSAdam Denchfield   cg->min_restart_num = 6; /* As in CG_DESCENT and KD2015*/
423c8bcdf1eSAdam Denchfield   cg->xi = 1.0;
42450b47da0SAdam Denchfield   cg->neg_xi = PETSC_TRUE;
425c8bcdf1eSAdam Denchfield   cg->spaced_restart = PETSC_FALSE;
426c8bcdf1eSAdam Denchfield   cg->tol_quad = 1e-8;
42761be54a6SAlp Dener   cg->as_step = 0.001;
42861be54a6SAlp Dener   cg->as_tol = 0.001;
42950b47da0SAdam Denchfield   cg->eps_23 = PetscPowReal(PETSC_MACHINE_EPSILON, 2.0/3.0); /* Just a little tighter*/
43061be54a6SAlp Dener   cg->as_type = CG_AS_BERTSEKAS;
431c8bcdf1eSAdam Denchfield   cg->cg_type = CG_SSML_BFGS;
432c8bcdf1eSAdam Denchfield   cg->alpha = 1.0;
433c8bcdf1eSAdam Denchfield   cg->diag_scaling = PETSC_TRUE;
434c8bcdf1eSAdam Denchfield   PetscFunctionReturn(0);
435c8bcdf1eSAdam Denchfield }
436c8bcdf1eSAdam Denchfield 
437c8bcdf1eSAdam Denchfield PetscErrorCode TaoBNCGResetUpdate(Tao tao, PetscReal gnormsq)
438c8bcdf1eSAdam Denchfield {
439c8bcdf1eSAdam Denchfield    TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
440c8bcdf1eSAdam Denchfield    PetscReal         scaling;
441c8bcdf1eSAdam Denchfield 
442c8bcdf1eSAdam Denchfield    PetscFunctionBegin;
443c8bcdf1eSAdam Denchfield    ++cg->resets;
444c8bcdf1eSAdam Denchfield    scaling = 2.0 * PetscMax(1.0, PetscAbsScalar(cg->f)) / PetscMax(gnormsq, cg->eps_23);
445484c7b14SAdam Denchfield    scaling = PetscMin(cg->delta_max, PetscMax(cg->delta_min, scaling));
446484c7b14SAdam Denchfield    if (cg->unscaled_restart) {
447484c7b14SAdam Denchfield      scaling = 1.0;
448484c7b14SAdam Denchfield      ++cg->pure_gd_steps;
449484c7b14SAdam Denchfield    }
4509566063dSJacob Faibussowitsch    PetscCall(VecAXPBY(tao->stepdirection, -scaling, 0.0, tao->gradient));
451c8bcdf1eSAdam Denchfield    /* Also want to reset our diagonal scaling with each restart */
452c8bcdf1eSAdam Denchfield    if (cg->diag_scaling) {
4539566063dSJacob Faibussowitsch      PetscCall(MatLMVMReset(cg->B, PETSC_FALSE));
454c8bcdf1eSAdam Denchfield    }
455c8bcdf1eSAdam Denchfield    PetscFunctionReturn(0);
456c8bcdf1eSAdam Denchfield  }
457c8bcdf1eSAdam Denchfield 
458c8bcdf1eSAdam Denchfield PetscErrorCode TaoBNCGCheckDynamicRestart(Tao tao, PetscReal stepsize, PetscReal gd, PetscReal gd_old, PetscBool *dynrestart, PetscReal fold)
459c8bcdf1eSAdam Denchfield {
460c8bcdf1eSAdam Denchfield    TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
461c8bcdf1eSAdam Denchfield    PetscReal         quadinterp;
462c8bcdf1eSAdam Denchfield 
463c8bcdf1eSAdam Denchfield    PetscFunctionBegin;
46450b47da0SAdam Denchfield    if (cg->f < cg->min_quad/10) {
46550b47da0SAdam Denchfield      *dynrestart = PETSC_FALSE;
46650b47da0SAdam Denchfield      PetscFunctionReturn(0);
46750b47da0SAdam Denchfield    } /* just skip this since this strategy doesn't work well for functions near zero */
468484c7b14SAdam Denchfield    quadinterp = 2.0*(cg->f - fold)/(stepsize*(gd + gd_old));
46950b47da0SAdam Denchfield    if (PetscAbs(quadinterp - 1.0) < cg->tol_quad) ++cg->iter_quad;
470c8bcdf1eSAdam Denchfield    else {
471c8bcdf1eSAdam Denchfield      cg->iter_quad = 0;
472c8bcdf1eSAdam Denchfield      *dynrestart = PETSC_FALSE;
473c8bcdf1eSAdam Denchfield    }
474c8bcdf1eSAdam Denchfield    if (cg->iter_quad >= cg->min_quad) {
475c8bcdf1eSAdam Denchfield      cg->iter_quad = 0;
476c8bcdf1eSAdam Denchfield      *dynrestart = PETSC_TRUE;
477c8bcdf1eSAdam Denchfield    }
478c8bcdf1eSAdam Denchfield    PetscFunctionReturn(0);
479c8bcdf1eSAdam Denchfield  }
480c8bcdf1eSAdam Denchfield 
4818ca2df50S PETSC_INTERN PetscErrorCode TaoBNCGStepDirectionUpdate(Tao tao, PetscReal gnorm2, PetscReal step, PetscReal fold, PetscReal gnorm2_old, PetscReal dnorm, PetscBool pcgd_fallback)
48250b47da0SAdam Denchfield {
483c8bcdf1eSAdam Denchfield   TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
48450b47da0SAdam Denchfield   PetscReal         gamma = 1.0, tau_k, beta;
485484c7b14SAdam Denchfield   PetscReal         tmp = 1.0, ynorm, ynorm2 = 1.0, snorm = 1.0, dk_yk=1.0, gd;
48650b47da0SAdam Denchfield   PetscReal         gkp1_yk, gd_old, tau_bfgs, tau_dfp, gkp1D_yk, gtDg;
487c8bcdf1eSAdam Denchfield   PetscInt          dim;
488484c7b14SAdam Denchfield   PetscBool         cg_restart = PETSC_FALSE;
489c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
490c8bcdf1eSAdam Denchfield 
49150b47da0SAdam Denchfield   /* Local curvature check to see if we need to restart */
492414d97d3SAlp Dener   if (tao->niter >= 1 || tao->recycle) {
4939566063dSJacob Faibussowitsch     PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
4949566063dSJacob Faibussowitsch     PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
495c8bcdf1eSAdam Denchfield     ynorm2 = ynorm*ynorm;
4969566063dSJacob Faibussowitsch     PetscCall(VecDot(cg->yk, tao->stepdirection, &dk_yk));
497484c7b14SAdam Denchfield     if (step*dnorm < PETSC_MACHINE_EPSILON || step*dk_yk < PETSC_MACHINE_EPSILON) {
498e2570530SAlp Dener       cg_restart = PETSC_TRUE;
499484c7b14SAdam Denchfield       ++cg->skipped_updates;
500484c7b14SAdam Denchfield     }
50150b47da0SAdam Denchfield     if (cg->spaced_restart) {
5029566063dSJacob Faibussowitsch       PetscCall(VecGetSize(tao->gradient, &dim));
503e2570530SAlp Dener       if (tao->niter % (dim*cg->min_restart_num)) cg_restart = PETSC_TRUE;
50450b47da0SAdam Denchfield     }
50550b47da0SAdam Denchfield   }
50650b47da0SAdam Denchfield   /* If the user wants regular restarts, do it every 6n iterations, where n=dimension */
50750b47da0SAdam Denchfield   if (cg->spaced_restart) {
5089566063dSJacob Faibussowitsch     PetscCall(VecGetSize(tao->gradient, &dim));
509e2570530SAlp Dener     if (0 == tao->niter % (6*dim)) cg_restart = PETSC_TRUE;
51050b47da0SAdam Denchfield   }
51150b47da0SAdam Denchfield   /* Compute the diagonal scaling vector if applicable */
51250b47da0SAdam Denchfield   if (cg->diag_scaling) {
5139566063dSJacob Faibussowitsch     PetscCall(MatLMVMUpdate(cg->B, tao->solution, tao->gradient));
51450b47da0SAdam Denchfield   }
51550b47da0SAdam Denchfield 
516484c7b14SAdam Denchfield   /* A note on diagonal scaling (to be added to paper):
517484c7b14SAdam Denchfield    For the FR, PR, PRP, and DY methods, the diagonally scaled versions
518484c7b14SAdam Denchfield    must be derived as a preconditioned CG method rather than as
519484c7b14SAdam Denchfield    a Hessian initialization like in the Broyden methods. */
52050b47da0SAdam Denchfield 
521484c7b14SAdam Denchfield   /* In that case, one writes the objective function as
522484c7b14SAdam Denchfield    f(x) \equiv f(Ay). Gradient evaluations yield g(x_k) = A g(Ay_k) = A g(x_k).
523484c7b14SAdam Denchfield    Furthermore, the direction d_k \equiv (x_k - x_{k-1})/step according to
524484c7b14SAdam Denchfield    HZ (2006) becomes A^{-1} d_k, such that d_k^T g_k remains the
525484c7b14SAdam Denchfield    same under preconditioning. Note that A is diagonal, such that A^T = A. */
52650b47da0SAdam Denchfield 
527484c7b14SAdam Denchfield   /* This yields questions like what the dot product d_k^T y_k
528484c7b14SAdam Denchfield    should look like. HZ mistakenly treats that as the same under
529484c7b14SAdam Denchfield    preconditioning, but that is not necessarily true. */
53050b47da0SAdam Denchfield 
531484c7b14SAdam Denchfield   /* Observe y_k \equiv g_k - g_{k-1}, and under the P.C. transformation,
532484c7b14SAdam 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}),
533484c7b14SAdam 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
534484c7b14SAdam Denchfield    NOT the same if our preconditioning matrix is updated between iterations.
535484c7b14SAdam Denchfield    This same issue is found when considering dot products of the form g_{k+1}^T y_k. */
53650b47da0SAdam Denchfield 
53750b47da0SAdam Denchfield   /* Compute CG step direction */
53850b47da0SAdam Denchfield   if (cg_restart) {
5399566063dSJacob Faibussowitsch     PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
540484c7b14SAdam Denchfield   } else if (pcgd_fallback) {
541484c7b14SAdam Denchfield     /* Just like preconditioned CG */
5429566063dSJacob Faibussowitsch     PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5439566063dSJacob Faibussowitsch     PetscCall(VecAXPBY(tao->stepdirection, -1.0, 0.0, cg->g_work));
54450b47da0SAdam Denchfield   } else if (ynorm2 > PETSC_MACHINE_EPSILON) {
54550b47da0SAdam Denchfield     switch (cg->cg_type) {
546484c7b14SAdam Denchfield     case CG_PCGradientDescent:
54750b47da0SAdam Denchfield       if (!cg->diag_scaling) {
548484c7b14SAdam Denchfield         if (!cg->no_scaling) {
54950b47da0SAdam Denchfield         cg->sts = step*step*dnorm*dnorm;
5509566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->sts, &tau_k, cg->alpha));
551484c7b14SAdam Denchfield         } else {
552484c7b14SAdam Denchfield           tau_k = 1.0;
553484c7b14SAdam Denchfield           ++cg->pure_gd_steps;
554484c7b14SAdam Denchfield         }
5559566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, 0.0, tao->gradient));
55650b47da0SAdam Denchfield       } else {
5579566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5589566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, 0.0, cg->g_work));
55950b47da0SAdam Denchfield       }
56050b47da0SAdam Denchfield       break;
561484c7b14SAdam Denchfield 
56250b47da0SAdam Denchfield     case CG_HestenesStiefel:
56350b47da0SAdam Denchfield       /* Classic Hestenes-Stiefel method, modified with scalar and diagonal preconditioning. */
56450b47da0SAdam Denchfield       if (!cg->diag_scaling) {
56550b47da0SAdam Denchfield         cg->sts = step*step*dnorm*dnorm;
5669566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
5679566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->sts, &tau_k, cg->alpha));
56850b47da0SAdam Denchfield         beta = tau_k*gkp1_yk/dk_yk;
5699566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
57050b47da0SAdam Denchfield       } else {
5719566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5729566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
57350b47da0SAdam Denchfield         beta = gkp1_yk/dk_yk;
5749566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
57550b47da0SAdam Denchfield       }
576c8bcdf1eSAdam Denchfield       break;
577484c7b14SAdam Denchfield 
578c8bcdf1eSAdam Denchfield     case CG_FletcherReeves:
5799566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
5809566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
5819566063dSJacob Faibussowitsch       PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
58250b47da0SAdam Denchfield       ynorm2 = ynorm*ynorm;
5839566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->yk, tao->stepdirection, &dk_yk));
58450b47da0SAdam Denchfield       if (!cg->diag_scaling) {
5859566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, step*step*dnorm*dnorm, &tau_k, cg->alpha));
58650b47da0SAdam Denchfield         beta = tau_k*gnorm2/gnorm2_old;
5879566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
58850b47da0SAdam Denchfield       } else {
5899566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old)); /* Before it's updated */
5909566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5919566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->gradient, cg->g_work, &tmp));
59250b47da0SAdam Denchfield         beta = tmp/gnorm2_old;
5939566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
59450b47da0SAdam Denchfield       }
595c8bcdf1eSAdam Denchfield       break;
596484c7b14SAdam Denchfield 
59750b47da0SAdam Denchfield     case CG_PolakRibierePolyak:
59850b47da0SAdam Denchfield       snorm = step*dnorm;
59950b47da0SAdam Denchfield       if (!cg->diag_scaling) {
6009566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
6019566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
6029566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, snorm*snorm, &tau_k, cg->alpha));
60350b47da0SAdam Denchfield         beta = tau_k*gkp1_yk/gnorm2_old;
6049566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
60550b47da0SAdam Denchfield       } else {
6069566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old));
6079566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6089566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
60950b47da0SAdam Denchfield         beta = gkp1_yk/gnorm2_old;
6109566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
61150b47da0SAdam Denchfield       }
612c8bcdf1eSAdam Denchfield       break;
613484c7b14SAdam Denchfield 
614c8bcdf1eSAdam Denchfield     case CG_PolakRibierePlus:
6159566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
6169566063dSJacob Faibussowitsch       PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
61750b47da0SAdam Denchfield       ynorm2 = ynorm*ynorm;
61850b47da0SAdam Denchfield       if (!cg->diag_scaling) {
6199566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
6209566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
6219566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, snorm*snorm, &tau_k, cg->alpha));
62250b47da0SAdam Denchfield         beta = tau_k*gkp1_yk/gnorm2_old;
62350b47da0SAdam Denchfield         beta = PetscMax(beta, 0.0);
6249566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
62550b47da0SAdam Denchfield       } else {
6269566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old)); /* Old gtDg */
6279566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6289566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
62950b47da0SAdam Denchfield         beta = gkp1_yk/gnorm2_old;
63050b47da0SAdam Denchfield         beta = PetscMax(beta, 0.0);
6319566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
63250b47da0SAdam Denchfield       }
633c8bcdf1eSAdam Denchfield       break;
634484c7b14SAdam Denchfield 
635484c7b14SAdam Denchfield     case CG_DaiYuan:
636484c7b14SAdam Denchfield       /* Dai, Yu-Hong, and Yaxiang Yuan. "A nonlinear conjugate gradient method with a strong global convergence property."
637484c7b14SAdam Denchfield          SIAM Journal on optimization 10, no. 1 (1999): 177-182. */
63850b47da0SAdam Denchfield       if (!cg->diag_scaling) {
6399566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, tao->gradient, &gd));
6409566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
6419566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->yts, &tau_k, cg->alpha));
64250b47da0SAdam Denchfield         beta = tau_k*gnorm2/(gd - gd_old);
6439566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
64450b47da0SAdam Denchfield       } else {
6459566063dSJacob Faibussowitsch         PetscCall(MatMult(cg->B, tao->stepdirection, cg->d_work));
6469566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6479566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, tao->gradient, &gtDg));
6489566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, cg->G_old, &gd_old));
6499566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->d_work, cg->g_work, &dk_yk));
65050b47da0SAdam Denchfield         dk_yk = dk_yk - gd_old;
65150b47da0SAdam Denchfield         beta = gtDg/dk_yk;
6529566063dSJacob Faibussowitsch         PetscCall(VecScale(cg->d_work, beta));
6539566063dSJacob Faibussowitsch         PetscCall(VecWAXPY(tao->stepdirection, -1.0, cg->g_work, cg->d_work));
65450b47da0SAdam Denchfield       }
655c8bcdf1eSAdam Denchfield       break;
656484c7b14SAdam Denchfield 
657c8bcdf1eSAdam Denchfield     case CG_HagerZhang:
658484c7b14SAdam Denchfield       /* Hager, William W., and Hongchao Zhang. "Algorithm 851: CG_DESCENT, a conjugate gradient method with guaranteed descent."
659484c7b14SAdam Denchfield          ACM Transactions on Mathematical Software (TOMS) 32, no. 1 (2006): 113-137. */
6609566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
6619566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
6629566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
66350b47da0SAdam Denchfield       snorm = dnorm*step;
66450b47da0SAdam Denchfield       cg->yts = step*dk_yk;
665c8bcdf1eSAdam Denchfield       if (cg->use_dynamic_restart) {
6669566063dSJacob Faibussowitsch         PetscCall(TaoBNCGCheckDynamicRestart(tao, step, gd, gd_old, &cg->dynamic_restart, fold));
667c8bcdf1eSAdam Denchfield       }
66850b47da0SAdam Denchfield       if (cg->dynamic_restart) {
6699566063dSJacob Faibussowitsch         PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
670c8bcdf1eSAdam Denchfield       } else {
671c8bcdf1eSAdam Denchfield         if (!cg->diag_scaling) {
6729566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
6739566063dSJacob Faibussowitsch           PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha));
674c8bcdf1eSAdam Denchfield           /* Supplying cg->alpha = -1.0 will give the CG_DESCENT 5.3 special case of tau_k = 1.0 */
675c8bcdf1eSAdam Denchfield           tmp = gd/dk_yk;
676c8bcdf1eSAdam Denchfield           beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk));
677c8bcdf1eSAdam Denchfield           /* Bound beta as in CG_DESCENT 5.3, as implemented, with the third comparison from DK 2013 */
67850b47da0SAdam Denchfield           beta = PetscMax(PetscMax(beta, cg->hz_eta*tau_k*gd_old/(dnorm*dnorm)), cg->dk_eta*tau_k*gd/(dnorm*dnorm));
679c8bcdf1eSAdam Denchfield           /* d <- -t*g + beta*t*d */
6809566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
681c8bcdf1eSAdam Denchfield         } else {
682c8bcdf1eSAdam Denchfield           /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
683c8bcdf1eSAdam Denchfield           cg->yty = ynorm2;
684c8bcdf1eSAdam Denchfield           cg->sts = snorm*snorm;
68550b47da0SAdam Denchfield           /* Apply the diagonal scaling to all my vectors */
6869566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6879566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
6889566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->stepdirection, cg->d_work));
689c8bcdf1eSAdam Denchfield           /* Construct the constant ytDgkp1 */
6909566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
691c8bcdf1eSAdam Denchfield           /* Construct the constant for scaling Dkyk in the update */
692c8bcdf1eSAdam Denchfield           tmp = gd/dk_yk;
6939566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
694c8bcdf1eSAdam Denchfield           tau_k = -tau_k*gd/(dk_yk*dk_yk);
695c8bcdf1eSAdam Denchfield           /* beta is the constant which adds the dk contribution */
696484c7b14SAdam Denchfield           beta = gkp1_yk/dk_yk + cg->hz_theta*tau_k; /* HZ; (1.15) from DK 2013 */
697c8bcdf1eSAdam Denchfield           /* From HZ2013, modified to account for diagonal scaling*/
6989566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->G_old, cg->d_work, &gd_old));
6999566063dSJacob Faibussowitsch           PetscCall(VecDot(tao->stepdirection, cg->g_work, &gd));
70050b47da0SAdam Denchfield           beta = PetscMax(PetscMax(beta, cg->hz_eta*gd_old/(dnorm*dnorm)), cg->dk_eta*gd/(dnorm*dnorm));
701c8bcdf1eSAdam Denchfield           /* Do the update */
7029566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
70350b47da0SAdam Denchfield         }
70450b47da0SAdam Denchfield       }
705c8bcdf1eSAdam Denchfield       break;
706484c7b14SAdam Denchfield 
707c8bcdf1eSAdam Denchfield     case CG_DaiKou:
708484c7b14SAdam Denchfield       /* Dai, Yu-Hong, and Cai-Xia Kou. "A nonlinear conjugate gradient algorithm with an optimal property and an improved Wolfe line search."
709484c7b14SAdam Denchfield          SIAM Journal on Optimization 23, no. 1 (2013): 296-320. */
7109566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
7119566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
7129566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
71350b47da0SAdam Denchfield       snorm = step*dnorm;
71450b47da0SAdam Denchfield       cg->yts = dk_yk*step;
715c8bcdf1eSAdam Denchfield       if (!cg->diag_scaling) {
7169566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
7179566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha));
718c8bcdf1eSAdam Denchfield         /* Use cg->alpha = -1.0 to get tau_k = 1.0 as in CG_DESCENT 5.3 */
719c8bcdf1eSAdam Denchfield         tmp = gd/dk_yk;
72050b47da0SAdam Denchfield         beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk) + gd/(dnorm*dnorm)) - step*gd/dk_yk;
72150b47da0SAdam Denchfield         beta = PetscMax(PetscMax(beta, cg->hz_eta*tau_k*gd_old/(dnorm*dnorm)), cg->dk_eta*tau_k*gd/(dnorm*dnorm));
722c8bcdf1eSAdam Denchfield         /* d <- -t*g + beta*t*d */
7239566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, 0.0, beta, tao->gradient, cg->yk));
724c8bcdf1eSAdam Denchfield       } else {
725c8bcdf1eSAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
726c8bcdf1eSAdam Denchfield         cg->yty = ynorm2;
727c8bcdf1eSAdam Denchfield         cg->sts = snorm*snorm;
7289566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
7299566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
7309566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->stepdirection, cg->d_work));
731c8bcdf1eSAdam Denchfield         /* Construct the constant ytDgkp1 */
7329566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
7339566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
734c8bcdf1eSAdam Denchfield         tau_k = tau_k*gd/(dk_yk*dk_yk);
735c8bcdf1eSAdam Denchfield         tmp = gd/dk_yk;
736c8bcdf1eSAdam Denchfield         /* beta is the constant which adds the dk contribution */
737484c7b14SAdam Denchfield         beta = gkp1_yk/dk_yk - step*tmp - tau_k;
738c8bcdf1eSAdam Denchfield         /* Update this for the last term in beta */
7399566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, tao->stepdirection, &dk_yk));
740c8bcdf1eSAdam Denchfield         beta += tmp*dk_yk/(dnorm*dnorm); /* projection of y_work onto dk */
7419566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, cg->g_work, &gd));
7429566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->d_work, &gd_old));
74350b47da0SAdam Denchfield         beta = PetscMax(PetscMax(beta, cg->hz_eta*gd_old/(dnorm*dnorm)), cg->dk_eta*gd/(dnorm*dnorm));
744c8bcdf1eSAdam Denchfield         /* Do the update */
7459566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
74650b47da0SAdam Denchfield       }
747c8bcdf1eSAdam Denchfield       break;
748484c7b14SAdam Denchfield 
749c8bcdf1eSAdam Denchfield     case CG_KouDai:
750110fc3b0SBarry Smith       /* Kou, Cai-Xia, and Yu-Hong Dai. "A modified self-scaling memoryless Broyden-Fletcher-Goldfarb-Shanno method for unconstrained optimization."
751484c7b14SAdam Denchfield          Journal of Optimization Theory and Applications 165, no. 1 (2015): 209-224. */
7529566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
7539566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
7549566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
75550b47da0SAdam Denchfield       snorm = step*dnorm;
75650b47da0SAdam Denchfield       cg->yts = dk_yk*step;
757c8bcdf1eSAdam Denchfield       if (cg->use_dynamic_restart) {
7589566063dSJacob Faibussowitsch         PetscCall(TaoBNCGCheckDynamicRestart(tao, step, gd, gd_old, &cg->dynamic_restart, fold));
759c8bcdf1eSAdam Denchfield       }
76050b47da0SAdam Denchfield       if (cg->dynamic_restart) {
7619566063dSJacob Faibussowitsch         PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
762c8bcdf1eSAdam Denchfield       } else {
763c8bcdf1eSAdam Denchfield         if (!cg->diag_scaling) {
7649566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
7659566063dSJacob Faibussowitsch           PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha));
766c8bcdf1eSAdam Denchfield           beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk)) - step*gd/dk_yk;
767c8bcdf1eSAdam Denchfield           if (beta < cg->zeta*tau_k*gd/(dnorm*dnorm)) /* 0.1 is KD's zeta parameter */
768c8bcdf1eSAdam Denchfield           {
769c8bcdf1eSAdam Denchfield             beta = cg->zeta*tau_k*gd/(dnorm*dnorm);
770c8bcdf1eSAdam Denchfield             gamma = 0.0;
771c8bcdf1eSAdam Denchfield           } else {
772c8bcdf1eSAdam Denchfield             if (gkp1_yk < 0 && cg->neg_xi) gamma = -1.0*gd/dk_yk;
773484c7b14SAdam Denchfield             /* This seems to be very effective when there's no tau_k scaling.
774484c7b14SAdam Denchfield                This guarantees a large descent step every iteration, going through DK 2015 Lemma 3.1's proof but allowing for negative xi */
77550b47da0SAdam Denchfield             else {
77650b47da0SAdam Denchfield               gamma = cg->xi*gd/dk_yk;
77750b47da0SAdam Denchfield             }
778c8bcdf1eSAdam Denchfield           }
779c8bcdf1eSAdam Denchfield           /* d <- -t*g + beta*t*d + t*tmp*yk */
7809566063dSJacob Faibussowitsch           PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, gamma*tau_k, beta, tao->gradient, cg->yk));
781c8bcdf1eSAdam Denchfield         } else {
782c8bcdf1eSAdam Denchfield           /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
783c8bcdf1eSAdam Denchfield           cg->yty = ynorm2;
784c8bcdf1eSAdam Denchfield           cg->sts = snorm*snorm;
7859566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
7869566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
787c8bcdf1eSAdam Denchfield           /* Construct the constant ytDgkp1 */
7889566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->g_work, &gkp1D_yk));
789c8bcdf1eSAdam Denchfield           /* Construct the constant for scaling Dkyk in the update */
790c8bcdf1eSAdam Denchfield           gamma = gd/dk_yk;
791c8bcdf1eSAdam Denchfield           /* tau_k = -ytDy/(ytd)^2 * gd */
7929566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
793c8bcdf1eSAdam Denchfield           tau_k = tau_k*gd/(dk_yk*dk_yk);
794c8bcdf1eSAdam Denchfield           /* beta is the constant which adds the d_k contribution */
795c8bcdf1eSAdam Denchfield           beta = gkp1D_yk/dk_yk - step*gamma - tau_k;
796c8bcdf1eSAdam Denchfield           /* Here is the requisite check */
7979566063dSJacob Faibussowitsch           PetscCall(VecDot(tao->stepdirection, cg->g_work, &tmp));
798c8bcdf1eSAdam Denchfield           if (cg->neg_xi) {
799c8bcdf1eSAdam Denchfield             /* modified KD implementation */
800c8bcdf1eSAdam Denchfield             if (gkp1D_yk/dk_yk < 0) gamma = -1.0*gd/dk_yk;
80150b47da0SAdam Denchfield             else {
80250b47da0SAdam Denchfield               gamma = cg->xi*gd/dk_yk;
80350b47da0SAdam Denchfield             }
804c8bcdf1eSAdam Denchfield             if (beta < cg->zeta*tmp/(dnorm*dnorm)) {
805c8bcdf1eSAdam Denchfield               beta = cg->zeta*tmp/(dnorm*dnorm);
806c8bcdf1eSAdam Denchfield               gamma = 0.0;
807c8bcdf1eSAdam Denchfield             }
808c8bcdf1eSAdam Denchfield           } else { /* original KD 2015 implementation */
809c8bcdf1eSAdam Denchfield             if (beta < cg->zeta*tmp/(dnorm*dnorm)) {
810c8bcdf1eSAdam Denchfield               beta = cg->zeta*tmp/(dnorm*dnorm);
811c8bcdf1eSAdam Denchfield               gamma = 0.0;
812c8bcdf1eSAdam Denchfield             } else {
813c8bcdf1eSAdam Denchfield               gamma = cg->xi*gd/dk_yk;
814c8bcdf1eSAdam Denchfield             }
815c8bcdf1eSAdam Denchfield           }
816c8bcdf1eSAdam Denchfield           /* Do the update in two steps */
8179566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
8189566063dSJacob Faibussowitsch           PetscCall(VecAXPY(tao->stepdirection, gamma, cg->y_work));
81950b47da0SAdam Denchfield         }
82050b47da0SAdam Denchfield       }
821c8bcdf1eSAdam Denchfield       break;
822484c7b14SAdam Denchfield 
823484c7b14SAdam Denchfield     case CG_SSML_BFGS:
824484c7b14SAdam Denchfield       /* Perry, J. M. "A class of conjugate gradient algorithms with a two-step variable-metric memory."
825484c7b14SAdam Denchfield          Discussion Papers 269 (1977). */
8269566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
8279566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
828484c7b14SAdam Denchfield       snorm = step*dnorm;
829484c7b14SAdam Denchfield       cg->yts = dk_yk*step;
830484c7b14SAdam Denchfield       cg->yty = ynorm2;
831484c7b14SAdam Denchfield       cg->sts = snorm*snorm;
832484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
8339566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
8349566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, cg->yts, cg->sts, &tau_k, cg->alpha));
835484c7b14SAdam Denchfield         tmp = gd/dk_yk;
836484c7b14SAdam Denchfield         beta = tau_k*(gkp1_yk/dk_yk - cg->yty*gd/(dk_yk*dk_yk)) - step*tmp;
837484c7b14SAdam Denchfield         /* d <- -t*g + beta*t*d + t*tmp*yk */
8389566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp*tau_k, beta, tao->gradient, cg->yk));
839484c7b14SAdam Denchfield       } else {
840484c7b14SAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
8419566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
8429566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
843484c7b14SAdam Denchfield         /* compute scalar gamma */
8449566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
8459566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
846484c7b14SAdam Denchfield         gamma = gd/dk_yk;
847484c7b14SAdam Denchfield         /* Compute scalar beta */
848484c7b14SAdam Denchfield         beta = (gkp1_yk/dk_yk - gd*tmp/(dk_yk*dk_yk)) - step*gd/dk_yk;
849484c7b14SAdam Denchfield         /* Compute stepdirection d_kp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
8509566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
851484c7b14SAdam Denchfield       }
852484c7b14SAdam Denchfield       break;
853484c7b14SAdam Denchfield 
854484c7b14SAdam Denchfield     case CG_SSML_DFP:
8559566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
8569566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
857484c7b14SAdam Denchfield       snorm = step*dnorm;
858484c7b14SAdam Denchfield       cg->yts = dk_yk*step;
859484c7b14SAdam Denchfield       cg->yty = ynorm2;
860484c7b14SAdam Denchfield       cg->sts = snorm*snorm;
861484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
862484c7b14SAdam Denchfield         /* Instead of a regular convex combination, we will solve a quadratic formula. */
8639566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, cg->yts, cg->sts, &tau_k, cg->alpha));
8649566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
865484c7b14SAdam Denchfield         tau_k = cg->dfp_scale*tau_k;
866484c7b14SAdam Denchfield         tmp = tau_k*gkp1_yk/cg->yty;
867484c7b14SAdam Denchfield         beta = -step*gd/dk_yk;
868484c7b14SAdam Denchfield         /* d <- -t*g + beta*d + tmp*yk */
8699566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp, beta, tao->gradient, cg->yk));
870484c7b14SAdam Denchfield       } else {
871484c7b14SAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless DFP step */
8729566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
8739566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
874484c7b14SAdam Denchfield         /* compute scalar gamma */
8759566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
8769566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
877484c7b14SAdam Denchfield         gamma = (gkp1_yk/tmp);
878484c7b14SAdam Denchfield         /* Compute scalar beta */
879484c7b14SAdam Denchfield         beta = -step*gd/dk_yk;
880484c7b14SAdam Denchfield         /* Compute stepdirection d_kp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
8819566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
882484c7b14SAdam Denchfield       }
883484c7b14SAdam Denchfield       break;
884484c7b14SAdam Denchfield 
885484c7b14SAdam Denchfield     case CG_SSML_BROYDEN:
8869566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
8879566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
888484c7b14SAdam Denchfield       snorm = step*dnorm;
889484c7b14SAdam Denchfield       cg->yts = step*dk_yk;
890484c7b14SAdam Denchfield       cg->yty = ynorm2;
891484c7b14SAdam Denchfield       cg->sts = snorm*snorm;
892484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
893484c7b14SAdam Denchfield         /* Instead of a regular convex combination, we will solve a quadratic formula. */
8949566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, step*dk_yk, snorm*snorm, &tau_bfgs, cg->bfgs_scale));
8959566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, step*dk_yk, snorm*snorm, &tau_dfp, cg->dfp_scale));
8969566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
897484c7b14SAdam Denchfield         tau_k = cg->theta*tau_bfgs + (1.0-cg->theta)*tau_dfp;
898484c7b14SAdam Denchfield         /* If bfgs_scale = 1.0, it should reproduce the bfgs tau_bfgs. If bfgs_scale = 0.0,
899484c7b14SAdam Denchfield            it should reproduce the tau_dfp scaling. Same with dfp_scale.   */
900484c7b14SAdam Denchfield         tmp = cg->theta*tau_bfgs*gd/dk_yk + (1-cg->theta)*tau_dfp*gkp1_yk/cg->yty;
901484c7b14SAdam Denchfield         beta = cg->theta*tau_bfgs*(gkp1_yk/dk_yk - cg->yty*gd/(dk_yk*dk_yk)) - step*gd/dk_yk;
902484c7b14SAdam Denchfield         /* d <- -t*g + beta*d + tmp*yk */
9039566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp, beta, tao->gradient, cg->yk));
904484c7b14SAdam Denchfield       } else {
905484c7b14SAdam Denchfield         /* We have diagonal scaling enabled */
9069566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
9079566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
908484c7b14SAdam Denchfield         /* compute scalar gamma */
9099566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
9109566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
911484c7b14SAdam Denchfield         gamma = cg->theta*gd/dk_yk + (1-cg->theta)*(gkp1_yk/tmp);
912484c7b14SAdam Denchfield         /* Compute scalar beta */
913484c7b14SAdam Denchfield         beta = cg->theta*(gkp1_yk/dk_yk - gd*tmp/(dk_yk*dk_yk)) - step*gd/dk_yk;
914484c7b14SAdam Denchfield         /* Compute stepdirection dkp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
9159566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
916484c7b14SAdam Denchfield       }
917484c7b14SAdam Denchfield       break;
918484c7b14SAdam Denchfield 
919c8bcdf1eSAdam Denchfield     default:
920c8bcdf1eSAdam Denchfield       break;
921484c7b14SAdam Denchfield 
922c8bcdf1eSAdam Denchfield     }
923c8bcdf1eSAdam Denchfield   }
924c8bcdf1eSAdam Denchfield   PetscFunctionReturn(0);
925c8bcdf1eSAdam Denchfield }
926c8bcdf1eSAdam Denchfield 
927c8bcdf1eSAdam Denchfield PETSC_INTERN PetscErrorCode TaoBNCGConductIteration(Tao tao, PetscReal gnorm)
928c8bcdf1eSAdam Denchfield {
929c8bcdf1eSAdam Denchfield   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
930c8bcdf1eSAdam Denchfield   TaoLineSearchConvergedReason ls_status = TAOLINESEARCH_CONTINUE_ITERATING;
9318ca2df50S   PetscReal                    step=1.0,gnorm2,gd,dnorm=0.0;
932c8bcdf1eSAdam Denchfield   PetscReal                    gnorm2_old,f_old,resnorm, gnorm_old;
933c624ebd3SAlp Dener   PetscBool                    pcgd_fallback = PETSC_FALSE;
934c8bcdf1eSAdam Denchfield 
935c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
936c8bcdf1eSAdam Denchfield   /* We are now going to perform a line search along the direction. */
937c8bcdf1eSAdam Denchfield   /* Store solution and gradient info before it changes */
9389566063dSJacob Faibussowitsch   PetscCall(VecCopy(tao->solution, cg->X_old));
9399566063dSJacob Faibussowitsch   PetscCall(VecCopy(tao->gradient, cg->G_old));
9409566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, cg->unprojected_gradient_old));
941c8bcdf1eSAdam Denchfield 
942c8bcdf1eSAdam Denchfield   gnorm_old = gnorm;
943c8bcdf1eSAdam Denchfield   gnorm2_old = gnorm_old*gnorm_old;
944c8bcdf1eSAdam Denchfield   f_old = cg->f;
945484c7b14SAdam Denchfield   /* Perform bounded line search. If we are recycling a solution from a previous */
946484c7b14SAdam Denchfield   /* TaoSolve, then we want to immediately skip to calculating a new direction rather than performing a linesearch */
947414d97d3SAlp Dener   if (!(tao->recycle && 0 == tao->niter)) {
948484c7b14SAdam Denchfield     /* Above logic: the below code happens every iteration, except for the first iteration of a recycled TaoSolve */
9499566063dSJacob Faibussowitsch     PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
9509566063dSJacob Faibussowitsch     PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
9519566063dSJacob Faibussowitsch     PetscCall(TaoAddLineSearchCounts(tao));
952c8bcdf1eSAdam Denchfield 
953c8bcdf1eSAdam Denchfield     /*  Check linesearch failure */
954c8bcdf1eSAdam Denchfield     if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
955c8bcdf1eSAdam Denchfield       ++cg->ls_fails;
956c624ebd3SAlp Dener       if (cg->cg_type == CG_GradientDescent) {
957c8bcdf1eSAdam Denchfield         /* Nothing left to do but fail out of the optimization */
958c8bcdf1eSAdam Denchfield         step = 0.0;
959c8bcdf1eSAdam Denchfield         tao->reason = TAO_DIVERGED_LS_FAILURE;
960c8bcdf1eSAdam Denchfield       } else {
961484c7b14SAdam Denchfield         /* Restore previous point, perform preconditioned GD and regular GD steps at the last good point */
9629566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->X_old, tao->solution));
9639566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->G_old, tao->gradient));
9649566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->unprojected_gradient_old, cg->unprojected_gradient));
965c8bcdf1eSAdam Denchfield         gnorm = gnorm_old;
966c8bcdf1eSAdam Denchfield         gnorm2 = gnorm2_old;
967c8bcdf1eSAdam Denchfield         cg->f = f_old;
968c8bcdf1eSAdam Denchfield 
969484c7b14SAdam Denchfield         /* Fall back on preconditioned CG (so long as you're not already using it) */
970484c7b14SAdam Denchfield         if (cg->cg_type != CG_PCGradientDescent && cg->diag_scaling) {
971e2570530SAlp Dener           pcgd_fallback = PETSC_TRUE;
9729566063dSJacob Faibussowitsch           PetscCall(TaoBNCGStepDirectionUpdate(tao, gnorm2, step, f_old, gnorm2_old, dnorm, pcgd_fallback));
973484c7b14SAdam Denchfield 
9749566063dSJacob Faibussowitsch           PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
9759566063dSJacob Faibussowitsch           PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
976c8bcdf1eSAdam Denchfield 
9779566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
9789566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
9799566063dSJacob Faibussowitsch           PetscCall(TaoAddLineSearchCounts(tao));
980c8bcdf1eSAdam Denchfield 
981484c7b14SAdam Denchfield           pcgd_fallback = PETSC_FALSE;
982484c7b14SAdam Denchfield           if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
983484c7b14SAdam Denchfield             /* Going to perform a regular gradient descent step. */
984484c7b14SAdam Denchfield             ++cg->ls_fails;
985484c7b14SAdam Denchfield             step = 0.0;
986484c7b14SAdam Denchfield           }
987484c7b14SAdam Denchfield         }
988484c7b14SAdam Denchfield         /* Fall back on the scaled gradient step */
989484c7b14SAdam Denchfield         if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
990484c7b14SAdam Denchfield           ++cg->ls_fails;
9919566063dSJacob Faibussowitsch           PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
9929566063dSJacob Faibussowitsch           PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
9939566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
9949566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
9959566063dSJacob Faibussowitsch           PetscCall(TaoAddLineSearchCounts(tao));
996484c7b14SAdam Denchfield         }
997484c7b14SAdam Denchfield 
998c8bcdf1eSAdam Denchfield         if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
999c8bcdf1eSAdam Denchfield           /* Nothing left to do but fail out of the optimization */
100050b47da0SAdam Denchfield           ++cg->ls_fails;
1001c8bcdf1eSAdam Denchfield           step = 0.0;
1002c8bcdf1eSAdam Denchfield           tao->reason = TAO_DIVERGED_LS_FAILURE;
1003484c7b14SAdam Denchfield         } else {
1004484c7b14SAdam Denchfield           /* One of the fallbacks worked. Set them both back equal to false. */
1005484c7b14SAdam Denchfield           pcgd_fallback = PETSC_FALSE;
1006c8bcdf1eSAdam Denchfield         }
1007c8bcdf1eSAdam Denchfield       }
1008c8bcdf1eSAdam Denchfield     }
1009c8bcdf1eSAdam Denchfield     /* Convergence test for line search failure */
1010c8bcdf1eSAdam Denchfield     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
1011c8bcdf1eSAdam Denchfield 
1012c8bcdf1eSAdam Denchfield     /* Standard convergence test */
10139566063dSJacob Faibussowitsch     PetscCall(VecFischer(tao->solution, cg->unprojected_gradient, tao->XL, tao->XU, cg->W));
10149566063dSJacob Faibussowitsch     PetscCall(VecNorm(cg->W, NORM_2, &resnorm));
10153c859ba3SBarry Smith     PetscCheck(!PetscIsInfOrNanReal(resnorm),PetscObjectComm((PetscObject)tao),PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
10169566063dSJacob Faibussowitsch     PetscCall(TaoLogConvergenceHistory(tao, cg->f, resnorm, 0.0, tao->ksp_its));
10179566063dSJacob Faibussowitsch     PetscCall(TaoMonitor(tao, tao->niter, cg->f, resnorm, 0.0, step));
10189566063dSJacob Faibussowitsch     PetscCall((*tao->ops->convergencetest)(tao,tao->cnvP));
1019c8bcdf1eSAdam Denchfield     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
1020484c7b14SAdam Denchfield   }
1021c8bcdf1eSAdam Denchfield   /* Assert we have an updated step and we need at least one more iteration. */
1022c8bcdf1eSAdam Denchfield   /* Calculate the next direction */
1023c8bcdf1eSAdam Denchfield   /* Estimate the active set at the new solution */
10249566063dSJacob Faibussowitsch   PetscCall(TaoBNCGEstimateActiveSet(tao, cg->as_type));
1025c8bcdf1eSAdam Denchfield   /* Compute the projected gradient and its norm */
10269566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, tao->gradient));
10279566063dSJacob Faibussowitsch   PetscCall(VecISSet(tao->gradient, cg->active_idx, 0.0));
10289566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->gradient,NORM_2,&gnorm));
1029c8bcdf1eSAdam Denchfield   gnorm2 = gnorm*gnorm;
1030c8bcdf1eSAdam Denchfield 
1031484c7b14SAdam Denchfield   /* Calculate some quantities used in the StepDirectionUpdate. */
10329566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->stepdirection, NORM_2, &dnorm));
1033484c7b14SAdam Denchfield   /* Update the step direction. */
10349566063dSJacob Faibussowitsch   PetscCall(TaoBNCGStepDirectionUpdate(tao, gnorm2, step, f_old, gnorm2_old, dnorm, pcgd_fallback));
1035484c7b14SAdam Denchfield   ++tao->niter;
10369566063dSJacob Faibussowitsch   PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
1037c8bcdf1eSAdam Denchfield 
1038c8bcdf1eSAdam Denchfield   if (cg->cg_type != CG_GradientDescent) {
1039c8bcdf1eSAdam Denchfield     /* Figure out which previously active variables became inactive this iteration */
10409566063dSJacob Faibussowitsch     PetscCall(ISDestroy(&cg->new_inactives));
1041c8bcdf1eSAdam Denchfield     if (cg->inactive_idx && cg->inactive_old) {
10429566063dSJacob Faibussowitsch       PetscCall(ISDifference(cg->inactive_idx, cg->inactive_old, &cg->new_inactives));
1043c8bcdf1eSAdam Denchfield     }
1044c8bcdf1eSAdam Denchfield     /* Selectively reset the CG step those freshly inactive variables */
1045c8bcdf1eSAdam Denchfield     if (cg->new_inactives) {
10469566063dSJacob Faibussowitsch       PetscCall(VecGetSubVector(tao->stepdirection, cg->new_inactives, &cg->inactive_step));
10479566063dSJacob Faibussowitsch       PetscCall(VecGetSubVector(cg->unprojected_gradient, cg->new_inactives, &cg->inactive_grad));
10489566063dSJacob Faibussowitsch       PetscCall(VecCopy(cg->inactive_grad, cg->inactive_step));
10499566063dSJacob Faibussowitsch       PetscCall(VecScale(cg->inactive_step, -1.0));
10509566063dSJacob Faibussowitsch       PetscCall(VecRestoreSubVector(tao->stepdirection, cg->new_inactives, &cg->inactive_step));
10519566063dSJacob Faibussowitsch       PetscCall(VecRestoreSubVector(cg->unprojected_gradient, cg->new_inactives, &cg->inactive_grad));
1052c8bcdf1eSAdam Denchfield     }
1053c8bcdf1eSAdam Denchfield     /* Verify that this is a descent direction */
10549566063dSJacob Faibussowitsch     PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
10559566063dSJacob Faibussowitsch     PetscCall(VecNorm(tao->stepdirection, NORM_2, &dnorm));
105650b47da0SAdam Denchfield     if (PetscIsInfOrNanReal(gd) || (gd/(dnorm*dnorm) <= -1e10 || gd/(dnorm*dnorm) >= -1e-10)) {
1057c8bcdf1eSAdam Denchfield       /* Not a descent direction, so we reset back to projected gradient descent */
10589566063dSJacob Faibussowitsch       PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
10599566063dSJacob Faibussowitsch       PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
1060c8bcdf1eSAdam Denchfield       ++cg->descent_error;
1061c8bcdf1eSAdam Denchfield     } else {
1062c8bcdf1eSAdam Denchfield     }
1063c8bcdf1eSAdam Denchfield   }
1064ac9112b8SAlp Dener   PetscFunctionReturn(0);
1065ac9112b8SAlp Dener }
1066484c7b14SAdam Denchfield 
1067484c7b14SAdam Denchfield PetscErrorCode TaoBNCGSetH0(Tao tao, Mat H0)
1068484c7b14SAdam Denchfield {
1069484c7b14SAdam Denchfield   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
1070484c7b14SAdam Denchfield 
1071484c7b14SAdam Denchfield   PetscFunctionBegin;
10729566063dSJacob Faibussowitsch   PetscCall(PetscObjectReference((PetscObject)H0));
1073484c7b14SAdam Denchfield   cg->pc = H0;
1074484c7b14SAdam Denchfield   PetscFunctionReturn(0);
1075484c7b14SAdam Denchfield }
1076