xref: /petsc/src/tao/unconstrained/impls/ntr/ntr.c (revision 6aeb920f53dc9577f1df4ff00bd447ebf24efec0)
1 #include <../src/tao/matrix/lmvmmat.h>
2 #include <../src/tao/unconstrained/impls/ntr/ntr.h>
3 
4 #include <petscksp.h>
5 #include <petscpc.h>
6 #include <petsc-private/kspimpl.h>
7 #include <petsc-private/pcimpl.h>
8 
9 #define NTR_KSP_NASH    0
10 #define NTR_KSP_STCG    1
11 #define NTR_KSP_GLTR    2
12 #define NTR_KSP_TYPES   3
13 
14 #define NTR_PC_NONE     0
15 #define NTR_PC_AHESS    1
16 #define NTR_PC_BFGS     2
17 #define NTR_PC_PETSC    3
18 #define NTR_PC_TYPES    4
19 
20 #define BFGS_SCALE_AHESS   0
21 #define BFGS_SCALE_BFGS    1
22 #define BFGS_SCALE_TYPES   2
23 
24 #define NTR_INIT_CONSTANT         0
25 #define NTR_INIT_DIRECTION        1
26 #define NTR_INIT_INTERPOLATION    2
27 #define NTR_INIT_TYPES            3
28 
29 #define NTR_UPDATE_REDUCTION      0
30 #define NTR_UPDATE_INTERPOLATION  1
31 #define NTR_UPDATE_TYPES          2
32 
33 static const char *NTR_KSP[64] = {  "nash", "stcg", "gltr"};
34 
35 static const char *NTR_PC[64] = {  "none", "ahess", "bfgs", "petsc"};
36 
37 static const char *BFGS_SCALE[64] = {  "ahess", "bfgs"};
38 
39 static const char *NTR_INIT[64] = {  "constant", "direction", "interpolation"};
40 
41 static const char *NTR_UPDATE[64] = {  "reduction", "interpolation"};
42 
43 /*  Routine for BFGS preconditioner */
44 static PetscErrorCode MatLMVMSolveShell(PC pc, Vec xin, Vec xout);
45 
46 /*
47    TaoSolve_NTR - Implements Newton's Method with a trust region approach
48    for solving unconstrained minimization problems.
49 
50    The basic algorithm is taken from MINPACK-2 (dstrn).
51 
52    TaoSolve_NTR computes a local minimizer of a twice differentiable function
53    f by applying a trust region variant of Newton's method.  At each stage
54    of the algorithm, we use the prconditioned conjugate gradient method to
55    determine an approximate minimizer of the quadratic equation
56 
57         q(s) = <s, Hs + g>
58 
59    subject to the trust region constraint
60 
61         || s ||_M <= radius,
62 
63    where radius is the trust region radius and M is a symmetric positive
64    definite matrix (the preconditioner).  Here g is the gradient and H
65    is the Hessian matrix.
66 
67    Note:  TaoSolve_NTR MUST use the iterative solver KSPNASH, KSPSTCG,
68           or KSPGLTR.  Thus, we set KSPNASH, KSPSTCG, or KSPGLTR in this
69           routine regardless of what the user may have previously specified.
70 */
71 #undef __FUNCT__
72 #define __FUNCT__ "TaoSolve_NTR"
73 static PetscErrorCode TaoSolve_NTR(Tao tao)
74 {
75   TAO_NTR            *tr = (TAO_NTR *)tao->data;
76   PC                 pc;
77   KSPConvergedReason ksp_reason;
78   TaoConvergedReason reason;
79   PetscReal          fmin, ftrial, prered, actred, kappa, sigma, beta;
80   PetscReal          tau, tau_1, tau_2, tau_max, tau_min, max_radius;
81   PetscReal          f, gnorm;
82 
83   PetscReal          delta;
84   PetscReal          norm_d;
85   PetscErrorCode     ierr;
86   PetscInt           iter = 0;
87   PetscInt           bfgsUpdates = 0;
88   PetscInt           needH;
89 
90   PetscInt           i_max = 5;
91   PetscInt           j_max = 1;
92   PetscInt           i, j, N, n, its;
93 
94   PetscFunctionBegin;
95   if (tao->XL || tao->XU || tao->ops->computebounds) {
96     ierr = PetscPrintf(((PetscObject)tao)->comm,"WARNING: Variable bounds have been set but will be ignored by ntr algorithm\n");CHKERRQ(ierr);
97   }
98 
99   tao->trust = tao->trust0;
100 
101   /* Modify the radius if it is too large or small */
102   tao->trust = PetscMax(tao->trust, tr->min_radius);
103   tao->trust = PetscMin(tao->trust, tr->max_radius);
104 
105 
106   if (NTR_PC_BFGS == tr->pc_type && !tr->M) {
107     ierr = VecGetLocalSize(tao->solution,&n);CHKERRQ(ierr);
108     ierr = VecGetSize(tao->solution,&N);CHKERRQ(ierr);
109     ierr = MatCreateLMVM(((PetscObject)tao)->comm,n,N,&tr->M);CHKERRQ(ierr);
110     ierr = MatLMVMAllocateVectors(tr->M,tao->solution);CHKERRQ(ierr);
111   }
112 
113   /* Check convergence criteria */
114   ierr = TaoComputeObjectiveAndGradient(tao, tao->solution, &f, tao->gradient);CHKERRQ(ierr);
115   ierr = VecNorm(tao->gradient,NORM_2,&gnorm);CHKERRQ(ierr);
116   if (PetscIsInfOrNanReal(f) || PetscIsInfOrNanReal(gnorm)) SETERRQ(PETSC_COMM_SELF,1, "User provided compute function generated Inf or NaN");
117   needH = 1;
118 
119   ierr = TaoMonitor(tao, iter, f, gnorm, 0.0, 1.0, &reason);CHKERRQ(ierr);
120   if (reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
121 
122   /* Create vectors for the limited memory preconditioner */
123   if ((NTR_PC_BFGS == tr->pc_type) &&
124       (BFGS_SCALE_BFGS != tr->bfgs_scale_type)) {
125     if (!tr->Diag) {
126         ierr = VecDuplicate(tao->solution, &tr->Diag);CHKERRQ(ierr);
127     }
128   }
129 
130   switch(tr->ksp_type) {
131   case NTR_KSP_NASH:
132     ierr = KSPSetType(tao->ksp, KSPNASH);CHKERRQ(ierr);
133     if (tao->ksp->ops->setfromoptions) {
134       (*tao->ksp->ops->setfromoptions)(tao->ksp);
135     }
136     break;
137 
138   case NTR_KSP_STCG:
139     ierr = KSPSetType(tao->ksp, KSPSTCG);CHKERRQ(ierr);
140     if (tao->ksp->ops->setfromoptions) {
141       (*tao->ksp->ops->setfromoptions)(tao->ksp);
142     }
143     break;
144 
145   default:
146     ierr = KSPSetType(tao->ksp, KSPGLTR);CHKERRQ(ierr);
147     if (tao->ksp->ops->setfromoptions) {
148       (*tao->ksp->ops->setfromoptions)(tao->ksp);
149     }
150     break;
151   }
152 
153   /*  Modify the preconditioner to use the bfgs approximation */
154   ierr = KSPGetPC(tao->ksp, &pc);CHKERRQ(ierr);
155   switch(tr->pc_type) {
156   case NTR_PC_NONE:
157     ierr = PCSetType(pc, PCNONE);CHKERRQ(ierr);
158     if (pc->ops->setfromoptions) {
159       (*pc->ops->setfromoptions)(pc);
160     }
161     break;
162 
163   case NTR_PC_AHESS:
164     ierr = PCSetType(pc, PCJACOBI);CHKERRQ(ierr);
165     if (pc->ops->setfromoptions) {
166       (*pc->ops->setfromoptions)(pc);
167     }
168     ierr = PCJacobiSetUseAbs(pc);CHKERRQ(ierr);
169     break;
170 
171   case NTR_PC_BFGS:
172     ierr = PCSetType(pc, PCSHELL);CHKERRQ(ierr);
173     if (pc->ops->setfromoptions) {
174       (*pc->ops->setfromoptions)(pc);
175     }
176     ierr = PCShellSetName(pc, "bfgs");CHKERRQ(ierr);
177     ierr = PCShellSetContext(pc, tr->M);CHKERRQ(ierr);
178     ierr = PCShellSetApply(pc, MatLMVMSolveShell);CHKERRQ(ierr);
179     break;
180 
181   default:
182     /*  Use the pc method set by pc_type */
183     break;
184   }
185 
186   /*  Initialize trust-region radius */
187   switch(tr->init_type) {
188   case NTR_INIT_CONSTANT:
189     /*  Use the initial radius specified */
190     break;
191 
192   case NTR_INIT_INTERPOLATION:
193     /*  Use the initial radius specified */
194     max_radius = 0.0;
195 
196     for (j = 0; j < j_max; ++j) {
197       fmin = f;
198       sigma = 0.0;
199 
200       if (needH) {
201         ierr = TaoComputeHessian(tao,tao->solution,tao->hessian,tao->hessian_pre);CHKERRQ(ierr);
202         needH = 0;
203       }
204 
205       for (i = 0; i < i_max; ++i) {
206 
207         ierr = VecCopy(tao->solution, tr->W);CHKERRQ(ierr);
208         ierr = VecAXPY(tr->W, -tao->trust/gnorm, tao->gradient);CHKERRQ(ierr);
209         ierr = TaoComputeObjective(tao, tr->W, &ftrial);CHKERRQ(ierr);
210 
211         if (PetscIsInfOrNanReal(ftrial)) {
212           tau = tr->gamma1_i;
213         }
214         else {
215           if (ftrial < fmin) {
216             fmin = ftrial;
217             sigma = -tao->trust / gnorm;
218           }
219 
220           ierr = MatMult(tao->hessian, tao->gradient, tao->stepdirection);CHKERRQ(ierr);
221           ierr = VecDot(tao->gradient, tao->stepdirection, &prered);CHKERRQ(ierr);
222 
223           prered = tao->trust * (gnorm - 0.5 * tao->trust * prered / (gnorm * gnorm));
224           actred = f - ftrial;
225           if ((PetscAbsScalar(actred) <= tr->epsilon) &&
226               (PetscAbsScalar(prered) <= tr->epsilon)) {
227             kappa = 1.0;
228           }
229           else {
230             kappa = actred / prered;
231           }
232 
233           tau_1 = tr->theta_i * gnorm * tao->trust / (tr->theta_i * gnorm * tao->trust + (1.0 - tr->theta_i) * prered - actred);
234           tau_2 = tr->theta_i * gnorm * tao->trust / (tr->theta_i * gnorm * tao->trust - (1.0 + tr->theta_i) * prered + actred);
235           tau_min = PetscMin(tau_1, tau_2);
236           tau_max = PetscMax(tau_1, tau_2);
237 
238           if (PetscAbsScalar(kappa - 1.0) <= tr->mu1_i) {
239             /*  Great agreement */
240             max_radius = PetscMax(max_radius, tao->trust);
241 
242             if (tau_max < 1.0) {
243               tau = tr->gamma3_i;
244             }
245             else if (tau_max > tr->gamma4_i) {
246               tau = tr->gamma4_i;
247             }
248             else {
249               tau = tau_max;
250             }
251           }
252           else if (PetscAbsScalar(kappa - 1.0) <= tr->mu2_i) {
253             /*  Good agreement */
254             max_radius = PetscMax(max_radius, tao->trust);
255 
256             if (tau_max < tr->gamma2_i) {
257               tau = tr->gamma2_i;
258             }
259             else if (tau_max > tr->gamma3_i) {
260               tau = tr->gamma3_i;
261             }
262             else {
263               tau = tau_max;
264             }
265           }
266           else {
267             /*  Not good agreement */
268             if (tau_min > 1.0) {
269               tau = tr->gamma2_i;
270             }
271             else if (tau_max < tr->gamma1_i) {
272               tau = tr->gamma1_i;
273             }
274             else if ((tau_min < tr->gamma1_i) && (tau_max >= 1.0)) {
275               tau = tr->gamma1_i;
276             }
277             else if ((tau_1 >= tr->gamma1_i) && (tau_1 < 1.0) &&
278                      ((tau_2 < tr->gamma1_i) || (tau_2 >= 1.0))) {
279               tau = tau_1;
280             }
281             else if ((tau_2 >= tr->gamma1_i) && (tau_2 < 1.0) &&
282                      ((tau_1 < tr->gamma1_i) || (tau_2 >= 1.0))) {
283               tau = tau_2;
284             }
285             else {
286               tau = tau_max;
287             }
288           }
289         }
290         tao->trust = tau * tao->trust;
291       }
292 
293       if (fmin < f) {
294         f = fmin;
295         ierr = VecAXPY(tao->solution, sigma, tao->gradient);CHKERRQ(ierr);
296         ierr = TaoComputeGradient(tao,tao->solution, tao->gradient);CHKERRQ(ierr);
297 
298         ierr = VecNorm(tao->gradient, NORM_2, &gnorm);CHKERRQ(ierr);
299 
300         if (PetscIsInfOrNanReal(f) || PetscIsInfOrNanReal(gnorm)) SETERRQ(PETSC_COMM_SELF,1, "User provided compute function generated Inf or NaN");
301         needH = 1;
302 
303         ierr = TaoMonitor(tao, iter, f, gnorm, 0.0, 1.0, &reason);CHKERRQ(ierr);
304         if (reason != TAO_CONTINUE_ITERATING) {
305           PetscFunctionReturn(0);
306         }
307       }
308     }
309     tao->trust = PetscMax(tao->trust, max_radius);
310 
311     /*  Modify the radius if it is too large or small */
312     tao->trust = PetscMax(tao->trust, tr->min_radius);
313     tao->trust = PetscMin(tao->trust, tr->max_radius);
314     break;
315 
316   default:
317     /*  Norm of the first direction will initialize radius */
318     tao->trust = 0.0;
319     break;
320   }
321 
322   /* Set initial scaling for the BFGS preconditioner
323      This step is done after computing the initial trust-region radius
324      since the function value may have decreased */
325   if (NTR_PC_BFGS == tr->pc_type) {
326     if (f != 0.0) {
327       delta = 2.0 * PetscAbsScalar(f) / (gnorm*gnorm);
328     }
329     else {
330       delta = 2.0 / (gnorm*gnorm);
331     }
332     ierr = MatLMVMSetDelta(tr->M,delta);CHKERRQ(ierr);
333   }
334 
335   /* Have not converged; continue with Newton method */
336   while (reason == TAO_CONTINUE_ITERATING) {
337     ++iter;
338     tao->ksp_its=0;
339     /* Compute the Hessian */
340     if (needH) {
341       ierr = TaoComputeHessian(tao,tao->solution,tao->hessian,tao->hessian_pre);CHKERRQ(ierr);
342       needH = 0;
343     }
344 
345     if (NTR_PC_BFGS == tr->pc_type) {
346       if (BFGS_SCALE_AHESS == tr->bfgs_scale_type) {
347         /* Obtain diagonal for the bfgs preconditioner */
348         ierr = MatGetDiagonal(tao->hessian, tr->Diag);CHKERRQ(ierr);
349         ierr = VecAbs(tr->Diag);CHKERRQ(ierr);
350         ierr = VecReciprocal(tr->Diag);CHKERRQ(ierr);
351         ierr = MatLMVMSetScale(tr->M,tr->Diag);CHKERRQ(ierr);
352       }
353 
354       /* Update the limited memory preconditioner */
355       ierr = MatLMVMUpdate(tr->M, tao->solution, tao->gradient);CHKERRQ(ierr);
356       ++bfgsUpdates;
357     }
358 
359     while (reason == TAO_CONTINUE_ITERATING) {
360       ierr = KSPSetOperators(tao->ksp, tao->hessian, tao->hessian_pre);CHKERRQ(ierr);
361 
362       /* Solve the trust region subproblem */
363       if (NTR_KSP_NASH == tr->ksp_type) {
364         ierr = KSPNASHSetRadius(tao->ksp,tao->trust);CHKERRQ(ierr);
365         ierr = KSPSolve(tao->ksp, tao->gradient, tao->stepdirection);CHKERRQ(ierr);
366         ierr = KSPGetIterationNumber(tao->ksp,&its);CHKERRQ(ierr);
367         tao->ksp_its+=its;
368         tao->ksp_tot_its+=its;
369         ierr = KSPNASHGetNormD(tao->ksp, &norm_d);CHKERRQ(ierr);
370       } else if (NTR_KSP_STCG == tr->ksp_type) {
371         ierr = KSPSTCGSetRadius(tao->ksp,tao->trust);CHKERRQ(ierr);
372         ierr = KSPSolve(tao->ksp, tao->gradient, tao->stepdirection);CHKERRQ(ierr);
373         ierr = KSPGetIterationNumber(tao->ksp,&its);CHKERRQ(ierr);
374         tao->ksp_its+=its;
375         tao->ksp_tot_its+=its;
376         ierr = KSPSTCGGetNormD(tao->ksp, &norm_d);CHKERRQ(ierr);
377       } else { /* NTR_KSP_GLTR */
378         ierr = KSPGLTRSetRadius(tao->ksp,tao->trust);CHKERRQ(ierr);
379         ierr = KSPSolve(tao->ksp, tao->gradient, tao->stepdirection);CHKERRQ(ierr);
380         ierr = KSPGetIterationNumber(tao->ksp,&its);CHKERRQ(ierr);
381         tao->ksp_its+=its;
382         ierr = KSPGLTRGetNormD(tao->ksp, &norm_d);CHKERRQ(ierr);
383       }
384 
385       if (0.0 == tao->trust) {
386         /* Radius was uninitialized; use the norm of the direction */
387         if (norm_d > 0.0) {
388           tao->trust = norm_d;
389 
390           /* Modify the radius if it is too large or small */
391           tao->trust = PetscMax(tao->trust, tr->min_radius);
392           tao->trust = PetscMin(tao->trust, tr->max_radius);
393         }
394         else {
395           /* The direction was bad; set radius to default value and re-solve
396              the trust-region subproblem to get a direction */
397           tao->trust = tao->trust0;
398 
399           /* Modify the radius if it is too large or small */
400           tao->trust = PetscMax(tao->trust, tr->min_radius);
401           tao->trust = PetscMin(tao->trust, tr->max_radius);
402 
403           if (NTR_KSP_NASH == tr->ksp_type) {
404             ierr = KSPNASHSetRadius(tao->ksp,tao->trust);CHKERRQ(ierr);
405             ierr = KSPSolve(tao->ksp, tao->gradient, tao->stepdirection);CHKERRQ(ierr);
406             ierr = KSPGetIterationNumber(tao->ksp,&its);CHKERRQ(ierr);
407             tao->ksp_its+=its;
408             ierr = KSPNASHGetNormD(tao->ksp, &norm_d);CHKERRQ(ierr);
409           } else if (NTR_KSP_STCG == tr->ksp_type) {
410             ierr = KSPSTCGSetRadius(tao->ksp,tao->trust);CHKERRQ(ierr);
411             ierr = KSPSolve(tao->ksp, tao->gradient, tao->stepdirection);CHKERRQ(ierr);
412             ierr = KSPGetIterationNumber(tao->ksp,&its);CHKERRQ(ierr);
413             tao->ksp_its+=its;
414             ierr = KSPSTCGGetNormD(tao->ksp, &norm_d);CHKERRQ(ierr);
415           } else { /* NTR_KSP_GLTR */
416             ierr = KSPGLTRSetRadius(tao->ksp,tao->trust);CHKERRQ(ierr);
417             ierr = KSPSolve(tao->ksp, tao->gradient, tao->stepdirection);CHKERRQ(ierr);
418             ierr = KSPGetIterationNumber(tao->ksp,&its);CHKERRQ(ierr);
419             tao->ksp_its+=its;
420             ierr = KSPGLTRGetNormD(tao->ksp, &norm_d);CHKERRQ(ierr);
421           }
422 
423           if (norm_d == 0.0) SETERRQ(PETSC_COMM_SELF,1, "Initial direction zero");
424         }
425       }
426       ierr = VecScale(tao->stepdirection, -1.0);CHKERRQ(ierr);
427       ierr = KSPGetConvergedReason(tao->ksp, &ksp_reason);CHKERRQ(ierr);
428       if ((KSP_DIVERGED_INDEFINITE_PC == ksp_reason) &&
429           (NTR_PC_BFGS == tr->pc_type) && (bfgsUpdates > 1)) {
430         /* Preconditioner is numerically indefinite; reset the
431            approximate if using BFGS preconditioning. */
432 
433         if (f != 0.0) {
434           delta = 2.0 * PetscAbsScalar(f) / (gnorm*gnorm);
435         }
436         else {
437           delta = 2.0 / (gnorm*gnorm);
438         }
439         ierr = MatLMVMSetDelta(tr->M, delta);CHKERRQ(ierr);
440         ierr = MatLMVMReset(tr->M);CHKERRQ(ierr);
441         ierr = MatLMVMUpdate(tr->M, tao->solution, tao->gradient);CHKERRQ(ierr);
442         bfgsUpdates = 1;
443       }
444 
445       if (NTR_UPDATE_REDUCTION == tr->update_type) {
446         /* Get predicted reduction */
447         if (NTR_KSP_NASH == tr->ksp_type) {
448           ierr = KSPNASHGetObjFcn(tao->ksp,&prered);CHKERRQ(ierr);
449         } else if (NTR_KSP_STCG == tr->ksp_type) {
450           ierr = KSPSTCGGetObjFcn(tao->ksp,&prered);CHKERRQ(ierr);
451         } else { /* gltr */
452           ierr = KSPGLTRGetObjFcn(tao->ksp,&prered);CHKERRQ(ierr);
453         }
454 
455         if (prered >= 0.0) {
456           /* The predicted reduction has the wrong sign.  This cannot
457              happen in infinite precision arithmetic.  Step should
458              be rejected! */
459           tao->trust = tr->alpha1 * PetscMin(tao->trust, norm_d);
460         }
461         else {
462           /* Compute trial step and function value */
463           ierr = VecCopy(tao->solution,tr->W);CHKERRQ(ierr);
464           ierr = VecAXPY(tr->W, 1.0, tao->stepdirection);CHKERRQ(ierr);
465           ierr = TaoComputeObjective(tao, tr->W, &ftrial);CHKERRQ(ierr);
466 
467           if (PetscIsInfOrNanReal(ftrial)) {
468             tao->trust = tr->alpha1 * PetscMin(tao->trust, norm_d);
469           } else {
470             /* Compute and actual reduction */
471             actred = f - ftrial;
472             prered = -prered;
473             if ((PetscAbsScalar(actred) <= tr->epsilon) &&
474                 (PetscAbsScalar(prered) <= tr->epsilon)) {
475               kappa = 1.0;
476             }
477             else {
478               kappa = actred / prered;
479             }
480 
481             /* Accept or reject the step and update radius */
482             if (kappa < tr->eta1) {
483               /* Reject the step */
484               tao->trust = tr->alpha1 * PetscMin(tao->trust, norm_d);
485             }
486             else {
487               /* Accept the step */
488               if (kappa < tr->eta2) {
489                 /* Marginal bad step */
490                 tao->trust = tr->alpha2 * PetscMin(tao->trust, norm_d);
491               }
492               else if (kappa < tr->eta3) {
493                 /* Reasonable step */
494                 tao->trust = tr->alpha3 * tao->trust;
495               }
496               else if (kappa < tr->eta4) {
497                 /* Good step */
498                 tao->trust = PetscMax(tr->alpha4 * norm_d, tao->trust);
499               }
500               else {
501                 /* Very good step */
502                 tao->trust = PetscMax(tr->alpha5 * norm_d, tao->trust);
503               }
504               break;
505             }
506           }
507         }
508       }
509       else {
510         /* Get predicted reduction */
511         if (NTR_KSP_NASH == tr->ksp_type) {
512           ierr = KSPNASHGetObjFcn(tao->ksp,&prered);CHKERRQ(ierr);
513         } else if (NTR_KSP_STCG == tr->ksp_type) {
514           ierr = KSPSTCGGetObjFcn(tao->ksp,&prered);CHKERRQ(ierr);
515         } else { /* gltr */
516           ierr = KSPGLTRGetObjFcn(tao->ksp,&prered);CHKERRQ(ierr);
517         }
518 
519         if (prered >= 0.0) {
520           /* The predicted reduction has the wrong sign.  This cannot
521              happen in infinite precision arithmetic.  Step should
522              be rejected! */
523           tao->trust = tr->gamma1 * PetscMin(tao->trust, norm_d);
524         }
525         else {
526           ierr = VecCopy(tao->solution, tr->W);CHKERRQ(ierr);
527           ierr = VecAXPY(tr->W, 1.0, tao->stepdirection);CHKERRQ(ierr);
528           ierr = TaoComputeObjective(tao, tr->W, &ftrial);CHKERRQ(ierr);
529           if (PetscIsInfOrNanReal(ftrial)) {
530             tao->trust = tr->gamma1 * PetscMin(tao->trust, norm_d);
531           }
532           else {
533             ierr = VecDot(tao->gradient, tao->stepdirection, &beta);CHKERRQ(ierr);
534             actred = f - ftrial;
535             prered = -prered;
536             if ((PetscAbsScalar(actred) <= tr->epsilon) &&
537                 (PetscAbsScalar(prered) <= tr->epsilon)) {
538               kappa = 1.0;
539             }
540             else {
541               kappa = actred / prered;
542             }
543 
544             tau_1 = tr->theta * beta / (tr->theta * beta - (1.0 - tr->theta) * prered + actred);
545             tau_2 = tr->theta * beta / (tr->theta * beta + (1.0 + tr->theta) * prered - actred);
546             tau_min = PetscMin(tau_1, tau_2);
547             tau_max = PetscMax(tau_1, tau_2);
548 
549             if (kappa >= 1.0 - tr->mu1) {
550               /* Great agreement; accept step and update radius */
551               if (tau_max < 1.0) {
552                 tao->trust = PetscMax(tao->trust, tr->gamma3 * norm_d);
553               }
554               else if (tau_max > tr->gamma4) {
555                 tao->trust = PetscMax(tao->trust, tr->gamma4 * norm_d);
556               }
557               else {
558                 tao->trust = PetscMax(tao->trust, tau_max * norm_d);
559               }
560               break;
561             }
562             else if (kappa >= 1.0 - tr->mu2) {
563               /* Good agreement */
564 
565               if (tau_max < tr->gamma2) {
566                 tao->trust = tr->gamma2 * PetscMin(tao->trust, norm_d);
567               }
568               else if (tau_max > tr->gamma3) {
569                 tao->trust = PetscMax(tao->trust, tr->gamma3 * norm_d);
570               }
571               else if (tau_max < 1.0) {
572                 tao->trust = tau_max * PetscMin(tao->trust, norm_d);
573               }
574               else {
575                 tao->trust = PetscMax(tao->trust, tau_max * norm_d);
576               }
577               break;
578             }
579             else {
580               /* Not good agreement */
581               if (tau_min > 1.0) {
582                 tao->trust = tr->gamma2 * PetscMin(tao->trust, norm_d);
583               }
584               else if (tau_max < tr->gamma1) {
585                 tao->trust = tr->gamma1 * PetscMin(tao->trust, norm_d);
586               }
587               else if ((tau_min < tr->gamma1) && (tau_max >= 1.0)) {
588                 tao->trust = tr->gamma1 * PetscMin(tao->trust, norm_d);
589               }
590               else if ((tau_1 >= tr->gamma1) && (tau_1 < 1.0) &&
591                        ((tau_2 < tr->gamma1) || (tau_2 >= 1.0))) {
592                 tao->trust = tau_1 * PetscMin(tao->trust, norm_d);
593               }
594               else if ((tau_2 >= tr->gamma1) && (tau_2 < 1.0) &&
595                        ((tau_1 < tr->gamma1) || (tau_2 >= 1.0))) {
596                 tao->trust = tau_2 * PetscMin(tao->trust, norm_d);
597               }
598               else {
599                 tao->trust = tau_max * PetscMin(tao->trust, norm_d);
600               }
601             }
602           }
603         }
604       }
605 
606       /* The step computed was not good and the radius was decreased.
607          Monitor the radius to terminate. */
608       ierr = TaoMonitor(tao, iter, f, gnorm, 0.0, tao->trust, &reason);CHKERRQ(ierr);
609     }
610 
611     /* The radius may have been increased; modify if it is too large */
612     tao->trust = PetscMin(tao->trust, tr->max_radius);
613 
614     if (reason == TAO_CONTINUE_ITERATING) {
615       ierr = VecCopy(tr->W, tao->solution);CHKERRQ(ierr);
616       f = ftrial;
617       ierr = TaoComputeGradient(tao, tao->solution, tao->gradient);
618       ierr = VecNorm(tao->gradient, NORM_2, &gnorm);CHKERRQ(ierr);
619       if (PetscIsInfOrNanReal(f) || PetscIsInfOrNanReal(gnorm)) SETERRQ(PETSC_COMM_SELF,1, "User provided compute function generated Inf or NaN");
620       needH = 1;
621       ierr = TaoMonitor(tao, iter, f, gnorm, 0.0, tao->trust, &reason);CHKERRQ(ierr);
622     }
623   }
624   PetscFunctionReturn(0);
625 }
626 
627 /*------------------------------------------------------------*/
628 #undef __FUNCT__
629 #define __FUNCT__ "TaoSetUp_NTR"
630 static PetscErrorCode TaoSetUp_NTR(Tao tao)
631 {
632   TAO_NTR *tr = (TAO_NTR *)tao->data;
633   PetscErrorCode ierr;
634 
635   PetscFunctionBegin;
636 
637   if (!tao->gradient) {ierr = VecDuplicate(tao->solution, &tao->gradient);CHKERRQ(ierr);}
638   if (!tao->stepdirection) {ierr = VecDuplicate(tao->solution, &tao->stepdirection);CHKERRQ(ierr);}
639   if (!tr->W) {ierr = VecDuplicate(tao->solution, &tr->W);CHKERRQ(ierr);}
640 
641   tr->Diag = 0;
642   tr->M = 0;
643 
644 
645   PetscFunctionReturn(0);
646 }
647 
648 /*------------------------------------------------------------*/
649 #undef __FUNCT__
650 #define __FUNCT__ "TaoDestroy_NTR"
651 static PetscErrorCode TaoDestroy_NTR(Tao tao)
652 {
653   TAO_NTR        *tr = (TAO_NTR *)tao->data;
654   PetscErrorCode ierr;
655 
656   PetscFunctionBegin;
657   if (tao->setupcalled) {
658     ierr = VecDestroy(&tr->W);CHKERRQ(ierr);
659   }
660   ierr = MatDestroy(&tr->M);CHKERRQ(ierr);
661   ierr = VecDestroy(&tr->Diag);CHKERRQ(ierr);
662   ierr = PetscFree(tao->data);CHKERRQ(ierr);
663   PetscFunctionReturn(0);
664 }
665 
666 /*------------------------------------------------------------*/
667 #undef __FUNCT__
668 #define __FUNCT__ "TaoSetFromOptions_NTR"
669 static PetscErrorCode TaoSetFromOptions_NTR(Tao tao)
670 {
671   TAO_NTR *tr = (TAO_NTR *)tao->data;
672   PetscErrorCode ierr;
673 
674   PetscFunctionBegin;
675   ierr = PetscOptionsHead("Newton trust region method for unconstrained optimization");CHKERRQ(ierr);
676   ierr = PetscOptionsEList("-tao_ntr_ksp_type", "ksp type", "", NTR_KSP, NTR_KSP_TYPES, NTR_KSP[tr->ksp_type], &tr->ksp_type, 0);CHKERRQ(ierr);
677   ierr = PetscOptionsEList("-tao_ntr_pc_type", "pc type", "", NTR_PC, NTR_PC_TYPES, NTR_PC[tr->pc_type], &tr->pc_type, 0);CHKERRQ(ierr);
678   ierr = PetscOptionsEList("-tao_ntr_bfgs_scale_type", "bfgs scale type", "", BFGS_SCALE, BFGS_SCALE_TYPES, BFGS_SCALE[tr->bfgs_scale_type], &tr->bfgs_scale_type, 0);CHKERRQ(ierr);
679   ierr = PetscOptionsEList("-tao_ntr_init_type", "tao->trust initialization type", "", NTR_INIT, NTR_INIT_TYPES, NTR_INIT[tr->init_type], &tr->init_type, 0);CHKERRQ(ierr);
680   ierr = PetscOptionsEList("-tao_ntr_update_type", "radius update type", "", NTR_UPDATE, NTR_UPDATE_TYPES, NTR_UPDATE[tr->update_type], &tr->update_type, 0);CHKERRQ(ierr);
681   ierr = PetscOptionsReal("-tao_ntr_eta1", "step is unsuccessful if actual reduction < eta1 * predicted reduction", "", tr->eta1, &tr->eta1, 0);CHKERRQ(ierr);
682   ierr = PetscOptionsReal("-tao_ntr_eta2", "", "", tr->eta2, &tr->eta2, 0);CHKERRQ(ierr);
683   ierr = PetscOptionsReal("-tao_ntr_eta3", "", "", tr->eta3, &tr->eta3, 0);CHKERRQ(ierr);
684   ierr = PetscOptionsReal("-tao_ntr_eta4", "", "", tr->eta4, &tr->eta4, 0);CHKERRQ(ierr);
685   ierr = PetscOptionsReal("-tao_ntr_alpha1", "", "", tr->alpha1, &tr->alpha1, 0);CHKERRQ(ierr);
686   ierr = PetscOptionsReal("-tao_ntr_alpha2", "", "", tr->alpha2, &tr->alpha2, 0);CHKERRQ(ierr);
687   ierr = PetscOptionsReal("-tao_ntr_alpha3", "", "", tr->alpha3, &tr->alpha3, 0);CHKERRQ(ierr);
688   ierr = PetscOptionsReal("-tao_ntr_alpha4", "", "", tr->alpha4, &tr->alpha4, 0);CHKERRQ(ierr);
689   ierr = PetscOptionsReal("-tao_ntr_alpha5", "", "", tr->alpha5, &tr->alpha5, 0);CHKERRQ(ierr);
690   ierr = PetscOptionsReal("-tao_ntr_mu1", "", "", tr->mu1, &tr->mu1, 0);CHKERRQ(ierr);
691   ierr = PetscOptionsReal("-tao_ntr_mu2", "", "", tr->mu2, &tr->mu2, 0);CHKERRQ(ierr);
692   ierr = PetscOptionsReal("-tao_ntr_gamma1", "", "", tr->gamma1, &tr->gamma1, 0);CHKERRQ(ierr);
693   ierr = PetscOptionsReal("-tao_ntr_gamma2", "", "", tr->gamma2, &tr->gamma2, 0);CHKERRQ(ierr);
694   ierr = PetscOptionsReal("-tao_ntr_gamma3", "", "", tr->gamma3, &tr->gamma3, 0);CHKERRQ(ierr);
695   ierr = PetscOptionsReal("-tao_ntr_gamma4", "", "", tr->gamma4, &tr->gamma4, 0);CHKERRQ(ierr);
696   ierr = PetscOptionsReal("-tao_ntr_theta", "", "", tr->theta, &tr->theta, 0);CHKERRQ(ierr);
697   ierr = PetscOptionsReal("-tao_ntr_mu1_i", "", "", tr->mu1_i, &tr->mu1_i, 0);CHKERRQ(ierr);
698   ierr = PetscOptionsReal("-tao_ntr_mu2_i", "", "", tr->mu2_i, &tr->mu2_i, 0);CHKERRQ(ierr);
699   ierr = PetscOptionsReal("-tao_ntr_gamma1_i", "", "", tr->gamma1_i, &tr->gamma1_i, 0);CHKERRQ(ierr);
700   ierr = PetscOptionsReal("-tao_ntr_gamma2_i", "", "", tr->gamma2_i, &tr->gamma2_i, 0);CHKERRQ(ierr);
701   ierr = PetscOptionsReal("-tao_ntr_gamma3_i", "", "", tr->gamma3_i, &tr->gamma3_i, 0);CHKERRQ(ierr);
702   ierr = PetscOptionsReal("-tao_ntr_gamma4_i", "", "", tr->gamma4_i, &tr->gamma4_i, 0);CHKERRQ(ierr);
703   ierr = PetscOptionsReal("-tao_ntr_theta_i", "", "", tr->theta_i, &tr->theta_i, 0);CHKERRQ(ierr);
704   ierr = PetscOptionsReal("-tao_ntr_min_radius", "lower bound on initial trust-region radius", "", tr->min_radius, &tr->min_radius, 0);CHKERRQ(ierr);
705   ierr = PetscOptionsReal("-tao_ntr_max_radius", "upper bound on trust-region radius", "", tr->max_radius, &tr->max_radius, 0);CHKERRQ(ierr);
706   ierr = PetscOptionsReal("-tao_ntr_epsilon", "tolerance used when computing actual and predicted reduction", "", tr->epsilon, &tr->epsilon, 0);CHKERRQ(ierr);
707   ierr = PetscOptionsTail();CHKERRQ(ierr);
708   ierr = KSPSetFromOptions(tao->ksp);CHKERRQ(ierr);
709   PetscFunctionReturn(0);
710 }
711 
712 /*------------------------------------------------------------*/
713 #undef __FUNCT__
714 #define __FUNCT__ "TaoView_NTR"
715 static PetscErrorCode TaoView_NTR(Tao tao, PetscViewer viewer)
716 {
717   TAO_NTR        *tr = (TAO_NTR *)tao->data;
718   PetscErrorCode ierr;
719   PetscInt       nrejects;
720   PetscBool      isascii;
721 
722   PetscFunctionBegin;
723   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&isascii);CHKERRQ(ierr);
724   if (isascii) {
725     ierr = PetscViewerASCIIPushTab(viewer);CHKERRQ(ierr);
726     if (NTR_PC_BFGS == tr->pc_type && tr->M) {
727       ierr = MatLMVMGetRejects(tr->M, &nrejects);CHKERRQ(ierr);
728       ierr = PetscViewerASCIIPrintf(viewer, "Rejected matrix updates: %D\n", nrejects);CHKERRQ(ierr);
729     }
730     ierr = PetscViewerASCIIPopTab(viewer);CHKERRQ(ierr);
731   }
732   PetscFunctionReturn(0);
733 }
734 
735 /*------------------------------------------------------------*/
736 /*MC
737   TAONTR - Newton's method with trust region for unconstrained minimization.
738   At each iteration, the Newton trust region method solves the system.
739   NTR expects a KSP solver with a trust region radius.
740             min_d  .5 dT Hk d + gkT d,  s.t.   ||d|| < Delta_k
741 
742   Options Database Keys:
743 + -tao_ntr_ksp_type - "nash","stcg","gltr"
744 . -tao_ntr_pc_type - "none","ahess","bfgs","petsc"
745 . -tao_ntr_bfgs_scale_type - type of scaling with bfgs pc, "ahess" or "bfgs"
746 . -tao_ntr_init_type - "constant","direction","interpolation"
747 . -tao_ntr_update_type - "reduction","interpolation"
748 . -tao_ntr_min_radius - lower bound on trust region radius
749 . -tao_ntr_max_radius - upper bound on trust region radius
750 . -tao_ntr_epsilon - tolerance for accepting actual / predicted reduction
751 . -tao_ntr_mu1_i - mu1 interpolation init factor
752 . -tao_ntr_mu2_i - mu2 interpolation init factor
753 . -tao_ntr_gamma1_i - gamma1 interpolation init factor
754 . -tao_ntr_gamma2_i - gamma2 interpolation init factor
755 . -tao_ntr_gamma3_i - gamma3 interpolation init factor
756 . -tao_ntr_gamma4_i - gamma4 interpolation init factor
757 . -tao_ntr_theta_i - thetha1 interpolation init factor
758 . -tao_ntr_eta1 - eta1 reduction update factor
759 . -tao_ntr_eta2 - eta2 reduction update factor
760 . -tao_ntr_eta3 - eta3 reduction update factor
761 . -tao_ntr_eta4 - eta4 reduction update factor
762 . -tao_ntr_alpha1 - alpha1 reduction update factor
763 . -tao_ntr_alpha2 - alpha2 reduction update factor
764 . -tao_ntr_alpha3 - alpha3 reduction update factor
765 . -tao_ntr_alpha4 - alpha4 reduction update factor
766 . -tao_ntr_alpha4 - alpha4 reduction update factor
767 . -tao_ntr_mu1 - mu1 interpolation update
768 . -tao_ntr_mu2 - mu2 interpolation update
769 . -tao_ntr_gamma1 - gamma1 interpolcation update
770 . -tao_ntr_gamma2 - gamma2 interpolcation update
771 . -tao_ntr_gamma3 - gamma3 interpolcation update
772 . -tao_ntr_gamma4 - gamma4 interpolation update
773 - -tao_ntr_theta - theta interpolation update
774 
775   Level: beginner
776 M*/
777 
778 EXTERN_C_BEGIN
779 #undef __FUNCT__
780 #define __FUNCT__ "TaoCreate_NTR"
781 PetscErrorCode TaoCreate_NTR(Tao tao)
782 {
783   TAO_NTR *tr;
784   PetscErrorCode ierr;
785 
786   PetscFunctionBegin;
787 
788   ierr = PetscNewLog(tao,&tr);CHKERRQ(ierr);
789 
790   tao->ops->setup = TaoSetUp_NTR;
791   tao->ops->solve = TaoSolve_NTR;
792   tao->ops->view = TaoView_NTR;
793   tao->ops->setfromoptions = TaoSetFromOptions_NTR;
794   tao->ops->destroy = TaoDestroy_NTR;
795 
796   tao->max_it = 50;
797 #if defined(PETSC_USE_REAL_SINGLE)
798   tao->fatol = 1e-5;
799   tao->frtol = 1e-5;
800 #else
801   tao->fatol = 1e-10;
802   tao->frtol = 1e-10;
803 #endif
804   tao->data = (void*)tr;
805 
806   tao->trust0 = 100.0;
807 
808   /*  Standard trust region update parameters */
809   tr->eta1 = 1.0e-4;
810   tr->eta2 = 0.25;
811   tr->eta3 = 0.50;
812   tr->eta4 = 0.90;
813 
814   tr->alpha1 = 0.25;
815   tr->alpha2 = 0.50;
816   tr->alpha3 = 1.00;
817   tr->alpha4 = 2.00;
818   tr->alpha5 = 4.00;
819 
820   /*  Interpolation parameters */
821   tr->mu1_i = 0.35;
822   tr->mu2_i = 0.50;
823 
824   tr->gamma1_i = 0.0625;
825   tr->gamma2_i = 0.50;
826   tr->gamma3_i = 2.00;
827   tr->gamma4_i = 5.00;
828 
829   tr->theta_i = 0.25;
830 
831   /*  Interpolation trust region update parameters */
832   tr->mu1 = 0.10;
833   tr->mu2 = 0.50;
834 
835   tr->gamma1 = 0.25;
836   tr->gamma2 = 0.50;
837   tr->gamma3 = 2.00;
838   tr->gamma4 = 4.00;
839 
840   tr->theta = 0.05;
841 
842   tr->min_radius = 1.0e-10;
843   tr->max_radius = 1.0e10;
844   tr->epsilon = 1.0e-6;
845 
846   tr->ksp_type        = NTR_KSP_STCG;
847   tr->pc_type         = NTR_PC_BFGS;
848   tr->bfgs_scale_type = BFGS_SCALE_AHESS;
849   tr->init_type       = NTR_INIT_INTERPOLATION;
850   tr->update_type     = NTR_UPDATE_REDUCTION;
851 
852 
853   /* Set linear solver to default for trust region */
854   ierr = KSPCreate(((PetscObject)tao)->comm, &tao->ksp);CHKERRQ(ierr);
855 
856   PetscFunctionReturn(0);
857 
858 
859 }
860 EXTERN_C_END
861 
862 
863 #undef __FUNCT__
864 #define __FUNCT__ "MatLMVMSolveShell"
865 static PetscErrorCode MatLMVMSolveShell(PC pc, Vec b, Vec x)
866 {
867     PetscErrorCode ierr;
868     Mat M;
869     PetscFunctionBegin;
870     PetscValidHeaderSpecific(pc,PC_CLASSID,1);
871     PetscValidHeaderSpecific(b,VEC_CLASSID,2);
872     PetscValidHeaderSpecific(x,VEC_CLASSID,3);
873     ierr = PCShellGetContext(pc,(void**)&M);CHKERRQ(ierr);
874     ierr = MatLMVMSolve(M, b, x);CHKERRQ(ierr);
875     PetscFunctionReturn(0);
876 }
877