xref: /petsc/src/tao/constrained/impls/almm/almm.c (revision 7f296bb328fcd4c99f2da7bfe8ba7ed8a4ebceee)
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 
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, 1.0));
26     }
27     if (tao->eq_constrained) PetscCall(VecSet(auglag->Ye, 1.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, 0.1 / 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 
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 
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   }
280   PetscCall(TaoSetUp(auglag->subsolver));
281   PetscFunctionReturn(PETSC_SUCCESS);
282 }
283 
284 static PetscErrorCode TaoDestroy_ALMM(Tao tao)
285 {
286   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
287 
288   PetscFunctionBegin;
289   PetscCall(TaoDestroy(&auglag->subsolver));
290   PetscCall(VecDestroy(&auglag->Psub));
291   PetscCall(VecDestroy(&auglag->G));
292   if (tao->setupcalled) {
293     PetscCall(VecDestroy(&auglag->Xwork));
294     if (tao->eq_constrained) {
295       PetscCall(VecDestroy(&auglag->Ye));     /* equality multipliers */
296       PetscCall(VecDestroy(&auglag->Cework)); /* equality work vector */
297     }
298     if (tao->ineq_constrained) {
299       PetscCall(VecDestroy(&auglag->Ps));     /* slack vars */
300       PetscCall(PetscFree(auglag->Parr));     /* array of primal vectors */
301       PetscCall(VecDestroy(&auglag->LgradS)); /* slack grad */
302       PetscCall(VecDestroy(&auglag->Cizero)); /* zero vector for pointwise max */
303       PetscCall(VecDestroy(&auglag->Yi));     /* inequality multipliers */
304       PetscCall(VecDestroy(&auglag->Ciwork)); /* inequality work vector */
305       PetscCall(VecDestroy(&auglag->P));      /* combo primal solution */
306       PetscCall(ISDestroy(&auglag->Pis[0]));  /* index set for X inside P */
307       PetscCall(ISDestroy(&auglag->Pis[1]));  /* index set for S inside P */
308       PetscCall(PetscFree(auglag->Pis));      /* array of P index sets */
309       PetscCall(VecScatterDestroy(&auglag->Pscatter[0]));
310       PetscCall(VecScatterDestroy(&auglag->Pscatter[1]));
311       PetscCall(PetscFree(auglag->Pscatter));
312       if (tao->eq_constrained) {
313         PetscCall(VecDestroy(&auglag->Y));     /* combo multipliers */
314         PetscCall(PetscFree(auglag->Yarr));    /* array of dual vectors */
315         PetscCall(VecDestroy(&auglag->C));     /* combo constraints */
316         PetscCall(ISDestroy(&auglag->Yis[0])); /* index set for Ye inside Y */
317         PetscCall(ISDestroy(&auglag->Yis[1])); /* index set for Yi inside Y */
318         PetscCall(PetscFree(auglag->Yis));
319         PetscCall(VecScatterDestroy(&auglag->Yscatter[0]));
320         PetscCall(VecScatterDestroy(&auglag->Yscatter[1]));
321         PetscCall(PetscFree(auglag->Yscatter));
322       }
323     }
324     if (tao->bounded) {
325       PetscCall(VecDestroy(&auglag->PL)); /* lower bounds for subsolver */
326       PetscCall(VecDestroy(&auglag->PU)); /* upper bounds for subsolver */
327     }
328   }
329   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetType_C", NULL));
330   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetType_C", NULL));
331   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetSubsolver_C", NULL));
332   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetSubsolver_C", NULL));
333   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetMultipliers_C", NULL));
334   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetMultipliers_C", NULL));
335   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetPrimalIS_C", NULL));
336   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetDualIS_C", NULL));
337   PetscCall(PetscFree(tao->data));
338   PetscFunctionReturn(PETSC_SUCCESS);
339 }
340 
341 static PetscErrorCode TaoSetFromOptions_ALMM(Tao tao, PetscOptionItems PetscOptionsObject)
342 {
343   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
344   PetscInt  i;
345 
346   PetscFunctionBegin;
347   PetscOptionsHeadBegin(PetscOptionsObject, "Augmented Lagrangian multiplier method solves problems with general constraints by converting them into a sequence of unconstrained problems.");
348   PetscCall(PetscOptionsReal("-tao_almm_mu_init", "initial penalty parameter", "", auglag->mu0, &auglag->mu0, NULL));
349   PetscCall(PetscOptionsReal("-tao_almm_mu_factor", "increase factor for the penalty parameter", "", auglag->mu_fac, &auglag->mu_fac, NULL));
350   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));
351   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));
352   PetscCall(PetscOptionsReal("-tao_almm_mu_max", "maximum safeguard for penalty parameter updates", "", auglag->mu_max, &auglag->mu_max, NULL));
353   PetscCall(PetscOptionsReal("-tao_almm_ye_min", "minimum safeguard for equality multiplier updates", "", auglag->ye_min, &auglag->ye_min, NULL));
354   PetscCall(PetscOptionsReal("-tao_almm_ye_max", "maximum safeguard for equality multipliers updates", "", auglag->ye_max, &auglag->ye_max, NULL));
355   PetscCall(PetscOptionsReal("-tao_almm_yi_min", "minimum safeguard for inequality multipliers updates", "", auglag->yi_min, &auglag->yi_min, NULL));
356   PetscCall(PetscOptionsReal("-tao_almm_yi_max", "maximum safeguard for inequality multipliers updates", "", auglag->yi_max, &auglag->yi_max, NULL));
357   PetscCall(PetscOptionsEnum("-tao_almm_type", "augmented Lagrangian formulation type for the subproblem", "TaoALMMType", TaoALMMTypes, (PetscEnum)auglag->type, (PetscEnum *)&auglag->type, NULL));
358   PetscOptionsHeadEnd();
359   PetscCall(TaoSetOptionsPrefix(auglag->subsolver, ((PetscObject)tao)->prefix));
360   PetscCall(TaoAppendOptionsPrefix(auglag->subsolver, "tao_almm_subsolver_"));
361   PetscCall(TaoSetFromOptions(auglag->subsolver));
362   for (i = 0; i < tao->numbermonitors; i++) {
363     PetscCall(PetscObjectReference((PetscObject)tao->monitorcontext[i]));
364     PetscCall(TaoMonitorSet(auglag->subsolver, tao->monitor[i], tao->monitorcontext[i], tao->monitordestroy[i]));
365     if (tao->monitor[i] == TaoMonitorDefault || tao->monitor[i] == TaoMonitorConstraintNorm || tao->monitor[i] == TaoMonitorGlobalization || tao->monitor[i] == TaoMonitorDefaultShort) auglag->info = PETSC_TRUE;
366   }
367   PetscFunctionReturn(PETSC_SUCCESS);
368 }
369 
370 /* -------------------------------------------------------- */
371 
372 /*MC
373   TAOALMM - Augmented Lagrangian multiplier method for solving nonlinear optimization problems with general constraints.
374 
375   Options Database Keys:
376 + -tao_almm_mu_init <real>       - initial penalty parameter (default: 10.)
377 . -tao_almm_mu_factor <real>     - increase factor for the penalty parameter (default: 100.)
378 . -tao_almm_mu_max <real>        - maximum safeguard for penalty parameter updates (default: 1.e20)
379 . -tao_almm_mu_power_good <real> - exponential for penalty parameter when multiplier update is accepted (default: 0.9)
380 . -tao_almm_mu_power_bad <real>  - exponential for penalty parameter when multiplier update is rejected (default: 0.1)
381 . -tao_almm_ye_min <real>        - minimum safeguard for equality multiplier updates (default: -1.e20)
382 . -tao_almm_ye_max <real>        - maximum safeguard for equality multiplier updates (default: 1.e20)
383 . -tao_almm_yi_min <real>        - minimum safeguard for inequality multiplier updates (default: -1.e20)
384 . -tao_almm_yi_max <real>        - maximum safeguard for inequality multiplier updates (default: 1.e20)
385 - -tao_almm_type <phr,classic>   - change formulation of the augmented Lagrangian merit function for the subproblem (default: phr)
386 
387   Level: beginner
388 
389   Notes:
390   This method converts a constrained problem into a sequence of unconstrained problems via the augmented
391   Lagrangian merit function. Bound constraints are pushed down to the subproblem without any modifications.
392 
393   Two formulations are offered for the subproblem: canonical Hestenes-Powell augmented Lagrangian with slack
394   variables for inequality constraints, and a slack-less Powell-Hestenes-Rockafellar (PHR) formulation utilizing a
395   pointwise max() penalty on inequality constraints. The canonical augmented Lagrangian formulation may converge
396   faster for smaller problems but is highly susceptible to poor step lengths in the subproblem due to the positivity
397   constraint on slack variables. PHR avoids this issue by eliminating the slack variables entirely, and is highly
398   desirable for problems with a large number of inequality constraints.
399 
400   The subproblem is solved using a nested first-order TAO solver (default: `TAOBQNLS`). The user can retrieve a
401   pointer to the subsolver via `TaoALMMGetSubsolver()` or pass command line arguments to it using the
402   "-tao_almm_subsolver_" prefix. Currently, `TAOALMM` does not support second-order methods for the subproblem.
403 
404 .vb
405   while unconverged
406     solve argmin_x L(x) s.t. l <= x <= u
407     if ||c|| <= y_tol
408       if ||c|| <= c_tol && ||Lgrad|| <= g_tol:
409         problem converged, return solution
410       else
411         constraints sufficiently improved
412         update multipliers and tighten tolerances
413       endif
414     else
415       constraints did not improve
416       update penalty and loosen tolerances
417     endif
418   endwhile
419 .ve
420 
421 .seealso: `TAOALMM`, `Tao`, `TaoALMMGetType()`, `TaoALMMSetType()`, `TaoALMMSetSubsolver()`, `TaoALMMGetSubsolver()`,
422           `TaoALMMGetMultipliers()`, `TaoALMMSetMultipliers()`, `TaoALMMGetPrimalIS()`, `TaoALMMGetDualIS()`
423 M*/
424 PETSC_EXTERN PetscErrorCode TaoCreate_ALMM(Tao tao)
425 {
426   TAO_ALMM *auglag;
427 
428   PetscFunctionBegin;
429   PetscCall(PetscNew(&auglag));
430 
431   tao->ops->destroy        = TaoDestroy_ALMM;
432   tao->ops->setup          = TaoSetUp_ALMM;
433   tao->ops->setfromoptions = TaoSetFromOptions_ALMM;
434   tao->ops->view           = TaoView_ALMM;
435   tao->ops->solve          = TaoSolve_ALMM;
436 
437   PetscCall(TaoParametersInitialize(tao));
438   PetscObjectParameterSetDefault(tao, gatol, 1.e-5);
439   PetscObjectParameterSetDefault(tao, grtol, 0.0);
440   PetscObjectParameterSetDefault(tao, gttol, 0.0);
441   PetscObjectParameterSetDefault(tao, catol, 1.e-5);
442   PetscObjectParameterSetDefault(tao, crtol, 0.0);
443 
444   tao->data           = (void *)auglag;
445   auglag->parent      = tao;
446   auglag->mu0         = 10.0;
447   auglag->mu          = auglag->mu0;
448   auglag->mu_fac      = 10.0;
449   auglag->mu_max      = PETSC_INFINITY;
450   auglag->mu_pow_good = 0.9;
451   auglag->mu_pow_bad  = 0.1;
452   auglag->ye_min      = PETSC_NINFINITY;
453   auglag->ye_max      = PETSC_INFINITY;
454   auglag->yi_min      = PETSC_NINFINITY;
455   auglag->yi_max      = PETSC_INFINITY;
456   auglag->ytol0       = 0.1 / PetscPowReal(auglag->mu0, auglag->mu_pow_bad);
457   auglag->ytol        = auglag->ytol0;
458   auglag->gtol0       = 1.0 / auglag->mu0;
459   auglag->gtol        = auglag->gtol0;
460 
461   auglag->sub_obj = TaoALMMComputeAugLagAndGradient_Private;
462   auglag->type    = TAO_ALMM_PHR;
463   auglag->info    = PETSC_FALSE;
464 
465   PetscCall(TaoCreate(PetscObjectComm((PetscObject)tao), &auglag->subsolver));
466   PetscCall(TaoSetType(auglag->subsolver, TAOBQNLS));
467   PetscCall(TaoSetTolerances(auglag->subsolver, auglag->gtol, 0.0, 0.0));
468   PetscCall(TaoSetMaximumIterations(auglag->subsolver, 1000));
469   PetscCall(TaoSetMaximumFunctionEvaluations(auglag->subsolver, 10000));
470   PetscCall(TaoSetFunctionLowerBound(auglag->subsolver, PETSC_NINFINITY));
471   PetscCall(PetscObjectIncrementTabLevel((PetscObject)auglag->subsolver, (PetscObject)tao, 1));
472 
473   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetType_C", TaoALMMGetType_Private));
474   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetType_C", TaoALMMSetType_Private));
475   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetSubsolver_C", TaoALMMGetSubsolver_Private));
476   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetSubsolver_C", TaoALMMSetSubsolver_Private));
477   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetMultipliers_C", TaoALMMGetMultipliers_Private));
478   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMSetMultipliers_C", TaoALMMSetMultipliers_Private));
479   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetPrimalIS_C", TaoALMMGetPrimalIS_Private));
480   PetscCall(PetscObjectComposeFunction((PetscObject)tao, "TaoALMMGetDualIS_C", TaoALMMGetDualIS_Private));
481   PetscFunctionReturn(PETSC_SUCCESS);
482 }
483 
484 static PetscErrorCode TaoALMMCombinePrimal_Private(Tao tao, Vec X, Vec S, Vec P)
485 {
486   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
487 
488   PetscFunctionBegin;
489   if (tao->ineq_constrained) {
490     PetscCall(VecScatterBegin(auglag->Pscatter[0], X, P, INSERT_VALUES, SCATTER_REVERSE));
491     PetscCall(VecScatterEnd(auglag->Pscatter[0], X, P, INSERT_VALUES, SCATTER_REVERSE));
492     PetscCall(VecScatterBegin(auglag->Pscatter[1], S, P, INSERT_VALUES, SCATTER_REVERSE));
493     PetscCall(VecScatterEnd(auglag->Pscatter[1], S, P, INSERT_VALUES, SCATTER_REVERSE));
494   } else {
495     PetscCall(VecCopy(X, P));
496   }
497   PetscFunctionReturn(PETSC_SUCCESS);
498 }
499 
500 static PetscErrorCode TaoALMMCombineDual_Private(Tao tao, Vec EQ, Vec IN, Vec Y)
501 {
502   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
503 
504   PetscFunctionBegin;
505   if (tao->eq_constrained) {
506     if (tao->ineq_constrained) {
507       PetscCall(VecScatterBegin(auglag->Yscatter[0], EQ, Y, INSERT_VALUES, SCATTER_REVERSE));
508       PetscCall(VecScatterEnd(auglag->Yscatter[0], EQ, Y, INSERT_VALUES, SCATTER_REVERSE));
509       PetscCall(VecScatterBegin(auglag->Yscatter[1], IN, Y, INSERT_VALUES, SCATTER_REVERSE));
510       PetscCall(VecScatterEnd(auglag->Yscatter[1], IN, Y, INSERT_VALUES, SCATTER_REVERSE));
511     } else {
512       PetscCall(VecCopy(EQ, Y));
513     }
514   } else {
515     PetscCall(VecCopy(IN, Y));
516   }
517   PetscFunctionReturn(PETSC_SUCCESS);
518 }
519 
520 static PetscErrorCode TaoALMMSplitPrimal_Private(Tao tao, Vec P, Vec X, Vec S)
521 {
522   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
523 
524   PetscFunctionBegin;
525   if (tao->ineq_constrained) {
526     PetscCall(VecScatterBegin(auglag->Pscatter[0], P, X, INSERT_VALUES, SCATTER_FORWARD));
527     PetscCall(VecScatterEnd(auglag->Pscatter[0], P, X, INSERT_VALUES, SCATTER_FORWARD));
528     PetscCall(VecScatterBegin(auglag->Pscatter[1], P, S, INSERT_VALUES, SCATTER_FORWARD));
529     PetscCall(VecScatterEnd(auglag->Pscatter[1], P, S, INSERT_VALUES, SCATTER_FORWARD));
530   } else {
531     PetscCall(VecCopy(P, X));
532   }
533   PetscFunctionReturn(PETSC_SUCCESS);
534 }
535 
536 /* this assumes that the latest constraints are stored in Ce and Ci, and also combined in C */
537 static PetscErrorCode TaoALMMComputeOptimalityNorms_Private(Tao tao)
538 {
539   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
540 
541   PetscFunctionBegin;
542   /* if bounded, project the gradient */
543   if (tao->bounded) PetscCall(VecBoundGradientProjection(auglag->LgradX, auglag->Px, tao->XL, tao->XU, auglag->LgradX));
544   if (auglag->type == TAO_ALMM_PHR) {
545     PetscCall(VecNorm(auglag->LgradX, NORM_INFINITY, &auglag->gnorm));
546     auglag->cenorm = 0.0;
547     if (tao->eq_constrained) PetscCall(VecNorm(auglag->Ce, NORM_INFINITY, &auglag->cenorm));
548     auglag->cinorm = 0.0;
549     if (tao->ineq_constrained) {
550       PetscCall(VecCopy(auglag->Yi, auglag->Ciwork));
551       PetscCall(VecScale(auglag->Ciwork, -1.0 / auglag->mu));
552       PetscCall(VecPointwiseMax(auglag->Ciwork, auglag->Ci, auglag->Ciwork));
553       PetscCall(VecNorm(auglag->Ciwork, NORM_INFINITY, &auglag->cinorm));
554     }
555     auglag->cnorm_old = auglag->cnorm;
556     auglag->cnorm     = PetscMax(auglag->cenorm, auglag->cinorm);
557     auglag->ytol      = auglag->ytol0 * auglag->cnorm_old;
558   } else {
559     PetscCall(VecNorm(auglag->LgradX, NORM_2, &auglag->gnorm));
560     PetscCall(VecNorm(auglag->C, NORM_2, &auglag->cnorm));
561   }
562   PetscFunctionReturn(PETSC_SUCCESS);
563 }
564 
565 static PetscErrorCode TaoALMMEvaluateIterate_Private(Tao tao)
566 {
567   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
568 
569   PetscFunctionBegin;
570   /* split solution into primal and slack components */
571   PetscCall(TaoALMMSplitPrimal_Private(tao, auglag->P, auglag->Px, auglag->Ps));
572 
573   /* compute f, df/dx and the constraints */
574   PetscCall(TaoComputeObjectiveAndGradient(tao, auglag->Px, &auglag->fval, auglag->LgradX));
575   if (tao->eq_constrained) {
576     PetscCall(TaoComputeEqualityConstraints(tao, auglag->Px, auglag->Ce));
577     PetscCall(TaoComputeJacobianEquality(tao, auglag->Px, auglag->Ae, auglag->Ae));
578   }
579   if (tao->ineq_constrained) {
580     PetscCall(TaoComputeInequalityConstraints(tao, auglag->Px, auglag->Ci));
581     PetscCall(TaoComputeJacobianInequality(tao, auglag->Px, auglag->Ai, auglag->Ai));
582     switch (auglag->type) {
583     case TAO_ALMM_CLASSIC:
584       /* classic formulation converts inequality to equality constraints via slack variables */
585       PetscCall(VecAXPY(auglag->Ci, -1.0, auglag->Ps));
586       break;
587     case TAO_ALMM_PHR:
588       /* PHR is based on Ci <= 0 while TAO defines Ci >= 0 so we hit it with a negative sign */
589       PetscCall(VecScale(auglag->Ci, -1.0));
590       PetscCall(MatScale(auglag->Ai, -1.0));
591       break;
592     default:
593       break;
594     }
595   }
596   /* combine constraints into one vector */
597   PetscCall(TaoALMMCombineDual_Private(tao, auglag->Ce, auglag->Ci, auglag->C));
598   PetscFunctionReturn(PETSC_SUCCESS);
599 }
600 
601 /*
602 Lphr = f + 0.5*mu*[ (Ce + Ye/mu)^T (Ce + Ye/mu) + pmin(0, Ci + Yi/mu)^T pmin(0, Ci + Yi/mu)]
603 
604 dLphr/dX = dF/dX + mu*[ (Ce + Ye/mu)^T Ae + pmin(0, Ci + Yi/mu)^T Ai]
605 
606 dLphr/dS = 0
607 */
608 static PetscErrorCode TaoALMMComputePHRLagAndGradient_Private(Tao tao)
609 {
610   TAO_ALMM *auglag  = (TAO_ALMM *)tao->data;
611   PetscReal eq_norm = 0.0, ineq_norm = 0.0;
612 
613   PetscFunctionBegin;
614   PetscCall(TaoALMMEvaluateIterate_Private(tao));
615   if (tao->eq_constrained) {
616     /* Ce_work = mu*(Ce + Ye/mu) */
617     PetscCall(VecWAXPY(auglag->Cework, 1.0 / auglag->mu, auglag->Ye, auglag->Ce));
618     PetscCall(VecDot(auglag->Cework, auglag->Cework, &eq_norm)); /* contribution to scalar Lagrangian */
619     PetscCall(VecScale(auglag->Cework, auglag->mu));
620     /* dL/dX += mu*(Ce + Ye/mu)^T Ae */
621     PetscCall(MatMultTransposeAdd(auglag->Ae, auglag->Cework, auglag->LgradX, auglag->LgradX));
622   }
623   if (tao->ineq_constrained) {
624     /* Ci_work = mu * pmax(0, Ci + Yi/mu) where pmax() is pointwise max() */
625     PetscCall(VecWAXPY(auglag->Ciwork, 1.0 / auglag->mu, auglag->Yi, auglag->Ci));
626     PetscCall(VecPointwiseMax(auglag->Ciwork, auglag->Cizero, auglag->Ciwork));
627     PetscCall(VecDot(auglag->Ciwork, auglag->Ciwork, &ineq_norm)); /* contribution to scalar Lagrangian */
628     /* dL/dX += mu * pmax(0, Ci + Yi/mu)^T Ai */
629     PetscCall(VecScale(auglag->Ciwork, auglag->mu));
630     PetscCall(MatMultTransposeAdd(auglag->Ai, auglag->Ciwork, auglag->LgradX, auglag->LgradX));
631     /* dL/dS = 0 because there are no slacks in PHR */
632     PetscCall(VecZeroEntries(auglag->LgradS));
633   }
634   /* combine gradient together */
635   PetscCall(TaoALMMCombinePrimal_Private(tao, auglag->LgradX, auglag->LgradS, auglag->G));
636   /* compute L = f + 0.5 * mu * [(Ce + Ye/mu)^T (Ce + Ye/mu) + pmax(0, Ci + Yi/mu)^T pmax(0, Ci + Yi/mu)] */
637   auglag->Lval = auglag->fval + 0.5 * auglag->mu * (eq_norm + ineq_norm);
638   PetscFunctionReturn(PETSC_SUCCESS);
639 }
640 
641 /*
642 Lc = F + Ye^TCe + Yi^T(Ci - S) + 0.5*mu*[Ce^TCe + (Ci - S)^T(Ci - S)]
643 
644 dLc/dX = dF/dX + Ye^TAe + Yi^TAi + 0.5*mu*[Ce^TAe + (Ci - S)^TAi]
645 
646 dLc/dS = -[Yi + mu*(Ci - S)]
647 */
648 static PetscErrorCode TaoALMMComputeAugLagAndGradient_Private(Tao tao)
649 {
650   TAO_ALMM *auglag = (TAO_ALMM *)tao->data;
651   PetscReal yeTce = 0.0, yiTcims = 0.0, ceTce = 0.0, cimsTcims = 0.0;
652 
653   PetscFunctionBegin;
654   PetscCall(TaoALMMEvaluateIterate_Private(tao));
655   if (tao->eq_constrained) {
656     /* compute scalar contributions */
657     PetscCall(VecDot(auglag->Ye, auglag->Ce, &yeTce));
658     PetscCall(VecDot(auglag->Ce, auglag->Ce, &ceTce));
659     /* dL/dX += ye^T Ae */
660     PetscCall(MatMultTransposeAdd(auglag->Ae, auglag->Ye, auglag->LgradX, auglag->LgradX));
661     /* dL/dX += 0.5 * mu * ce^T Ae */
662     PetscCall(MatMultTranspose(auglag->Ae, auglag->Ce, auglag->Xwork));
663     PetscCall(VecAXPY(auglag->LgradX, 0.5 * auglag->mu, auglag->Xwork));
664   }
665   if (tao->ineq_constrained) {
666     /* compute scalar contributions */
667     PetscCall(VecDot(auglag->Yi, auglag->Ci, &yiTcims));
668     PetscCall(VecDot(auglag->Ci, auglag->Ci, &cimsTcims));
669     /* dL/dX += yi^T Ai */
670     PetscCall(MatMultTransposeAdd(auglag->Ai, auglag->Yi, auglag->LgradX, auglag->LgradX));
671     /* dL/dX += 0.5 * mu * (ci - s)^T Ai */
672     PetscCall(MatMultTranspose(auglag->Ai, auglag->Ci, auglag->Xwork));
673     PetscCall(VecAXPY(auglag->LgradX, 0.5 * auglag->mu, auglag->Xwork));
674     /* dL/dS = -[yi + mu*(ci - s)] */
675     PetscCall(VecWAXPY(auglag->LgradS, auglag->mu, auglag->Ci, auglag->Yi));
676     PetscCall(VecScale(auglag->LgradS, -1.0));
677   }
678   /* combine gradient together */
679   PetscCall(TaoALMMCombinePrimal_Private(tao, auglag->LgradX, auglag->LgradS, auglag->G));
680   /* compute L = f + ye^T ce + yi^T (ci - s) + 0.5*mu*||ce||^2 + 0.5*mu*||ci - s||^2 */
681   auglag->Lval = auglag->fval + yeTce + yiTcims + 0.5 * auglag->mu * (ceTce + cimsTcims);
682   PetscFunctionReturn(PETSC_SUCCESS);
683 }
684 
685 PetscErrorCode TaoALMMSubsolverObjective_Private(Tao tao, Vec P, PetscReal *Lval, void *ctx)
686 {
687   TAO_ALMM *auglag = (TAO_ALMM *)ctx;
688 
689   PetscFunctionBegin;
690   PetscCall(VecCopy(P, auglag->P));
691   PetscCall((*auglag->sub_obj)(auglag->parent));
692   *Lval = auglag->Lval;
693   PetscFunctionReturn(PETSC_SUCCESS);
694 }
695 
696 PetscErrorCode TaoALMMSubsolverObjectiveAndGradient_Private(Tao tao, Vec P, PetscReal *Lval, Vec G, void *ctx)
697 {
698   TAO_ALMM *auglag = (TAO_ALMM *)ctx;
699 
700   PetscFunctionBegin;
701   PetscCall(VecCopy(P, auglag->P));
702   PetscCall((*auglag->sub_obj)(auglag->parent));
703   PetscCall(VecCopy(auglag->G, G));
704   *Lval = auglag->Lval;
705   PetscFunctionReturn(PETSC_SUCCESS);
706 }
707