Actual source code: almm.c

  1: #include <../src/tao/constrained/impls/almm/almm.h>
  2: #include <petsctao.h>
  3: #include <petsc/private/petscimpl.h>
  4: #include <petsc/private/vecimpl.h>

  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);

 13: static PetscErrorCode TaoSolve_ALMM(Tao tao)
 14: {
 15:   TAO_ALMM          *auglag = (TAO_ALMM *)tao->data;
 16:   TaoConvergedReason reason;
 17:   PetscReal          updated;

 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:   }

 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));

 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: }

121: static PetscErrorCode TaoView_ALMM(Tao tao, PetscViewer viewer)
122: {
123:   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
124:   PetscBool isascii;

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: }

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;

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: }

323: static PetscErrorCode TaoDestroy_ALMM(Tao tao)
324: {
325:   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;

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: }

385: static PetscErrorCode TaoSetFromOptions_ALMM(Tao tao, PetscOptionItems PetscOptionsObject)
386: {
387:   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;

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: }

408: /*MC
409:   TAOALMM - Augmented Lagrangian multiplier method for solving nonlinear optimization problems with general constraints.

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)

423:   Level: beginner

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.

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.

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.

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

457: .seealso: `TAOALMM`, `Tao`, `TaoALMMGetType()`, `TaoALMMSetType()`, `TaoALMMSetSubsolver()`, `TaoALMMGetSubsolver()`,
458:           `TaoALMMGetMultipliers()`, `TaoALMMSetMultipliers()`, `TaoALMMGetPrimalIS()`, `TaoALMMGetDualIS()`
459: M*/
460: PETSC_EXTERN PetscErrorCode TaoCreate_ALMM(Tao tao)
461: {
462:   TAO_ALMM *auglag;

464:   PetscFunctionBegin;
465:   PetscCall(PetscNew(&auglag));

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;

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);

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;

497:   auglag->sub_obj = TaoALMMComputeAugLagAndGradient_Private;
498:   auglag->type    = TAO_ALMM_PHR;

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));

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: }

519: static PetscErrorCode TaoALMMCombinePrimal_Private(Tao tao, Vec X, Vec S, Vec P)
520: {
521:   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;

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: }

535: static PetscErrorCode TaoALMMCombineDual_Private(Tao tao, Vec EQ, Vec IN, Vec Y)
536: {
537:   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;

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: }

555: static PetscErrorCode TaoALMMSplitPrimal_Private(Tao tao, Vec P, Vec X, Vec S)
556: {
557:   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;

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: }

571: /* this assumes that the latest constraints are stored in Ce and Ci, and also combined in C */
572: static PetscErrorCode TaoALMMComputeOptimalityNorms_Private(Tao tao)
573: {
574:   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;

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: }

600: static PetscErrorCode TaoALMMEvaluateIterate_Private(Tao tao)
601: {
602:   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;

604:   PetscFunctionBegin;
605:   /* split solution into primal and slack components */
606:   PetscCall(TaoALMMSplitPrimal_Private(tao, auglag->P, auglag->Px, auglag->Ps));

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: }

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)]

639: dLphr/dX = dF/dX + mu*[ (Ce + Ye/mu)^T Ae + pmax(0, Ci + Yi/mu)^T Ai]

641: dLphr/dS = 0
642: */
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;

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: }

676: /*
677: Lc = F + Ye^TCe + Yi^T(Ci - S) + 0.5*mu*[Ce^TCe + (Ci - S)^T(Ci - S)]

679: dLc/dX = dF/dX + Ye^TAe + Yi^TAi + mu*[Ce^TAe + (Ci - S)^TAi]

681: dLc/dS = -[Yi + mu*(Ci - S)]
682: */
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;

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: }

720: PetscErrorCode TaoALMMSubsolverObjective_Private(Tao tao, Vec P, PetscReal *Lval, PetscCtx ctx)
721: {
722:   TAO_ALMM *auglag = (TAO_ALMM *)ctx;

724:   PetscFunctionBegin;
725:   PetscCall(VecCopy(P, auglag->P));
726:   PetscCall((*auglag->sub_obj)(auglag->parent));
727:   *Lval = auglag->Lval;
728:   PetscFunctionReturn(PETSC_SUCCESS);
729: }

731: PetscErrorCode TaoALMMSubsolverObjectiveAndGradient_Private(Tao tao, Vec P, PetscReal *Lval, Vec G, PetscCtx ctx)
732: {
733:   TAO_ALMM *auglag = (TAO_ALMM *)ctx;

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: }