xref: /petsc/src/tao/bound/impls/bncg/bncg.c (revision 3ba1676111f5c958fe6c2729b46ca4d523958bb3)
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 
28d71ae5a4SJacob Faibussowitsch PetscErrorCode TaoBNCGEstimateActiveSet(Tao tao, PetscInt asType)
29d71ae5a4SJacob Faibussowitsch {
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));
509371c9d4SSatish Balay     PetscCall(TaoEstimateActiveBounds(tao->solution, tao->XL, tao->XU, cg->unprojected_gradient, cg->W, cg->work, cg->as_step, &cg->as_tol, &cg->active_lower, &cg->active_upper, &cg->active_fixed, &cg->active_idx, &cg->inactive_idx));
51c4b75bccSAlp Dener     break;
52d71ae5a4SJacob Faibussowitsch   default:
53d71ae5a4SJacob Faibussowitsch     break;
5461be54a6SAlp Dener   }
55*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
5661be54a6SAlp Dener }
5761be54a6SAlp Dener 
58d71ae5a4SJacob Faibussowitsch PetscErrorCode TaoBNCGBoundStep(Tao tao, PetscInt asType, Vec step)
59d71ae5a4SJacob Faibussowitsch {
6061be54a6SAlp Dener   TAO_BNCG *cg = (TAO_BNCG *)tao->data;
6161be54a6SAlp Dener 
6261be54a6SAlp Dener   PetscFunctionBegin;
63a1318120SAlp Dener   switch (asType) {
64d71ae5a4SJacob Faibussowitsch   case CG_AS_NONE:
65d71ae5a4SJacob Faibussowitsch     PetscCall(VecISSet(step, cg->active_idx, 0.0));
66d71ae5a4SJacob Faibussowitsch     break;
6761be54a6SAlp Dener 
68d71ae5a4SJacob Faibussowitsch   case CG_AS_BERTSEKAS:
69d71ae5a4SJacob Faibussowitsch     PetscCall(TaoBoundStep(tao->solution, tao->XL, tao->XU, cg->active_lower, cg->active_upper, cg->active_fixed, 1.0, step));
70d71ae5a4SJacob Faibussowitsch     break;
7161be54a6SAlp Dener 
72d71ae5a4SJacob Faibussowitsch   default:
73d71ae5a4SJacob Faibussowitsch     break;
7461be54a6SAlp Dener   }
75*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
7661be54a6SAlp Dener }
7761be54a6SAlp Dener 
78d71ae5a4SJacob Faibussowitsch static PetscErrorCode TaoSolve_BNCG(Tao tao)
79d71ae5a4SJacob Faibussowitsch {
80ac9112b8SAlp Dener   TAO_BNCG *cg   = (TAO_BNCG *)tao->data;
81484c7b14SAdam Denchfield   PetscReal step = 1.0, gnorm, gnorm2, resnorm;
82c4b75bccSAlp Dener   PetscInt  nDiff;
83ac9112b8SAlp Dener 
84ac9112b8SAlp Dener   PetscFunctionBegin;
85ac9112b8SAlp Dener   /*   Project the current point onto the feasible set */
869566063dSJacob Faibussowitsch   PetscCall(TaoComputeVariableBounds(tao));
879566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchSetVariableBounds(tao->linesearch, tao->XL, tao->XU));
88ac9112b8SAlp Dener 
89c8bcdf1eSAdam Denchfield   /* Project the initial point onto the feasible region */
909566063dSJacob Faibussowitsch   PetscCall(TaoBoundSolution(tao->solution, tao->XL, tao->XU, 0.0, &nDiff, tao->solution));
91484c7b14SAdam Denchfield 
9248a46eb9SPierre Jolivet   if (nDiff > 0 || !tao->recycle) PetscCall(TaoComputeObjectiveAndGradient(tao, tao->solution, &cg->f, cg->unprojected_gradient));
939566063dSJacob Faibussowitsch   PetscCall(VecNorm(cg->unprojected_gradient, NORM_2, &gnorm));
943c859ba3SBarry Smith   PetscCheck(!PetscIsInfOrNanReal(cg->f) && !PetscIsInfOrNanReal(gnorm), PetscObjectComm((PetscObject)tao), PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
95ac9112b8SAlp Dener 
9661be54a6SAlp Dener   /* Estimate the active set and compute the projected gradient */
979566063dSJacob Faibussowitsch   PetscCall(TaoBNCGEstimateActiveSet(tao, cg->as_type));
9861be54a6SAlp Dener 
99ac9112b8SAlp Dener   /* Project the gradient and calculate the norm */
1009566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, tao->gradient));
1019566063dSJacob Faibussowitsch   PetscCall(VecISSet(tao->gradient, cg->active_idx, 0.0));
1029566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->gradient, NORM_2, &gnorm));
103ac9112b8SAlp Dener   gnorm2 = gnorm * gnorm;
104ac9112b8SAlp Dener 
105c8bcdf1eSAdam Denchfield   /* Initialize counters */
106e031d6f5SAlp Dener   tao->niter   = 0;
10750b47da0SAdam Denchfield   cg->ls_fails = cg->descent_error = 0;
108c8bcdf1eSAdam Denchfield   cg->resets                       = -1;
109484c7b14SAdam Denchfield   cg->skipped_updates = cg->pure_gd_steps = 0;
110c8bcdf1eSAdam Denchfield   cg->iter_quad                           = 0;
111c8bcdf1eSAdam Denchfield 
112c8bcdf1eSAdam Denchfield   /* Convergence test at the starting point. */
113ac9112b8SAlp Dener   tao->reason = TAO_CONTINUE_ITERATING;
114484c7b14SAdam Denchfield 
1159566063dSJacob Faibussowitsch   PetscCall(VecFischer(tao->solution, cg->unprojected_gradient, tao->XL, tao->XU, cg->W));
1169566063dSJacob Faibussowitsch   PetscCall(VecNorm(cg->W, NORM_2, &resnorm));
1173c859ba3SBarry Smith   PetscCheck(!PetscIsInfOrNanReal(resnorm), PetscObjectComm((PetscObject)tao), PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
1189566063dSJacob Faibussowitsch   PetscCall(TaoLogConvergenceHistory(tao, cg->f, resnorm, 0.0, tao->ksp_its));
1199566063dSJacob Faibussowitsch   PetscCall(TaoMonitor(tao, tao->niter, cg->f, resnorm, 0.0, step));
120dbbe0bcdSBarry Smith   PetscUseTypeMethod(tao, convergencetest, tao->cnvP);
121*3ba16761SJacob Faibussowitsch   if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(PETSC_SUCCESS);
122484c7b14SAdam Denchfield   /* Calculate initial direction. */
123414d97d3SAlp Dener   if (!tao->recycle) {
124484c7b14SAdam Denchfield     /* We are not recycling a solution/history from a past TaoSolve */
1259566063dSJacob Faibussowitsch     PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
126ac9112b8SAlp Dener   }
127c8bcdf1eSAdam Denchfield   /* Initial gradient descent step. Scaling by 1.0 also does a decent job for some problems. */
128c8bcdf1eSAdam Denchfield   while (1) {
129e1e80dc8SAlp Dener     /* Call general purpose update function */
130e1e80dc8SAlp Dener     if (tao->ops->update) {
131dbbe0bcdSBarry Smith       PetscUseTypeMethod(tao, update, tao->niter, tao->user_update);
1327494f0b1SStefano Zampini       PetscCall(TaoComputeObjectiveAndGradient(tao, tao->solution, &cg->f, cg->unprojected_gradient));
133e1e80dc8SAlp Dener     }
1349566063dSJacob Faibussowitsch     PetscCall(TaoBNCGConductIteration(tao, gnorm));
135*3ba16761SJacob Faibussowitsch     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(PETSC_SUCCESS);
136ac9112b8SAlp Dener   }
137ac9112b8SAlp Dener }
138ac9112b8SAlp Dener 
139d71ae5a4SJacob Faibussowitsch static PetscErrorCode TaoSetUp_BNCG(Tao tao)
140d71ae5a4SJacob Faibussowitsch {
141ac9112b8SAlp Dener   TAO_BNCG *cg = (TAO_BNCG *)tao->data;
142ac9112b8SAlp Dener 
143ac9112b8SAlp Dener   PetscFunctionBegin;
14448a46eb9SPierre Jolivet   if (!tao->gradient) PetscCall(VecDuplicate(tao->solution, &tao->gradient));
14548a46eb9SPierre Jolivet   if (!tao->stepdirection) PetscCall(VecDuplicate(tao->solution, &tao->stepdirection));
14648a46eb9SPierre Jolivet   if (!cg->W) PetscCall(VecDuplicate(tao->solution, &cg->W));
14748a46eb9SPierre Jolivet   if (!cg->work) PetscCall(VecDuplicate(tao->solution, &cg->work));
14848a46eb9SPierre Jolivet   if (!cg->sk) PetscCall(VecDuplicate(tao->solution, &cg->sk));
14948a46eb9SPierre Jolivet   if (!cg->yk) PetscCall(VecDuplicate(tao->gradient, &cg->yk));
15048a46eb9SPierre Jolivet   if (!cg->X_old) PetscCall(VecDuplicate(tao->solution, &cg->X_old));
15148a46eb9SPierre Jolivet   if (!cg->G_old) PetscCall(VecDuplicate(tao->gradient, &cg->G_old));
152c8bcdf1eSAdam Denchfield   if (cg->diag_scaling) {
1539566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution, &cg->d_work));
1549566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution, &cg->y_work));
1559566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution, &cg->g_work));
156c4b75bccSAlp Dener   }
15748a46eb9SPierre Jolivet   if (!cg->unprojected_gradient) PetscCall(VecDuplicate(tao->gradient, &cg->unprojected_gradient));
15848a46eb9SPierre Jolivet   if (!cg->unprojected_gradient_old) PetscCall(VecDuplicate(tao->gradient, &cg->unprojected_gradient_old));
1599566063dSJacob Faibussowitsch   PetscCall(MatLMVMAllocate(cg->B, cg->sk, cg->yk));
1601baa6e33SBarry Smith   if (cg->pc) PetscCall(MatLMVMSetJ0(cg->B, cg->pc));
161*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
162ac9112b8SAlp Dener }
163ac9112b8SAlp Dener 
164d71ae5a4SJacob Faibussowitsch static PetscErrorCode TaoDestroy_BNCG(Tao tao)
165d71ae5a4SJacob Faibussowitsch {
166ac9112b8SAlp Dener   TAO_BNCG *cg = (TAO_BNCG *)tao->data;
167ac9112b8SAlp Dener 
168ac9112b8SAlp Dener   PetscFunctionBegin;
169ac9112b8SAlp Dener   if (tao->setupcalled) {
1709566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->W));
1719566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->work));
1729566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->X_old));
1739566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->G_old));
1749566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->unprojected_gradient));
1759566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->unprojected_gradient_old));
1769566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->g_work));
1779566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->d_work));
1789566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->y_work));
1799566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->sk));
1809566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->yk));
181ac9112b8SAlp Dener   }
1829566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_lower));
1839566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_upper));
1849566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_fixed));
1859566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_idx));
1869566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->inactive_idx));
1879566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->inactive_old));
1889566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->new_inactives));
1899566063dSJacob Faibussowitsch   PetscCall(MatDestroy(&cg->B));
19048a46eb9SPierre Jolivet   if (cg->pc) PetscCall(MatDestroy(&cg->pc));
1919566063dSJacob Faibussowitsch   PetscCall(PetscFree(tao->data));
192*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
193ac9112b8SAlp Dener }
194ac9112b8SAlp Dener 
195d71ae5a4SJacob Faibussowitsch static PetscErrorCode TaoSetFromOptions_BNCG(Tao tao, PetscOptionItems *PetscOptionsObject)
196d71ae5a4SJacob Faibussowitsch {
197ac9112b8SAlp Dener   TAO_BNCG *cg = (TAO_BNCG *)tao->data;
198ac9112b8SAlp Dener 
199ac9112b8SAlp Dener   PetscFunctionBegin;
200d0609cedSBarry Smith   PetscOptionsHeadBegin(PetscOptionsObject, "Nonlinear Conjugate Gradient method for unconstrained optimization");
2019566063dSJacob Faibussowitsch   PetscCall(PetscOptionsEList("-tao_bncg_type", "cg formula", "", CG_Table, CGTypes, CG_Table[cg->cg_type], &cg->cg_type, NULL));
2028ebe3e4eSStefano Zampini   if (cg->cg_type != CG_SSML_BFGS) cg->alpha = -1.0; /* Setting defaults for non-BFGS methods. User can change it below. */
203484c7b14SAdam Denchfield   if (CG_GradientDescent == cg->cg_type) {
204484c7b14SAdam Denchfield     cg->cg_type = CG_PCGradientDescent;
205484c7b14SAdam Denchfield     /* Set scaling equal to none or, at best, scalar scaling. */
206484c7b14SAdam Denchfield     cg->unscaled_restart = PETSC_TRUE;
207484c7b14SAdam Denchfield     cg->diag_scaling     = PETSC_FALSE;
208484c7b14SAdam Denchfield   }
2099566063dSJacob 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));
2109566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_hz_eta", "(developer) cutoff tolerance for HZ", "", cg->hz_eta, &cg->hz_eta, NULL));
2119566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_eps", "(developer) cutoff value for restarts", "", cg->epsilon, &cg->epsilon, NULL));
2129566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_dk_eta", "(developer) cutoff tolerance for DK", "", cg->dk_eta, &cg->dk_eta, NULL));
2139566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_xi", "(developer) Parameter in the KD method", "", cg->xi, &cg->xi, NULL));
2149566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_theta", "(developer) update parameter for the Broyden method", "", cg->theta, &cg->theta, NULL));
2159566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_hz_theta", "(developer) parameter for the HZ (2006) method", "", cg->hz_theta, &cg->hz_theta, NULL));
2169566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_alpha", "(developer) parameter for the scalar scaling", "", cg->alpha, &cg->alpha, NULL));
2179566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_bfgs_scale", "(developer) update parameter for bfgs/brdn CG methods", "", cg->bfgs_scale, &cg->bfgs_scale, NULL));
2189566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_dfp_scale", "(developer) update parameter for bfgs/brdn CG methods", "", cg->dfp_scale, &cg->dfp_scale, NULL));
2199566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_diag_scaling", "Enable diagonal Broyden-like preconditioning", "", cg->diag_scaling, &cg->diag_scaling, NULL));
2209566063dSJacob 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));
2219566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_unscaled_restart", "(developer) use unscaled gradient restarts", "", cg->unscaled_restart, &cg->unscaled_restart, NULL));
2229566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_zeta", "(developer) Free parameter for the Kou-Dai method", "", cg->zeta, &cg->zeta, NULL));
2239566063dSJacob 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));
2249566063dSJacob 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));
2259566063dSJacob 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));
2269566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_no_scaling", "Disable all scaling except in restarts", "", cg->no_scaling, &cg->no_scaling, NULL));
227484c7b14SAdam Denchfield   if (cg->no_scaling) {
228484c7b14SAdam Denchfield     cg->diag_scaling = PETSC_FALSE;
229484c7b14SAdam Denchfield     cg->alpha        = -1.0;
230484c7b14SAdam Denchfield   }
231b474139fSKarl Rupp   if (cg->alpha == -1.0 && cg->cg_type == CG_KouDai && !cg->diag_scaling) { /* Some more default options that appear to be good. */
232484c7b14SAdam Denchfield     cg->neg_xi = PETSC_TRUE;
233484c7b14SAdam Denchfield   }
2349566063dSJacob 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));
2359566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_as_tol", "(developer) initial tolerance used when estimating actively bounded variables", "", cg->as_tol, &cg->as_tol, NULL));
2369566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_as_step", "(developer) step length used when estimating actively bounded variables", "", cg->as_step, &cg->as_step, NULL));
2379566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_delta_min", "(developer) minimum scaling factor used for scaled gradient restarts", "", cg->delta_min, &cg->delta_min, NULL));
2389566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_delta_max", "(developer) maximum scaling factor used for scaled gradient restarts", "", cg->delta_max, &cg->delta_max, NULL));
23950b47da0SAdam Denchfield 
240d0609cedSBarry Smith   PetscOptionsHeadEnd();
2419566063dSJacob Faibussowitsch   PetscCall(MatSetOptionsPrefix(cg->B, ((PetscObject)tao)->prefix));
2429566063dSJacob Faibussowitsch   PetscCall(MatAppendOptionsPrefix(cg->B, "tao_bncg_"));
2439566063dSJacob Faibussowitsch   PetscCall(MatSetFromOptions(cg->B));
244*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
245ac9112b8SAlp Dener }
246ac9112b8SAlp Dener 
247d71ae5a4SJacob Faibussowitsch static PetscErrorCode TaoView_BNCG(Tao tao, PetscViewer viewer)
248d71ae5a4SJacob Faibussowitsch {
249ac9112b8SAlp Dener   PetscBool isascii;
250ac9112b8SAlp Dener   TAO_BNCG *cg = (TAO_BNCG *)tao->data;
251ac9112b8SAlp Dener 
252ac9112b8SAlp Dener   PetscFunctionBegin;
2539566063dSJacob Faibussowitsch   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
254ac9112b8SAlp Dener   if (isascii) {
2559566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPushTab(viewer));
2569566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "CG Type: %s\n", CG_Table[cg->cg_type]));
25763a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Skipped Stepdirection Updates: %" PetscInt_FMT "\n", cg->skipped_updates));
25863a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Scaled gradient steps: %" PetscInt_FMT "\n", cg->resets));
25963a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Pure gradient steps: %" PetscInt_FMT "\n", cg->pure_gd_steps));
26063a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Not a descent direction: %" PetscInt_FMT "\n", cg->descent_error));
26163a3b9bcSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Line search fails: %" PetscInt_FMT "\n", cg->ls_fails));
262484c7b14SAdam Denchfield     if (cg->diag_scaling) {
2639566063dSJacob Faibussowitsch       PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
264484c7b14SAdam Denchfield       if (isascii) {
2659566063dSJacob Faibussowitsch         PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_INFO));
2669566063dSJacob Faibussowitsch         PetscCall(MatView(cg->B, viewer));
2679566063dSJacob Faibussowitsch         PetscCall(PetscViewerPopFormat(viewer));
268484c7b14SAdam Denchfield       }
269484c7b14SAdam Denchfield     }
2709566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPopTab(viewer));
271ac9112b8SAlp Dener   }
272*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
273ac9112b8SAlp Dener }
274ac9112b8SAlp Dener 
275d71ae5a4SJacob Faibussowitsch PetscErrorCode TaoBNCGComputeScalarScaling(PetscReal yty, PetscReal yts, PetscReal sts, PetscReal *scale, PetscReal alpha)
276d71ae5a4SJacob Faibussowitsch {
277c8bcdf1eSAdam Denchfield   PetscReal a, b, c, sig1, sig2;
278c8bcdf1eSAdam Denchfield 
279c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
280c8bcdf1eSAdam Denchfield   *scale = 0.0;
2818ebe3e4eSStefano Zampini   if (1.0 == alpha) *scale = yts / yty;
2828ebe3e4eSStefano Zampini   else if (0.0 == alpha) *scale = sts / yts;
28350b47da0SAdam Denchfield   else if (-1.0 == alpha) *scale = 1.0;
284c8bcdf1eSAdam Denchfield   else {
285c8bcdf1eSAdam Denchfield     a = yty;
286c8bcdf1eSAdam Denchfield     b = yts;
287c8bcdf1eSAdam Denchfield     c = sts;
288c8bcdf1eSAdam Denchfield     a *= alpha;
289c8bcdf1eSAdam Denchfield     b *= -(2.0 * alpha - 1.0);
290c8bcdf1eSAdam Denchfield     c *= alpha - 1.0;
291c8bcdf1eSAdam Denchfield     sig1 = (-b + PetscSqrtReal(b * b - 4.0 * a * c)) / (2.0 * a);
292c8bcdf1eSAdam Denchfield     sig2 = (-b - PetscSqrtReal(b * b - 4.0 * a * c)) / (2.0 * a);
293c8bcdf1eSAdam Denchfield     /* accept the positive root as the scalar */
2948ebe3e4eSStefano Zampini     if (sig1 > 0.0) *scale = sig1;
2958ebe3e4eSStefano Zampini     else if (sig2 > 0.0) *scale = sig2;
2968ebe3e4eSStefano Zampini     else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_CONV_FAILED, "Cannot find positive scalar");
297c8bcdf1eSAdam Denchfield   }
298*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
299c8bcdf1eSAdam Denchfield }
300c8bcdf1eSAdam Denchfield 
301ac9112b8SAlp Dener /*MC
302ac9112b8SAlp Dener   TAOBNCG - Bound-constrained Nonlinear Conjugate Gradient method.
303ac9112b8SAlp Dener 
304ac9112b8SAlp Dener   Options Database Keys:
30550b47da0SAdam Denchfield + -tao_bncg_recycle - enable recycling the latest calculated gradient vector in subsequent TaoSolve() calls (currently disabled)
306c4b75bccSAlp Dener . -tao_bncg_eta <r> - restart tolerance
30761be54a6SAlp Dener . -tao_bncg_type <taocg_type> - cg formula
308c4b75bccSAlp Dener . -tao_bncg_as_type <none,bertsekas> - active set estimation method
309c4b75bccSAlp Dener . -tao_bncg_as_tol <r> - tolerance used in Bertsekas active-set estimation
310c4b75bccSAlp Dener . -tao_bncg_as_step <r> - trial step length used in Bertsekas active-set estimation
31150b47da0SAdam 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.
31250b47da0SAdam Denchfield . -tao_bncg_theta <r> - convex combination parameter for the Broyden method
31350b47da0SAdam Denchfield . -tao_bncg_hz_eta <r> - cutoff tolerance for the beta term in the HZ, DK methods
31450b47da0SAdam Denchfield . -tao_bncg_dk_eta <r> - cutoff tolerance for the beta term in the HZ, DK methods
31550b47da0SAdam Denchfield . -tao_bncg_xi <r> - Multiplicative constant of the gamma term in the KD method
31650b47da0SAdam Denchfield . -tao_bncg_hz_theta <r> - Multiplicative constant of the theta term for the HZ method
31750b47da0SAdam Denchfield . -tao_bncg_bfgs_scale <r> - Scaling parameter of the bfgs contribution to the scalar Broyden method
31850b47da0SAdam Denchfield . -tao_bncg_dfp_scale <r> - Scaling parameter of the dfp contribution to the scalar Broyden method
31950b47da0SAdam Denchfield . -tao_bncg_diag_scaling <b> - Whether or not to use diagonal initialization/preconditioning for the CG methods. Default True.
32050b47da0SAdam Denchfield . -tao_bncg_dynamic_restart <b> - use dynamic restart strategy in the HZ, DK, KD methods
32150b47da0SAdam Denchfield . -tao_bncg_unscaled_restart <b> - whether or not to scale the gradient when doing gradient descent restarts
32250b47da0SAdam Denchfield . -tao_bncg_zeta <r> - Scaling parameter in the KD method
323484c7b14SAdam Denchfield . -tao_bncg_delta_min <r> - Minimum bound for rescaling during restarted gradient descent steps
324484c7b14SAdam Denchfield . -tao_bncg_delta_max <r> - Maximum bound for rescaling during restarted gradient descent steps
32550b47da0SAdam Denchfield . -tao_bncg_min_quad <i> - Number of quadratic-like steps in a row necessary to do a dynamic restart
32650b47da0SAdam 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
32750b47da0SAdam Denchfield . -tao_bncg_spaced_restart <b> - whether or not to do gradient descent steps every x*n iterations
328484c7b14SAdam Denchfield . -tao_bncg_no_scaling <b> - If true, eliminates all scaling, including defaults.
3293850be85SAlp Dener - -tao_bncg_neg_xi <b> - Whether or not to use negative xi in the KD method under certain conditions
330ac9112b8SAlp Dener 
331ac9112b8SAlp Dener   Notes:
332ac9112b8SAlp Dener     CG formulas are:
3333850be85SAlp Dener + "gd" - Gradient Descent
3343850be85SAlp Dener . "fr" - Fletcher-Reeves
3353850be85SAlp Dener . "pr" - Polak-Ribiere-Polyak
3363850be85SAlp Dener . "prp" - Polak-Ribiere-Plus
3373850be85SAlp Dener . "hs" - Hestenes-Steifel
3383850be85SAlp Dener . "dy" - Dai-Yuan
3393850be85SAlp Dener . "ssml_bfgs" - Self-Scaling Memoryless BFGS
3403850be85SAlp Dener . "ssml_dfp"  - Self-Scaling Memoryless DFP
3413850be85SAlp Dener . "ssml_brdn" - Self-Scaling Memoryless Broyden
3423850be85SAlp Dener . "hz" - Hager-Zhang (CG_DESCENT 5.3)
3433850be85SAlp Dener . "dk" - Dai-Kou (2013)
3443850be85SAlp Dener - "kd" - Kou-Dai (2015)
3459abc5736SPatrick Sanan 
346ac9112b8SAlp Dener   Level: beginner
347ac9112b8SAlp Dener M*/
348ac9112b8SAlp Dener 
349d71ae5a4SJacob Faibussowitsch PETSC_EXTERN PetscErrorCode TaoCreate_BNCG(Tao tao)
350d71ae5a4SJacob Faibussowitsch {
351ac9112b8SAlp Dener   TAO_BNCG   *cg;
352ac9112b8SAlp Dener   const char *morethuente_type = TAOLINESEARCHMT;
353ac9112b8SAlp Dener 
354ac9112b8SAlp Dener   PetscFunctionBegin;
355ac9112b8SAlp Dener   tao->ops->setup          = TaoSetUp_BNCG;
356ac9112b8SAlp Dener   tao->ops->solve          = TaoSolve_BNCG;
357ac9112b8SAlp Dener   tao->ops->view           = TaoView_BNCG;
358ac9112b8SAlp Dener   tao->ops->setfromoptions = TaoSetFromOptions_BNCG;
359ac9112b8SAlp Dener   tao->ops->destroy        = TaoDestroy_BNCG;
360ac9112b8SAlp Dener 
361ac9112b8SAlp Dener   /* Override default settings (unless already changed) */
362ac9112b8SAlp Dener   if (!tao->max_it_changed) tao->max_it = 2000;
363ac9112b8SAlp Dener   if (!tao->max_funcs_changed) tao->max_funcs = 4000;
364ac9112b8SAlp Dener 
365ac9112b8SAlp Dener   /*  Note: nondefault values should be used for nonlinear conjugate gradient  */
366ac9112b8SAlp Dener   /*  method.  In particular, gtol should be less that 0.5; the value used in  */
367ac9112b8SAlp Dener   /*  Nocedal and Wright is 0.10.  We use the default values for the  */
368ac9112b8SAlp Dener   /*  linesearch because it seems to work better. */
3699566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchCreate(((PetscObject)tao)->comm, &tao->linesearch));
3709566063dSJacob Faibussowitsch   PetscCall(PetscObjectIncrementTabLevel((PetscObject)tao->linesearch, (PetscObject)tao, 1));
3719566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchSetType(tao->linesearch, morethuente_type));
3729566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchUseTaoRoutines(tao->linesearch, tao));
373ac9112b8SAlp Dener 
3744dfa11a4SJacob Faibussowitsch   PetscCall(PetscNew(&cg));
375ac9112b8SAlp Dener   tao->data = (void *)cg;
3769566063dSJacob Faibussowitsch   PetscCall(KSPInitializePackage());
3779566063dSJacob Faibussowitsch   PetscCall(MatCreate(PetscObjectComm((PetscObject)tao), &cg->B));
3789566063dSJacob Faibussowitsch   PetscCall(PetscObjectIncrementTabLevel((PetscObject)cg->B, (PetscObject)tao, 1));
3799566063dSJacob Faibussowitsch   PetscCall(MatSetType(cg->B, MATLMVMDIAGBROYDEN));
38050b47da0SAdam Denchfield 
381484c7b14SAdam Denchfield   cg->pc = NULL;
382484c7b14SAdam Denchfield 
38350b47da0SAdam Denchfield   cg->dk_eta           = 0.5;
38450b47da0SAdam Denchfield   cg->hz_eta           = 0.4;
385c8bcdf1eSAdam Denchfield   cg->dynamic_restart  = PETSC_FALSE;
386c8bcdf1eSAdam Denchfield   cg->unscaled_restart = PETSC_FALSE;
387484c7b14SAdam Denchfield   cg->no_scaling       = PETSC_FALSE;
388484c7b14SAdam Denchfield   cg->delta_min        = 1e-7;
389484c7b14SAdam Denchfield   cg->delta_max        = 100;
390c8bcdf1eSAdam Denchfield   cg->theta            = 1.0;
391c8bcdf1eSAdam Denchfield   cg->hz_theta         = 1.0;
392c8bcdf1eSAdam Denchfield   cg->dfp_scale        = 1.0;
393c8bcdf1eSAdam Denchfield   cg->bfgs_scale       = 1.0;
39450b47da0SAdam Denchfield   cg->zeta             = 0.1;
39550b47da0SAdam Denchfield   cg->min_quad         = 6;
396c8bcdf1eSAdam Denchfield   cg->min_restart_num  = 6; /* As in CG_DESCENT and KD2015*/
397c8bcdf1eSAdam Denchfield   cg->xi               = 1.0;
39850b47da0SAdam Denchfield   cg->neg_xi           = PETSC_TRUE;
399c8bcdf1eSAdam Denchfield   cg->spaced_restart   = PETSC_FALSE;
400c8bcdf1eSAdam Denchfield   cg->tol_quad         = 1e-8;
40161be54a6SAlp Dener   cg->as_step          = 0.001;
40261be54a6SAlp Dener   cg->as_tol           = 0.001;
40350b47da0SAdam Denchfield   cg->eps_23           = PetscPowReal(PETSC_MACHINE_EPSILON, 2.0 / 3.0); /* Just a little tighter*/
40461be54a6SAlp Dener   cg->as_type          = CG_AS_BERTSEKAS;
405c8bcdf1eSAdam Denchfield   cg->cg_type          = CG_SSML_BFGS;
406c8bcdf1eSAdam Denchfield   cg->alpha            = 1.0;
407c8bcdf1eSAdam Denchfield   cg->diag_scaling     = PETSC_TRUE;
408*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
409c8bcdf1eSAdam Denchfield }
410c8bcdf1eSAdam Denchfield 
411d71ae5a4SJacob Faibussowitsch PetscErrorCode TaoBNCGResetUpdate(Tao tao, PetscReal gnormsq)
412d71ae5a4SJacob Faibussowitsch {
413c8bcdf1eSAdam Denchfield   TAO_BNCG *cg = (TAO_BNCG *)tao->data;
414c8bcdf1eSAdam Denchfield   PetscReal scaling;
415c8bcdf1eSAdam Denchfield 
416c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
417c8bcdf1eSAdam Denchfield   ++cg->resets;
418c8bcdf1eSAdam Denchfield   scaling = 2.0 * PetscMax(1.0, PetscAbsScalar(cg->f)) / PetscMax(gnormsq, cg->eps_23);
419484c7b14SAdam Denchfield   scaling = PetscMin(cg->delta_max, PetscMax(cg->delta_min, scaling));
420484c7b14SAdam Denchfield   if (cg->unscaled_restart) {
421484c7b14SAdam Denchfield     scaling = 1.0;
422484c7b14SAdam Denchfield     ++cg->pure_gd_steps;
423484c7b14SAdam Denchfield   }
4249566063dSJacob Faibussowitsch   PetscCall(VecAXPBY(tao->stepdirection, -scaling, 0.0, tao->gradient));
425c8bcdf1eSAdam Denchfield   /* Also want to reset our diagonal scaling with each restart */
4261baa6e33SBarry Smith   if (cg->diag_scaling) PetscCall(MatLMVMReset(cg->B, PETSC_FALSE));
427*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
428c8bcdf1eSAdam Denchfield }
429c8bcdf1eSAdam Denchfield 
430d71ae5a4SJacob Faibussowitsch PetscErrorCode TaoBNCGCheckDynamicRestart(Tao tao, PetscReal stepsize, PetscReal gd, PetscReal gd_old, PetscBool *dynrestart, PetscReal fold)
431d71ae5a4SJacob Faibussowitsch {
432c8bcdf1eSAdam Denchfield   TAO_BNCG *cg = (TAO_BNCG *)tao->data;
433c8bcdf1eSAdam Denchfield   PetscReal quadinterp;
434c8bcdf1eSAdam Denchfield 
435c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
43650b47da0SAdam Denchfield   if (cg->f < cg->min_quad / 10) {
43750b47da0SAdam Denchfield     *dynrestart = PETSC_FALSE;
438*3ba16761SJacob Faibussowitsch     PetscFunctionReturn(PETSC_SUCCESS);
43950b47da0SAdam Denchfield   } /* just skip this since this strategy doesn't work well for functions near zero */
440484c7b14SAdam Denchfield   quadinterp = 2.0 * (cg->f - fold) / (stepsize * (gd + gd_old));
44150b47da0SAdam Denchfield   if (PetscAbs(quadinterp - 1.0) < cg->tol_quad) ++cg->iter_quad;
442c8bcdf1eSAdam Denchfield   else {
443c8bcdf1eSAdam Denchfield     cg->iter_quad = 0;
444c8bcdf1eSAdam Denchfield     *dynrestart   = PETSC_FALSE;
445c8bcdf1eSAdam Denchfield   }
446c8bcdf1eSAdam Denchfield   if (cg->iter_quad >= cg->min_quad) {
447c8bcdf1eSAdam Denchfield     cg->iter_quad = 0;
448c8bcdf1eSAdam Denchfield     *dynrestart   = PETSC_TRUE;
449c8bcdf1eSAdam Denchfield   }
450*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
451c8bcdf1eSAdam Denchfield }
452c8bcdf1eSAdam Denchfield 
453d71ae5a4SJacob Faibussowitsch PETSC_INTERN PetscErrorCode TaoBNCGStepDirectionUpdate(Tao tao, PetscReal gnorm2, PetscReal step, PetscReal fold, PetscReal gnorm2_old, PetscReal dnorm, PetscBool pcgd_fallback)
454d71ae5a4SJacob Faibussowitsch {
455c8bcdf1eSAdam Denchfield   TAO_BNCG *cg    = (TAO_BNCG *)tao->data;
45650b47da0SAdam Denchfield   PetscReal gamma = 1.0, tau_k, beta;
457484c7b14SAdam Denchfield   PetscReal tmp = 1.0, ynorm, ynorm2 = 1.0, snorm = 1.0, dk_yk = 1.0, gd;
45850b47da0SAdam Denchfield   PetscReal gkp1_yk, gd_old, tau_bfgs, tau_dfp, gkp1D_yk, gtDg;
459c8bcdf1eSAdam Denchfield   PetscInt  dim;
460484c7b14SAdam Denchfield   PetscBool cg_restart = PETSC_FALSE;
461c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
462c8bcdf1eSAdam Denchfield 
46350b47da0SAdam Denchfield   /* Local curvature check to see if we need to restart */
464414d97d3SAlp Dener   if (tao->niter >= 1 || tao->recycle) {
4659566063dSJacob Faibussowitsch     PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
4669566063dSJacob Faibussowitsch     PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
467c8bcdf1eSAdam Denchfield     ynorm2 = ynorm * ynorm;
4689566063dSJacob Faibussowitsch     PetscCall(VecDot(cg->yk, tao->stepdirection, &dk_yk));
469484c7b14SAdam Denchfield     if (step * dnorm < PETSC_MACHINE_EPSILON || step * dk_yk < PETSC_MACHINE_EPSILON) {
470e2570530SAlp Dener       cg_restart = PETSC_TRUE;
471484c7b14SAdam Denchfield       ++cg->skipped_updates;
472484c7b14SAdam Denchfield     }
47350b47da0SAdam Denchfield     if (cg->spaced_restart) {
4749566063dSJacob Faibussowitsch       PetscCall(VecGetSize(tao->gradient, &dim));
475e2570530SAlp Dener       if (tao->niter % (dim * cg->min_restart_num)) cg_restart = PETSC_TRUE;
47650b47da0SAdam Denchfield     }
47750b47da0SAdam Denchfield   }
47850b47da0SAdam Denchfield   /* If the user wants regular restarts, do it every 6n iterations, where n=dimension */
47950b47da0SAdam Denchfield   if (cg->spaced_restart) {
4809566063dSJacob Faibussowitsch     PetscCall(VecGetSize(tao->gradient, &dim));
481e2570530SAlp Dener     if (0 == tao->niter % (6 * dim)) cg_restart = PETSC_TRUE;
48250b47da0SAdam Denchfield   }
48350b47da0SAdam Denchfield   /* Compute the diagonal scaling vector if applicable */
4841baa6e33SBarry Smith   if (cg->diag_scaling) PetscCall(MatLMVMUpdate(cg->B, tao->solution, tao->gradient));
48550b47da0SAdam Denchfield 
486484c7b14SAdam Denchfield   /* A note on diagonal scaling (to be added to paper):
487484c7b14SAdam Denchfield    For the FR, PR, PRP, and DY methods, the diagonally scaled versions
488484c7b14SAdam Denchfield    must be derived as a preconditioned CG method rather than as
489484c7b14SAdam Denchfield    a Hessian initialization like in the Broyden methods. */
49050b47da0SAdam Denchfield 
491484c7b14SAdam Denchfield   /* In that case, one writes the objective function as
492484c7b14SAdam Denchfield    f(x) \equiv f(Ay). Gradient evaluations yield g(x_k) = A g(Ay_k) = A g(x_k).
493484c7b14SAdam Denchfield    Furthermore, the direction d_k \equiv (x_k - x_{k-1})/step according to
494484c7b14SAdam Denchfield    HZ (2006) becomes A^{-1} d_k, such that d_k^T g_k remains the
495484c7b14SAdam Denchfield    same under preconditioning. Note that A is diagonal, such that A^T = A. */
49650b47da0SAdam Denchfield 
497484c7b14SAdam Denchfield   /* This yields questions like what the dot product d_k^T y_k
498484c7b14SAdam Denchfield    should look like. HZ mistakenly treats that as the same under
499484c7b14SAdam Denchfield    preconditioning, but that is not necessarily true. */
50050b47da0SAdam Denchfield 
501484c7b14SAdam Denchfield   /* Observe y_k \equiv g_k - g_{k-1}, and under the P.C. transformation,
502484c7b14SAdam 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}),
503484c7b14SAdam 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
504484c7b14SAdam Denchfield    NOT the same if our preconditioning matrix is updated between iterations.
505484c7b14SAdam Denchfield    This same issue is found when considering dot products of the form g_{k+1}^T y_k. */
50650b47da0SAdam Denchfield 
50750b47da0SAdam Denchfield   /* Compute CG step direction */
50850b47da0SAdam Denchfield   if (cg_restart) {
5099566063dSJacob Faibussowitsch     PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
510484c7b14SAdam Denchfield   } else if (pcgd_fallback) {
511484c7b14SAdam Denchfield     /* Just like preconditioned CG */
5129566063dSJacob Faibussowitsch     PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5139566063dSJacob Faibussowitsch     PetscCall(VecAXPBY(tao->stepdirection, -1.0, 0.0, cg->g_work));
51450b47da0SAdam Denchfield   } else if (ynorm2 > PETSC_MACHINE_EPSILON) {
51550b47da0SAdam Denchfield     switch (cg->cg_type) {
516484c7b14SAdam Denchfield     case CG_PCGradientDescent:
51750b47da0SAdam Denchfield       if (!cg->diag_scaling) {
518484c7b14SAdam Denchfield         if (!cg->no_scaling) {
51950b47da0SAdam Denchfield           cg->sts = step * step * dnorm * dnorm;
5209566063dSJacob Faibussowitsch           PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step * dk_yk, cg->sts, &tau_k, cg->alpha));
521484c7b14SAdam Denchfield         } else {
522484c7b14SAdam Denchfield           tau_k = 1.0;
523484c7b14SAdam Denchfield           ++cg->pure_gd_steps;
524484c7b14SAdam Denchfield         }
5259566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, 0.0, tao->gradient));
52650b47da0SAdam Denchfield       } else {
5279566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5289566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, 0.0, cg->g_work));
52950b47da0SAdam Denchfield       }
53050b47da0SAdam Denchfield       break;
531484c7b14SAdam Denchfield 
53250b47da0SAdam Denchfield     case CG_HestenesStiefel:
53350b47da0SAdam Denchfield       /* Classic Hestenes-Stiefel method, modified with scalar and diagonal preconditioning. */
53450b47da0SAdam Denchfield       if (!cg->diag_scaling) {
53550b47da0SAdam Denchfield         cg->sts = step * step * dnorm * dnorm;
5369566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
5379566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step * dk_yk, cg->sts, &tau_k, cg->alpha));
53850b47da0SAdam Denchfield         beta = tau_k * gkp1_yk / dk_yk;
5399566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
54050b47da0SAdam Denchfield       } else {
5419566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5429566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
54350b47da0SAdam Denchfield         beta = gkp1_yk / dk_yk;
5449566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
54550b47da0SAdam Denchfield       }
546c8bcdf1eSAdam Denchfield       break;
547484c7b14SAdam Denchfield 
548c8bcdf1eSAdam Denchfield     case CG_FletcherReeves:
5499566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
5509566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
5519566063dSJacob Faibussowitsch       PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
55250b47da0SAdam Denchfield       ynorm2 = ynorm * ynorm;
5539566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->yk, tao->stepdirection, &dk_yk));
55450b47da0SAdam Denchfield       if (!cg->diag_scaling) {
5559566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step * dk_yk, step * step * dnorm * dnorm, &tau_k, cg->alpha));
55650b47da0SAdam Denchfield         beta = tau_k * gnorm2 / gnorm2_old;
5579566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
55850b47da0SAdam Denchfield       } else {
5599566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old)); /* Before it's updated */
5609566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5619566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->gradient, cg->g_work, &tmp));
56250b47da0SAdam Denchfield         beta = tmp / gnorm2_old;
5639566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
56450b47da0SAdam Denchfield       }
565c8bcdf1eSAdam Denchfield       break;
566484c7b14SAdam Denchfield 
56750b47da0SAdam Denchfield     case CG_PolakRibierePolyak:
56850b47da0SAdam Denchfield       snorm = step * dnorm;
56950b47da0SAdam Denchfield       if (!cg->diag_scaling) {
5709566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
5719566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
5729566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step * dk_yk, snorm * snorm, &tau_k, cg->alpha));
57350b47da0SAdam Denchfield         beta = tau_k * gkp1_yk / gnorm2_old;
5749566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
57550b47da0SAdam Denchfield       } else {
5769566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old));
5779566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5789566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
57950b47da0SAdam Denchfield         beta = gkp1_yk / gnorm2_old;
5809566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
58150b47da0SAdam Denchfield       }
582c8bcdf1eSAdam Denchfield       break;
583484c7b14SAdam Denchfield 
584c8bcdf1eSAdam Denchfield     case CG_PolakRibierePlus:
5859566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
5869566063dSJacob Faibussowitsch       PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
58750b47da0SAdam Denchfield       ynorm2 = ynorm * ynorm;
58850b47da0SAdam Denchfield       if (!cg->diag_scaling) {
5899566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
5909566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
5919566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step * dk_yk, snorm * snorm, &tau_k, cg->alpha));
59250b47da0SAdam Denchfield         beta = tau_k * gkp1_yk / gnorm2_old;
59350b47da0SAdam Denchfield         beta = PetscMax(beta, 0.0);
5949566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
59550b47da0SAdam Denchfield       } else {
5969566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old)); /* Old gtDg */
5979566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
5989566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
59950b47da0SAdam Denchfield         beta = gkp1_yk / gnorm2_old;
60050b47da0SAdam Denchfield         beta = PetscMax(beta, 0.0);
6019566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
60250b47da0SAdam Denchfield       }
603c8bcdf1eSAdam Denchfield       break;
604484c7b14SAdam Denchfield 
605484c7b14SAdam Denchfield     case CG_DaiYuan:
606484c7b14SAdam Denchfield       /* Dai, Yu-Hong, and Yaxiang Yuan. "A nonlinear conjugate gradient method with a strong global convergence property."
607484c7b14SAdam Denchfield          SIAM Journal on optimization 10, no. 1 (1999): 177-182. */
60850b47da0SAdam Denchfield       if (!cg->diag_scaling) {
6099566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, tao->gradient, &gd));
6109566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
6119566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step * dk_yk, cg->yts, &tau_k, cg->alpha));
61250b47da0SAdam Denchfield         beta = tau_k * gnorm2 / (gd - gd_old);
6139566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
61450b47da0SAdam Denchfield       } else {
6159566063dSJacob Faibussowitsch         PetscCall(MatMult(cg->B, tao->stepdirection, cg->d_work));
6169566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6179566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, tao->gradient, &gtDg));
6189566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, cg->G_old, &gd_old));
6199566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->d_work, cg->g_work, &dk_yk));
62050b47da0SAdam Denchfield         dk_yk = dk_yk - gd_old;
62150b47da0SAdam Denchfield         beta  = gtDg / dk_yk;
6229566063dSJacob Faibussowitsch         PetscCall(VecScale(cg->d_work, beta));
6239566063dSJacob Faibussowitsch         PetscCall(VecWAXPY(tao->stepdirection, -1.0, cg->g_work, cg->d_work));
62450b47da0SAdam Denchfield       }
625c8bcdf1eSAdam Denchfield       break;
626484c7b14SAdam Denchfield 
627c8bcdf1eSAdam Denchfield     case CG_HagerZhang:
628484c7b14SAdam Denchfield       /* Hager, William W., and Hongchao Zhang. "Algorithm 851: CG_DESCENT, a conjugate gradient method with guaranteed descent."
629484c7b14SAdam Denchfield          ACM Transactions on Mathematical Software (TOMS) 32, no. 1 (2006): 113-137. */
6309566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
6319566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
6329566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
63350b47da0SAdam Denchfield       snorm   = dnorm * step;
63450b47da0SAdam Denchfield       cg->yts = step * dk_yk;
63548a46eb9SPierre Jolivet       if (cg->use_dynamic_restart) PetscCall(TaoBNCGCheckDynamicRestart(tao, step, gd, gd_old, &cg->dynamic_restart, fold));
63650b47da0SAdam Denchfield       if (cg->dynamic_restart) {
6379566063dSJacob Faibussowitsch         PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
638c8bcdf1eSAdam Denchfield       } else {
639c8bcdf1eSAdam Denchfield         if (!cg->diag_scaling) {
6409566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
6419566063dSJacob Faibussowitsch           PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm * snorm, &tau_k, cg->alpha));
642c8bcdf1eSAdam Denchfield           /* Supplying cg->alpha = -1.0 will give the CG_DESCENT 5.3 special case of tau_k = 1.0 */
643c8bcdf1eSAdam Denchfield           tmp  = gd / dk_yk;
644c8bcdf1eSAdam Denchfield           beta = tau_k * (gkp1_yk / dk_yk - ynorm2 * gd / (dk_yk * dk_yk));
645c8bcdf1eSAdam Denchfield           /* Bound beta as in CG_DESCENT 5.3, as implemented, with the third comparison from DK 2013 */
64650b47da0SAdam Denchfield           beta = PetscMax(PetscMax(beta, cg->hz_eta * tau_k * gd_old / (dnorm * dnorm)), cg->dk_eta * tau_k * gd / (dnorm * dnorm));
647c8bcdf1eSAdam Denchfield           /* d <- -t*g + beta*t*d */
6489566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
649c8bcdf1eSAdam Denchfield         } else {
650c8bcdf1eSAdam Denchfield           /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
651c8bcdf1eSAdam Denchfield           cg->yty = ynorm2;
652c8bcdf1eSAdam Denchfield           cg->sts = snorm * snorm;
65350b47da0SAdam Denchfield           /* Apply the diagonal scaling to all my vectors */
6549566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6559566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
6569566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->stepdirection, cg->d_work));
657c8bcdf1eSAdam Denchfield           /* Construct the constant ytDgkp1 */
6589566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
659c8bcdf1eSAdam Denchfield           /* Construct the constant for scaling Dkyk in the update */
660c8bcdf1eSAdam Denchfield           tmp = gd / dk_yk;
6619566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
662c8bcdf1eSAdam Denchfield           tau_k = -tau_k * gd / (dk_yk * dk_yk);
663c8bcdf1eSAdam Denchfield           /* beta is the constant which adds the dk contribution */
664484c7b14SAdam Denchfield           beta = gkp1_yk / dk_yk + cg->hz_theta * tau_k; /* HZ; (1.15) from DK 2013 */
665c8bcdf1eSAdam Denchfield           /* From HZ2013, modified to account for diagonal scaling*/
6669566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->G_old, cg->d_work, &gd_old));
6679566063dSJacob Faibussowitsch           PetscCall(VecDot(tao->stepdirection, cg->g_work, &gd));
66850b47da0SAdam Denchfield           beta = PetscMax(PetscMax(beta, cg->hz_eta * gd_old / (dnorm * dnorm)), cg->dk_eta * gd / (dnorm * dnorm));
669c8bcdf1eSAdam Denchfield           /* Do the update */
6709566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
67150b47da0SAdam Denchfield         }
67250b47da0SAdam Denchfield       }
673c8bcdf1eSAdam Denchfield       break;
674484c7b14SAdam Denchfield 
675c8bcdf1eSAdam Denchfield     case CG_DaiKou:
676484c7b14SAdam Denchfield       /* Dai, Yu-Hong, and Cai-Xia Kou. "A nonlinear conjugate gradient algorithm with an optimal property and an improved Wolfe line search."
677484c7b14SAdam Denchfield          SIAM Journal on Optimization 23, no. 1 (2013): 296-320. */
6789566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
6799566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
6809566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
68150b47da0SAdam Denchfield       snorm   = step * dnorm;
68250b47da0SAdam Denchfield       cg->yts = dk_yk * step;
683c8bcdf1eSAdam Denchfield       if (!cg->diag_scaling) {
6849566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
6859566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm * snorm, &tau_k, cg->alpha));
686c8bcdf1eSAdam Denchfield         /* Use cg->alpha = -1.0 to get tau_k = 1.0 as in CG_DESCENT 5.3 */
687c8bcdf1eSAdam Denchfield         tmp  = gd / dk_yk;
68850b47da0SAdam Denchfield         beta = tau_k * (gkp1_yk / dk_yk - ynorm2 * gd / (dk_yk * dk_yk) + gd / (dnorm * dnorm)) - step * gd / dk_yk;
68950b47da0SAdam Denchfield         beta = PetscMax(PetscMax(beta, cg->hz_eta * tau_k * gd_old / (dnorm * dnorm)), cg->dk_eta * tau_k * gd / (dnorm * dnorm));
690c8bcdf1eSAdam Denchfield         /* d <- -t*g + beta*t*d */
6919566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, 0.0, beta, tao->gradient, cg->yk));
692c8bcdf1eSAdam Denchfield       } else {
693c8bcdf1eSAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
694c8bcdf1eSAdam Denchfield         cg->yty = ynorm2;
695c8bcdf1eSAdam Denchfield         cg->sts = snorm * snorm;
6969566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
6979566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
6989566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->stepdirection, cg->d_work));
699c8bcdf1eSAdam Denchfield         /* Construct the constant ytDgkp1 */
7009566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
7019566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
702c8bcdf1eSAdam Denchfield         tau_k = tau_k * gd / (dk_yk * dk_yk);
703c8bcdf1eSAdam Denchfield         tmp   = gd / dk_yk;
704c8bcdf1eSAdam Denchfield         /* beta is the constant which adds the dk contribution */
705484c7b14SAdam Denchfield         beta = gkp1_yk / dk_yk - step * tmp - tau_k;
706c8bcdf1eSAdam Denchfield         /* Update this for the last term in beta */
7079566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, tao->stepdirection, &dk_yk));
708c8bcdf1eSAdam Denchfield         beta += tmp * dk_yk / (dnorm * dnorm); /* projection of y_work onto dk */
7099566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, cg->g_work, &gd));
7109566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->d_work, &gd_old));
71150b47da0SAdam Denchfield         beta = PetscMax(PetscMax(beta, cg->hz_eta * gd_old / (dnorm * dnorm)), cg->dk_eta * gd / (dnorm * dnorm));
712c8bcdf1eSAdam Denchfield         /* Do the update */
7139566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
71450b47da0SAdam Denchfield       }
715c8bcdf1eSAdam Denchfield       break;
716484c7b14SAdam Denchfield 
717c8bcdf1eSAdam Denchfield     case CG_KouDai:
718110fc3b0SBarry Smith       /* Kou, Cai-Xia, and Yu-Hong Dai. "A modified self-scaling memoryless Broyden-Fletcher-Goldfarb-Shanno method for unconstrained optimization."
719484c7b14SAdam Denchfield          Journal of Optimization Theory and Applications 165, no. 1 (2015): 209-224. */
7209566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
7219566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
7229566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
72350b47da0SAdam Denchfield       snorm   = step * dnorm;
72450b47da0SAdam Denchfield       cg->yts = dk_yk * step;
72548a46eb9SPierre Jolivet       if (cg->use_dynamic_restart) PetscCall(TaoBNCGCheckDynamicRestart(tao, step, gd, gd_old, &cg->dynamic_restart, fold));
72650b47da0SAdam Denchfield       if (cg->dynamic_restart) {
7279566063dSJacob Faibussowitsch         PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
728c8bcdf1eSAdam Denchfield       } else {
729c8bcdf1eSAdam Denchfield         if (!cg->diag_scaling) {
7309566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
7319566063dSJacob Faibussowitsch           PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm * snorm, &tau_k, cg->alpha));
732c8bcdf1eSAdam Denchfield           beta = tau_k * (gkp1_yk / dk_yk - ynorm2 * gd / (dk_yk * dk_yk)) - step * gd / dk_yk;
733c8bcdf1eSAdam Denchfield           if (beta < cg->zeta * tau_k * gd / (dnorm * dnorm)) /* 0.1 is KD's zeta parameter */
734c8bcdf1eSAdam Denchfield           {
735c8bcdf1eSAdam Denchfield             beta  = cg->zeta * tau_k * gd / (dnorm * dnorm);
736c8bcdf1eSAdam Denchfield             gamma = 0.0;
737c8bcdf1eSAdam Denchfield           } else {
738c8bcdf1eSAdam Denchfield             if (gkp1_yk < 0 && cg->neg_xi) gamma = -1.0 * gd / dk_yk;
739484c7b14SAdam Denchfield             /* This seems to be very effective when there's no tau_k scaling.
740484c7b14SAdam Denchfield                This guarantees a large descent step every iteration, going through DK 2015 Lemma 3.1's proof but allowing for negative xi */
741ad540459SPierre Jolivet             else gamma = cg->xi * gd / dk_yk;
742c8bcdf1eSAdam Denchfield           }
743c8bcdf1eSAdam Denchfield           /* d <- -t*g + beta*t*d + t*tmp*yk */
7449566063dSJacob Faibussowitsch           PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, gamma * tau_k, beta, tao->gradient, cg->yk));
745c8bcdf1eSAdam Denchfield         } else {
746c8bcdf1eSAdam Denchfield           /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
747c8bcdf1eSAdam Denchfield           cg->yty = ynorm2;
748c8bcdf1eSAdam Denchfield           cg->sts = snorm * snorm;
7499566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
7509566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
751c8bcdf1eSAdam Denchfield           /* Construct the constant ytDgkp1 */
7529566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->g_work, &gkp1D_yk));
753c8bcdf1eSAdam Denchfield           /* Construct the constant for scaling Dkyk in the update */
754c8bcdf1eSAdam Denchfield           gamma = gd / dk_yk;
755c8bcdf1eSAdam Denchfield           /* tau_k = -ytDy/(ytd)^2 * gd */
7569566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
757c8bcdf1eSAdam Denchfield           tau_k = tau_k * gd / (dk_yk * dk_yk);
758c8bcdf1eSAdam Denchfield           /* beta is the constant which adds the d_k contribution */
759c8bcdf1eSAdam Denchfield           beta = gkp1D_yk / dk_yk - step * gamma - tau_k;
760c8bcdf1eSAdam Denchfield           /* Here is the requisite check */
7619566063dSJacob Faibussowitsch           PetscCall(VecDot(tao->stepdirection, cg->g_work, &tmp));
762c8bcdf1eSAdam Denchfield           if (cg->neg_xi) {
763c8bcdf1eSAdam Denchfield             /* modified KD implementation */
764c8bcdf1eSAdam Denchfield             if (gkp1D_yk / dk_yk < 0) gamma = -1.0 * gd / dk_yk;
765ad540459SPierre Jolivet             else gamma = cg->xi * gd / dk_yk;
766c8bcdf1eSAdam Denchfield             if (beta < cg->zeta * tmp / (dnorm * dnorm)) {
767c8bcdf1eSAdam Denchfield               beta  = cg->zeta * tmp / (dnorm * dnorm);
768c8bcdf1eSAdam Denchfield               gamma = 0.0;
769c8bcdf1eSAdam Denchfield             }
770c8bcdf1eSAdam Denchfield           } else { /* original KD 2015 implementation */
771c8bcdf1eSAdam Denchfield             if (beta < cg->zeta * tmp / (dnorm * dnorm)) {
772c8bcdf1eSAdam Denchfield               beta  = cg->zeta * tmp / (dnorm * dnorm);
773c8bcdf1eSAdam Denchfield               gamma = 0.0;
774ad540459SPierre Jolivet             } else gamma = cg->xi * gd / dk_yk;
775c8bcdf1eSAdam Denchfield           }
776c8bcdf1eSAdam Denchfield           /* Do the update in two steps */
7779566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
7789566063dSJacob Faibussowitsch           PetscCall(VecAXPY(tao->stepdirection, gamma, cg->y_work));
77950b47da0SAdam Denchfield         }
78050b47da0SAdam Denchfield       }
781c8bcdf1eSAdam Denchfield       break;
782484c7b14SAdam Denchfield 
783484c7b14SAdam Denchfield     case CG_SSML_BFGS:
784484c7b14SAdam Denchfield       /* Perry, J. M. "A class of conjugate gradient algorithms with a two-step variable-metric memory."
785484c7b14SAdam Denchfield          Discussion Papers 269 (1977). */
7869566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
7879566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
788484c7b14SAdam Denchfield       snorm   = step * dnorm;
789484c7b14SAdam Denchfield       cg->yts = dk_yk * step;
790484c7b14SAdam Denchfield       cg->yty = ynorm2;
791484c7b14SAdam Denchfield       cg->sts = snorm * snorm;
792484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
7939566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
7949566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, cg->yts, cg->sts, &tau_k, cg->alpha));
795484c7b14SAdam Denchfield         tmp  = gd / dk_yk;
796484c7b14SAdam Denchfield         beta = tau_k * (gkp1_yk / dk_yk - cg->yty * gd / (dk_yk * dk_yk)) - step * tmp;
797484c7b14SAdam Denchfield         /* d <- -t*g + beta*t*d + t*tmp*yk */
7989566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp * tau_k, beta, tao->gradient, cg->yk));
799484c7b14SAdam Denchfield       } else {
800484c7b14SAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
8019566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
8029566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
803484c7b14SAdam Denchfield         /* compute scalar gamma */
8049566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
8059566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
806484c7b14SAdam Denchfield         gamma = gd / dk_yk;
807484c7b14SAdam Denchfield         /* Compute scalar beta */
808484c7b14SAdam Denchfield         beta = (gkp1_yk / dk_yk - gd * tmp / (dk_yk * dk_yk)) - step * gd / dk_yk;
809484c7b14SAdam Denchfield         /* Compute stepdirection d_kp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
8109566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
811484c7b14SAdam Denchfield       }
812484c7b14SAdam Denchfield       break;
813484c7b14SAdam Denchfield 
814484c7b14SAdam Denchfield     case CG_SSML_DFP:
8159566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
8169566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
817484c7b14SAdam Denchfield       snorm   = step * dnorm;
818484c7b14SAdam Denchfield       cg->yts = dk_yk * step;
819484c7b14SAdam Denchfield       cg->yty = ynorm2;
820484c7b14SAdam Denchfield       cg->sts = snorm * snorm;
821484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
822484c7b14SAdam Denchfield         /* Instead of a regular convex combination, we will solve a quadratic formula. */
8239566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, cg->yts, cg->sts, &tau_k, cg->alpha));
8249566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
825484c7b14SAdam Denchfield         tau_k = cg->dfp_scale * tau_k;
826484c7b14SAdam Denchfield         tmp   = tau_k * gkp1_yk / cg->yty;
827484c7b14SAdam Denchfield         beta  = -step * gd / dk_yk;
828484c7b14SAdam Denchfield         /* d <- -t*g + beta*d + tmp*yk */
8299566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp, beta, tao->gradient, cg->yk));
830484c7b14SAdam Denchfield       } else {
831484c7b14SAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless DFP step */
8329566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
8339566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
834484c7b14SAdam Denchfield         /* compute scalar gamma */
8359566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
8369566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
837484c7b14SAdam Denchfield         gamma = (gkp1_yk / tmp);
838484c7b14SAdam Denchfield         /* Compute scalar beta */
839484c7b14SAdam Denchfield         beta = -step * gd / dk_yk;
840484c7b14SAdam Denchfield         /* Compute stepdirection d_kp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
8419566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
842484c7b14SAdam Denchfield       }
843484c7b14SAdam Denchfield       break;
844484c7b14SAdam Denchfield 
845484c7b14SAdam Denchfield     case CG_SSML_BROYDEN:
8469566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
8479566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
848484c7b14SAdam Denchfield       snorm   = step * dnorm;
849484c7b14SAdam Denchfield       cg->yts = step * dk_yk;
850484c7b14SAdam Denchfield       cg->yty = ynorm2;
851484c7b14SAdam Denchfield       cg->sts = snorm * snorm;
852484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
853484c7b14SAdam Denchfield         /* Instead of a regular convex combination, we will solve a quadratic formula. */
8549566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, step * dk_yk, snorm * snorm, &tau_bfgs, cg->bfgs_scale));
8559566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, step * dk_yk, snorm * snorm, &tau_dfp, cg->dfp_scale));
8569566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
857484c7b14SAdam Denchfield         tau_k = cg->theta * tau_bfgs + (1.0 - cg->theta) * tau_dfp;
858484c7b14SAdam Denchfield         /* If bfgs_scale = 1.0, it should reproduce the bfgs tau_bfgs. If bfgs_scale = 0.0,
859484c7b14SAdam Denchfield            it should reproduce the tau_dfp scaling. Same with dfp_scale.   */
860484c7b14SAdam Denchfield         tmp  = cg->theta * tau_bfgs * gd / dk_yk + (1 - cg->theta) * tau_dfp * gkp1_yk / cg->yty;
861484c7b14SAdam Denchfield         beta = cg->theta * tau_bfgs * (gkp1_yk / dk_yk - cg->yty * gd / (dk_yk * dk_yk)) - step * gd / dk_yk;
862484c7b14SAdam Denchfield         /* d <- -t*g + beta*d + tmp*yk */
8639566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp, beta, tao->gradient, cg->yk));
864484c7b14SAdam Denchfield       } else {
865484c7b14SAdam Denchfield         /* We have diagonal scaling enabled */
8669566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
8679566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
868484c7b14SAdam Denchfield         /* compute scalar gamma */
8699566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
8709566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
871484c7b14SAdam Denchfield         gamma = cg->theta * gd / dk_yk + (1 - cg->theta) * (gkp1_yk / tmp);
872484c7b14SAdam Denchfield         /* Compute scalar beta */
873484c7b14SAdam Denchfield         beta = cg->theta * (gkp1_yk / dk_yk - gd * tmp / (dk_yk * dk_yk)) - step * gd / dk_yk;
874484c7b14SAdam Denchfield         /* Compute stepdirection dkp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
8759566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
876484c7b14SAdam Denchfield       }
877484c7b14SAdam Denchfield       break;
878484c7b14SAdam Denchfield 
879d71ae5a4SJacob Faibussowitsch     default:
880d71ae5a4SJacob Faibussowitsch       break;
881c8bcdf1eSAdam Denchfield     }
882c8bcdf1eSAdam Denchfield   }
883*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
884c8bcdf1eSAdam Denchfield }
885c8bcdf1eSAdam Denchfield 
886d71ae5a4SJacob Faibussowitsch PETSC_INTERN PetscErrorCode TaoBNCGConductIteration(Tao tao, PetscReal gnorm)
887d71ae5a4SJacob Faibussowitsch {
888c8bcdf1eSAdam Denchfield   TAO_BNCG                    *cg        = (TAO_BNCG *)tao->data;
889c8bcdf1eSAdam Denchfield   TaoLineSearchConvergedReason ls_status = TAOLINESEARCH_CONTINUE_ITERATING;
8908ca2df50S   PetscReal                    step = 1.0, gnorm2, gd, dnorm = 0.0;
891c8bcdf1eSAdam Denchfield   PetscReal                    gnorm2_old, f_old, resnorm, gnorm_old;
892c624ebd3SAlp Dener   PetscBool                    pcgd_fallback = PETSC_FALSE;
893c8bcdf1eSAdam Denchfield 
894c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
895c8bcdf1eSAdam Denchfield   /* We are now going to perform a line search along the direction. */
896c8bcdf1eSAdam Denchfield   /* Store solution and gradient info before it changes */
8979566063dSJacob Faibussowitsch   PetscCall(VecCopy(tao->solution, cg->X_old));
8989566063dSJacob Faibussowitsch   PetscCall(VecCopy(tao->gradient, cg->G_old));
8999566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, cg->unprojected_gradient_old));
900c8bcdf1eSAdam Denchfield 
901c8bcdf1eSAdam Denchfield   gnorm_old  = gnorm;
902c8bcdf1eSAdam Denchfield   gnorm2_old = gnorm_old * gnorm_old;
903c8bcdf1eSAdam Denchfield   f_old      = cg->f;
904484c7b14SAdam Denchfield   /* Perform bounded line search. If we are recycling a solution from a previous */
905484c7b14SAdam Denchfield   /* TaoSolve, then we want to immediately skip to calculating a new direction rather than performing a linesearch */
906414d97d3SAlp Dener   if (!(tao->recycle && 0 == tao->niter)) {
907484c7b14SAdam Denchfield     /* Above logic: the below code happens every iteration, except for the first iteration of a recycled TaoSolve */
9089566063dSJacob Faibussowitsch     PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
9099566063dSJacob Faibussowitsch     PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
9109566063dSJacob Faibussowitsch     PetscCall(TaoAddLineSearchCounts(tao));
911c8bcdf1eSAdam Denchfield 
912c8bcdf1eSAdam Denchfield     /*  Check linesearch failure */
913c8bcdf1eSAdam Denchfield     if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
914c8bcdf1eSAdam Denchfield       ++cg->ls_fails;
915c624ebd3SAlp Dener       if (cg->cg_type == CG_GradientDescent) {
916c8bcdf1eSAdam Denchfield         /* Nothing left to do but fail out of the optimization */
917c8bcdf1eSAdam Denchfield         step        = 0.0;
918c8bcdf1eSAdam Denchfield         tao->reason = TAO_DIVERGED_LS_FAILURE;
919c8bcdf1eSAdam Denchfield       } else {
920484c7b14SAdam Denchfield         /* Restore previous point, perform preconditioned GD and regular GD steps at the last good point */
9219566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->X_old, tao->solution));
9229566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->G_old, tao->gradient));
9239566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->unprojected_gradient_old, cg->unprojected_gradient));
924c8bcdf1eSAdam Denchfield         gnorm  = gnorm_old;
925c8bcdf1eSAdam Denchfield         gnorm2 = gnorm2_old;
926c8bcdf1eSAdam Denchfield         cg->f  = f_old;
927c8bcdf1eSAdam Denchfield 
928484c7b14SAdam Denchfield         /* Fall back on preconditioned CG (so long as you're not already using it) */
929484c7b14SAdam Denchfield         if (cg->cg_type != CG_PCGradientDescent && cg->diag_scaling) {
930e2570530SAlp Dener           pcgd_fallback = PETSC_TRUE;
9319566063dSJacob Faibussowitsch           PetscCall(TaoBNCGStepDirectionUpdate(tao, gnorm2, step, f_old, gnorm2_old, dnorm, pcgd_fallback));
932484c7b14SAdam Denchfield 
9339566063dSJacob Faibussowitsch           PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
9349566063dSJacob Faibussowitsch           PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
935c8bcdf1eSAdam Denchfield 
9369566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
9379566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
9389566063dSJacob Faibussowitsch           PetscCall(TaoAddLineSearchCounts(tao));
939c8bcdf1eSAdam Denchfield 
940484c7b14SAdam Denchfield           pcgd_fallback = PETSC_FALSE;
941484c7b14SAdam Denchfield           if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
942484c7b14SAdam Denchfield             /* Going to perform a regular gradient descent step. */
943484c7b14SAdam Denchfield             ++cg->ls_fails;
944484c7b14SAdam Denchfield             step = 0.0;
945484c7b14SAdam Denchfield           }
946484c7b14SAdam Denchfield         }
947484c7b14SAdam Denchfield         /* Fall back on the scaled gradient step */
948484c7b14SAdam Denchfield         if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
949484c7b14SAdam Denchfield           ++cg->ls_fails;
9509566063dSJacob Faibussowitsch           PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
9519566063dSJacob Faibussowitsch           PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
9529566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
9539566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
9549566063dSJacob Faibussowitsch           PetscCall(TaoAddLineSearchCounts(tao));
955484c7b14SAdam Denchfield         }
956484c7b14SAdam Denchfield 
957c8bcdf1eSAdam Denchfield         if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
958c8bcdf1eSAdam Denchfield           /* Nothing left to do but fail out of the optimization */
95950b47da0SAdam Denchfield           ++cg->ls_fails;
960c8bcdf1eSAdam Denchfield           step        = 0.0;
961c8bcdf1eSAdam Denchfield           tao->reason = TAO_DIVERGED_LS_FAILURE;
962484c7b14SAdam Denchfield         } else {
963484c7b14SAdam Denchfield           /* One of the fallbacks worked. Set them both back equal to false. */
964484c7b14SAdam Denchfield           pcgd_fallback = PETSC_FALSE;
965c8bcdf1eSAdam Denchfield         }
966c8bcdf1eSAdam Denchfield       }
967c8bcdf1eSAdam Denchfield     }
968c8bcdf1eSAdam Denchfield     /* Convergence test for line search failure */
969*3ba16761SJacob Faibussowitsch     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(PETSC_SUCCESS);
970c8bcdf1eSAdam Denchfield 
971c8bcdf1eSAdam Denchfield     /* Standard convergence test */
9729566063dSJacob Faibussowitsch     PetscCall(VecFischer(tao->solution, cg->unprojected_gradient, tao->XL, tao->XU, cg->W));
9739566063dSJacob Faibussowitsch     PetscCall(VecNorm(cg->W, NORM_2, &resnorm));
9743c859ba3SBarry Smith     PetscCheck(!PetscIsInfOrNanReal(resnorm), PetscObjectComm((PetscObject)tao), PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
9759566063dSJacob Faibussowitsch     PetscCall(TaoLogConvergenceHistory(tao, cg->f, resnorm, 0.0, tao->ksp_its));
9769566063dSJacob Faibussowitsch     PetscCall(TaoMonitor(tao, tao->niter, cg->f, resnorm, 0.0, step));
977dbbe0bcdSBarry Smith     PetscUseTypeMethod(tao, convergencetest, tao->cnvP);
978*3ba16761SJacob Faibussowitsch     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(PETSC_SUCCESS);
979484c7b14SAdam Denchfield   }
980c8bcdf1eSAdam Denchfield   /* Assert we have an updated step and we need at least one more iteration. */
981c8bcdf1eSAdam Denchfield   /* Calculate the next direction */
982c8bcdf1eSAdam Denchfield   /* Estimate the active set at the new solution */
9839566063dSJacob Faibussowitsch   PetscCall(TaoBNCGEstimateActiveSet(tao, cg->as_type));
984c8bcdf1eSAdam Denchfield   /* Compute the projected gradient and its norm */
9859566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, tao->gradient));
9869566063dSJacob Faibussowitsch   PetscCall(VecISSet(tao->gradient, cg->active_idx, 0.0));
9879566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->gradient, NORM_2, &gnorm));
988c8bcdf1eSAdam Denchfield   gnorm2 = gnorm * gnorm;
989c8bcdf1eSAdam Denchfield 
990484c7b14SAdam Denchfield   /* Calculate some quantities used in the StepDirectionUpdate. */
9919566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->stepdirection, NORM_2, &dnorm));
992484c7b14SAdam Denchfield   /* Update the step direction. */
9939566063dSJacob Faibussowitsch   PetscCall(TaoBNCGStepDirectionUpdate(tao, gnorm2, step, f_old, gnorm2_old, dnorm, pcgd_fallback));
994484c7b14SAdam Denchfield   ++tao->niter;
9959566063dSJacob Faibussowitsch   PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
996c8bcdf1eSAdam Denchfield 
997c8bcdf1eSAdam Denchfield   if (cg->cg_type != CG_GradientDescent) {
998c8bcdf1eSAdam Denchfield     /* Figure out which previously active variables became inactive this iteration */
9999566063dSJacob Faibussowitsch     PetscCall(ISDestroy(&cg->new_inactives));
100048a46eb9SPierre Jolivet     if (cg->inactive_idx && cg->inactive_old) PetscCall(ISDifference(cg->inactive_idx, cg->inactive_old, &cg->new_inactives));
1001c8bcdf1eSAdam Denchfield     /* Selectively reset the CG step those freshly inactive variables */
1002c8bcdf1eSAdam Denchfield     if (cg->new_inactives) {
10039566063dSJacob Faibussowitsch       PetscCall(VecGetSubVector(tao->stepdirection, cg->new_inactives, &cg->inactive_step));
10049566063dSJacob Faibussowitsch       PetscCall(VecGetSubVector(cg->unprojected_gradient, cg->new_inactives, &cg->inactive_grad));
10059566063dSJacob Faibussowitsch       PetscCall(VecCopy(cg->inactive_grad, cg->inactive_step));
10069566063dSJacob Faibussowitsch       PetscCall(VecScale(cg->inactive_step, -1.0));
10079566063dSJacob Faibussowitsch       PetscCall(VecRestoreSubVector(tao->stepdirection, cg->new_inactives, &cg->inactive_step));
10089566063dSJacob Faibussowitsch       PetscCall(VecRestoreSubVector(cg->unprojected_gradient, cg->new_inactives, &cg->inactive_grad));
1009c8bcdf1eSAdam Denchfield     }
1010c8bcdf1eSAdam Denchfield     /* Verify that this is a descent direction */
10119566063dSJacob Faibussowitsch     PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
10129566063dSJacob Faibussowitsch     PetscCall(VecNorm(tao->stepdirection, NORM_2, &dnorm));
101350b47da0SAdam Denchfield     if (PetscIsInfOrNanReal(gd) || (gd / (dnorm * dnorm) <= -1e10 || gd / (dnorm * dnorm) >= -1e-10)) {
1014c8bcdf1eSAdam Denchfield       /* Not a descent direction, so we reset back to projected gradient descent */
10159566063dSJacob Faibussowitsch       PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
10169566063dSJacob Faibussowitsch       PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
1017c8bcdf1eSAdam Denchfield       ++cg->descent_error;
1018c8bcdf1eSAdam Denchfield     } else {
1019c8bcdf1eSAdam Denchfield     }
1020c8bcdf1eSAdam Denchfield   }
1021*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1022ac9112b8SAlp Dener }
1023484c7b14SAdam Denchfield 
1024d71ae5a4SJacob Faibussowitsch PetscErrorCode TaoBNCGSetH0(Tao tao, Mat H0)
1025d71ae5a4SJacob Faibussowitsch {
1026484c7b14SAdam Denchfield   TAO_BNCG *cg = (TAO_BNCG *)tao->data;
1027484c7b14SAdam Denchfield 
1028484c7b14SAdam Denchfield   PetscFunctionBegin;
10299566063dSJacob Faibussowitsch   PetscCall(PetscObjectReference((PetscObject)H0));
1030484c7b14SAdam Denchfield   cg->pc = H0;
1031*3ba16761SJacob Faibussowitsch   PetscFunctionReturn(PETSC_SUCCESS);
1032484c7b14SAdam Denchfield }
1033