xref: /petsc/src/tao/constrained/impls/almm/almm.c (revision d756bedd70a89ca052be956bccd75c5761cb2ab4)
1 #include <../src/tao/constrained/impls/almm/almm.h> /*I "petsctao.h" I*/
2 #include <petsctao.h>
3 #include <petsc/private/petscimpl.h>
4 #include <petsc/private/vecimpl.h>
5 
6 static PetscErrorCode TaoALMMCombinePrimal_Private(Tao, Vec, Vec, Vec);
7 static PetscErrorCode TaoALMMCombineDual_Private(Tao, Vec, Vec, Vec);
8 static PetscErrorCode TaoALMMSplitPrimal_Private(Tao, Vec, Vec, Vec);
9 static PetscErrorCode TaoALMMComputeOptimalityNorms_Private(Tao);
10 static PetscErrorCode TaoALMMComputeAugLagAndGradient_Private(Tao);
11 static PetscErrorCode TaoALMMComputePHRLagAndGradient_Private(Tao);
12 
TaoSolve_ALMM(Tao tao)13 static PetscErrorCode TaoSolve_ALMM(Tao tao)
14 {
15   TAO_ALMM          *auglag = (TAO_ALMM *)tao->data;
16   TaoConvergedReason reason;
17   PetscReal          updated;
18 
19   PetscFunctionBegin;
20   /* reset initial multiplier/slack guess */
21   if (!tao->recycle) {
22     if (tao->ineq_constrained) {
23       PetscCall(VecZeroEntries(auglag->Ps));
24       PetscCall(TaoALMMCombinePrimal_Private(tao, auglag->Px, auglag->Ps, auglag->P));
25       PetscCall(VecSet(auglag->Yi, 0.0));
26     }
27     if (tao->eq_constrained) PetscCall(VecSet(auglag->Ye, 0.0));
28   }
29 
30   /* compute initial nonlinear Lagrangian and its derivatives */
31   PetscCall((*auglag->sub_obj)(tao));
32   PetscCall(TaoALMMComputeOptimalityNorms_Private(tao));
33   /* print initial step and check convergence */
34   PetscCall(PetscInfo(tao, "Solving with %s formulation\n", TaoALMMTypes[auglag->type]));
35   PetscCall(TaoLogConvergenceHistory(tao, auglag->Lval, auglag->gnorm, auglag->cnorm, tao->ksp_its));
36   PetscCall(TaoMonitor(tao, tao->niter, auglag->fval, auglag->gnorm, auglag->cnorm, 0.0));
37   PetscUseTypeMethod(tao, convergencetest, tao->cnvP);
38   /* set initial penalty factor and inner solver tolerance */
39   switch (auglag->type) {
40   case TAO_ALMM_CLASSIC:
41     auglag->mu = auglag->mu0;
42     break;
43   case TAO_ALMM_PHR:
44     auglag->cenorm = 0.0;
45     if (tao->eq_constrained) PetscCall(VecDot(auglag->Ce, auglag->Ce, &auglag->cenorm));
46     auglag->cinorm = 0.0;
47     if (tao->ineq_constrained) {
48       PetscCall(VecCopy(auglag->Ci, auglag->Ciwork));
49       PetscCall(VecScale(auglag->Ciwork, -1.0));
50       PetscCall(VecPointwiseMax(auglag->Ciwork, auglag->Cizero, auglag->Ciwork));
51       PetscCall(VecDot(auglag->Ciwork, auglag->Ciwork, &auglag->cinorm));
52     }
53     /* determine initial penalty factor based on the balance of constraint violation and objective function value */
54     if (PetscAbsReal(auglag->cenorm + auglag->cinorm) == 0.0) auglag->mu = 10.0;
55     else auglag->mu = PetscMax(1.e-6, PetscMin(10.0, 2.0 * PetscAbsReal(auglag->fval) / (auglag->cenorm + auglag->cinorm)));
56     break;
57   default:
58     break;
59   }
60   auglag->gtol = auglag->gtol0;
61   PetscCall(PetscInfo(tao, "Initial penalty: %.2g\n", (double)auglag->mu));
62 
63   /* start aug-lag outer loop */
64   while (tao->reason == TAO_CONTINUE_ITERATING) {
65     ++tao->niter;
66     /* update subsolver tolerance */
67     PetscCall(PetscInfo(tao, "Subsolver tolerance: ||G|| <= %e\n", (double)auglag->gtol));
68     PetscCall(TaoSetTolerances(auglag->subsolver, auglag->gtol, 0.0, 0.0));
69     /* solve the bound-constrained or unconstrained subproblem */
70     PetscCall(VecCopy(auglag->P, auglag->subsolver->solution));
71     PetscCall(TaoSolve(auglag->subsolver));
72     PetscCall(VecCopy(auglag->subsolver->solution, auglag->P));
73     PetscCall(TaoGetConvergedReason(auglag->subsolver, &reason));
74     tao->ksp_its += auglag->subsolver->ksp_its;
75     if (reason != TAO_CONVERGED_GATOL) PetscCall(PetscInfo(tao, "Subsolver failed to converge, reason: %s\n", TaoConvergedReasons[reason]));
76     /* evaluate solution and test convergence */
77     PetscCall((*auglag->sub_obj)(tao));
78     PetscCall(TaoALMMComputeOptimalityNorms_Private(tao));
79     /* decide whether to update multipliers or not */
80     updated = 0.0;
81     if (auglag->cnorm <= auglag->ytol) {
82       PetscCall(PetscInfo(tao, "Multipliers updated: ||C|| <= %e\n", (double)auglag->ytol));
83       /* constraints are good, update multipliers and convergence tolerances */
84       if (tao->eq_constrained) {
85         PetscCall(VecAXPY(auglag->Ye, auglag->mu, auglag->Ce));
86         PetscCall(VecSet(auglag->Cework, auglag->ye_max));
87         PetscCall(VecPointwiseMin(auglag->Ye, auglag->Cework, auglag->Ye));
88         PetscCall(VecSet(auglag->Cework, auglag->ye_min));
89         PetscCall(VecPointwiseMax(auglag->Ye, auglag->Cework, auglag->Ye));
90       }
91       if (tao->ineq_constrained) {
92         PetscCall(VecAXPY(auglag->Yi, auglag->mu, auglag->Ci));
93         PetscCall(VecSet(auglag->Ciwork, auglag->yi_max));
94         PetscCall(VecPointwiseMin(auglag->Yi, auglag->Ciwork, auglag->Yi));
95         PetscCall(VecSet(auglag->Ciwork, auglag->yi_min));
96         PetscCall(VecPointwiseMax(auglag->Yi, auglag->Ciwork, auglag->Yi));
97       }
98       /* tolerances are updated only for non-PHR methods */
99       if (auglag->type != TAO_ALMM_PHR) {
100         auglag->ytol = PetscMax(tao->catol, auglag->ytol / PetscPowReal(auglag->mu, auglag->mu_pow_good));
101         auglag->gtol = PetscMax(tao->gatol, auglag->gtol / auglag->mu);
102       }
103       updated = 1.0;
104     } else {
105       /* constraints are bad, update penalty factor */
106       auglag->mu = PetscMin(auglag->mu_max, auglag->mu_fac * auglag->mu);
107       /* tolerances are reset only for non-PHR methods */
108       if (auglag->type != TAO_ALMM_PHR) {
109         auglag->ytol = PetscMax(tao->catol, 1.0 / PetscPowReal(auglag->mu, auglag->mu_pow_bad));
110         auglag->gtol = PetscMax(tao->gatol, 1.0 / auglag->mu);
111       }
112       PetscCall(PetscInfo(tao, "Penalty increased: mu = %.2g\n", (double)auglag->mu));
113     }
114     PetscCall(TaoLogConvergenceHistory(tao, auglag->fval, auglag->gnorm, auglag->cnorm, tao->ksp_its));
115     PetscCall(TaoMonitor(tao, tao->niter, auglag->fval, auglag->gnorm, auglag->cnorm, updated));
116     PetscUseTypeMethod(tao, convergencetest, tao->cnvP);
117   }
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
TaoView_ALMM(Tao tao,PetscViewer viewer)121 static PetscErrorCode TaoView_ALMM(Tao tao, PetscViewer viewer)
122 {
123   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
124   PetscBool isascii;
125 
126   PetscFunctionBegin;
127   PetscCall(PetscViewerASCIIPushTab(viewer));
128   PetscCall(TaoView(auglag->subsolver, viewer));
129   PetscCall(PetscViewerASCIIPopTab(viewer));
130   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
131   if (isascii) {
132     PetscCall(PetscViewerASCIIPushTab(viewer));
133     PetscCall(PetscViewerASCIIPrintf(viewer, "ALMM Formulation Type: %s\n", TaoALMMTypes[auglag->type]));
134     PetscCall(PetscViewerASCIIPopTab(viewer));
135   }
136   PetscFunctionReturn(PETSC_SUCCESS);
137 }
138 
TaoSetUp_ALMM(Tao tao)139 static PetscErrorCode TaoSetUp_ALMM(Tao tao)
140 {
141   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
142   VecType   vec_type;
143   Vec       SL, SU;
144   PetscBool is_cg = PETSC_FALSE, is_lmvm = PETSC_FALSE;
145 
146   PetscFunctionBegin;
147   PetscCheck(!tao->ineq_doublesided, PetscObjectComm((PetscObject)tao), PETSC_ERR_ARG_WRONGSTATE, "TAOALMM does not support double-sided inequality constraint definition. Please restructure your inequality constraint to fit the form c(x) >= 0.");
148   PetscCheck(tao->eq_constrained || tao->ineq_constrained, PetscObjectComm((PetscObject)tao), PETSC_ERR_ORDER, "Equality and/or inequality constraints must be defined before solver setup.");
149   PetscCall(TaoComputeVariableBounds(tao));
150   /* alias base vectors and create extras */
151   PetscCall(VecGetType(tao->solution, &vec_type));
152   auglag->Px = tao->solution;
153   if (!tao->gradient) { /* base gradient */
154     PetscCall(VecDuplicate(tao->solution, &tao->gradient));
155   }
156   auglag->LgradX = tao->gradient;
157   if (!auglag->Xwork) { /* opt var work vector */
158     PetscCall(VecDuplicate(tao->solution, &auglag->Xwork));
159   }
160   if (tao->eq_constrained) {
161     auglag->Ce = tao->constraints_equality;
162     auglag->Ae = tao->jacobian_equality;
163     if (!auglag->Ye) { /* equality multipliers */
164       PetscCall(VecDuplicate(auglag->Ce, &auglag->Ye));
165     }
166     if (!auglag->Cework) PetscCall(VecDuplicate(auglag->Ce, &auglag->Cework));
167   }
168   if (tao->ineq_constrained) {
169     auglag->Ci = tao->constraints_inequality;
170     auglag->Ai = tao->jacobian_inequality;
171     if (!auglag->Yi) { /* inequality multipliers */
172       PetscCall(VecDuplicate(auglag->Ci, &auglag->Yi));
173     }
174     if (!auglag->Ciwork) PetscCall(VecDuplicate(auglag->Ci, &auglag->Ciwork));
175     if (!auglag->Cizero) {
176       PetscCall(VecDuplicate(auglag->Ci, &auglag->Cizero));
177       PetscCall(VecZeroEntries(auglag->Cizero));
178     }
179     if (!auglag->Ps) { /* slack vars */
180       PetscCall(VecDuplicate(auglag->Ci, &auglag->Ps));
181     }
182     if (!auglag->LgradS) { /* slack component of Lagrangian gradient */
183       PetscCall(VecDuplicate(auglag->Ci, &auglag->LgradS));
184     }
185     /* create vector for combined primal space and the associated communication objects */
186     if (!auglag->P) {
187       PetscCall(PetscMalloc1(2, &auglag->Parr));
188       auglag->Parr[0] = auglag->Px;
189       auglag->Parr[1] = auglag->Ps;
190       PetscCall(VecConcatenate(2, auglag->Parr, &auglag->P, &auglag->Pis));
191       PetscCall(PetscMalloc1(2, &auglag->Pscatter));
192       PetscCall(VecScatterCreate(auglag->P, auglag->Pis[0], auglag->Px, NULL, &auglag->Pscatter[0]));
193       PetscCall(VecScatterCreate(auglag->P, auglag->Pis[1], auglag->Ps, NULL, &auglag->Pscatter[1]));
194     }
195     if (tao->eq_constrained) {
196       /* create vector for combined dual space and the associated communication objects */
197       if (!auglag->Y) {
198         PetscCall(PetscMalloc1(2, &auglag->Yarr));
199         auglag->Yarr[0] = auglag->Ye;
200         auglag->Yarr[1] = auglag->Yi;
201         PetscCall(VecConcatenate(2, auglag->Yarr, &auglag->Y, &auglag->Yis));
202         PetscCall(PetscMalloc1(2, &auglag->Yscatter));
203         PetscCall(VecScatterCreate(auglag->Y, auglag->Yis[0], auglag->Ye, NULL, &auglag->Yscatter[0]));
204         PetscCall(VecScatterCreate(auglag->Y, auglag->Yis[1], auglag->Yi, NULL, &auglag->Yscatter[1]));
205       }
206       if (!auglag->C) PetscCall(VecDuplicate(auglag->Y, &auglag->C));
207     } else {
208       if (!auglag->C) auglag->C = auglag->Ci;
209       if (!auglag->Y) auglag->Y = auglag->Yi;
210     }
211   } else {
212     if (!auglag->P) auglag->P = auglag->Px;
213     if (!auglag->G) auglag->G = auglag->LgradX;
214     if (!auglag->C) auglag->C = auglag->Ce;
215     if (!auglag->Y) auglag->Y = auglag->Ye;
216   }
217   /* create tao primal solution and gradient to interface with subsolver */
218   PetscCall(VecDuplicate(auglag->P, &auglag->Psub));
219   PetscCall(VecDuplicate(auglag->P, &auglag->G));
220   /* initialize parameters */
221   if (auglag->type == TAO_ALMM_PHR) {
222     auglag->mu_fac = 10.0;
223     auglag->yi_min = 0.0;
224     auglag->ytol0  = 0.5;
225     auglag->gtol0  = tao->gatol;
226     if (tao->gatol != tao->default_gatol && tao->catol != tao->default_catol) {
227       PetscCall(PetscInfo(tao, "TAOALMM with PHR: different gradient and constraint tolerances are not supported, setting catol = gatol\n"));
228       tao->catol = tao->gatol;
229     }
230   }
231   /* set the Lagrangian formulation type for the subsolver */
232   switch (auglag->type) {
233   case TAO_ALMM_CLASSIC:
234     auglag->sub_obj = TaoALMMComputeAugLagAndGradient_Private;
235     break;
236   case TAO_ALMM_PHR:
237     auglag->sub_obj = TaoALMMComputePHRLagAndGradient_Private;
238     break;
239   default:
240     break;
241   }
242   /* set up the subsolver */
243   PetscCall(TaoSetSolution(auglag->subsolver, auglag->Psub));
244   PetscCall(TaoSetObjective(auglag->subsolver, TaoALMMSubsolverObjective_Private, (void *)auglag));
245   PetscCall(TaoSetObjectiveAndGradient(auglag->subsolver, NULL, TaoALMMSubsolverObjectiveAndGradient_Private, (void *)auglag));
246   if (tao->bounded) {
247     /* make sure that the subsolver is a bound-constrained method */
248     PetscCall(PetscObjectTypeCompare((PetscObject)auglag->subsolver, TAOCG, &is_cg));
249     PetscCall(PetscObjectTypeCompare((PetscObject)auglag->subsolver, TAOLMVM, &is_lmvm));
250     if (is_cg) {
251       PetscCall(TaoSetType(auglag->subsolver, TAOBNCG));
252       PetscCall(PetscInfo(tao, "TAOCG detected for bound-constrained problem, switching to TAOBNCG.\n"));
253     }
254     if (is_lmvm) {
255       PetscCall(TaoSetType(auglag->subsolver, TAOBQNLS));
256       PetscCall(PetscInfo(tao, "TAOLMVM detected for bound-constrained problem, switching to TAOBQNLS.\n"));
257     }
258     /* create lower and upper bound clone vectors for subsolver */
259     if (!auglag->PL) PetscCall(VecDuplicate(auglag->P, &auglag->PL));
260     if (!auglag->PU) PetscCall(VecDuplicate(auglag->P, &auglag->PU));
261     if (tao->ineq_constrained) {
262       /* create lower and upper bounds for slack, set lower to 0 */
263       PetscCall(VecDuplicate(auglag->Ci, &SL));
264       PetscCall(VecSet(SL, 0.0));
265       PetscCall(VecDuplicate(auglag->Ci, &SU));
266       PetscCall(VecSet(SU, PETSC_INFINITY));
267       /* combine opt var bounds with slack bounds */
268       PetscCall(TaoALMMCombinePrimal_Private(tao, tao->XL, SL, auglag->PL));
269       PetscCall(TaoALMMCombinePrimal_Private(tao, tao->XU, SU, auglag->PU));
270       /* destroy work vectors */
271       PetscCall(VecDestroy(&SL));
272       PetscCall(VecDestroy(&SU));
273     } else {
274       /* no inequality constraints, just copy bounds into the subsolver */
275       PetscCall(VecCopy(tao->XL, auglag->PL));
276       PetscCall(VecCopy(tao->XU, auglag->PU));
277     }
278     PetscCall(TaoSetVariableBounds(auglag->subsolver, auglag->PL, auglag->PU));
279   } else {
280     /* CLASSIC's slack variable is bounded, so need to set bounds */
281     //TODO what happens for non-constrained ALMM CLASSIC?
282     if (auglag->type == TAO_ALMM_CLASSIC) {
283       if (tao->ineq_constrained) {
284         /* create lower and upper bound clone vectors for subsolver
285          * They should be NFINITY and INFINITY                       */
286         if (!auglag->PL) PetscCall(VecDuplicate(auglag->P, &auglag->PL));
287         if (!auglag->PU) PetscCall(VecDuplicate(auglag->P, &auglag->PU));
288         PetscCall(VecSet(auglag->PL, PETSC_NINFINITY));
289         PetscCall(VecSet(auglag->PU, PETSC_INFINITY));
290         /* create lower and upper bounds for slack, set lower to 0 */
291         PetscCall(VecDuplicate(auglag->Ci, &SL));
292         PetscCall(VecSet(SL, 0.0));
293         PetscCall(VecDuplicate(auglag->Ci, &SU));
294         PetscCall(VecSet(SU, PETSC_INFINITY));
295         /* PL, PU is already set. Only copy Slack variable parts */
296         PetscCall(VecScatterBegin(auglag->Pscatter[1], SL, auglag->PL, INSERT_VALUES, SCATTER_REVERSE));
297         PetscCall(VecScatterEnd(auglag->Pscatter[1], SL, auglag->PL, INSERT_VALUES, SCATTER_REVERSE));
298         PetscCall(VecScatterBegin(auglag->Pscatter[1], SU, auglag->PU, INSERT_VALUES, SCATTER_REVERSE));
299         PetscCall(VecScatterEnd(auglag->Pscatter[1], SU, auglag->PU, INSERT_VALUES, SCATTER_REVERSE));
300         /* destroy work vectors */
301         PetscCall(VecDestroy(&SL));
302         PetscCall(VecDestroy(&SU));
303         /* make sure that the subsolver is a bound-constrained method
304          * Unfortunately duplicate code                                 */
305         PetscCall(PetscObjectTypeCompare((PetscObject)auglag->subsolver, TAOCG, &is_cg));
306         PetscCall(PetscObjectTypeCompare((PetscObject)auglag->subsolver, TAOLMVM, &is_lmvm));
307         if (is_cg) {
308           PetscCall(TaoSetType(auglag->subsolver, TAOBNCG));
309           PetscCall(PetscInfo(tao, "TAOCG detected for TAO_ALMM_CLASSIC, switching to TAOBNCG.\n"));
310         }
311         if (is_lmvm) {
312           PetscCall(TaoSetType(auglag->subsolver, TAOBQNLS));
313           PetscCall(PetscInfo(tao, "TAOLMVM detected for TAO_ALMM_CLASSIC, switching to TAOBQNLS.\n"));
314         }
315         PetscCall(TaoSetVariableBounds(auglag->subsolver, auglag->PL, auglag->PU));
316       }
317     }
318   }
319   PetscCall(TaoSetUp(auglag->subsolver));
320   PetscFunctionReturn(PETSC_SUCCESS);
321 }
322 
TaoDestroy_ALMM(Tao tao)323 static PetscErrorCode TaoDestroy_ALMM(Tao tao)
324 {
325   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
326 
327   PetscFunctionBegin;
328   PetscCall(TaoDestroy(&auglag->subsolver));
329   PetscCall(VecDestroy(&auglag->Psub));
330   PetscCall(VecDestroy(&auglag->G));
331   if (tao->setupcalled) {
332     PetscCall(VecDestroy(&auglag->Xwork));
333     if (tao->eq_constrained) {
334       PetscCall(VecDestroy(&auglag->Ye));     /* equality multipliers */
335       PetscCall(VecDestroy(&auglag->Cework)); /* equality work vector */
336     }
337     if (tao->ineq_constrained) {
338       PetscCall(VecDestroy(&auglag->Ps));     /* slack vars */
339       PetscCall(PetscFree(auglag->Parr));     /* array of primal vectors */
340       PetscCall(VecDestroy(&auglag->LgradS)); /* slack grad */
341       PetscCall(VecDestroy(&auglag->Cizero)); /* zero vector for pointwise max */
342       PetscCall(VecDestroy(&auglag->Yi));     /* inequality multipliers */
343       PetscCall(VecDestroy(&auglag->Ciwork)); /* inequality work vector */
344       PetscCall(VecDestroy(&auglag->P));      /* combo primal solution */
345       PetscCall(ISDestroy(&auglag->Pis[0]));  /* index set for X inside P */
346       PetscCall(ISDestroy(&auglag->Pis[1]));  /* index set for S inside P */
347       PetscCall(PetscFree(auglag->Pis));      /* array of P index sets */
348       PetscCall(VecScatterDestroy(&auglag->Pscatter[0]));
349       PetscCall(VecScatterDestroy(&auglag->Pscatter[1]));
350       PetscCall(PetscFree(auglag->Pscatter));
351       if (tao->eq_constrained) {
352         PetscCall(VecDestroy(&auglag->Y));     /* combo multipliers */
353         PetscCall(PetscFree(auglag->Yarr));    /* array of dual vectors */
354         PetscCall(VecDestroy(&auglag->C));     /* combo constraints */
355         PetscCall(ISDestroy(&auglag->Yis[0])); /* index set for Ye inside Y */
356         PetscCall(ISDestroy(&auglag->Yis[1])); /* index set for Yi inside Y */
357         PetscCall(PetscFree(auglag->Yis));
358         PetscCall(VecScatterDestroy(&auglag->Yscatter[0]));
359         PetscCall(VecScatterDestroy(&auglag->Yscatter[1]));
360         PetscCall(PetscFree(auglag->Yscatter));
361       }
362     }
363     if (tao->bounded) {
364       PetscCall(VecDestroy(&auglag->PL)); /* lower bounds for subsolver */
365       PetscCall(VecDestroy(&auglag->PU)); /* upper bounds for subsolver */
366     } else {
367       if (auglag->type == TAO_ALMM_CLASSIC) {
368         PetscCall(VecDestroy(&auglag->PL)); /* lower bounds for subsolver */
369         PetscCall(VecDestroy(&auglag->PU)); /* upper bounds for subsolver */
370       }
371     }
372   }
373   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetType_C", NULL));
374   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetType_C", NULL));
375   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetSubsolver_C", NULL));
376   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetSubsolver_C", NULL));
377   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetMultipliers_C", NULL));
378   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetMultipliers_C", NULL));
379   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetPrimalIS_C", NULL));
380   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetDualIS_C", NULL));
381   PetscCall(PetscFree(tao->data));
382   PetscFunctionReturn(PETSC_SUCCESS);
383 }
384 
TaoSetFromOptions_ALMM(Tao tao,PetscOptionItems PetscOptionsObject)385 static PetscErrorCode TaoSetFromOptions_ALMM(Tao tao, PetscOptionItems PetscOptionsObject)
386 {
387   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
388 
389   PetscFunctionBegin;
390   PetscOptionsHeadBegin(PetscOptionsObject, "Augmented Lagrangian multiplier method solves problems with general constraints by converting them into a sequence of unconstrained problems.");
391   PetscCall(PetscOptionsReal("-tao_almm_mu_init", "initial penalty parameter", "", auglag->mu0, &auglag->mu0, NULL));
392   PetscCall(PetscOptionsReal("-tao_almm_mu_factor", "increase factor for the penalty parameter", "", auglag->mu_fac, &auglag->mu_fac, NULL));
393   PetscCall(PetscOptionsReal("-tao_almm_mu_power_good", "exponential for penalty parameter when multiplier update is accepted", "", auglag->mu_pow_good, &auglag->mu_pow_good, NULL));
394   PetscCall(PetscOptionsReal("-tao_almm_mu_power_bad", "exponential for penalty parameter when multiplier update is rejected", "", auglag->mu_pow_bad, &auglag->mu_pow_bad, NULL));
395   PetscCall(PetscOptionsReal("-tao_almm_mu_max", "maximum safeguard for penalty parameter updates", "", auglag->mu_max, &auglag->mu_max, NULL));
396   PetscCall(PetscOptionsReal("-tao_almm_ye_min", "minimum safeguard for equality multiplier updates", "", auglag->ye_min, &auglag->ye_min, NULL));
397   PetscCall(PetscOptionsReal("-tao_almm_ye_max", "maximum safeguard for equality multipliers updates", "", auglag->ye_max, &auglag->ye_max, NULL));
398   PetscCall(PetscOptionsReal("-tao_almm_yi_min", "minimum safeguard for inequality multipliers updates", "", auglag->yi_min, &auglag->yi_min, NULL));
399   PetscCall(PetscOptionsReal("-tao_almm_yi_max", "maximum safeguard for inequality multipliers updates", "", auglag->yi_max, &auglag->yi_max, NULL));
400   PetscCall(PetscOptionsEnum("-tao_almm_type", "augmented Lagrangian formulation type for the subproblem", "TaoALMMType", TaoALMMTypes, (PetscEnum)auglag->type, (PetscEnum *)&auglag->type, NULL));
401   PetscOptionsHeadEnd();
402   PetscCall(TaoSetOptionsPrefix(auglag->subsolver, ((PetscObject)tao)->prefix));
403   PetscCall(TaoAppendOptionsPrefix(auglag->subsolver, "tao_almm_subsolver_"));
404   PetscCall(TaoSetFromOptions(auglag->subsolver));
405   PetscFunctionReturn(PETSC_SUCCESS);
406 }
407 
408 /*MC
409   TAOALMM - Augmented Lagrangian multiplier method for solving nonlinear optimization problems with general constraints.
410 
411   Options Database Keys:
412 + -tao_almm_mu_init <real>       - initial penalty parameter (default: 10.)
413 . -tao_almm_mu_factor <real>     - increase factor for the penalty parameter (default: 100.)
414 . -tao_almm_mu_max <real>        - maximum safeguard for penalty parameter updates (default: 1.e20)
415 . -tao_almm_mu_power_good <real> - exponential for penalty parameter when multiplier update is accepted (default: 0.9)
416 . -tao_almm_mu_power_bad <real>  - exponential for penalty parameter when multiplier update is rejected (default: 0.1)
417 . -tao_almm_ye_min <real>        - minimum safeguard for equality multiplier updates (default: -1.e20)
418 . -tao_almm_ye_max <real>        - maximum safeguard for equality multiplier updates (default: 1.e20)
419 . -tao_almm_yi_min <real>        - minimum safeguard for inequality multiplier updates (default: -1.e20)
420 . -tao_almm_yi_max <real>        - maximum safeguard for inequality multiplier updates (default: 1.e20)
421 - -tao_almm_type <phr,classic>   - change formulation of the augmented Lagrangian merit function for the subproblem (default: phr)
422 
423   Level: beginner
424 
425   Notes:
426   This method converts a constrained problem into a sequence of unconstrained problems via the augmented
427   Lagrangian merit function. Bound constraints are pushed down to the subproblem without any modifications.
428 
429   Two formulations are offered for the subproblem: canonical Hestenes-Powell augmented Lagrangian with slack
430   variables for inequality constraints, and a slack-less Powell-Hestenes-Rockafellar (PHR) formulation utilizing a
431   pointwise max() penalty on inequality constraints. The canonical augmented Lagrangian formulation may converge
432   faster for smaller problems but is highly susceptible to poor step lengths in the subproblem due to the positivity
433   constraint on slack variables. PHR avoids this issue by eliminating the slack variables entirely, and is highly
434   desirable for problems with a large number of inequality constraints.
435 
436   The subproblem is solved using a nested first-order TAO solver (default: `TAOBQNLS`). The user can retrieve a
437   pointer to the subsolver via `TaoALMMGetSubsolver()` or pass command line arguments to it using the
438   "-tao_almm_subsolver_" prefix. Currently, `TAOALMM` does not support second-order methods for the subproblem.
439 
440 .vb
441   while unconverged
442     solve argmin_x L(x) s.t. l <= x <= u
443     if ||c|| <= y_tol
444       if ||c|| <= c_tol && ||Lgrad|| <= g_tol:
445         problem converged, return solution
446       else
447         constraints sufficiently improved
448         update multipliers and tighten tolerances
449       endif
450     else
451       constraints did not improve
452       update penalty and loosen tolerances
453     endif
454   endwhile
455 .ve
456 
457 .seealso: `TAOALMM`, `Tao`, `TaoALMMGetType()`, `TaoALMMSetType()`, `TaoALMMSetSubsolver()`, `TaoALMMGetSubsolver()`,
458           `TaoALMMGetMultipliers()`, `TaoALMMSetMultipliers()`, `TaoALMMGetPrimalIS()`, `TaoALMMGetDualIS()`
459 M*/
TaoCreate_ALMM(Tao tao)460 PETSC_EXTERN PetscErrorCode TaoCreate_ALMM(Tao tao)
461 {
462   TAO_ALMM *auglag;
463 
464   PetscFunctionBegin;
465   PetscCall(PetscNew(&auglag));
466 
467   tao->ops->destroy        = TaoDestroy_ALMM;
468   tao->ops->setup          = TaoSetUp_ALMM;
469   tao->ops->setfromoptions = TaoSetFromOptions_ALMM;
470   tao->ops->view           = TaoView_ALMM;
471   tao->ops->solve          = TaoSolve_ALMM;
472 
473   PetscCall(TaoParametersInitialize(tao));
474   PetscObjectParameterSetDefault(tao, gatol, 1.e-5);
475   PetscObjectParameterSetDefault(tao, grtol, 0.0);
476   PetscObjectParameterSetDefault(tao, gttol, 0.0);
477   PetscObjectParameterSetDefault(tao, catol, 1.e-5);
478   PetscObjectParameterSetDefault(tao, crtol, 0.0);
479 
480   tao->data           = (void *)auglag;
481   auglag->parent      = tao;
482   auglag->mu0         = 10.0;
483   auglag->mu          = auglag->mu0;
484   auglag->mu_fac      = 10.0;
485   auglag->mu_max      = PETSC_INFINITY;
486   auglag->mu_pow_good = 0.9;
487   auglag->mu_pow_bad  = 0.1;
488   auglag->ye_min      = PETSC_NINFINITY;
489   auglag->ye_max      = PETSC_INFINITY;
490   auglag->yi_min      = PETSC_NINFINITY;
491   auglag->yi_max      = PETSC_INFINITY;
492   auglag->ytol0       = 1.0 / PetscPowReal(auglag->mu0, auglag->mu_pow_bad);
493   auglag->ytol        = auglag->ytol0;
494   auglag->gtol0       = 1.0 / auglag->mu0;
495   auglag->gtol        = auglag->gtol0;
496 
497   auglag->sub_obj = TaoALMMComputeAugLagAndGradient_Private;
498   auglag->type    = TAO_ALMM_PHR;
499 
500   PetscCall(TaoCreate(PetscObjectComm((PetscObject)tao), &auglag->subsolver));
501   PetscCall(TaoSetType(auglag->subsolver, TAOBQNLS));
502   PetscCall(TaoSetTolerances(auglag->subsolver, auglag->gtol, 0.0, 0.0));
503   PetscCall(TaoSetMaximumIterations(auglag->subsolver, 1000));
504   PetscCall(TaoSetMaximumFunctionEvaluations(auglag->subsolver, 10000));
505   PetscCall(TaoSetFunctionLowerBound(auglag->subsolver, PETSC_NINFINITY));
506   PetscCall(PetscObjectIncrementTabLevel((PetscObject)auglag->subsolver, (PetscObject)tao, 1));
507 
508   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetType_C", TaoALMMGetType_Private));
509   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetType_C", TaoALMMSetType_Private));
510   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetSubsolver_C", TaoALMMGetSubsolver_Private));
511   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetSubsolver_C", TaoALMMSetSubsolver_Private));
512   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetMultipliers_C", TaoALMMGetMultipliers_Private));
513   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetMultipliers_C", TaoALMMSetMultipliers_Private));
514   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetPrimalIS_C", TaoALMMGetPrimalIS_Private));
515   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetDualIS_C", TaoALMMGetDualIS_Private));
516   PetscFunctionReturn(PETSC_SUCCESS);
517 }
518 
TaoALMMCombinePrimal_Private(Tao tao,Vec X,Vec S,Vec P)519 static PetscErrorCode TaoALMMCombinePrimal_Private(Tao tao, Vec X, Vec S, Vec P)
520 {
521   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
522 
523   PetscFunctionBegin;
524   if (tao->ineq_constrained) {
525     PetscCall(VecScatterBegin(auglag->Pscatter[0], X, P, INSERT_VALUES, SCATTER_REVERSE));
526     PetscCall(VecScatterEnd(auglag->Pscatter[0], X, P, INSERT_VALUES, SCATTER_REVERSE));
527     PetscCall(VecScatterBegin(auglag->Pscatter[1], S, P, INSERT_VALUES, SCATTER_REVERSE));
528     PetscCall(VecScatterEnd(auglag->Pscatter[1], S, P, INSERT_VALUES, SCATTER_REVERSE));
529   } else {
530     PetscCall(VecCopy(X, P));
531   }
532   PetscFunctionReturn(PETSC_SUCCESS);
533 }
534 
TaoALMMCombineDual_Private(Tao tao,Vec EQ,Vec IN,Vec Y)535 static PetscErrorCode TaoALMMCombineDual_Private(Tao tao, Vec EQ, Vec IN, Vec Y)
536 {
537   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
538 
539   PetscFunctionBegin;
540   if (tao->eq_constrained) {
541     if (tao->ineq_constrained) {
542       PetscCall(VecScatterBegin(auglag->Yscatter[0], EQ, Y, INSERT_VALUES, SCATTER_REVERSE));
543       PetscCall(VecScatterEnd(auglag->Yscatter[0], EQ, Y, INSERT_VALUES, SCATTER_REVERSE));
544       PetscCall(VecScatterBegin(auglag->Yscatter[1], IN, Y, INSERT_VALUES, SCATTER_REVERSE));
545       PetscCall(VecScatterEnd(auglag->Yscatter[1], IN, Y, INSERT_VALUES, SCATTER_REVERSE));
546     } else {
547       PetscCall(VecCopy(EQ, Y));
548     }
549   } else {
550     PetscCall(VecCopy(IN, Y));
551   }
552   PetscFunctionReturn(PETSC_SUCCESS);
553 }
554 
TaoALMMSplitPrimal_Private(Tao tao,Vec P,Vec X,Vec S)555 static PetscErrorCode TaoALMMSplitPrimal_Private(Tao tao, Vec P, Vec X, Vec S)
556 {
557   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
558 
559   PetscFunctionBegin;
560   if (tao->ineq_constrained) {
561     PetscCall(VecScatterBegin(auglag->Pscatter[0], P, X, INSERT_VALUES, SCATTER_FORWARD));
562     PetscCall(VecScatterEnd(auglag->Pscatter[0], P, X, INSERT_VALUES, SCATTER_FORWARD));
563     PetscCall(VecScatterBegin(auglag->Pscatter[1], P, S, INSERT_VALUES, SCATTER_FORWARD));
564     PetscCall(VecScatterEnd(auglag->Pscatter[1], P, S, INSERT_VALUES, SCATTER_FORWARD));
565   } else {
566     PetscCall(VecCopy(P, X));
567   }
568   PetscFunctionReturn(PETSC_SUCCESS);
569 }
570 
571 /* this assumes that the latest constraints are stored in Ce and Ci, and also combined in C */
TaoALMMComputeOptimalityNorms_Private(Tao tao)572 static PetscErrorCode TaoALMMComputeOptimalityNorms_Private(Tao tao)
573 {
574   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
575 
576   PetscFunctionBegin;
577   /* if bounded, project the gradient */
578   if (tao->bounded) PetscCall(VecBoundGradientProjection(auglag->LgradX, auglag->Px, tao->XL, tao->XU, auglag->LgradX));
579   if (auglag->type == TAO_ALMM_PHR) {
580     PetscCall(VecNorm(auglag->LgradX, NORM_INFINITY, &auglag->gnorm));
581     auglag->cenorm = 0.0;
582     if (tao->eq_constrained) PetscCall(VecNorm(auglag->Ce, NORM_INFINITY, &auglag->cenorm));
583     auglag->cinorm = 0.0;
584     if (tao->ineq_constrained) {
585       PetscCall(VecCopy(auglag->Yi, auglag->Ciwork));
586       PetscCall(VecScale(auglag->Ciwork, -1.0 / auglag->mu));
587       PetscCall(VecPointwiseMax(auglag->Ciwork, auglag->Ci, auglag->Ciwork));
588       PetscCall(VecNorm(auglag->Ciwork, NORM_INFINITY, &auglag->cinorm));
589     }
590     auglag->cnorm_old = auglag->cnorm;
591     auglag->cnorm     = PetscMax(auglag->cenorm, auglag->cinorm);
592     auglag->ytol      = auglag->ytol0 * auglag->cnorm_old;
593   } else {
594     PetscCall(VecNorm(auglag->LgradX, NORM_2, &auglag->gnorm));
595     PetscCall(VecNorm(auglag->C, NORM_2, &auglag->cnorm));
596   }
597   PetscFunctionReturn(PETSC_SUCCESS);
598 }
599 
TaoALMMEvaluateIterate_Private(Tao tao)600 static PetscErrorCode TaoALMMEvaluateIterate_Private(Tao tao)
601 {
602   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
603 
604   PetscFunctionBegin;
605   /* split solution into primal and slack components */
606   PetscCall(TaoALMMSplitPrimal_Private(tao, auglag->P, auglag->Px, auglag->Ps));
607 
608   /* compute f, df/dx and the constraints */
609   PetscCall(TaoComputeObjectiveAndGradient(tao, auglag->Px, &auglag->fval, auglag->LgradX));
610   if (tao->eq_constrained) {
611     PetscCall(TaoComputeEqualityConstraints(tao, auglag->Px, auglag->Ce));
612     PetscCall(TaoComputeJacobianEquality(tao, auglag->Px, auglag->Ae, auglag->Ae));
613   }
614   if (tao->ineq_constrained) {
615     PetscCall(TaoComputeInequalityConstraints(tao, auglag->Px, auglag->Ci));
616     PetscCall(TaoComputeJacobianInequality(tao, auglag->Px, auglag->Ai, auglag->Ai));
617     switch (auglag->type) {
618     case TAO_ALMM_CLASSIC:
619       /* classic formulation converts inequality to equality constraints via slack variables */
620       PetscCall(VecAXPY(auglag->Ci, -1.0, auglag->Ps));
621       break;
622     case TAO_ALMM_PHR:
623       /* PHR is based on Ci <= 0 while TAO defines Ci >= 0 so we hit it with a negative sign */
624       PetscCall(VecScale(auglag->Ci, -1.0));
625       PetscCall(MatScale(auglag->Ai, -1.0));
626       break;
627     default:
628       break;
629     }
630   }
631   /* combine constraints into one vector */
632   PetscCall(TaoALMMCombineDual_Private(tao, auglag->Ce, auglag->Ci, auglag->C));
633   PetscFunctionReturn(PETSC_SUCCESS);
634 }
635 
636 /*
637 Lphr = f + 0.5*mu*[ (Ce + Ye/mu)^T (Ce + Ye/mu) + pmax(0, Ci + Yi/mu)^T pmax(0, Ci + Yi/mu)]
638 
639 dLphr/dX = dF/dX + mu*[ (Ce + Ye/mu)^T Ae + pmax(0, Ci + Yi/mu)^T Ai]
640 
641 dLphr/dS = 0
642 */
TaoALMMComputePHRLagAndGradient_Private(Tao tao)643 static PetscErrorCode TaoALMMComputePHRLagAndGradient_Private(Tao tao)
644 {
645   TAO_ALMM *auglag  = (TAO_ALMM *)tao->data;
646   PetscReal eq_norm = 0.0, ineq_norm = 0.0;
647 
648   PetscFunctionBegin;
649   PetscCall(TaoALMMEvaluateIterate_Private(tao));
650   if (tao->eq_constrained) {
651     /* Ce_work = mu*(Ce + Ye/mu) */
652     PetscCall(VecWAXPY(auglag->Cework, 1.0 / auglag->mu, auglag->Ye, auglag->Ce));
653     PetscCall(VecDot(auglag->Cework, auglag->Cework, &eq_norm)); /* contribution to scalar Lagrangian */
654     PetscCall(VecScale(auglag->Cework, auglag->mu));
655     /* dL/dX += mu*(Ce + Ye/mu)^T Ae */
656     PetscCall(MatMultTransposeAdd(auglag->Ae, auglag->Cework, auglag->LgradX, auglag->LgradX));
657   }
658   if (tao->ineq_constrained) {
659     /* Ci_work = mu * pmax(0, Ci + Yi/mu) where pmax() is pointwise max() */
660     PetscCall(VecWAXPY(auglag->Ciwork, 1.0 / auglag->mu, auglag->Yi, auglag->Ci));
661     PetscCall(VecPointwiseMax(auglag->Ciwork, auglag->Cizero, auglag->Ciwork));
662     PetscCall(VecDot(auglag->Ciwork, auglag->Ciwork, &ineq_norm)); /* contribution to scalar Lagrangian */
663     /* dL/dX += mu * pmax(0, Ci + Yi/mu)^T Ai */
664     PetscCall(VecScale(auglag->Ciwork, auglag->mu));
665     PetscCall(MatMultTransposeAdd(auglag->Ai, auglag->Ciwork, auglag->LgradX, auglag->LgradX));
666     /* dL/dS = 0 because there are no slacks in PHR */
667     PetscCall(VecZeroEntries(auglag->LgradS));
668   }
669   /* combine gradient together */
670   PetscCall(TaoALMMCombinePrimal_Private(tao, auglag->LgradX, auglag->LgradS, auglag->G));
671   /* compute L = f + 0.5 * mu * [(Ce + Ye/mu)^T (Ce + Ye/mu) + pmax(0, Ci + Yi/mu)^T pmax(0, Ci + Yi/mu)] */
672   auglag->Lval = auglag->fval + 0.5 * auglag->mu * (eq_norm + ineq_norm);
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 /*
677 Lc = F + Ye^TCe + Yi^T(Ci - S) + 0.5*mu*[Ce^TCe + (Ci - S)^T(Ci - S)]
678 
679 dLc/dX = dF/dX + Ye^TAe + Yi^TAi + mu*[Ce^TAe + (Ci - S)^TAi]
680 
681 dLc/dS = -[Yi + mu*(Ci - S)]
682 */
TaoALMMComputeAugLagAndGradient_Private(Tao tao)683 static PetscErrorCode TaoALMMComputeAugLagAndGradient_Private(Tao tao)
684 {
685   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
686   PetscReal yeTce = 0.0, yiTcims = 0.0, ceTce = 0.0, cimsTcims = 0.0;
687 
688   PetscFunctionBegin;
689   PetscCall(TaoALMMEvaluateIterate_Private(tao));
690   if (tao->eq_constrained) {
691     /* compute scalar contributions */
692     PetscCall(VecDot(auglag->Ye, auglag->Ce, &yeTce));
693     PetscCall(VecDot(auglag->Ce, auglag->Ce, &ceTce));
694     /* dL/dX += ye^T Ae */
695     PetscCall(MatMultTransposeAdd(auglag->Ae, auglag->Ye, auglag->LgradX, auglag->LgradX));
696     /* dL/dX += mu * ce^T Ae */
697     PetscCall(MatMultTranspose(auglag->Ae, auglag->Ce, auglag->Xwork));
698     PetscCall(VecAXPY(auglag->LgradX, auglag->mu, auglag->Xwork));
699   }
700   if (tao->ineq_constrained) {
701     /* compute scalar contributions */
702     PetscCall(VecDot(auglag->Yi, auglag->Ci, &yiTcims));
703     PetscCall(VecDot(auglag->Ci, auglag->Ci, &cimsTcims));
704     /* dL/dX += yi^T Ai */
705     PetscCall(MatMultTransposeAdd(auglag->Ai, auglag->Yi, auglag->LgradX, auglag->LgradX));
706     /* dL/dX += mu * (ci - s)^T Ai */
707     PetscCall(MatMultTranspose(auglag->Ai, auglag->Ci, auglag->Xwork));
708     PetscCall(VecAXPY(auglag->LgradX, auglag->mu, auglag->Xwork));
709     /* dL/dS = -[yi + mu*(ci - s)] */
710     PetscCall(VecWAXPY(auglag->LgradS, auglag->mu, auglag->Ci, auglag->Yi));
711     PetscCall(VecScale(auglag->LgradS, -1.0));
712   }
713   /* combine gradient together */
714   PetscCall(TaoALMMCombinePrimal_Private(tao, auglag->LgradX, auglag->LgradS, auglag->G));
715   /* compute L = f + ye^T ce + yi^T (ci - s) + 0.5*mu*||ce||^2 + 0.5*mu*||ci - s||^2 */
716   auglag->Lval = auglag->fval + yeTce + yiTcims + 0.5 * auglag->mu * (ceTce + cimsTcims);
717   PetscFunctionReturn(PETSC_SUCCESS);
718 }
719 
TaoALMMSubsolverObjective_Private(Tao tao,Vec P,PetscReal * Lval,PetscCtx ctx)720 PetscErrorCode TaoALMMSubsolverObjective_Private(Tao tao, Vec P, PetscReal *Lval, PetscCtx ctx)
721 {
722   TAO_ALMM *auglag = (TAO_ALMM *)ctx;
723 
724   PetscFunctionBegin;
725   PetscCall(VecCopy(P, auglag->P));
726   PetscCall((*auglag->sub_obj)(auglag->parent));
727   *Lval = auglag->Lval;
728   PetscFunctionReturn(PETSC_SUCCESS);
729 }
730 
TaoALMMSubsolverObjectiveAndGradient_Private(Tao tao,Vec P,PetscReal * Lval,Vec G,PetscCtx ctx)731 PetscErrorCode TaoALMMSubsolverObjectiveAndGradient_Private(Tao tao, Vec P, PetscReal *Lval, Vec G, PetscCtx ctx)
732 {
733   TAO_ALMM *auglag = (TAO_ALMM *)ctx;
734 
735   PetscFunctionBegin;
736   PetscCall(VecCopy(P, auglag->P));
737   PetscCall((*auglag->sub_obj)(auglag->parent));
738   PetscCall(VecCopy(auglag->G, G));
739   *Lval = auglag->Lval;
740   PetscFunctionReturn(PETSC_SUCCESS);
741 }
742