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