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