xref: /petsc/src/tao/bound/impls/bncg/bncg.c (revision 9566063d113dddea24716c546802770db7481bc0)
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   PetscErrorCode               ierr;
3161be54a6SAlp Dener   TAO_BNCG                     *cg = (TAO_BNCG *)tao->data;
3261be54a6SAlp Dener 
3361be54a6SAlp Dener   PetscFunctionBegin;
34*9566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->inactive_old));
3561be54a6SAlp Dener   if (cg->inactive_idx) {
36*9566063dSJacob Faibussowitsch     PetscCall(ISDuplicate(cg->inactive_idx, &cg->inactive_old));
37*9566063dSJacob Faibussowitsch     PetscCall(ISCopy(cg->inactive_idx, cg->inactive_old));
3861be54a6SAlp Dener   }
3961be54a6SAlp Dener   switch (asType) {
4061be54a6SAlp Dener   case CG_AS_NONE:
41*9566063dSJacob Faibussowitsch     PetscCall(ISDestroy(&cg->inactive_idx));
42*9566063dSJacob Faibussowitsch     PetscCall(VecWhichInactive(tao->XL, tao->solution, cg->unprojected_gradient, tao->XU, PETSC_TRUE, &cg->inactive_idx));
43*9566063dSJacob Faibussowitsch     PetscCall(ISDestroy(&cg->active_idx));
44*9566063dSJacob Faibussowitsch     PetscCall(ISComplementVec(cg->inactive_idx, tao->solution, &cg->active_idx));
4561be54a6SAlp Dener     break;
4661be54a6SAlp Dener 
4761be54a6SAlp Dener   case CG_AS_BERTSEKAS:
4861be54a6SAlp Dener     /* Use gradient descent to estimate the active set */
49*9566063dSJacob Faibussowitsch     PetscCall(VecCopy(cg->unprojected_gradient, cg->W));
50*9566063dSJacob Faibussowitsch     PetscCall(VecScale(cg->W, -1.0));
5189da521bSAlp Dener     ierr = TaoEstimateActiveBounds(tao->solution, tao->XL, tao->XU, cg->unprojected_gradient, cg->W, cg->work, cg->as_step, &cg->as_tol,
52*9566063dSJacob Faibussowitsch                                    &cg->active_lower, &cg->active_upper, &cg->active_fixed, &cg->active_idx, &cg->inactive_idx);PetscCall(ierr);
53c4b75bccSAlp Dener     break;
5461be54a6SAlp Dener 
5561be54a6SAlp Dener   default:
5661be54a6SAlp Dener     break;
5761be54a6SAlp Dener   }
5861be54a6SAlp Dener   PetscFunctionReturn(0);
5961be54a6SAlp Dener }
6061be54a6SAlp Dener 
61a1318120SAlp Dener PetscErrorCode TaoBNCGBoundStep(Tao tao, PetscInt asType, Vec step)
6261be54a6SAlp Dener {
6361be54a6SAlp Dener   TAO_BNCG                     *cg = (TAO_BNCG *)tao->data;
6461be54a6SAlp Dener 
6561be54a6SAlp Dener   PetscFunctionBegin;
66a1318120SAlp Dener   switch (asType) {
6761be54a6SAlp Dener   case CG_AS_NONE:
68*9566063dSJacob Faibussowitsch     PetscCall(VecISSet(step, cg->active_idx, 0.0));
6961be54a6SAlp Dener     break;
7061be54a6SAlp Dener 
7161be54a6SAlp Dener   case CG_AS_BERTSEKAS:
72*9566063dSJacob Faibussowitsch     PetscCall(TaoBoundStep(tao->solution, tao->XL, tao->XU, cg->active_lower, cg->active_upper, cg->active_fixed, 1.0, step));
7361be54a6SAlp Dener     break;
7461be54a6SAlp Dener 
7561be54a6SAlp Dener   default:
7661be54a6SAlp Dener     break;
7761be54a6SAlp Dener   }
7861be54a6SAlp Dener   PetscFunctionReturn(0);
7961be54a6SAlp Dener }
8061be54a6SAlp Dener 
81ac9112b8SAlp Dener static PetscErrorCode TaoSolve_BNCG(Tao tao)
82ac9112b8SAlp Dener {
83ac9112b8SAlp Dener   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
84484c7b14SAdam Denchfield   PetscReal                    step=1.0,gnorm,gnorm2, resnorm;
85c4b75bccSAlp Dener   PetscInt                     nDiff;
86ac9112b8SAlp Dener 
87ac9112b8SAlp Dener   PetscFunctionBegin;
88ac9112b8SAlp Dener   /*   Project the current point onto the feasible set */
89*9566063dSJacob Faibussowitsch   PetscCall(TaoComputeVariableBounds(tao));
90*9566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchSetVariableBounds(tao->linesearch,tao->XL,tao->XU));
91ac9112b8SAlp Dener 
92c8bcdf1eSAdam Denchfield   /* Project the initial point onto the feasible region */
93*9566063dSJacob Faibussowitsch   PetscCall(TaoBoundSolution(tao->solution, tao->XL,tao->XU, 0.0, &nDiff, tao->solution));
94484c7b14SAdam Denchfield 
95414d97d3SAlp Dener   if (nDiff > 0 || !tao->recycle) {
96*9566063dSJacob Faibussowitsch     PetscCall(TaoComputeObjectiveAndGradient(tao, tao->solution, &cg->f, cg->unprojected_gradient));
97484c7b14SAdam Denchfield   }
98*9566063dSJacob Faibussowitsch   PetscCall(VecNorm(cg->unprojected_gradient,NORM_2,&gnorm));
993c859ba3SBarry Smith   PetscCheck(!PetscIsInfOrNanReal(cg->f) && !PetscIsInfOrNanReal(gnorm),PetscObjectComm((PetscObject)tao),PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
100ac9112b8SAlp Dener 
10161be54a6SAlp Dener   /* Estimate the active set and compute the projected gradient */
102*9566063dSJacob Faibussowitsch   PetscCall(TaoBNCGEstimateActiveSet(tao, cg->as_type));
10361be54a6SAlp Dener 
104ac9112b8SAlp Dener   /* Project the gradient and calculate the norm */
105*9566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, tao->gradient));
106*9566063dSJacob Faibussowitsch   PetscCall(VecISSet(tao->gradient, cg->active_idx, 0.0));
107*9566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->gradient,NORM_2,&gnorm));
108ac9112b8SAlp Dener   gnorm2 = gnorm*gnorm;
109ac9112b8SAlp Dener 
110c8bcdf1eSAdam Denchfield   /* Initialize counters */
111e031d6f5SAlp Dener   tao->niter = 0;
11250b47da0SAdam Denchfield   cg->ls_fails = cg->descent_error = 0;
113c8bcdf1eSAdam Denchfield   cg->resets = -1;
114484c7b14SAdam Denchfield   cg->skipped_updates = cg->pure_gd_steps = 0;
115c8bcdf1eSAdam Denchfield   cg->iter_quad = 0;
116c8bcdf1eSAdam Denchfield 
117c8bcdf1eSAdam Denchfield   /* Convergence test at the starting point. */
118ac9112b8SAlp Dener   tao->reason = TAO_CONTINUE_ITERATING;
119484c7b14SAdam Denchfield 
120*9566063dSJacob Faibussowitsch   PetscCall(VecFischer(tao->solution, cg->unprojected_gradient, tao->XL, tao->XU, cg->W));
121*9566063dSJacob Faibussowitsch   PetscCall(VecNorm(cg->W, NORM_2, &resnorm));
1223c859ba3SBarry Smith   PetscCheck(!PetscIsInfOrNanReal(resnorm),PetscObjectComm((PetscObject)tao),PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
123*9566063dSJacob Faibussowitsch   PetscCall(TaoLogConvergenceHistory(tao, cg->f, resnorm, 0.0, tao->ksp_its));
124*9566063dSJacob Faibussowitsch   PetscCall(TaoMonitor(tao, tao->niter, cg->f, resnorm, 0.0, step));
125*9566063dSJacob Faibussowitsch   PetscCall((*tao->ops->convergencetest)(tao,tao->cnvP));
126ac9112b8SAlp Dener   if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
127484c7b14SAdam Denchfield   /* Calculate initial direction. */
128414d97d3SAlp Dener   if (!tao->recycle) {
129484c7b14SAdam Denchfield     /* We are not recycling a solution/history from a past TaoSolve */
130*9566063dSJacob Faibussowitsch     PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
131ac9112b8SAlp Dener   }
132c8bcdf1eSAdam Denchfield   /* Initial gradient descent step. Scaling by 1.0 also does a decent job for some problems. */
133c8bcdf1eSAdam Denchfield   while (1) {
134e1e80dc8SAlp Dener     /* Call general purpose update function */
135e1e80dc8SAlp Dener     if (tao->ops->update) {
136*9566063dSJacob Faibussowitsch       PetscCall((*tao->ops->update)(tao, tao->niter, tao->user_update));
137e1e80dc8SAlp Dener     }
138*9566063dSJacob Faibussowitsch     PetscCall(TaoBNCGConductIteration(tao, gnorm));
139c8bcdf1eSAdam Denchfield     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
140ac9112b8SAlp Dener   }
141ac9112b8SAlp Dener }
142ac9112b8SAlp Dener 
143ac9112b8SAlp Dener static PetscErrorCode TaoSetUp_BNCG(Tao tao)
144ac9112b8SAlp Dener {
145ac9112b8SAlp Dener   TAO_BNCG         *cg = (TAO_BNCG*)tao->data;
146ac9112b8SAlp Dener 
147ac9112b8SAlp Dener   PetscFunctionBegin;
148c4b75bccSAlp Dener   if (!tao->gradient) {
149*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&tao->gradient));
150c4b75bccSAlp Dener   }
151c4b75bccSAlp Dener   if (!tao->stepdirection) {
152*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&tao->stepdirection));
153c4b75bccSAlp Dener   }
154c4b75bccSAlp Dener   if (!cg->W) {
155*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->W));
156c4b75bccSAlp Dener   }
157c4b75bccSAlp Dener   if (!cg->work) {
158*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->work));
159c4b75bccSAlp Dener   }
160c8bcdf1eSAdam Denchfield   if (!cg->sk) {
161*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->sk));
162c8bcdf1eSAdam Denchfield   }
163c8bcdf1eSAdam Denchfield   if (!cg->yk) {
164*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->yk));
165c8bcdf1eSAdam Denchfield   }
166c4b75bccSAlp Dener   if (!cg->X_old) {
167*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->X_old));
168c4b75bccSAlp Dener   }
169c4b75bccSAlp Dener   if (!cg->G_old) {
170*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->G_old));
171c8bcdf1eSAdam Denchfield   }
172c8bcdf1eSAdam Denchfield   if (cg->diag_scaling) {
173*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->d_work));
174*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->y_work));
175*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->solution,&cg->g_work));
176c4b75bccSAlp Dener   }
177c4b75bccSAlp Dener   if (!cg->unprojected_gradient) {
178*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->unprojected_gradient));
179c4b75bccSAlp Dener   }
180c4b75bccSAlp Dener   if (!cg->unprojected_gradient_old) {
181*9566063dSJacob Faibussowitsch     PetscCall(VecDuplicate(tao->gradient,&cg->unprojected_gradient_old));
182c4b75bccSAlp Dener   }
183*9566063dSJacob Faibussowitsch   PetscCall(MatLMVMAllocate(cg->B, cg->sk, cg->yk));
184484c7b14SAdam Denchfield   if (cg->pc) {
185*9566063dSJacob Faibussowitsch     PetscCall(MatLMVMSetJ0(cg->B, cg->pc));
186484c7b14SAdam Denchfield   }
187ac9112b8SAlp Dener   PetscFunctionReturn(0);
188ac9112b8SAlp Dener }
189ac9112b8SAlp Dener 
190ac9112b8SAlp Dener static PetscErrorCode TaoDestroy_BNCG(Tao tao)
191ac9112b8SAlp Dener {
192ac9112b8SAlp Dener   TAO_BNCG       *cg = (TAO_BNCG*) tao->data;
193ac9112b8SAlp Dener 
194ac9112b8SAlp Dener   PetscFunctionBegin;
195ac9112b8SAlp Dener   if (tao->setupcalled) {
196*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->W));
197*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->work));
198*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->X_old));
199*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->G_old));
200*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->unprojected_gradient));
201*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->unprojected_gradient_old));
202*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->g_work));
203*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->d_work));
204*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->y_work));
205*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->sk));
206*9566063dSJacob Faibussowitsch     PetscCall(VecDestroy(&cg->yk));
207ac9112b8SAlp Dener   }
208*9566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_lower));
209*9566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_upper));
210*9566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_fixed));
211*9566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->active_idx));
212*9566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->inactive_idx));
213*9566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->inactive_old));
214*9566063dSJacob Faibussowitsch   PetscCall(ISDestroy(&cg->new_inactives));
215*9566063dSJacob Faibussowitsch   PetscCall(MatDestroy(&cg->B));
216484c7b14SAdam Denchfield   if (cg->pc) {
217*9566063dSJacob Faibussowitsch     PetscCall(MatDestroy(&cg->pc));
218484c7b14SAdam Denchfield   }
219*9566063dSJacob Faibussowitsch   PetscCall(PetscFree(tao->data));
220ac9112b8SAlp Dener   PetscFunctionReturn(0);
221ac9112b8SAlp Dener }
222ac9112b8SAlp Dener 
223ac9112b8SAlp Dener static PetscErrorCode TaoSetFromOptions_BNCG(PetscOptionItems *PetscOptionsObject,Tao tao)
224ac9112b8SAlp Dener {
225ac9112b8SAlp Dener   TAO_BNCG       *cg = (TAO_BNCG*)tao->data;
226ac9112b8SAlp Dener 
227ac9112b8SAlp Dener   PetscFunctionBegin;
228*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsHead(PetscOptionsObject,"Nonlinear Conjugate Gradient method for unconstrained optimization"));
229*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsEList("-tao_bncg_type","cg formula", "", CG_Table, CGTypes, CG_Table[cg->cg_type], &cg->cg_type,NULL));
2308ebe3e4eSStefano Zampini   if (cg->cg_type != CG_SSML_BFGS) cg->alpha = -1.0; /* Setting defaults for non-BFGS methods. User can change it below. */
231484c7b14SAdam Denchfield   if (CG_GradientDescent == cg->cg_type) {
232484c7b14SAdam Denchfield     cg->cg_type = CG_PCGradientDescent;
233484c7b14SAdam Denchfield     /* Set scaling equal to none or, at best, scalar scaling. */
234484c7b14SAdam Denchfield     cg->unscaled_restart = PETSC_TRUE;
235484c7b14SAdam Denchfield     cg->diag_scaling = PETSC_FALSE;
236484c7b14SAdam Denchfield   }
237*9566063dSJacob 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));
238*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_hz_eta","(developer) cutoff tolerance for HZ", "", cg->hz_eta,&cg->hz_eta,NULL));
239*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_eps","(developer) cutoff value for restarts", "", cg->epsilon,&cg->epsilon,NULL));
240*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_dk_eta","(developer) cutoff tolerance for DK", "", cg->dk_eta,&cg->dk_eta,NULL));
241*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_xi","(developer) Parameter in the KD method", "", cg->xi,&cg->xi,NULL));
242*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_theta", "(developer) update parameter for the Broyden method", "", cg->theta, &cg->theta, NULL));
243*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_hz_theta", "(developer) parameter for the HZ (2006) method", "", cg->hz_theta, &cg->hz_theta, NULL));
244*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_alpha","(developer) parameter for the scalar scaling","",cg->alpha,&cg->alpha,NULL));
245*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_bfgs_scale", "(developer) update parameter for bfgs/brdn CG methods", "", cg->bfgs_scale, &cg->bfgs_scale, NULL));
246*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_dfp_scale", "(developer) update parameter for bfgs/brdn CG methods", "", cg->dfp_scale, &cg->dfp_scale, NULL));
247*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_diag_scaling","Enable diagonal Broyden-like preconditioning","",cg->diag_scaling,&cg->diag_scaling,NULL));
248*9566063dSJacob 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));
249*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_unscaled_restart","(developer) use unscaled gradient restarts","",cg->unscaled_restart,&cg->unscaled_restart,NULL));
250*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_zeta", "(developer) Free parameter for the Kou-Dai method", "", cg->zeta, &cg->zeta, NULL));
251*9566063dSJacob 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));
252*9566063dSJacob 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));
253*9566063dSJacob 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));
254*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsBool("-tao_bncg_no_scaling","Disable all scaling except in restarts","",cg->no_scaling,&cg->no_scaling,NULL));
255484c7b14SAdam Denchfield   if (cg->no_scaling) {
256484c7b14SAdam Denchfield     cg->diag_scaling = PETSC_FALSE;
257484c7b14SAdam Denchfield     cg->alpha = -1.0;
258484c7b14SAdam Denchfield   }
259b474139fSKarl Rupp   if (cg->alpha == -1.0 && cg->cg_type == CG_KouDai && !cg->diag_scaling) { /* Some more default options that appear to be good. */
260484c7b14SAdam Denchfield     cg->neg_xi = PETSC_TRUE;
261484c7b14SAdam Denchfield   }
262*9566063dSJacob 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));
263*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_as_tol", "(developer) initial tolerance used when estimating actively bounded variables","",cg->as_tol,&cg->as_tol,NULL));
264*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_as_step", "(developer) step length used when estimating actively bounded variables","",cg->as_step,&cg->as_step,NULL));
265*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_delta_min", "(developer) minimum scaling factor used for scaled gradient restarts","",cg->delta_min,&cg->delta_min,NULL));
266*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsReal("-tao_bncg_delta_max", "(developer) maximum scaling factor used for scaled gradient restarts","",cg->delta_max,&cg->delta_max,NULL));
26750b47da0SAdam Denchfield 
268*9566063dSJacob Faibussowitsch   PetscCall(PetscOptionsTail());
269*9566063dSJacob Faibussowitsch   PetscCall(MatSetOptionsPrefix(cg->B, ((PetscObject)tao)->prefix));
270*9566063dSJacob Faibussowitsch   PetscCall(MatAppendOptionsPrefix(cg->B, "tao_bncg_"));
271*9566063dSJacob Faibussowitsch   PetscCall(MatSetFromOptions(cg->B));
272ac9112b8SAlp Dener   PetscFunctionReturn(0);
273ac9112b8SAlp Dener }
274ac9112b8SAlp Dener 
275ac9112b8SAlp Dener static PetscErrorCode TaoView_BNCG(Tao tao, PetscViewer viewer)
276ac9112b8SAlp Dener {
277ac9112b8SAlp Dener   PetscBool      isascii;
278ac9112b8SAlp Dener   TAO_BNCG       *cg = (TAO_BNCG*)tao->data;
279ac9112b8SAlp Dener 
280ac9112b8SAlp Dener   PetscFunctionBegin;
281*9566063dSJacob Faibussowitsch   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
282ac9112b8SAlp Dener   if (isascii) {
283*9566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPushTab(viewer));
284*9566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "CG Type: %s\n", CG_Table[cg->cg_type]));
285*9566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Skipped Stepdirection Updates: %i\n", cg->skipped_updates));
286*9566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Scaled gradient steps: %i\n", cg->resets));
287*9566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Pure gradient steps: %i\n", cg->pure_gd_steps));
288*9566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Not a descent direction: %i\n", cg->descent_error));
289*9566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPrintf(viewer, "Line search fails: %i\n", cg->ls_fails));
290484c7b14SAdam Denchfield     if (cg->diag_scaling) {
291*9566063dSJacob Faibussowitsch       PetscCall(PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii));
292484c7b14SAdam Denchfield       if (isascii) {
293*9566063dSJacob Faibussowitsch         PetscCall(PetscViewerPushFormat(viewer, PETSC_VIEWER_ASCII_INFO));
294*9566063dSJacob Faibussowitsch         PetscCall(MatView(cg->B, viewer));
295*9566063dSJacob Faibussowitsch         PetscCall(PetscViewerPopFormat(viewer));
296484c7b14SAdam Denchfield       }
297484c7b14SAdam Denchfield     }
298*9566063dSJacob Faibussowitsch     PetscCall(PetscViewerASCIIPopTab(viewer));
299ac9112b8SAlp Dener   }
300ac9112b8SAlp Dener   PetscFunctionReturn(0);
301ac9112b8SAlp Dener }
302ac9112b8SAlp Dener 
303c8bcdf1eSAdam Denchfield PetscErrorCode TaoBNCGComputeScalarScaling(PetscReal yty, PetscReal yts, PetscReal sts, PetscReal *scale, PetscReal alpha)
304c8bcdf1eSAdam Denchfield {
305c8bcdf1eSAdam Denchfield   PetscReal a, b, c, sig1, sig2;
306c8bcdf1eSAdam Denchfield 
307c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
308c8bcdf1eSAdam Denchfield   *scale = 0.0;
3098ebe3e4eSStefano Zampini   if (1.0 == alpha) *scale = yts/yty;
3108ebe3e4eSStefano Zampini   else if (0.0 == alpha) *scale = sts/yts;
31150b47da0SAdam Denchfield   else if (-1.0 == alpha) *scale = 1.0;
312c8bcdf1eSAdam Denchfield   else {
313c8bcdf1eSAdam Denchfield     a = yty;
314c8bcdf1eSAdam Denchfield     b = yts;
315c8bcdf1eSAdam Denchfield     c = sts;
316c8bcdf1eSAdam Denchfield     a *= alpha;
317c8bcdf1eSAdam Denchfield     b *= -(2.0*alpha - 1.0);
318c8bcdf1eSAdam Denchfield     c *= alpha - 1.0;
319c8bcdf1eSAdam Denchfield     sig1 = (-b + PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
320c8bcdf1eSAdam Denchfield     sig2 = (-b - PetscSqrtReal(b*b - 4.0*a*c))/(2.0*a);
321c8bcdf1eSAdam Denchfield     /* accept the positive root as the scalar */
3228ebe3e4eSStefano Zampini     if (sig1 > 0.0) *scale = sig1;
3238ebe3e4eSStefano Zampini     else if (sig2 > 0.0) *scale = sig2;
3248ebe3e4eSStefano Zampini     else SETERRQ(PETSC_COMM_SELF, PETSC_ERR_CONV_FAILED, "Cannot find positive scalar");
325c8bcdf1eSAdam Denchfield   }
326c8bcdf1eSAdam Denchfield   PetscFunctionReturn(0);
327c8bcdf1eSAdam Denchfield }
328c8bcdf1eSAdam Denchfield 
329ac9112b8SAlp Dener /*MC
330ac9112b8SAlp Dener   TAOBNCG - Bound-constrained Nonlinear Conjugate Gradient method.
331ac9112b8SAlp Dener 
332ac9112b8SAlp Dener   Options Database Keys:
33350b47da0SAdam Denchfield + -tao_bncg_recycle - enable recycling the latest calculated gradient vector in subsequent TaoSolve() calls (currently disabled)
334c4b75bccSAlp Dener . -tao_bncg_eta <r> - restart tolerance
33561be54a6SAlp Dener . -tao_bncg_type <taocg_type> - cg formula
336c4b75bccSAlp Dener . -tao_bncg_as_type <none,bertsekas> - active set estimation method
337c4b75bccSAlp Dener . -tao_bncg_as_tol <r> - tolerance used in Bertsekas active-set estimation
338c4b75bccSAlp Dener . -tao_bncg_as_step <r> - trial step length used in Bertsekas active-set estimation
33950b47da0SAdam 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.
34050b47da0SAdam Denchfield . -tao_bncg_theta <r> - convex combination parameter for the Broyden method
34150b47da0SAdam Denchfield . -tao_bncg_hz_eta <r> - cutoff tolerance for the beta term in the HZ, DK methods
34250b47da0SAdam Denchfield . -tao_bncg_dk_eta <r> - cutoff tolerance for the beta term in the HZ, DK methods
34350b47da0SAdam Denchfield . -tao_bncg_xi <r> - Multiplicative constant of the gamma term in the KD method
34450b47da0SAdam Denchfield . -tao_bncg_hz_theta <r> - Multiplicative constant of the theta term for the HZ method
34550b47da0SAdam Denchfield . -tao_bncg_bfgs_scale <r> - Scaling parameter of the bfgs contribution to the scalar Broyden method
34650b47da0SAdam Denchfield . -tao_bncg_dfp_scale <r> - Scaling parameter of the dfp contribution to the scalar Broyden method
34750b47da0SAdam Denchfield . -tao_bncg_diag_scaling <b> - Whether or not to use diagonal initialization/preconditioning for the CG methods. Default True.
34850b47da0SAdam Denchfield . -tao_bncg_dynamic_restart <b> - use dynamic restart strategy in the HZ, DK, KD methods
34950b47da0SAdam Denchfield . -tao_bncg_unscaled_restart <b> - whether or not to scale the gradient when doing gradient descent restarts
35050b47da0SAdam Denchfield . -tao_bncg_zeta <r> - Scaling parameter in the KD method
351484c7b14SAdam Denchfield . -tao_bncg_delta_min <r> - Minimum bound for rescaling during restarted gradient descent steps
352484c7b14SAdam Denchfield . -tao_bncg_delta_max <r> - Maximum bound for rescaling during restarted gradient descent steps
35350b47da0SAdam Denchfield . -tao_bncg_min_quad <i> - Number of quadratic-like steps in a row necessary to do a dynamic restart
35450b47da0SAdam 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
35550b47da0SAdam Denchfield . -tao_bncg_spaced_restart <b> - whether or not to do gradient descent steps every x*n iterations
356484c7b14SAdam Denchfield . -tao_bncg_no_scaling <b> - If true, eliminates all scaling, including defaults.
3573850be85SAlp Dener - -tao_bncg_neg_xi <b> - Whether or not to use negative xi in the KD method under certain conditions
358ac9112b8SAlp Dener 
359ac9112b8SAlp Dener   Notes:
360ac9112b8SAlp Dener     CG formulas are:
3613850be85SAlp Dener + "gd" - Gradient Descent
3623850be85SAlp Dener . "fr" - Fletcher-Reeves
3633850be85SAlp Dener . "pr" - Polak-Ribiere-Polyak
3643850be85SAlp Dener . "prp" - Polak-Ribiere-Plus
3653850be85SAlp Dener . "hs" - Hestenes-Steifel
3663850be85SAlp Dener . "dy" - Dai-Yuan
3673850be85SAlp Dener . "ssml_bfgs" - Self-Scaling Memoryless BFGS
3683850be85SAlp Dener . "ssml_dfp"  - Self-Scaling Memoryless DFP
3693850be85SAlp Dener . "ssml_brdn" - Self-Scaling Memoryless Broyden
3703850be85SAlp Dener . "hz" - Hager-Zhang (CG_DESCENT 5.3)
3713850be85SAlp Dener . "dk" - Dai-Kou (2013)
3723850be85SAlp Dener - "kd" - Kou-Dai (2015)
3739abc5736SPatrick Sanan 
374ac9112b8SAlp Dener   Level: beginner
375ac9112b8SAlp Dener M*/
376ac9112b8SAlp Dener 
377ac9112b8SAlp Dener PETSC_EXTERN PetscErrorCode TaoCreate_BNCG(Tao tao)
378ac9112b8SAlp Dener {
379ac9112b8SAlp Dener   TAO_BNCG       *cg;
380ac9112b8SAlp Dener   const char     *morethuente_type = TAOLINESEARCHMT;
381ac9112b8SAlp Dener 
382ac9112b8SAlp Dener   PetscFunctionBegin;
383ac9112b8SAlp Dener   tao->ops->setup = TaoSetUp_BNCG;
384ac9112b8SAlp Dener   tao->ops->solve = TaoSolve_BNCG;
385ac9112b8SAlp Dener   tao->ops->view = TaoView_BNCG;
386ac9112b8SAlp Dener   tao->ops->setfromoptions = TaoSetFromOptions_BNCG;
387ac9112b8SAlp Dener   tao->ops->destroy = TaoDestroy_BNCG;
388ac9112b8SAlp Dener 
389ac9112b8SAlp Dener   /* Override default settings (unless already changed) */
390ac9112b8SAlp Dener   if (!tao->max_it_changed) tao->max_it = 2000;
391ac9112b8SAlp Dener   if (!tao->max_funcs_changed) tao->max_funcs = 4000;
392ac9112b8SAlp Dener 
393ac9112b8SAlp Dener   /*  Note: nondefault values should be used for nonlinear conjugate gradient  */
394ac9112b8SAlp Dener   /*  method.  In particular, gtol should be less that 0.5; the value used in  */
395ac9112b8SAlp Dener   /*  Nocedal and Wright is 0.10.  We use the default values for the  */
396ac9112b8SAlp Dener   /*  linesearch because it seems to work better. */
397*9566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchCreate(((PetscObject)tao)->comm, &tao->linesearch));
398*9566063dSJacob Faibussowitsch   PetscCall(PetscObjectIncrementTabLevel((PetscObject)tao->linesearch, (PetscObject)tao, 1));
399*9566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchSetType(tao->linesearch, morethuente_type));
400*9566063dSJacob Faibussowitsch   PetscCall(TaoLineSearchUseTaoRoutines(tao->linesearch, tao));
401ac9112b8SAlp Dener 
402*9566063dSJacob Faibussowitsch   PetscCall(PetscNewLog(tao,&cg));
403ac9112b8SAlp Dener   tao->data = (void*)cg;
404*9566063dSJacob Faibussowitsch   PetscCall(KSPInitializePackage());
405*9566063dSJacob Faibussowitsch   PetscCall(MatCreate(PetscObjectComm((PetscObject)tao), &cg->B));
406*9566063dSJacob Faibussowitsch   PetscCall(PetscObjectIncrementTabLevel((PetscObject)cg->B, (PetscObject)tao, 1));
407*9566063dSJacob Faibussowitsch   PetscCall(MatSetType(cg->B, MATLMVMDIAGBROYDEN));
40850b47da0SAdam Denchfield 
409484c7b14SAdam Denchfield   cg->pc = NULL;
410484c7b14SAdam Denchfield 
41150b47da0SAdam Denchfield   cg->dk_eta = 0.5;
41250b47da0SAdam Denchfield   cg->hz_eta = 0.4;
413c8bcdf1eSAdam Denchfield   cg->dynamic_restart = PETSC_FALSE;
414c8bcdf1eSAdam Denchfield   cg->unscaled_restart = PETSC_FALSE;
415484c7b14SAdam Denchfield   cg->no_scaling = PETSC_FALSE;
416484c7b14SAdam Denchfield   cg->delta_min = 1e-7;
417484c7b14SAdam Denchfield   cg->delta_max = 100;
418c8bcdf1eSAdam Denchfield   cg->theta = 1.0;
419c8bcdf1eSAdam Denchfield   cg->hz_theta = 1.0;
420c8bcdf1eSAdam Denchfield   cg->dfp_scale = 1.0;
421c8bcdf1eSAdam Denchfield   cg->bfgs_scale = 1.0;
42250b47da0SAdam Denchfield   cg->zeta = 0.1;
42350b47da0SAdam Denchfield   cg->min_quad = 6;
424c8bcdf1eSAdam Denchfield   cg->min_restart_num = 6; /* As in CG_DESCENT and KD2015*/
425c8bcdf1eSAdam Denchfield   cg->xi = 1.0;
42650b47da0SAdam Denchfield   cg->neg_xi = PETSC_TRUE;
427c8bcdf1eSAdam Denchfield   cg->spaced_restart = PETSC_FALSE;
428c8bcdf1eSAdam Denchfield   cg->tol_quad = 1e-8;
42961be54a6SAlp Dener   cg->as_step = 0.001;
43061be54a6SAlp Dener   cg->as_tol = 0.001;
43150b47da0SAdam Denchfield   cg->eps_23 = PetscPowReal(PETSC_MACHINE_EPSILON, 2.0/3.0); /* Just a little tighter*/
43261be54a6SAlp Dener   cg->as_type = CG_AS_BERTSEKAS;
433c8bcdf1eSAdam Denchfield   cg->cg_type = CG_SSML_BFGS;
434c8bcdf1eSAdam Denchfield   cg->alpha = 1.0;
435c8bcdf1eSAdam Denchfield   cg->diag_scaling = PETSC_TRUE;
436c8bcdf1eSAdam Denchfield   PetscFunctionReturn(0);
437c8bcdf1eSAdam Denchfield }
438c8bcdf1eSAdam Denchfield 
439c8bcdf1eSAdam Denchfield PetscErrorCode TaoBNCGResetUpdate(Tao tao, PetscReal gnormsq)
440c8bcdf1eSAdam Denchfield {
441c8bcdf1eSAdam Denchfield    TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
442c8bcdf1eSAdam Denchfield    PetscReal         scaling;
443c8bcdf1eSAdam Denchfield 
444c8bcdf1eSAdam Denchfield    PetscFunctionBegin;
445c8bcdf1eSAdam Denchfield    ++cg->resets;
446c8bcdf1eSAdam Denchfield    scaling = 2.0 * PetscMax(1.0, PetscAbsScalar(cg->f)) / PetscMax(gnormsq, cg->eps_23);
447484c7b14SAdam Denchfield    scaling = PetscMin(cg->delta_max, PetscMax(cg->delta_min, scaling));
448484c7b14SAdam Denchfield    if (cg->unscaled_restart) {
449484c7b14SAdam Denchfield      scaling = 1.0;
450484c7b14SAdam Denchfield      ++cg->pure_gd_steps;
451484c7b14SAdam Denchfield    }
452*9566063dSJacob Faibussowitsch    PetscCall(VecAXPBY(tao->stepdirection, -scaling, 0.0, tao->gradient));
453c8bcdf1eSAdam Denchfield    /* Also want to reset our diagonal scaling with each restart */
454c8bcdf1eSAdam Denchfield    if (cg->diag_scaling) {
455*9566063dSJacob Faibussowitsch      PetscCall(MatLMVMReset(cg->B, PETSC_FALSE));
456c8bcdf1eSAdam Denchfield    }
457c8bcdf1eSAdam Denchfield    PetscFunctionReturn(0);
458c8bcdf1eSAdam Denchfield  }
459c8bcdf1eSAdam Denchfield 
460c8bcdf1eSAdam Denchfield PetscErrorCode TaoBNCGCheckDynamicRestart(Tao tao, PetscReal stepsize, PetscReal gd, PetscReal gd_old, PetscBool *dynrestart, PetscReal fold)
461c8bcdf1eSAdam Denchfield {
462c8bcdf1eSAdam Denchfield    TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
463c8bcdf1eSAdam Denchfield    PetscReal         quadinterp;
464c8bcdf1eSAdam Denchfield 
465c8bcdf1eSAdam Denchfield    PetscFunctionBegin;
46650b47da0SAdam Denchfield    if (cg->f < cg->min_quad/10) {
46750b47da0SAdam Denchfield      *dynrestart = PETSC_FALSE;
46850b47da0SAdam Denchfield      PetscFunctionReturn(0);
46950b47da0SAdam Denchfield    } /* just skip this since this strategy doesn't work well for functions near zero */
470484c7b14SAdam Denchfield    quadinterp = 2.0*(cg->f - fold)/(stepsize*(gd + gd_old));
47150b47da0SAdam Denchfield    if (PetscAbs(quadinterp - 1.0) < cg->tol_quad) ++cg->iter_quad;
472c8bcdf1eSAdam Denchfield    else {
473c8bcdf1eSAdam Denchfield      cg->iter_quad = 0;
474c8bcdf1eSAdam Denchfield      *dynrestart = PETSC_FALSE;
475c8bcdf1eSAdam Denchfield    }
476c8bcdf1eSAdam Denchfield    if (cg->iter_quad >= cg->min_quad) {
477c8bcdf1eSAdam Denchfield      cg->iter_quad = 0;
478c8bcdf1eSAdam Denchfield      *dynrestart = PETSC_TRUE;
479c8bcdf1eSAdam Denchfield    }
480c8bcdf1eSAdam Denchfield    PetscFunctionReturn(0);
481c8bcdf1eSAdam Denchfield  }
482c8bcdf1eSAdam Denchfield 
4838ca2df50S PETSC_INTERN PetscErrorCode TaoBNCGStepDirectionUpdate(Tao tao, PetscReal gnorm2, PetscReal step, PetscReal fold, PetscReal gnorm2_old, PetscReal dnorm, PetscBool pcgd_fallback)
48450b47da0SAdam Denchfield {
485c8bcdf1eSAdam Denchfield   TAO_BNCG          *cg = (TAO_BNCG*)tao->data;
48650b47da0SAdam Denchfield   PetscReal         gamma = 1.0, tau_k, beta;
487484c7b14SAdam Denchfield   PetscReal         tmp = 1.0, ynorm, ynorm2 = 1.0, snorm = 1.0, dk_yk=1.0, gd;
48850b47da0SAdam Denchfield   PetscReal         gkp1_yk, gd_old, tau_bfgs, tau_dfp, gkp1D_yk, gtDg;
489c8bcdf1eSAdam Denchfield   PetscInt          dim;
490484c7b14SAdam Denchfield   PetscBool         cg_restart = PETSC_FALSE;
491c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
492c8bcdf1eSAdam Denchfield 
49350b47da0SAdam Denchfield   /* Local curvature check to see if we need to restart */
494414d97d3SAlp Dener   if (tao->niter >= 1 || tao->recycle) {
495*9566063dSJacob Faibussowitsch     PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
496*9566063dSJacob Faibussowitsch     PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
497c8bcdf1eSAdam Denchfield     ynorm2 = ynorm*ynorm;
498*9566063dSJacob Faibussowitsch     PetscCall(VecDot(cg->yk, tao->stepdirection, &dk_yk));
499484c7b14SAdam Denchfield     if (step*dnorm < PETSC_MACHINE_EPSILON || step*dk_yk < PETSC_MACHINE_EPSILON) {
500e2570530SAlp Dener       cg_restart = PETSC_TRUE;
501484c7b14SAdam Denchfield       ++cg->skipped_updates;
502484c7b14SAdam Denchfield     }
50350b47da0SAdam Denchfield     if (cg->spaced_restart) {
504*9566063dSJacob Faibussowitsch       PetscCall(VecGetSize(tao->gradient, &dim));
505e2570530SAlp Dener       if (tao->niter % (dim*cg->min_restart_num)) cg_restart = PETSC_TRUE;
50650b47da0SAdam Denchfield     }
50750b47da0SAdam Denchfield   }
50850b47da0SAdam Denchfield   /* If the user wants regular restarts, do it every 6n iterations, where n=dimension */
50950b47da0SAdam Denchfield   if (cg->spaced_restart) {
510*9566063dSJacob Faibussowitsch     PetscCall(VecGetSize(tao->gradient, &dim));
511e2570530SAlp Dener     if (0 == tao->niter % (6*dim)) cg_restart = PETSC_TRUE;
51250b47da0SAdam Denchfield   }
51350b47da0SAdam Denchfield   /* Compute the diagonal scaling vector if applicable */
51450b47da0SAdam Denchfield   if (cg->diag_scaling) {
515*9566063dSJacob Faibussowitsch     PetscCall(MatLMVMUpdate(cg->B, tao->solution, tao->gradient));
51650b47da0SAdam Denchfield   }
51750b47da0SAdam Denchfield 
518484c7b14SAdam Denchfield   /* A note on diagonal scaling (to be added to paper):
519484c7b14SAdam Denchfield    For the FR, PR, PRP, and DY methods, the diagonally scaled versions
520484c7b14SAdam Denchfield    must be derived as a preconditioned CG method rather than as
521484c7b14SAdam Denchfield    a Hessian initialization like in the Broyden methods. */
52250b47da0SAdam Denchfield 
523484c7b14SAdam Denchfield   /* In that case, one writes the objective function as
524484c7b14SAdam Denchfield    f(x) \equiv f(Ay). Gradient evaluations yield g(x_k) = A g(Ay_k) = A g(x_k).
525484c7b14SAdam Denchfield    Furthermore, the direction d_k \equiv (x_k - x_{k-1})/step according to
526484c7b14SAdam Denchfield    HZ (2006) becomes A^{-1} d_k, such that d_k^T g_k remains the
527484c7b14SAdam Denchfield    same under preconditioning. Note that A is diagonal, such that A^T = A. */
52850b47da0SAdam Denchfield 
529484c7b14SAdam Denchfield   /* This yields questions like what the dot product d_k^T y_k
530484c7b14SAdam Denchfield    should look like. HZ mistakenly treats that as the same under
531484c7b14SAdam Denchfield    preconditioning, but that is not necessarily true. */
53250b47da0SAdam Denchfield 
533484c7b14SAdam Denchfield   /* Observe y_k \equiv g_k - g_{k-1}, and under the P.C. transformation,
534484c7b14SAdam 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}),
535484c7b14SAdam 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
536484c7b14SAdam Denchfield    NOT the same if our preconditioning matrix is updated between iterations.
537484c7b14SAdam Denchfield    This same issue is found when considering dot products of the form g_{k+1}^T y_k. */
53850b47da0SAdam Denchfield 
53950b47da0SAdam Denchfield   /* Compute CG step direction */
54050b47da0SAdam Denchfield   if (cg_restart) {
541*9566063dSJacob Faibussowitsch     PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
542484c7b14SAdam Denchfield   } else if (pcgd_fallback) {
543484c7b14SAdam Denchfield     /* Just like preconditioned CG */
544*9566063dSJacob Faibussowitsch     PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
545*9566063dSJacob Faibussowitsch     PetscCall(VecAXPBY(tao->stepdirection, -1.0, 0.0, cg->g_work));
54650b47da0SAdam Denchfield   } else if (ynorm2 > PETSC_MACHINE_EPSILON) {
54750b47da0SAdam Denchfield     switch (cg->cg_type) {
548484c7b14SAdam Denchfield     case CG_PCGradientDescent:
54950b47da0SAdam Denchfield       if (!cg->diag_scaling) {
550484c7b14SAdam Denchfield         if (!cg->no_scaling) {
55150b47da0SAdam Denchfield         cg->sts = step*step*dnorm*dnorm;
552*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->sts, &tau_k, cg->alpha));
553484c7b14SAdam Denchfield         } else {
554484c7b14SAdam Denchfield           tau_k = 1.0;
555484c7b14SAdam Denchfield           ++cg->pure_gd_steps;
556484c7b14SAdam Denchfield         }
557*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, 0.0, tao->gradient));
55850b47da0SAdam Denchfield       } else {
559*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
560*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, 0.0, cg->g_work));
56150b47da0SAdam Denchfield       }
56250b47da0SAdam Denchfield       break;
563484c7b14SAdam Denchfield 
56450b47da0SAdam Denchfield     case CG_HestenesStiefel:
56550b47da0SAdam Denchfield       /* Classic Hestenes-Stiefel method, modified with scalar and diagonal preconditioning. */
56650b47da0SAdam Denchfield       if (!cg->diag_scaling) {
56750b47da0SAdam Denchfield         cg->sts = step*step*dnorm*dnorm;
568*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
569*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->sts, &tau_k, cg->alpha));
57050b47da0SAdam Denchfield         beta = tau_k*gkp1_yk/dk_yk;
571*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
57250b47da0SAdam Denchfield       } else {
573*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
574*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
57550b47da0SAdam Denchfield         beta = gkp1_yk/dk_yk;
576*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
57750b47da0SAdam Denchfield       }
578c8bcdf1eSAdam Denchfield       break;
579484c7b14SAdam Denchfield 
580c8bcdf1eSAdam Denchfield     case CG_FletcherReeves:
581*9566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
582*9566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
583*9566063dSJacob Faibussowitsch       PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
58450b47da0SAdam Denchfield       ynorm2 = ynorm*ynorm;
585*9566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->yk, tao->stepdirection, &dk_yk));
58650b47da0SAdam Denchfield       if (!cg->diag_scaling) {
587*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, step*step*dnorm*dnorm, &tau_k, cg->alpha));
58850b47da0SAdam Denchfield         beta = tau_k*gnorm2/gnorm2_old;
589*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
59050b47da0SAdam Denchfield       } else {
591*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old)); /* Before it's updated */
592*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
593*9566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->gradient, cg->g_work, &tmp));
59450b47da0SAdam Denchfield         beta = tmp/gnorm2_old;
595*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
59650b47da0SAdam Denchfield       }
597c8bcdf1eSAdam Denchfield       break;
598484c7b14SAdam Denchfield 
59950b47da0SAdam Denchfield     case CG_PolakRibierePolyak:
60050b47da0SAdam Denchfield       snorm = step*dnorm;
60150b47da0SAdam Denchfield       if (!cg->diag_scaling) {
602*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
603*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
604*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, snorm*snorm, &tau_k, cg->alpha));
60550b47da0SAdam Denchfield         beta = tau_k*gkp1_yk/gnorm2_old;
606*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
60750b47da0SAdam Denchfield       } else {
608*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old));
609*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
610*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
61150b47da0SAdam Denchfield         beta = gkp1_yk/gnorm2_old;
612*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
61350b47da0SAdam Denchfield       }
614c8bcdf1eSAdam Denchfield       break;
615484c7b14SAdam Denchfield 
616c8bcdf1eSAdam Denchfield     case CG_PolakRibierePlus:
617*9566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->yk, -1.0, cg->G_old, tao->gradient));
618*9566063dSJacob Faibussowitsch       PetscCall(VecNorm(cg->yk, NORM_2, &ynorm));
61950b47da0SAdam Denchfield       ynorm2 = ynorm*ynorm;
62050b47da0SAdam Denchfield       if (!cg->diag_scaling) {
621*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->G_old, &gnorm2_old));
622*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
623*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, snorm*snorm, &tau_k, cg->alpha));
62450b47da0SAdam Denchfield         beta = tau_k*gkp1_yk/gnorm2_old;
62550b47da0SAdam Denchfield         beta = PetscMax(beta, 0.0);
626*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
62750b47da0SAdam Denchfield       } else {
628*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->g_work, &gnorm2_old)); /* Old gtDg */
629*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
630*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
63150b47da0SAdam Denchfield         beta = gkp1_yk/gnorm2_old;
63250b47da0SAdam Denchfield         beta = PetscMax(beta, 0.0);
633*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
63450b47da0SAdam Denchfield       }
635c8bcdf1eSAdam Denchfield       break;
636484c7b14SAdam Denchfield 
637484c7b14SAdam Denchfield     case CG_DaiYuan:
638484c7b14SAdam Denchfield       /* Dai, Yu-Hong, and Yaxiang Yuan. "A nonlinear conjugate gradient method with a strong global convergence property."
639484c7b14SAdam Denchfield          SIAM Journal on optimization 10, no. 1 (1999): 177-182. */
64050b47da0SAdam Denchfield       if (!cg->diag_scaling) {
641*9566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, tao->gradient, &gd));
642*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
643*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, step*dk_yk, cg->yts, &tau_k, cg->alpha));
64450b47da0SAdam Denchfield         beta = tau_k*gnorm2/(gd - gd_old);
645*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
64650b47da0SAdam Denchfield       } else {
647*9566063dSJacob Faibussowitsch         PetscCall(MatMult(cg->B, tao->stepdirection, cg->d_work));
648*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
649*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, tao->gradient, &gtDg));
650*9566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, cg->G_old, &gd_old));
651*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->d_work, cg->g_work, &dk_yk));
65250b47da0SAdam Denchfield         dk_yk = dk_yk - gd_old;
65350b47da0SAdam Denchfield         beta = gtDg/dk_yk;
654*9566063dSJacob Faibussowitsch         PetscCall(VecScale(cg->d_work, beta));
655*9566063dSJacob Faibussowitsch         PetscCall(VecWAXPY(tao->stepdirection, -1.0, cg->g_work, cg->d_work));
65650b47da0SAdam Denchfield       }
657c8bcdf1eSAdam Denchfield       break;
658484c7b14SAdam Denchfield 
659c8bcdf1eSAdam Denchfield     case CG_HagerZhang:
660484c7b14SAdam Denchfield       /* Hager, William W., and Hongchao Zhang. "Algorithm 851: CG_DESCENT, a conjugate gradient method with guaranteed descent."
661484c7b14SAdam Denchfield          ACM Transactions on Mathematical Software (TOMS) 32, no. 1 (2006): 113-137. */
662*9566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
663*9566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
664*9566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
66550b47da0SAdam Denchfield       snorm = dnorm*step;
66650b47da0SAdam Denchfield       cg->yts = step*dk_yk;
667c8bcdf1eSAdam Denchfield       if (cg->use_dynamic_restart) {
668*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGCheckDynamicRestart(tao, step, gd, gd_old, &cg->dynamic_restart, fold));
669c8bcdf1eSAdam Denchfield       }
67050b47da0SAdam Denchfield       if (cg->dynamic_restart) {
671*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
672c8bcdf1eSAdam Denchfield       } else {
673c8bcdf1eSAdam Denchfield         if (!cg->diag_scaling) {
674*9566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
675*9566063dSJacob Faibussowitsch           PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha));
676c8bcdf1eSAdam Denchfield           /* Supplying cg->alpha = -1.0 will give the CG_DESCENT 5.3 special case of tau_k = 1.0 */
677c8bcdf1eSAdam Denchfield           tmp = gd/dk_yk;
678c8bcdf1eSAdam Denchfield           beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk));
679c8bcdf1eSAdam Denchfield           /* Bound beta as in CG_DESCENT 5.3, as implemented, with the third comparison from DK 2013 */
68050b47da0SAdam Denchfield           beta = PetscMax(PetscMax(beta, cg->hz_eta*tau_k*gd_old/(dnorm*dnorm)), cg->dk_eta*tau_k*gd/(dnorm*dnorm));
681c8bcdf1eSAdam Denchfield           /* d <- -t*g + beta*t*d */
682*9566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -tau_k, beta, tao->gradient));
683c8bcdf1eSAdam Denchfield         } else {
684c8bcdf1eSAdam Denchfield           /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
685c8bcdf1eSAdam Denchfield           cg->yty = ynorm2;
686c8bcdf1eSAdam Denchfield           cg->sts = snorm*snorm;
68750b47da0SAdam Denchfield           /* Apply the diagonal scaling to all my vectors */
688*9566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
689*9566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
690*9566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->stepdirection, cg->d_work));
691c8bcdf1eSAdam Denchfield           /* Construct the constant ytDgkp1 */
692*9566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
693c8bcdf1eSAdam Denchfield           /* Construct the constant for scaling Dkyk in the update */
694c8bcdf1eSAdam Denchfield           tmp = gd/dk_yk;
695*9566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
696c8bcdf1eSAdam Denchfield           tau_k = -tau_k*gd/(dk_yk*dk_yk);
697c8bcdf1eSAdam Denchfield           /* beta is the constant which adds the dk contribution */
698484c7b14SAdam Denchfield           beta = gkp1_yk/dk_yk + cg->hz_theta*tau_k; /* HZ; (1.15) from DK 2013 */
699c8bcdf1eSAdam Denchfield           /* From HZ2013, modified to account for diagonal scaling*/
700*9566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->G_old, cg->d_work, &gd_old));
701*9566063dSJacob Faibussowitsch           PetscCall(VecDot(tao->stepdirection, cg->g_work, &gd));
70250b47da0SAdam Denchfield           beta = PetscMax(PetscMax(beta, cg->hz_eta*gd_old/(dnorm*dnorm)), cg->dk_eta*gd/(dnorm*dnorm));
703c8bcdf1eSAdam Denchfield           /* Do the update */
704*9566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
70550b47da0SAdam Denchfield         }
70650b47da0SAdam Denchfield       }
707c8bcdf1eSAdam Denchfield       break;
708484c7b14SAdam Denchfield 
709c8bcdf1eSAdam Denchfield     case CG_DaiKou:
710484c7b14SAdam Denchfield       /* Dai, Yu-Hong, and Cai-Xia Kou. "A nonlinear conjugate gradient algorithm with an optimal property and an improved Wolfe line search."
711484c7b14SAdam Denchfield          SIAM Journal on Optimization 23, no. 1 (2013): 296-320. */
712*9566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
713*9566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
714*9566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
71550b47da0SAdam Denchfield       snorm = step*dnorm;
71650b47da0SAdam Denchfield       cg->yts = dk_yk*step;
717c8bcdf1eSAdam Denchfield       if (!cg->diag_scaling) {
718*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
719*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha));
720c8bcdf1eSAdam Denchfield         /* Use cg->alpha = -1.0 to get tau_k = 1.0 as in CG_DESCENT 5.3 */
721c8bcdf1eSAdam Denchfield         tmp = gd/dk_yk;
72250b47da0SAdam Denchfield         beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk) + gd/(dnorm*dnorm)) - step*gd/dk_yk;
72350b47da0SAdam Denchfield         beta = PetscMax(PetscMax(beta, cg->hz_eta*tau_k*gd_old/(dnorm*dnorm)), cg->dk_eta*tau_k*gd/(dnorm*dnorm));
724c8bcdf1eSAdam Denchfield         /* d <- -t*g + beta*t*d */
725*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, 0.0, beta, tao->gradient, cg->yk));
726c8bcdf1eSAdam Denchfield       } else {
727c8bcdf1eSAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
728c8bcdf1eSAdam Denchfield         cg->yty = ynorm2;
729c8bcdf1eSAdam Denchfield         cg->sts = snorm*snorm;
730*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
731*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
732*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->stepdirection, cg->d_work));
733c8bcdf1eSAdam Denchfield         /* Construct the constant ytDgkp1 */
734*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->g_work, &gkp1_yk));
735*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
736c8bcdf1eSAdam Denchfield         tau_k = tau_k*gd/(dk_yk*dk_yk);
737c8bcdf1eSAdam Denchfield         tmp = gd/dk_yk;
738c8bcdf1eSAdam Denchfield         /* beta is the constant which adds the dk contribution */
739484c7b14SAdam Denchfield         beta = gkp1_yk/dk_yk - step*tmp - tau_k;
740c8bcdf1eSAdam Denchfield         /* Update this for the last term in beta */
741*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, tao->stepdirection, &dk_yk));
742c8bcdf1eSAdam Denchfield         beta += tmp*dk_yk/(dnorm*dnorm); /* projection of y_work onto dk */
743*9566063dSJacob Faibussowitsch         PetscCall(VecDot(tao->stepdirection, cg->g_work, &gd));
744*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->G_old, cg->d_work, &gd_old));
74550b47da0SAdam Denchfield         beta = PetscMax(PetscMax(beta, cg->hz_eta*gd_old/(dnorm*dnorm)), cg->dk_eta*gd/(dnorm*dnorm));
746c8bcdf1eSAdam Denchfield         /* Do the update */
747*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
74850b47da0SAdam Denchfield       }
749c8bcdf1eSAdam Denchfield       break;
750484c7b14SAdam Denchfield 
751c8bcdf1eSAdam Denchfield     case CG_KouDai:
752110fc3b0SBarry Smith       /* Kou, Cai-Xia, and Yu-Hong Dai. "A modified self-scaling memoryless Broyden-Fletcher-Goldfarb-Shanno method for unconstrained optimization."
753484c7b14SAdam Denchfield          Journal of Optimization Theory and Applications 165, no. 1 (2015): 209-224. */
754*9566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
755*9566063dSJacob Faibussowitsch       PetscCall(VecDot(cg->G_old, tao->stepdirection, &gd_old));
756*9566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
75750b47da0SAdam Denchfield       snorm = step*dnorm;
75850b47da0SAdam Denchfield       cg->yts = dk_yk*step;
759c8bcdf1eSAdam Denchfield       if (cg->use_dynamic_restart) {
760*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGCheckDynamicRestart(tao, step, gd, gd_old, &cg->dynamic_restart, fold));
761c8bcdf1eSAdam Denchfield       }
76250b47da0SAdam Denchfield       if (cg->dynamic_restart) {
763*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
764c8bcdf1eSAdam Denchfield       } else {
765c8bcdf1eSAdam Denchfield         if (!cg->diag_scaling) {
766*9566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
767*9566063dSJacob Faibussowitsch           PetscCall(TaoBNCGComputeScalarScaling(ynorm2, cg->yts, snorm*snorm, &tau_k, cg->alpha));
768c8bcdf1eSAdam Denchfield           beta = tau_k*(gkp1_yk/dk_yk - ynorm2*gd/(dk_yk*dk_yk)) - step*gd/dk_yk;
769c8bcdf1eSAdam Denchfield           if (beta < cg->zeta*tau_k*gd/(dnorm*dnorm)) /* 0.1 is KD's zeta parameter */
770c8bcdf1eSAdam Denchfield           {
771c8bcdf1eSAdam Denchfield             beta = cg->zeta*tau_k*gd/(dnorm*dnorm);
772c8bcdf1eSAdam Denchfield             gamma = 0.0;
773c8bcdf1eSAdam Denchfield           } else {
774c8bcdf1eSAdam Denchfield             if (gkp1_yk < 0 && cg->neg_xi) gamma = -1.0*gd/dk_yk;
775484c7b14SAdam Denchfield             /* This seems to be very effective when there's no tau_k scaling.
776484c7b14SAdam Denchfield                This guarantees a large descent step every iteration, going through DK 2015 Lemma 3.1's proof but allowing for negative xi */
77750b47da0SAdam Denchfield             else {
77850b47da0SAdam Denchfield               gamma = cg->xi*gd/dk_yk;
77950b47da0SAdam Denchfield             }
780c8bcdf1eSAdam Denchfield           }
781c8bcdf1eSAdam Denchfield           /* d <- -t*g + beta*t*d + t*tmp*yk */
782*9566063dSJacob Faibussowitsch           PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, gamma*tau_k, beta, tao->gradient, cg->yk));
783c8bcdf1eSAdam Denchfield         } else {
784c8bcdf1eSAdam Denchfield           /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
785c8bcdf1eSAdam Denchfield           cg->yty = ynorm2;
786c8bcdf1eSAdam Denchfield           cg->sts = snorm*snorm;
787*9566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
788*9566063dSJacob Faibussowitsch           PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
789c8bcdf1eSAdam Denchfield           /* Construct the constant ytDgkp1 */
790*9566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->g_work, &gkp1D_yk));
791c8bcdf1eSAdam Denchfield           /* Construct the constant for scaling Dkyk in the update */
792c8bcdf1eSAdam Denchfield           gamma = gd/dk_yk;
793c8bcdf1eSAdam Denchfield           /* tau_k = -ytDy/(ytd)^2 * gd */
794*9566063dSJacob Faibussowitsch           PetscCall(VecDot(cg->yk, cg->y_work, &tau_k));
795c8bcdf1eSAdam Denchfield           tau_k = tau_k*gd/(dk_yk*dk_yk);
796c8bcdf1eSAdam Denchfield           /* beta is the constant which adds the d_k contribution */
797c8bcdf1eSAdam Denchfield           beta = gkp1D_yk/dk_yk - step*gamma - tau_k;
798c8bcdf1eSAdam Denchfield           /* Here is the requisite check */
799*9566063dSJacob Faibussowitsch           PetscCall(VecDot(tao->stepdirection, cg->g_work, &tmp));
800c8bcdf1eSAdam Denchfield           if (cg->neg_xi) {
801c8bcdf1eSAdam Denchfield             /* modified KD implementation */
802c8bcdf1eSAdam Denchfield             if (gkp1D_yk/dk_yk < 0) gamma = -1.0*gd/dk_yk;
80350b47da0SAdam Denchfield             else {
80450b47da0SAdam Denchfield               gamma = cg->xi*gd/dk_yk;
80550b47da0SAdam Denchfield             }
806c8bcdf1eSAdam Denchfield             if (beta < cg->zeta*tmp/(dnorm*dnorm)) {
807c8bcdf1eSAdam Denchfield               beta = cg->zeta*tmp/(dnorm*dnorm);
808c8bcdf1eSAdam Denchfield               gamma = 0.0;
809c8bcdf1eSAdam Denchfield             }
810c8bcdf1eSAdam Denchfield           } else { /* original KD 2015 implementation */
811c8bcdf1eSAdam Denchfield             if (beta < cg->zeta*tmp/(dnorm*dnorm)) {
812c8bcdf1eSAdam Denchfield               beta = cg->zeta*tmp/(dnorm*dnorm);
813c8bcdf1eSAdam Denchfield               gamma = 0.0;
814c8bcdf1eSAdam Denchfield             } else {
815c8bcdf1eSAdam Denchfield               gamma = cg->xi*gd/dk_yk;
816c8bcdf1eSAdam Denchfield             }
817c8bcdf1eSAdam Denchfield           }
818c8bcdf1eSAdam Denchfield           /* Do the update in two steps */
819*9566063dSJacob Faibussowitsch           PetscCall(VecAXPBY(tao->stepdirection, -1.0, beta, cg->g_work));
820*9566063dSJacob Faibussowitsch           PetscCall(VecAXPY(tao->stepdirection, gamma, cg->y_work));
82150b47da0SAdam Denchfield         }
82250b47da0SAdam Denchfield       }
823c8bcdf1eSAdam Denchfield       break;
824484c7b14SAdam Denchfield 
825484c7b14SAdam Denchfield     case CG_SSML_BFGS:
826484c7b14SAdam Denchfield       /* Perry, J. M. "A class of conjugate gradient algorithms with a two-step variable-metric memory."
827484c7b14SAdam Denchfield          Discussion Papers 269 (1977). */
828*9566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
829*9566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
830484c7b14SAdam Denchfield       snorm = step*dnorm;
831484c7b14SAdam Denchfield       cg->yts = dk_yk*step;
832484c7b14SAdam Denchfield       cg->yty = ynorm2;
833484c7b14SAdam Denchfield       cg->sts = snorm*snorm;
834484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
835*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
836*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, cg->yts, cg->sts, &tau_k, cg->alpha));
837484c7b14SAdam Denchfield         tmp = gd/dk_yk;
838484c7b14SAdam Denchfield         beta = tau_k*(gkp1_yk/dk_yk - cg->yty*gd/(dk_yk*dk_yk)) - step*tmp;
839484c7b14SAdam Denchfield         /* d <- -t*g + beta*t*d + t*tmp*yk */
840*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp*tau_k, beta, tao->gradient, cg->yk));
841484c7b14SAdam Denchfield       } else {
842484c7b14SAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless BFGS step */
843*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
844*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
845484c7b14SAdam Denchfield         /* compute scalar gamma */
846*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
847*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
848484c7b14SAdam Denchfield         gamma = gd/dk_yk;
849484c7b14SAdam Denchfield         /* Compute scalar beta */
850484c7b14SAdam Denchfield         beta = (gkp1_yk/dk_yk - gd*tmp/(dk_yk*dk_yk)) - step*gd/dk_yk;
851484c7b14SAdam Denchfield         /* Compute stepdirection d_kp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
852*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
853484c7b14SAdam Denchfield       }
854484c7b14SAdam Denchfield       break;
855484c7b14SAdam Denchfield 
856484c7b14SAdam Denchfield     case CG_SSML_DFP:
857*9566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
858*9566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
859484c7b14SAdam Denchfield       snorm = step*dnorm;
860484c7b14SAdam Denchfield       cg->yts = dk_yk*step;
861484c7b14SAdam Denchfield       cg->yty = ynorm2;
862484c7b14SAdam Denchfield       cg->sts = snorm*snorm;
863484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
864484c7b14SAdam Denchfield         /* Instead of a regular convex combination, we will solve a quadratic formula. */
865*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, cg->yts, cg->sts, &tau_k, cg->alpha));
866*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
867484c7b14SAdam Denchfield         tau_k = cg->dfp_scale*tau_k;
868484c7b14SAdam Denchfield         tmp = tau_k*gkp1_yk/cg->yty;
869484c7b14SAdam Denchfield         beta = -step*gd/dk_yk;
870484c7b14SAdam Denchfield         /* d <- -t*g + beta*d + tmp*yk */
871*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp, beta, tao->gradient, cg->yk));
872484c7b14SAdam Denchfield       } else {
873484c7b14SAdam Denchfield         /* We have diagonal scaling enabled and are taking a diagonally-scaled memoryless DFP step */
874*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
875*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
876484c7b14SAdam Denchfield         /* compute scalar gamma */
877*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
878*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
879484c7b14SAdam Denchfield         gamma = (gkp1_yk/tmp);
880484c7b14SAdam Denchfield         /* Compute scalar beta */
881484c7b14SAdam Denchfield         beta = -step*gd/dk_yk;
882484c7b14SAdam Denchfield         /* Compute stepdirection d_kp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
883*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
884484c7b14SAdam Denchfield       }
885484c7b14SAdam Denchfield       break;
886484c7b14SAdam Denchfield 
887484c7b14SAdam Denchfield     case CG_SSML_BROYDEN:
888*9566063dSJacob Faibussowitsch       PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
889*9566063dSJacob Faibussowitsch       PetscCall(VecWAXPY(cg->sk, -1.0, cg->X_old, tao->solution));
890484c7b14SAdam Denchfield       snorm = step*dnorm;
891484c7b14SAdam Denchfield       cg->yts = step*dk_yk;
892484c7b14SAdam Denchfield       cg->yty = ynorm2;
893484c7b14SAdam Denchfield       cg->sts = snorm*snorm;
894484c7b14SAdam Denchfield       if (!cg->diag_scaling) {
895484c7b14SAdam Denchfield         /* Instead of a regular convex combination, we will solve a quadratic formula. */
896*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, step*dk_yk, snorm*snorm, &tau_bfgs, cg->bfgs_scale));
897*9566063dSJacob Faibussowitsch         PetscCall(TaoBNCGComputeScalarScaling(cg->yty, step*dk_yk, snorm*snorm, &tau_dfp, cg->dfp_scale));
898*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->yk, tao->gradient, &gkp1_yk));
899484c7b14SAdam Denchfield         tau_k = cg->theta*tau_bfgs + (1.0-cg->theta)*tau_dfp;
900484c7b14SAdam Denchfield         /* If bfgs_scale = 1.0, it should reproduce the bfgs tau_bfgs. If bfgs_scale = 0.0,
901484c7b14SAdam Denchfield            it should reproduce the tau_dfp scaling. Same with dfp_scale.   */
902484c7b14SAdam Denchfield         tmp = cg->theta*tau_bfgs*gd/dk_yk + (1-cg->theta)*tau_dfp*gkp1_yk/cg->yty;
903484c7b14SAdam Denchfield         beta = cg->theta*tau_bfgs*(gkp1_yk/dk_yk - cg->yty*gd/(dk_yk*dk_yk)) - step*gd/dk_yk;
904484c7b14SAdam Denchfield         /* d <- -t*g + beta*d + tmp*yk */
905*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -tau_k, tmp, beta, tao->gradient, cg->yk));
906484c7b14SAdam Denchfield       } else {
907484c7b14SAdam Denchfield         /* We have diagonal scaling enabled */
908*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, tao->gradient, cg->g_work));
909*9566063dSJacob Faibussowitsch         PetscCall(MatSolve(cg->B, cg->yk, cg->y_work));
910484c7b14SAdam Denchfield         /* compute scalar gamma */
911*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->g_work, cg->yk, &gkp1_yk));
912*9566063dSJacob Faibussowitsch         PetscCall(VecDot(cg->y_work, cg->yk, &tmp));
913484c7b14SAdam Denchfield         gamma = cg->theta*gd/dk_yk + (1-cg->theta)*(gkp1_yk/tmp);
914484c7b14SAdam Denchfield         /* Compute scalar beta */
915484c7b14SAdam Denchfield         beta = cg->theta*(gkp1_yk/dk_yk - gd*tmp/(dk_yk*dk_yk)) - step*gd/dk_yk;
916484c7b14SAdam Denchfield         /* Compute stepdirection dkp1 = gamma*Dkyk + beta*dk - Dkgkp1 */
917*9566063dSJacob Faibussowitsch         PetscCall(VecAXPBYPCZ(tao->stepdirection, -1.0, gamma, beta, cg->g_work, cg->y_work));
918484c7b14SAdam Denchfield       }
919484c7b14SAdam Denchfield       break;
920484c7b14SAdam Denchfield 
921c8bcdf1eSAdam Denchfield     default:
922c8bcdf1eSAdam Denchfield       break;
923484c7b14SAdam Denchfield 
924c8bcdf1eSAdam Denchfield     }
925c8bcdf1eSAdam Denchfield   }
926c8bcdf1eSAdam Denchfield   PetscFunctionReturn(0);
927c8bcdf1eSAdam Denchfield }
928c8bcdf1eSAdam Denchfield 
929c8bcdf1eSAdam Denchfield PETSC_INTERN PetscErrorCode TaoBNCGConductIteration(Tao tao, PetscReal gnorm)
930c8bcdf1eSAdam Denchfield {
931c8bcdf1eSAdam Denchfield   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
932c8bcdf1eSAdam Denchfield   TaoLineSearchConvergedReason ls_status = TAOLINESEARCH_CONTINUE_ITERATING;
9338ca2df50S   PetscReal                    step=1.0,gnorm2,gd,dnorm=0.0;
934c8bcdf1eSAdam Denchfield   PetscReal                    gnorm2_old,f_old,resnorm, gnorm_old;
935c624ebd3SAlp Dener   PetscBool                    pcgd_fallback = PETSC_FALSE;
936c8bcdf1eSAdam Denchfield 
937c8bcdf1eSAdam Denchfield   PetscFunctionBegin;
938c8bcdf1eSAdam Denchfield   /* We are now going to perform a line search along the direction. */
939c8bcdf1eSAdam Denchfield   /* Store solution and gradient info before it changes */
940*9566063dSJacob Faibussowitsch   PetscCall(VecCopy(tao->solution, cg->X_old));
941*9566063dSJacob Faibussowitsch   PetscCall(VecCopy(tao->gradient, cg->G_old));
942*9566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, cg->unprojected_gradient_old));
943c8bcdf1eSAdam Denchfield 
944c8bcdf1eSAdam Denchfield   gnorm_old = gnorm;
945c8bcdf1eSAdam Denchfield   gnorm2_old = gnorm_old*gnorm_old;
946c8bcdf1eSAdam Denchfield   f_old = cg->f;
947484c7b14SAdam Denchfield   /* Perform bounded line search. If we are recycling a solution from a previous */
948484c7b14SAdam Denchfield   /* TaoSolve, then we want to immediately skip to calculating a new direction rather than performing a linesearch */
949414d97d3SAlp Dener   if (!(tao->recycle && 0 == tao->niter)) {
950484c7b14SAdam Denchfield     /* Above logic: the below code happens every iteration, except for the first iteration of a recycled TaoSolve */
951*9566063dSJacob Faibussowitsch     PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
952*9566063dSJacob Faibussowitsch     PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
953*9566063dSJacob Faibussowitsch     PetscCall(TaoAddLineSearchCounts(tao));
954c8bcdf1eSAdam Denchfield 
955c8bcdf1eSAdam Denchfield     /*  Check linesearch failure */
956c8bcdf1eSAdam Denchfield     if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
957c8bcdf1eSAdam Denchfield       ++cg->ls_fails;
958c624ebd3SAlp Dener       if (cg->cg_type == CG_GradientDescent) {
959c8bcdf1eSAdam Denchfield         /* Nothing left to do but fail out of the optimization */
960c8bcdf1eSAdam Denchfield         step = 0.0;
961c8bcdf1eSAdam Denchfield         tao->reason = TAO_DIVERGED_LS_FAILURE;
962c8bcdf1eSAdam Denchfield       } else {
963484c7b14SAdam Denchfield         /* Restore previous point, perform preconditioned GD and regular GD steps at the last good point */
964*9566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->X_old, tao->solution));
965*9566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->G_old, tao->gradient));
966*9566063dSJacob Faibussowitsch         PetscCall(VecCopy(cg->unprojected_gradient_old, cg->unprojected_gradient));
967c8bcdf1eSAdam Denchfield         gnorm = gnorm_old;
968c8bcdf1eSAdam Denchfield         gnorm2 = gnorm2_old;
969c8bcdf1eSAdam Denchfield         cg->f = f_old;
970c8bcdf1eSAdam Denchfield 
971484c7b14SAdam Denchfield         /* Fall back on preconditioned CG (so long as you're not already using it) */
972484c7b14SAdam Denchfield         if (cg->cg_type != CG_PCGradientDescent && cg->diag_scaling) {
973e2570530SAlp Dener           pcgd_fallback = PETSC_TRUE;
974*9566063dSJacob Faibussowitsch           PetscCall(TaoBNCGStepDirectionUpdate(tao, gnorm2, step, f_old, gnorm2_old, dnorm, pcgd_fallback));
975484c7b14SAdam Denchfield 
976*9566063dSJacob Faibussowitsch           PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
977*9566063dSJacob Faibussowitsch           PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
978c8bcdf1eSAdam Denchfield 
979*9566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
980*9566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
981*9566063dSJacob Faibussowitsch           PetscCall(TaoAddLineSearchCounts(tao));
982c8bcdf1eSAdam Denchfield 
983484c7b14SAdam Denchfield           pcgd_fallback = PETSC_FALSE;
984484c7b14SAdam Denchfield           if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
985484c7b14SAdam Denchfield             /* Going to perform a regular gradient descent step. */
986484c7b14SAdam Denchfield             ++cg->ls_fails;
987484c7b14SAdam Denchfield             step = 0.0;
988484c7b14SAdam Denchfield           }
989484c7b14SAdam Denchfield         }
990484c7b14SAdam Denchfield         /* Fall back on the scaled gradient step */
991484c7b14SAdam Denchfield         if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
992484c7b14SAdam Denchfield           ++cg->ls_fails;
993*9566063dSJacob Faibussowitsch           PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
994*9566063dSJacob Faibussowitsch           PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
995*9566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchSetInitialStepLength(tao->linesearch, 1.0));
996*9566063dSJacob Faibussowitsch           PetscCall(TaoLineSearchApply(tao->linesearch, tao->solution, &cg->f, cg->unprojected_gradient, tao->stepdirection, &step, &ls_status));
997*9566063dSJacob Faibussowitsch           PetscCall(TaoAddLineSearchCounts(tao));
998484c7b14SAdam Denchfield         }
999484c7b14SAdam Denchfield 
1000c8bcdf1eSAdam Denchfield         if (ls_status != TAOLINESEARCH_SUCCESS && ls_status != TAOLINESEARCH_SUCCESS_USER) {
1001c8bcdf1eSAdam Denchfield           /* Nothing left to do but fail out of the optimization */
100250b47da0SAdam Denchfield           ++cg->ls_fails;
1003c8bcdf1eSAdam Denchfield           step = 0.0;
1004c8bcdf1eSAdam Denchfield           tao->reason = TAO_DIVERGED_LS_FAILURE;
1005484c7b14SAdam Denchfield         } else {
1006484c7b14SAdam Denchfield           /* One of the fallbacks worked. Set them both back equal to false. */
1007484c7b14SAdam Denchfield           pcgd_fallback = PETSC_FALSE;
1008c8bcdf1eSAdam Denchfield         }
1009c8bcdf1eSAdam Denchfield       }
1010c8bcdf1eSAdam Denchfield     }
1011c8bcdf1eSAdam Denchfield     /* Convergence test for line search failure */
1012c8bcdf1eSAdam Denchfield     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
1013c8bcdf1eSAdam Denchfield 
1014c8bcdf1eSAdam Denchfield     /* Standard convergence test */
1015*9566063dSJacob Faibussowitsch     PetscCall(VecFischer(tao->solution, cg->unprojected_gradient, tao->XL, tao->XU, cg->W));
1016*9566063dSJacob Faibussowitsch     PetscCall(VecNorm(cg->W, NORM_2, &resnorm));
10173c859ba3SBarry Smith     PetscCheck(!PetscIsInfOrNanReal(resnorm),PetscObjectComm((PetscObject)tao),PETSC_ERR_USER, "User provided compute function generated Inf or NaN");
1018*9566063dSJacob Faibussowitsch     PetscCall(TaoLogConvergenceHistory(tao, cg->f, resnorm, 0.0, tao->ksp_its));
1019*9566063dSJacob Faibussowitsch     PetscCall(TaoMonitor(tao, tao->niter, cg->f, resnorm, 0.0, step));
1020*9566063dSJacob Faibussowitsch     PetscCall((*tao->ops->convergencetest)(tao,tao->cnvP));
1021c8bcdf1eSAdam Denchfield     if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
1022484c7b14SAdam Denchfield   }
1023c8bcdf1eSAdam Denchfield   /* Assert we have an updated step and we need at least one more iteration. */
1024c8bcdf1eSAdam Denchfield   /* Calculate the next direction */
1025c8bcdf1eSAdam Denchfield   /* Estimate the active set at the new solution */
1026*9566063dSJacob Faibussowitsch   PetscCall(TaoBNCGEstimateActiveSet(tao, cg->as_type));
1027c8bcdf1eSAdam Denchfield   /* Compute the projected gradient and its norm */
1028*9566063dSJacob Faibussowitsch   PetscCall(VecCopy(cg->unprojected_gradient, tao->gradient));
1029*9566063dSJacob Faibussowitsch   PetscCall(VecISSet(tao->gradient, cg->active_idx, 0.0));
1030*9566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->gradient,NORM_2,&gnorm));
1031c8bcdf1eSAdam Denchfield   gnorm2 = gnorm*gnorm;
1032c8bcdf1eSAdam Denchfield 
1033484c7b14SAdam Denchfield   /* Calculate some quantities used in the StepDirectionUpdate. */
1034*9566063dSJacob Faibussowitsch   PetscCall(VecNorm(tao->stepdirection, NORM_2, &dnorm));
1035484c7b14SAdam Denchfield   /* Update the step direction. */
1036*9566063dSJacob Faibussowitsch   PetscCall(TaoBNCGStepDirectionUpdate(tao, gnorm2, step, f_old, gnorm2_old, dnorm, pcgd_fallback));
1037484c7b14SAdam Denchfield   ++tao->niter;
1038*9566063dSJacob Faibussowitsch   PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
1039c8bcdf1eSAdam Denchfield 
1040c8bcdf1eSAdam Denchfield   if (cg->cg_type != CG_GradientDescent) {
1041c8bcdf1eSAdam Denchfield     /* Figure out which previously active variables became inactive this iteration */
1042*9566063dSJacob Faibussowitsch     PetscCall(ISDestroy(&cg->new_inactives));
1043c8bcdf1eSAdam Denchfield     if (cg->inactive_idx && cg->inactive_old) {
1044*9566063dSJacob Faibussowitsch       PetscCall(ISDifference(cg->inactive_idx, cg->inactive_old, &cg->new_inactives));
1045c8bcdf1eSAdam Denchfield     }
1046c8bcdf1eSAdam Denchfield     /* Selectively reset the CG step those freshly inactive variables */
1047c8bcdf1eSAdam Denchfield     if (cg->new_inactives) {
1048*9566063dSJacob Faibussowitsch       PetscCall(VecGetSubVector(tao->stepdirection, cg->new_inactives, &cg->inactive_step));
1049*9566063dSJacob Faibussowitsch       PetscCall(VecGetSubVector(cg->unprojected_gradient, cg->new_inactives, &cg->inactive_grad));
1050*9566063dSJacob Faibussowitsch       PetscCall(VecCopy(cg->inactive_grad, cg->inactive_step));
1051*9566063dSJacob Faibussowitsch       PetscCall(VecScale(cg->inactive_step, -1.0));
1052*9566063dSJacob Faibussowitsch       PetscCall(VecRestoreSubVector(tao->stepdirection, cg->new_inactives, &cg->inactive_step));
1053*9566063dSJacob Faibussowitsch       PetscCall(VecRestoreSubVector(cg->unprojected_gradient, cg->new_inactives, &cg->inactive_grad));
1054c8bcdf1eSAdam Denchfield     }
1055c8bcdf1eSAdam Denchfield     /* Verify that this is a descent direction */
1056*9566063dSJacob Faibussowitsch     PetscCall(VecDot(tao->gradient, tao->stepdirection, &gd));
1057*9566063dSJacob Faibussowitsch     PetscCall(VecNorm(tao->stepdirection, NORM_2, &dnorm));
105850b47da0SAdam Denchfield     if (PetscIsInfOrNanReal(gd) || (gd/(dnorm*dnorm) <= -1e10 || gd/(dnorm*dnorm) >= -1e-10)) {
1059c8bcdf1eSAdam Denchfield       /* Not a descent direction, so we reset back to projected gradient descent */
1060*9566063dSJacob Faibussowitsch       PetscCall(TaoBNCGResetUpdate(tao, gnorm2));
1061*9566063dSJacob Faibussowitsch       PetscCall(TaoBNCGBoundStep(tao, cg->as_type, tao->stepdirection));
1062c8bcdf1eSAdam Denchfield       ++cg->descent_error;
1063c8bcdf1eSAdam Denchfield     } else {
1064c8bcdf1eSAdam Denchfield     }
1065c8bcdf1eSAdam Denchfield   }
1066ac9112b8SAlp Dener   PetscFunctionReturn(0);
1067ac9112b8SAlp Dener }
1068484c7b14SAdam Denchfield 
1069484c7b14SAdam Denchfield PetscErrorCode TaoBNCGSetH0(Tao tao, Mat H0)
1070484c7b14SAdam Denchfield {
1071484c7b14SAdam Denchfield   TAO_BNCG                     *cg = (TAO_BNCG*)tao->data;
1072484c7b14SAdam Denchfield 
1073484c7b14SAdam Denchfield   PetscFunctionBegin;
1074*9566063dSJacob Faibussowitsch   PetscCall(PetscObjectReference((PetscObject)H0));
1075484c7b14SAdam Denchfield   cg->pc = H0;
1076484c7b14SAdam Denchfield   PetscFunctionReturn(0);
1077484c7b14SAdam Denchfield }
1078