xref: /petsc/src/tao/bound/impls/bnk/bntr.c (revision e761ccfdf5122cb059ab5d837772ebc1fcc5a85c)
1fed79b8eSAlp Dener #include <../src/tao/bound/impls/bnk/bnk.h>
2fed79b8eSAlp Dener #include <petscksp.h>
3fed79b8eSAlp Dener 
4fed79b8eSAlp Dener /*
5fed79b8eSAlp Dener  Implements Newton's Method with a trust region approach for solving
6fed79b8eSAlp Dener  bound constrained minimization problems.
7fed79b8eSAlp Dener 
8198282dbSAlp Dener  ------------------------------------------------------------
9198282dbSAlp Dener 
10198282dbSAlp Dener  initialize trust radius (default: BNK_INIT_INTERPOLATION)
11198282dbSAlp Dener  x_0 = VecMedian(x_0)
12198282dbSAlp Dener  f_0, g_0 = TaoComputeObjectiveAndGradient(x_0)
13198282dbSAlp Dener  pg_0 = VecBoundGradientProjection(g_0)
14198282dbSAlp Dener  check convergence at pg_0
15198282dbSAlp Dener  niter = 0
16198282dbSAlp Dener  step_accepted = true
17198282dbSAlp Dener 
18198282dbSAlp Dener  while niter <= max_it
19198282dbSAlp Dener     if step_accepted
20198282dbSAlp Dener       niter += 1
21198282dbSAlp Dener       H_k = TaoComputeHessian(x_k)
22198282dbSAlp Dener       if pc_type == BNK_PC_BFGS
23198282dbSAlp Dener         add correction to BFGS approx
24198282dbSAlp Dener         if scale_type == BNK_SCALE_AHESS
25198282dbSAlp Dener           D = VecMedian(1e-6, abs(diag(H_k)), 1e6)
26198282dbSAlp Dener           scale BFGS with VecReciprocal(D)
27198282dbSAlp Dener         end
28198282dbSAlp Dener       end
29198282dbSAlp Dener     end
30198282dbSAlp Dener 
31198282dbSAlp Dener     if pc_type = BNK_PC_BFGS
32198282dbSAlp Dener       B_k = BFGS
33198282dbSAlp Dener     else
34198282dbSAlp Dener       B_k = VecMedian(1e-6, abs(diag(H_k)), 1e6)
35198282dbSAlp Dener       B_k = VecReciprocal(B_k)
36198282dbSAlp Dener     end
37198282dbSAlp Dener     w = x_k - VecMedian(x_k - 0.001*B_k*g_k)
38198282dbSAlp Dener     eps = min(eps, norm2(w))
39198282dbSAlp Dener     determine the active and inactive index sets such that
40198282dbSAlp Dener       L = {i : (x_k)_i <= l_i + eps && (g_k)_i > 0}
41198282dbSAlp Dener       U = {i : (x_k)_i >= u_i - eps && (g_k)_i < 0}
42198282dbSAlp Dener       F = {i : l_i = (x_k)_i = u_i}
43198282dbSAlp Dener       A = {L + U + F}
44198282dbSAlp Dener       I = {i : i not in A}
45198282dbSAlp Dener 
46198282dbSAlp Dener     generate the reduced system Hr_k dr_k = -gr_k for variables in I
47198282dbSAlp Dener     if pc_type == BNK_PC_BFGS && scale_type == BNK_SCALE_PHESS
48198282dbSAlp Dener       D = VecMedian(1e-6, abs(diag(Hr_k)), 1e6)
49198282dbSAlp Dener       scale BFGS with VecReciprocal(D)
50198282dbSAlp Dener     end
51198282dbSAlp Dener     solve Hr_k dr_k = -gr_k
52198282dbSAlp Dener     set d_k to (l - x) for variables in L, (u - x) for variables in U, and 0 for variables in F
53198282dbSAlp Dener 
54198282dbSAlp Dener     x_{k+1} = VecMedian(x_k + d_k)
55198282dbSAlp Dener     s = x_{k+1} - x_k
56198282dbSAlp Dener     prered = dot(s, 0.5*gr_k - Hr_k*s)
57198282dbSAlp Dener     f_{k+1} = TaoComputeObjective(x_{k+1})
58198282dbSAlp Dener     actred = f_k - f_{k+1}
59198282dbSAlp Dener 
60198282dbSAlp Dener     oldTrust = trust
61198282dbSAlp Dener     step_accepted, trust = TaoBNKUpdateTrustRadius(default: BNK_UPDATE_REDUCTION)
62198282dbSAlp Dener     if step_accepted
63198282dbSAlp Dener       g_{k+1} = TaoComputeGradient(x_{k+1})
64198282dbSAlp Dener       pg_{k+1} = VecBoundGradientProjection(g_{k+1})
65198282dbSAlp Dener       count the accepted Newton step
66198282dbSAlp Dener     else
67198282dbSAlp Dener       f_{k+1} = f_k
68198282dbSAlp Dener       x_{k+1} = x_k
69198282dbSAlp Dener       g_{k+1} = g_k
70198282dbSAlp Dener       pg_{k+1} = pg_k
71198282dbSAlp Dener       if trust == oldTrust
72198282dbSAlp Dener         terminate because we cannot shrink the radius any further
73198282dbSAlp Dener       end
74198282dbSAlp Dener     end
75198282dbSAlp Dener 
76198282dbSAlp Dener     check convergence at pg_{k+1}
77198282dbSAlp Dener  end
78fed79b8eSAlp Dener */
79fed79b8eSAlp Dener 
80fed79b8eSAlp Dener static PetscErrorCode TaoSolve_BNTR(Tao tao)
81fed79b8eSAlp Dener {
82fed79b8eSAlp Dener   PetscErrorCode               ierr;
83fed79b8eSAlp Dener   TAO_BNK                      *bnk = (TAO_BNK *)tao->data;
84e465cd6fSAlp Dener   KSPConvergedReason           ksp_reason;
85fed79b8eSAlp Dener 
869b6ef848SAlp Dener   PetscReal                    resnorm, oldTrust, prered, actred, stepNorm, steplen;
87937a31a1SAlp Dener   PetscBool                    cgTerminate, needH = PETSC_TRUE, stepAccepted, shift = PETSC_FALSE;
88e465cd6fSAlp Dener   PetscInt                     stepType = BNK_NEWTON;
89fed79b8eSAlp Dener 
90fed79b8eSAlp Dener   PetscFunctionBegin;
9128017e9fSAlp Dener   /* Initialize the preconditioner, KSP solver and trust radius/line search */
92fed79b8eSAlp Dener   tao->reason = TAO_CONTINUE_ITERATING;
93937a31a1SAlp Dener   ierr = TaoBNKInitialize(tao, bnk->init_type, &needH);CHKERRQ(ierr);
9428017e9fSAlp Dener   if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0);
95fed79b8eSAlp Dener 
96fed79b8eSAlp Dener   /* Have not converged; continue with Newton method */
97fed79b8eSAlp Dener   while (tao->reason == TAO_CONTINUE_ITERATING) {
98fed79b8eSAlp Dener     tao->niter++;
99e031d6f5SAlp Dener 
100937a31a1SAlp Dener     if (needH) {
101e031d6f5SAlp Dener       /* Take BNCG steps (if enabled) to trade-off Hessian evaluations for more gradient evaluations */
102e031d6f5SAlp Dener       ierr = TaoBNKTakeCGSteps(tao, &cgTerminate);CHKERRQ(ierr);
103e031d6f5SAlp Dener       if (cgTerminate) {
104e031d6f5SAlp Dener         tao->reason = bnk->bncg->reason;
105e031d6f5SAlp Dener         PetscFunctionReturn(0);
106fed79b8eSAlp Dener       }
107937a31a1SAlp Dener       /* Compute the hessian and update the BFGS preconditioner at the new iterate */
108937a31a1SAlp Dener       ierr = TaoBNKComputeHessian(tao);CHKERRQ(ierr);
109937a31a1SAlp Dener       needH = PETSC_FALSE;
110937a31a1SAlp Dener     }
111fed79b8eSAlp Dener 
112fed79b8eSAlp Dener     /* Store current solution before it changes */
113fed79b8eSAlp Dener     oldTrust = tao->trust;
114fed79b8eSAlp Dener     bnk->fold = bnk->f;
115fed79b8eSAlp Dener     ierr = VecCopy(tao->solution, bnk->Xold);CHKERRQ(ierr);
116fed79b8eSAlp Dener     ierr = VecCopy(tao->gradient, bnk->Gold);CHKERRQ(ierr);
117fed79b8eSAlp Dener     ierr = VecCopy(bnk->unprojected_gradient, bnk->unprojected_gradient_old);CHKERRQ(ierr);
118fed79b8eSAlp Dener 
119937a31a1SAlp Dener     /* Enter into trust region loops */
120937a31a1SAlp Dener     stepAccepted = PETSC_FALSE;
121937a31a1SAlp Dener     while (!stepAccepted && tao->reason == TAO_CONTINUE_ITERATING) {
122937a31a1SAlp Dener       tao->ksp_its=0;
123937a31a1SAlp Dener 
124937a31a1SAlp Dener       /* Use the common BNK kernel to compute the Newton step (for inactive variables only) */
125937a31a1SAlp Dener       ierr = TaoBNKComputeStep(tao, shift, &ksp_reason);CHKERRQ(ierr);
126937a31a1SAlp Dener 
127b1c2d0e3SAlp Dener       /* Temporarily accept the step and project it into the bounds */
128fed79b8eSAlp Dener       ierr = VecAXPY(tao->solution, 1.0, tao->stepdirection);CHKERRQ(ierr);
129b1c2d0e3SAlp Dener       ierr = VecMedian(tao->XL, tao->solution, tao->XU, tao->solution);CHKERRQ(ierr);
130b1c2d0e3SAlp Dener 
131b1c2d0e3SAlp Dener       /* Check if the projection changed the step direction */
132b1c2d0e3SAlp Dener       ierr = VecCopy(tao->solution, tao->stepdirection);CHKERRQ(ierr);
1338d5ead36SAlp Dener       ierr = VecAXPY(tao->stepdirection, -1.0, bnk->Xold);CHKERRQ(ierr);
134b1c2d0e3SAlp Dener       ierr = VecNorm(tao->stepdirection, NORM_2, &stepNorm);CHKERRQ(ierr);
135b1c2d0e3SAlp Dener       if (stepNorm != bnk->dnorm) {
1368d5ead36SAlp Dener         /* Projection changed the step, so we have to recompute predicted reduction.
1378d5ead36SAlp Dener            However, we deliberately do not change the step norm and the trust radius
1388d5ead36SAlp Dener            in order for the safeguard to more closely mimic a piece-wise linesearch
1398d5ead36SAlp Dener            along the bounds. */
1405e9b73cbSAlp Dener         ierr = TaoBNKRecomputePred(tao, tao->stepdirection, &prered);CHKERRQ(ierr);
141b1c2d0e3SAlp Dener       } else {
142b1c2d0e3SAlp Dener         /* Step did not change, so we can just recover the pre-computed prediction */
143b1c2d0e3SAlp Dener         ierr = KSPCGGetObjFcn(tao->ksp, &prered);CHKERRQ(ierr);
144b1c2d0e3SAlp Dener       }
145b1c2d0e3SAlp Dener       prered = -prered;
146b1c2d0e3SAlp Dener 
147b1c2d0e3SAlp Dener       /* Compute the actual reduction and update the trust radius */
148fed79b8eSAlp Dener       ierr = TaoComputeObjective(tao, tao->solution, &bnk->f);CHKERRQ(ierr);
149b1c2d0e3SAlp Dener       actred = bnk->fold - bnk->f;
150*e761ccfdSAlp Dener       oldTrust = tao->trust;
15128017e9fSAlp Dener       ierr = TaoBNKUpdateTrustRadius(tao, prered, actred, bnk->update_type, stepType, &stepAccepted);CHKERRQ(ierr);
152fed79b8eSAlp Dener 
153fed79b8eSAlp Dener       if (stepAccepted) {
154937a31a1SAlp Dener         /* Step is good, evaluate the gradient and flip the need-Hessian switch */
1558d5ead36SAlp Dener         steplen = 1.0;
156937a31a1SAlp Dener         needH = PETSC_TRUE;
157e465cd6fSAlp Dener         ++bnk->newt;
158fed79b8eSAlp Dener         ierr = TaoComputeGradient(tao, tao->solution, bnk->unprojected_gradient);CHKERRQ(ierr);
15961be54a6SAlp Dener         ierr = TaoBNKEstimateActiveSet(tao, bnk->as_type);CHKERRQ(ierr);
16061be54a6SAlp Dener         ierr = VecCopy(bnk->unprojected_gradient, tao->gradient);CHKERRQ(ierr);
16161be54a6SAlp Dener         ierr = VecISSet(tao->gradient, bnk->active_idx, 0.0);CHKERRQ(ierr);
1629b6ef848SAlp Dener         ierr = VecNorm(tao->gradient, NORM_2, &bnk->gnorm);CHKERRQ(ierr);
1639b6ef848SAlp Dener         if (PetscIsInfOrNanReal(bnk->gnorm)) SETERRQ(PETSC_COMM_SELF,1,"User provided compute function generated Not-a-Number");
164fed79b8eSAlp Dener       } else {
165fed79b8eSAlp Dener         /* Step is bad, revert old solution and re-solve with new radius*/
1668d5ead36SAlp Dener         steplen = 0.0;
167937a31a1SAlp Dener         needH = PETSC_FALSE;
168fed79b8eSAlp Dener         bnk->f = bnk->fold;
169fed79b8eSAlp Dener         ierr = VecCopy(bnk->Xold, tao->solution);CHKERRQ(ierr);
170fed79b8eSAlp Dener         ierr = VecCopy(bnk->Gold, tao->gradient);CHKERRQ(ierr);
171fed79b8eSAlp Dener         ierr = VecCopy(bnk->unprojected_gradient_old, bnk->unprojected_gradient);CHKERRQ(ierr);
17273e4db90SAlp Dener         if (oldTrust == tao->trust) {
17373e4db90SAlp Dener           /* Can't change the radius anymore so just terminate */
174fed79b8eSAlp Dener           tao->reason = TAO_DIVERGED_TR_REDUCTION;
175fed79b8eSAlp Dener         }
176fed79b8eSAlp Dener       }
177fed79b8eSAlp Dener 
178fed79b8eSAlp Dener       /*  Check for termination */
1799b6ef848SAlp Dener       ierr = VecFischer(tao->solution, bnk->unprojected_gradient, tao->XL, tao->XU, bnk->Gwork);CHKERRQ(ierr);
1809b6ef848SAlp Dener       ierr = VecNorm(bnk->Gwork, NORM_2, &resnorm);CHKERRQ(ierr);
1819b6ef848SAlp Dener       ierr = TaoLogConvergenceHistory(tao, bnk->f, resnorm, 0.0, tao->ksp_its);CHKERRQ(ierr);
1829b6ef848SAlp Dener       ierr = TaoMonitor(tao, tao->niter, bnk->f, resnorm, 0.0, steplen);CHKERRQ(ierr);
183fed79b8eSAlp Dener       ierr = (*tao->ops->convergencetest)(tao, tao->cnvP);CHKERRQ(ierr);
184fed79b8eSAlp Dener     }
185937a31a1SAlp Dener   }
186fed79b8eSAlp Dener   PetscFunctionReturn(0);
187fed79b8eSAlp Dener }
188fed79b8eSAlp Dener 
189df278d8fSAlp Dener /*------------------------------------------------------------*/
190df278d8fSAlp Dener 
1919b6ef848SAlp Dener PETSC_INTERN PetscErrorCode TaoSetUp_BNTR(Tao tao)
1929b6ef848SAlp Dener {
1939b6ef848SAlp Dener   TAO_BNK        *bnk = (TAO_BNK *)tao->data;
1949b6ef848SAlp Dener   PetscErrorCode ierr;
1959b6ef848SAlp Dener 
1969b6ef848SAlp Dener   PetscFunctionBegin;
1979b6ef848SAlp Dener   ierr = TaoSetUp_BNK(tao);CHKERRQ(ierr);
1989b6ef848SAlp Dener   if (!bnk->is_nash && !bnk->is_stcg && !bnk->is_gltr) SETERRQ(PETSC_COMM_SELF,1,"Must use a trust-region CG method for KSP (KSPNASH, KSPSTCG, KSPGLTR)");
1999b6ef848SAlp Dener   PetscFunctionReturn(0);
2009b6ef848SAlp Dener }
2019b6ef848SAlp Dener 
2029b6ef848SAlp Dener /*------------------------------------------------------------*/
2039b6ef848SAlp Dener 
2049b6ef848SAlp Dener PETSC_INTERN PetscErrorCode TaoCreate_BNTR(Tao tao)
205fed79b8eSAlp Dener {
206fed79b8eSAlp Dener   TAO_BNK        *bnk;
207fed79b8eSAlp Dener   PetscErrorCode ierr;
208fed79b8eSAlp Dener 
209fed79b8eSAlp Dener   PetscFunctionBegin;
210fed79b8eSAlp Dener   ierr = TaoCreate_BNK(tao);CHKERRQ(ierr);
211fed79b8eSAlp Dener   tao->ops->solve=TaoSolve_BNTR;
2129b6ef848SAlp Dener   tao->ops->setup=TaoSetUp_BNTR;
213fed79b8eSAlp Dener 
214fed79b8eSAlp Dener   bnk = (TAO_BNK *)tao->data;
21566ed3702SAlp Dener   bnk->update_type = BNK_UPDATE_REDUCTION; /* trust region updates based on predicted/actual reduction */
216fed79b8eSAlp Dener   PetscFunctionReturn(0);
217fed79b8eSAlp Dener }