1 /* _______________________________________________________________________
2
3 DAKOTA: Design Analysis Kit for Optimization and Terascale Applications
4 Copyright 2014-2020 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
5 This software is distributed under the GNU Lesser General Public License.
6 For more information, see the README file in the top Dakota directory.
7 _______________________________________________________________________ */
8
9 //- Class: SurrBasedMinimizer
10 //- Description: Implementation code for the SurrBasedMinimizer class
11 //- Owner: Mike Eldred, Sandia National Laboratories
12 //- Checked by:
13
14 #include "SurrBasedMinimizer.hpp"
15 #include "SurrBasedLevelData.hpp"
16 #include "ProblemDescDB.hpp"
17 #include "ParallelLibrary.hpp"
18 #include "ParamResponsePair.hpp"
19 #include "PRPMultiIndex.hpp"
20 #include "dakota_data_io.hpp"
21
22 static const char rcsId[]="@(#) $Id: SurrBasedMinimizer.cpp 4718 2007-11-15 21:44:58Z wjbohnh $";
23
24 //#define DEBUG
25
26
27 extern "C" {
28
29 #define NNLS_F77 F77_FUNC(nnls,NNLS)
30 void NNLS_F77( double* a, int& mda, int& m, int& n, double* b, double* x,
31 double& rnorm, double* w, double* zz, int* index, int& mode );
32
33 #ifdef DAKOTA_F90
34 #if defined(HAVE_CONFIG_H) && !defined(DISABLE_DAKOTA_CONFIG_H)
35
36 // Deprecated; continue to support legacy, clashing macros for ONE RELEASE
37 #define BVLS_WRAPPER_FC FC_FUNC_(bvls_wrapper,BVLS_WRAPPER)
38 void BVLS_WRAPPER_FC( Dakota::Real* a, int& m, int& n, Dakota::Real* b,
39 Dakota::Real* bnd, Dakota::Real* x, Dakota::Real& rnorm,
40 int& nsetp, Dakota::Real* w, int* index, int& ierr );
41 #else
42
43 // Use the CMake-generated fortran name mangling macros (eliminate warnings)
44 #include "dak_f90_config.h"
45 #define BVLS_WRAPPER_FC DAK_F90_GLOBAL_(bvls_wrapper,BVLS_WRAPPER)
46 void BVLS_WRAPPER_FC( Dakota::Real* a, int& m, int& n, Dakota::Real* b,
47 Dakota::Real* bnd, Dakota::Real* x, Dakota::Real& rnorm,
48 int& nsetp, Dakota::Real* w, int* index, int& ierr );
49 #endif // HAVE_CONFIG_H and !DISABLE_DAKOTA_CONFIG_H
50 #endif // DAKOTA_F90
51
52 }
53
54
55 using namespace std;
56
57 namespace Dakota {
58 extern PRPCache data_pairs; // global container
59
SurrBasedMinimizer(ProblemDescDB & problem_db,Model & model,std::shared_ptr<TraitsBase> traits)60 SurrBasedMinimizer::SurrBasedMinimizer(ProblemDescDB& problem_db, Model& model, std::shared_ptr<TraitsBase> traits):
61 Minimizer(problem_db, model, traits), globalIterCount(0),
62 // See Conn, Gould, and Toint, pp. 598-599
63 penaltyParameter(5.), eta(1.), alphaEta(0.1), betaEta(0.9),
64 etaSequence(eta*std::pow(2.*penaltyParameter, -alphaEta))
65 {
66 if (model.primary_fn_type() == OBJECTIVE_FNS)
67 optimizationFlag = true;
68 else if (model.primary_fn_type() == CALIB_TERMS)
69 optimizationFlag = false;
70 else {
71 Cerr << "Error: unsupported response type specification in "
72 << "SurrBasedMinimizer constructor." << std::endl;
73 abort_handler(-1);
74 }
75
76 // initialize attributes for merit function calculations
77 origNonlinIneqLowerBnds
78 = iteratedModel.nonlinear_ineq_constraint_lower_bounds();
79 origNonlinIneqUpperBnds
80 = iteratedModel.nonlinear_ineq_constraint_upper_bounds();
81 origNonlinEqTargets = iteratedModel.nonlinear_eq_constraint_targets();
82
83 // Verify that global bounds are available (some Constraints types can
84 // return empty vectors) and are not set to the +/- infinity defaults (TR
85 // size is relative to the global bounded region).
86 const RealVector& lower_bnds = iteratedModel.continuous_lower_bounds();
87 const RealVector& upper_bnds = iteratedModel.continuous_upper_bounds();
88 if (lower_bnds.length() != numContinuousVars ||
89 upper_bnds.length() != numContinuousVars) {
90 Cerr << "\nError: mismatch in length of variable bounds array in "
91 << "SurrBasedMinimizer." << std::endl;
92 abort_handler(-1);
93 }
94 for (size_t i=0; i<numContinuousVars; i++)
95 if (lower_bnds[i] <= -bigRealBoundSize ||
96 upper_bnds[i] >= bigRealBoundSize) {
97 Cerr << "\nError: variable bounds are required in SurrBasedMinimizer."
98 << std::endl;
99 abort_handler(-1);
100 }
101 }
102
103
~SurrBasedMinimizer()104 SurrBasedMinimizer::~SurrBasedMinimizer()
105 { }
106
107
derived_init_communicators(ParLevLIter pl_iter)108 void SurrBasedMinimizer::derived_init_communicators(ParLevLIter pl_iter)
109 {
110 // iteratedModel is evaluated to add truth data (single evaluate())
111 iteratedModel.init_communicators(pl_iter, maxEvalConcurrency);
112
113 // Allocate comms in approxSubProbModel/iteratedModel for parallel SBM.
114 // For DataFitSurrModel, concurrency is from daceIterator evals (global) or
115 // numerical derivs (local/multipt) on actualModel. For HierarchSurrModel,
116 // concurrency is from approxSubProbMinimizer on lowFidelityModel.
117 // As for constructors, we recursively set and restore DB list nodes
118 // (initiated from the restored starting point following construction).
119 size_t method_index = probDescDB.get_db_method_node(),
120 model_index = probDescDB.get_db_model_node(); // for restoration
121 // As in SurrBasedLocalMinimizer::initialize_sub_minimizer(), the SBLM
122 // model_pointer is relevant and any sub-method model_pointer is ignored
123 probDescDB.set_db_method_node(approxSubProbMinimizer.method_id());
124 probDescDB.set_db_model_nodes(iteratedModel.model_id());
125 approxSubProbMinimizer.init_communicators(pl_iter);
126 probDescDB.set_db_method_node(method_index); // restore method only
127 probDescDB.set_db_model_nodes(model_index); // restore all model nodes
128 }
129
130
derived_set_communicators(ParLevLIter pl_iter)131 void SurrBasedMinimizer::derived_set_communicators(ParLevLIter pl_iter)
132 {
133 miPLIndex = methodPCIter->mi_parallel_level_index(pl_iter);
134
135 // iteratedModel is evaluated to add truth data (single evaluate())
136 iteratedModel.set_communicators(pl_iter, maxEvalConcurrency);
137
138 // set communicators for approxSubProbModel/iteratedModel
139 approxSubProbMinimizer.set_communicators(pl_iter);
140 }
141
142
derived_free_communicators(ParLevLIter pl_iter)143 void SurrBasedMinimizer::derived_free_communicators(ParLevLIter pl_iter)
144 {
145 // free communicators for approxSubProbModel/iteratedModel
146 approxSubProbMinimizer.free_communicators(pl_iter);
147
148 // iteratedModel is evaluated to add truth data (single evaluate())
149 iteratedModel.free_communicators(pl_iter, maxEvalConcurrency);
150 }
151
152
153 /** For the Rockafellar augmented Lagrangian, simple Lagrange multiplier
154 updates are available which do not require the active constraint
155 gradients. For the basic Lagrangian, Lagrange multipliers are estimated
156 through solution of a nonnegative linear least squares problem. */
157 void SurrBasedMinimizer::
update_lagrange_multipliers(const RealVector & fn_vals,const RealMatrix & fn_grads,SurrBasedLevelData & tr_data)158 update_lagrange_multipliers(const RealVector& fn_vals,
159 const RealMatrix& fn_grads, SurrBasedLevelData& tr_data)
160 {
161 // solve nonnegative linear least squares [A]{lambda} = -{grad_f} for
162 // lambda where A = gradient matrix for active/violated constraints.
163
164 // identify active inequality constraints (equalities always active)
165 size_t i, j, n = 0, cntr = 0, num_free_continuous_vars;
166 IntList active_lag_ineq, lag_index;
167 for (j=0; j<numNonlinearIneqConstraints; j++) {
168 int ineq_id = j+1; // can't use +/- 0
169 Real g = fn_vals[numUserPrimaryFns+j], l_bnd = origNonlinIneqLowerBnds[j],
170 u_bnd = origNonlinIneqUpperBnds[j];
171 // check for active, not violated --> apply constraintTol on feasible side
172 // g < l_bnd + constraintTol, g > u_bnd - constraintTol
173 if (l_bnd > -bigRealBoundSize) { // g has a lower bound
174 if (g < l_bnd + constraintTol) {
175 active_lag_ineq.push_back(-ineq_id);
176 lag_index.push_back(cntr);
177 }
178 ++cntr;
179 }
180 if (u_bnd < bigRealBoundSize) { // g has an upper bound
181 if (g > u_bnd - constraintTol) {
182 active_lag_ineq.push_back(ineq_id);
183 lag_index.push_back(cntr);
184 }
185 ++cntr;
186 }
187 }
188
189 // if there are active constraints, estimate the Lagrange multipliers
190 size_t num_active_lag_ineq = active_lag_ineq.size(),
191 num_active_lag = num_active_lag_ineq + numNonlinearEqConstraints;
192 lagrangeMult = 0.;
193 if (num_active_lag) {
194 RealVector m_grad_f;
195 const BoolDeque& sense = iteratedModel.primary_response_fn_sense();
196 const RealVector& wts = iteratedModel.primary_response_fn_weights();
197 const RealVector& lower_bnds = iteratedModel.continuous_lower_bounds();
198 const RealVector& upper_bnds = iteratedModel.continuous_upper_bounds();
199 objective_gradient(fn_vals, fn_grads, sense, wts, m_grad_f);
200
201 RealVector A(num_active_lag*numContinuousVars);
202 ILIter iter;
203 const RealVector& c_vars = tr_data.c_vars_center();
204 for (i=0; i<numContinuousVars; i++) {
205 Real c_var = c_vars[i], l_bnd = lower_bnds[i], u_bnd = upper_bnds[i];
206 // Determine if the calculated gradient component dg/dx_i is directed into
207 // an active bound constraint.
208 Cout << "c_var=" << c_var << "\n";
209 bool active_lower_bnd = ( (l_bnd == 0.0 && std::fabs(c_var) < 1.e-10) ||
210 (l_bnd != 0.0 && std::fabs(1.0 - c_var/l_bnd) < 1.e-10) );
211 bool active_upper_bnd = ( (u_bnd == 0.0 && std::fabs(c_var) < 1.e-10) ||
212 (u_bnd != 0.0 && std::fabs(1.0 - c_var/u_bnd) < 1.e-10) );
213 if ( !( (active_lower_bnd && m_grad_f[i] > 0.0) ||
214 (active_upper_bnd && m_grad_f[i] < 0.0) ) ) {
215 Cout << "active variable, i=" << i << ", n=" << n << "\n";
216 for (j=0, iter=active_lag_ineq.begin(); j<num_active_lag_ineq; j++, iter++){
217 int ineq_id = *iter;
218 size_t index = numUserPrimaryFns + std::abs(ineq_id) - 1;
219 const Real* grad_g = fn_grads[index];
220 // form [A]
221 Cout << "constraint, j=" << j << "\n";
222 A[j+n*num_active_lag] = (ineq_id > 0) ? grad_g[i] : -grad_g[i];
223 }
224 for (j=0; j<numNonlinearEqConstraints; j++) {
225 const Real* grad_h = fn_grads[numUserPrimaryFns+numNonlinearIneqConstraints+j];
226 A[j+num_active_lag_ineq+n*num_active_lag] = grad_h[i];
227 }
228 // form -{grad_f}
229 m_grad_f[n] = -m_grad_f[n];
230 ++n;
231 }
232 }
233 num_free_continuous_vars = n;
234 #ifdef DEBUG
235 Cout << "number of free variables, n = " << num_free_continuous_vars << "\n";
236 Cout << "[A]:\n" << A << "-{grad_f}:\n" << m_grad_f;
237 #endif
238
239 // solve bound-constrained least squares using Lawson & Hanson routines:
240 // > if inequality-constrained, use non-negative least squares (NNLS)
241 // > if equality-constrained, use bound-constrained least squares (BVLS)
242 RealVector lambda(num_active_lag), w(num_active_lag);
243 IntVector index(num_active_lag);
244 double res_norm;
245 int m = num_free_continuous_vars, n = num_active_lag;
246 if (numNonlinearEqConstraints) {
247 #ifdef DAKOTA_F90
248 int nsetp, ierr;
249 RealVector bnd(2*num_active_lag); // bounds on lambda
250 // lawson_hanson2.f90: BVLS ignore bounds based on huge(), so +/-DBL_MAX
251 // is sufficient here
252 for (i=0; i<num_active_lag; ++i) {
253 bnd[i*2] = (i<num_active_lag_ineq) ? 0. : -DBL_MAX; // lower bound
254 bnd[i*2+1] = DBL_MAX; // upper bound
255 }
256 BVLS_WRAPPER_FC( A.values(), m, n, m_grad_f.values(), bnd.values(),
257 lambda.values(), res_norm, nsetp, w.values(),
258 index.values(), ierr );
259 if (ierr) {
260 Cerr << "\nError: BVLS failed in update_lagrange_multipliers()."
261 << std::endl;
262 abort_handler(-1);
263 }
264 #endif // DAKOTA_F90
265 }
266 else {
267 int mda = numContinuousVars, mode;
268 RealVector zz(numContinuousVars);
269 NNLS_F77( A.values(), mda, m, n, m_grad_f.values(), lambda.values(),
270 res_norm, w.values(), zz.values(), index.values(), mode );
271 if (mode != 1) {
272 Cerr << "\nError: NNLS failed in update_lagrange_multipliers()."
273 << std::endl;
274 abort_handler(-1);
275 }
276 }
277 // Cout << "{lambda}:\n" << lambda << "res_norm: " << res_norm << '\n';
278
279 // update lagrangeMult from least squares solution
280 cntr = 0;
281 for (iter=lag_index.begin(); iter!=lag_index.end(); ++iter)
282 lagrangeMult[*iter] = lambda[cntr++];
283 }
284
285 #ifdef DEBUG
286 Cout << "Lagrange multipliers updated:\n" << lagrangeMult << '\n';
287 #endif
288 }
289
290
291 /** For the Rockafellar augmented Lagrangian, simple Lagrange multiplier
292 updates are available which do not require the active constraint
293 gradients. For the basic Lagrangian, Lagrange multipliers are estimated
294 through solution of a nonnegative linear least squares problem. */
295 void SurrBasedMinimizer::
update_augmented_lagrange_multipliers(const RealVector & fn_vals)296 update_augmented_lagrange_multipliers(const RealVector& fn_vals)
297 {
298 size_t i, j, cntr = 0;
299 // The Rockafellar augmented Lagrangian has simple and explicit multiplier
300 // updates. augLagrangeMult is an "extended" multiplier vector in this case
301 // and the update formulas are applied even for inactive constraints.
302 for (i=0; i<numNonlinearIneqConstraints; i++) {
303 Real g = fn_vals[numUserPrimaryFns+i], g0, psi,
304 l_bnd = origNonlinIneqLowerBnds[i], u_bnd = origNonlinIneqUpperBnds[i];
305 if (l_bnd > -bigRealBoundSize) { // g has a lower bound
306 g0 = l_bnd - g; // convert l <= g to l - g <= 0
307 psi = std::max(g0, -augLagrangeMult[cntr]/2./penaltyParameter);
308 augLagrangeMult[cntr++] += 2.*penaltyParameter*psi;
309 }
310 if (u_bnd < bigRealBoundSize) { // g has an upper bound
311 g0 = g - u_bnd; // convert g <= u to g - u <= 0
312 psi = std::max(g0, -augLagrangeMult[cntr]/2./penaltyParameter);
313 augLagrangeMult[cntr++] += 2.*penaltyParameter*psi;
314 }
315 }
316 for (i=0; i<numNonlinearEqConstraints; i++) {
317 // convert to h0 = 0
318 Real h0 = fn_vals[numUserPrimaryFns+numNonlinearIneqConstraints+i]
319 - origNonlinEqTargets[i];
320 augLagrangeMult[cntr++] += 2.*penaltyParameter*h0;
321 }
322
323 // New logic follows Conn, Gould, and Toint, section 14.4, Step 2.
324 // penaltyParameter could be increased, but is not (see CGT)
325 Real mu = 1./2./penaltyParameter; // conversion between r_p and mu penalties
326 etaSequence *= std::pow(mu, betaEta);
327
328 #ifdef DEBUG
329 Cout << "Augmented Lagrange multipliers updated:\n" << augLagrangeMult
330 << "\neta updated: " << etaSequence << '\n';
331 #endif
332 }
333
334
335 void SurrBasedMinimizer::
initialize_filter(SurrBasedLevelData & tr_data,const RealVector & fn_vals)336 initialize_filter(SurrBasedLevelData& tr_data, const RealVector& fn_vals)
337 {
338 Real new_f = objective(fn_vals, iteratedModel.primary_response_fn_sense(),
339 iteratedModel.primary_response_fn_weights());
340 Real new_g = (numNonlinearConstraints)
341 ? constraint_violation(fn_vals, 0.) : 0.;
342 tr_data.initialize_filter(new_f, new_g);
343 }
344
345
346 /** Update the paretoFilter with fn_vals if new iterate is non-dominated. */
347 bool SurrBasedMinimizer::
update_filter(SurrBasedLevelData & tr_data,const RealVector & fn_vals)348 update_filter(SurrBasedLevelData& tr_data, const RealVector& fn_vals)
349 {
350 Real new_f = objective(fn_vals, iteratedModel.primary_response_fn_sense(),
351 iteratedModel.primary_response_fn_weights());
352 return (numNonlinearConstraints) ?
353 tr_data.update_filter(new_f, constraint_violation(fn_vals, 0.)) :
354 tr_data.update_filter(new_f);
355 }
356
357
358 /* Return a filter-based merit function. As a first cut, use the area
359 swept out from the two points only.
360 Real SurrBasedMinimizer::
361 filter_merit(const RealVector& fns_center, const RealVector& fns_star)
362 {
363 Real obj_delta = objective(fns_star, sense, wts)
364 - objective(fns_center, sense, wts),
365 cv_delta = constraint_violation(fns_star, 0.)
366 - constraint_violation(fns_center, 0.);
367
368 // This filter merit can be positive or negative. The sign is not critical,
369 // but the sign of the ratio is. If one delta is zero, return the other.
370 // *** TO DO: approx/truth must be in synch!!
371 // *** TO DO: handle unconstrained and feasible cases properly!
372 if (fabs(obj_delta) < DBL_MIN)
373 return cv_delta;
374 else if (std::fabs(cv_delta) < DBL_MIN)
375 return obj_delta;
376 else
377 return obj_delta * cv_delta;
378 }
379 */
380
381
382 /** The Lagrangian function computation sums the objective function
383 and the Lagrange multipler terms for inequality/equality
384 constraints. This implementation follows the convention in
385 Vanderplaats with g<=0 and h=0. The bounds/targets passed in may
386 reflect the original constraints or the relaxed constraints. */
387 Real SurrBasedMinimizer::
lagrangian_merit(const RealVector & fn_vals,const BoolDeque & sense,const RealVector & primary_wts,const RealVector & nln_ineq_l_bnds,const RealVector & nln_ineq_u_bnds,const RealVector & nln_eq_tgts)388 lagrangian_merit(const RealVector& fn_vals, const BoolDeque& sense,
389 const RealVector& primary_wts,
390 const RealVector& nln_ineq_l_bnds,
391 const RealVector& nln_ineq_u_bnds,
392 const RealVector& nln_eq_tgts)
393 {
394 size_t i, cntr = 0;
395
396 // objective function portion
397 Real lag = objective(fn_vals, sense, primary_wts);
398
399 // inequality constraint portion
400 Real g0;
401 for (i=0; i<numNonlinearIneqConstraints; i++) {
402 // check for active, not violated --> apply constraintTol on feasible side
403 // g < l_bnd + constraintTol, g > u_bnd - constraintTol
404 // Note: if original bounds/targets, lagrangeMult will be 0 for inactive
405 const Real& g = fn_vals[numUserPrimaryFns+i];
406 const Real& l_bnd = nln_ineq_l_bnds[i];
407 const Real& u_bnd = nln_ineq_u_bnds[i];
408 if (l_bnd > -bigRealBoundSize) { // g has a lower bound
409 g0 = l_bnd - g; // convert l <= g to l - g <= 0
410 if (g0 + constraintTol > 0.) // g is active
411 lag += lagrangeMult[cntr]*g0;
412 ++cntr;
413 }
414 if (u_bnd < bigRealBoundSize) { // g has an upper bound
415 g0 = g - u_bnd; // convert g <= u to g - u <= 0
416 if (g0 + constraintTol > 0.) // g is active
417 lag += lagrangeMult[cntr]*g0;
418 ++cntr;
419 }
420 }
421
422 // equality constraint portion
423 for (i=0; i<numNonlinearEqConstraints; i++) {
424 // convert to h0 = 0
425 Real h0 = fn_vals[numUserPrimaryFns+numNonlinearIneqConstraints+i]
426 - nln_eq_tgts[i];
427 lag += lagrangeMult[cntr++]*h0;
428 }
429 return lag;
430 }
431
432
433 void SurrBasedMinimizer::
lagrangian_gradient(const RealVector & fn_vals,const RealMatrix & fn_grads,const BoolDeque & sense,const RealVector & primary_wts,const RealVector & nln_ineq_l_bnds,const RealVector & nln_ineq_u_bnds,const RealVector & nln_eq_tgts,RealVector & lag_grad)434 lagrangian_gradient(const RealVector& fn_vals, const RealMatrix& fn_grads,
435 const BoolDeque& sense, const RealVector& primary_wts,
436 const RealVector& nln_ineq_l_bnds,
437 const RealVector& nln_ineq_u_bnds,
438 const RealVector& nln_eq_tgts, RealVector& lag_grad)
439 {
440 size_t i, j, cntr = 0;
441
442 // objective function portion
443 objective_gradient(fn_vals, fn_grads, sense, primary_wts, lag_grad);
444
445 // inequality constraint portion
446 for (i=0; i<numNonlinearIneqConstraints; i++) {
447 // check for active, not violated --> apply constraintTol on feasible side
448 // g < l_bnd + constraintTol, g > u_bnd - constraintTol
449 // Note: if original bounds/targets, lagrangeMult will be 0 for inactive
450 const Real& g = fn_vals[numUserPrimaryFns+i];
451 const Real* grad_g = fn_grads[numUserPrimaryFns+i];
452 const Real& l_bnd = nln_ineq_l_bnds[i];
453 const Real& u_bnd = nln_ineq_u_bnds[i];
454 if (l_bnd > -bigRealBoundSize) { // g has a lower bound
455 if (g < l_bnd + constraintTol) // g is active
456 for (j=0; j<numContinuousVars; j++) // l - g <= 0 -> grad g0 = -grad g
457 lag_grad[j] -= lagrangeMult[cntr] * grad_g[j];
458 ++cntr;
459 }
460 if (u_bnd < bigRealBoundSize) { // g has an upper bound
461 if (g > u_bnd - constraintTol) // g is active
462 for (j=0; j<numContinuousVars; j++) // g - u <= 0 -> grad g0 = +grad g
463 lag_grad[j] += lagrangeMult[cntr] * grad_g[j];
464 ++cntr;
465 }
466 }
467
468 // equality constraint portion
469 for (i=0; i<numNonlinearEqConstraints; i++) {
470 const Real* grad_h
471 = fn_grads[numUserPrimaryFns+numNonlinearIneqConstraints+i];
472 for (j=0; j<numContinuousVars; j++)
473 lag_grad[j] += lagrangeMult[cntr] * grad_h[j];
474 ++cntr;
475 }
476 }
477
478
479 void SurrBasedMinimizer::
lagrangian_hessian(const RealVector & fn_vals,const RealMatrix & fn_grads,const RealSymMatrixArray & fn_hessians,const BoolDeque & sense,const RealVector & primary_wts,const RealVector & nln_ineq_l_bnds,const RealVector & nln_ineq_u_bnds,const RealVector & nln_eq_tgts,RealSymMatrix & lag_hess)480 lagrangian_hessian(const RealVector& fn_vals, const RealMatrix& fn_grads,
481 const RealSymMatrixArray& fn_hessians,
482 const BoolDeque& sense, const RealVector& primary_wts,
483 const RealVector& nln_ineq_l_bnds,
484 const RealVector& nln_ineq_u_bnds,
485 const RealVector& nln_eq_tgts, RealSymMatrix& lag_hess)
486 {
487 size_t i, j, k, index, cntr = 0;
488
489 // objective function portion
490 objective_hessian(fn_vals, fn_grads, fn_hessians, sense, primary_wts,
491 lag_hess);
492
493 // inequality constraint portion
494 for (i=0; i<numNonlinearIneqConstraints; i++) {
495 // check for active, not violated --> apply constraintTol on feasible side
496 // g < l_bnd + constraintTol, g > u_bnd - constraintTol
497 // Note: if original bounds/targets, lagrangeMult will be 0 for inactive
498 index = i + numUserPrimaryFns;
499 const Real& g = fn_vals[index];
500 const RealSymMatrix& hess_g = fn_hessians[index];
501 const Real& l_bnd = nln_ineq_l_bnds[i];
502 const Real& u_bnd = nln_ineq_u_bnds[i];
503 if (l_bnd > -bigRealBoundSize) { // g has a lower bound
504 if (g < l_bnd + constraintTol) // g is active
505 for (j=0; j<numContinuousVars; j++) // l - g <= 0 -> hess g0 = -hess g
506 for (k=0; k<=j; ++k)
507 lag_hess(j,k) -= lagrangeMult[cntr] * hess_g(j,k);
508 ++cntr;
509 }
510 if (u_bnd < bigRealBoundSize) { // g has an upper bound
511 if (g > u_bnd - constraintTol) // g is active
512 for (j=0; j<numContinuousVars; j++) // g - u <= 0 -> hess g0 = +hess g
513 for (k=0; k<=j; ++k)
514 lag_hess(j,k) += lagrangeMult[cntr] * hess_g(j,k);
515 ++cntr;
516 }
517 }
518
519 // equality constraint portion
520 for (i=0; i<numNonlinearEqConstraints; i++) {
521 index = i + numUserPrimaryFns + numNonlinearIneqConstraints;
522 const RealSymMatrix& hess_h = fn_hessians[index];
523 for (j=0; j<numContinuousVars; j++)
524 for (k=0; k<=j; ++k)
525 lag_hess(j,k) += lagrangeMult[cntr] * hess_h(j,k);
526 ++cntr;
527 }
528 }
529
530
531 /** The Rockafellar augmented Lagrangian function sums the objective
532 function, Lagrange multipler terms for inequality/equality
533 constraints, and quadratic penalty terms for inequality/equality
534 constraints. This implementation follows the convention in
535 Vanderplaats with g<=0 and h=0. The bounds/targets passed in may
536 reflect the original constraints or the relaxed constraints.*/
537 Real SurrBasedMinimizer::
augmented_lagrangian_merit(const RealVector & fn_vals,const BoolDeque & sense,const RealVector & primary_wts,const RealVector & nln_ineq_l_bnds,const RealVector & nln_ineq_u_bnds,const RealVector & nln_eq_tgts)538 augmented_lagrangian_merit(const RealVector& fn_vals, const BoolDeque& sense,
539 const RealVector& primary_wts,
540 const RealVector& nln_ineq_l_bnds,
541 const RealVector& nln_ineq_u_bnds,
542 const RealVector& nln_eq_tgts)
543 {
544 size_t i, cntr = 0;
545
546 // objective function portion
547 Real aug_lag = objective(fn_vals, sense, primary_wts);
548
549 // inequality constraint portion
550 Real g0, psi;
551 for (i=0; i<numNonlinearIneqConstraints; i++) {
552 // For the Rockafellar augmented Lagrangian, augLagrangeMult is an
553 // "extended" multiplier vector and includes inactive constraints.
554 const Real& g = fn_vals[numUserPrimaryFns+i];
555 const Real& l_bnd = nln_ineq_l_bnds[i];
556 const Real& u_bnd = nln_ineq_u_bnds[i];
557 if (l_bnd > -bigRealBoundSize) { // g has a lower bound
558 g0 = l_bnd - g; // convert l <= g to l - g <= 0
559 psi = std::max(g0, -augLagrangeMult[cntr]/2./penaltyParameter);
560 aug_lag += (augLagrangeMult[cntr++] + penaltyParameter*psi)*psi;
561 }
562 if (u_bnd < bigRealBoundSize) { // g has an upper bound
563 g0 = g - u_bnd; // convert g <= u to g - u <= 0
564 psi = std::max(g0, -augLagrangeMult[cntr]/2./penaltyParameter);
565 aug_lag += (augLagrangeMult[cntr++] + penaltyParameter*psi)*psi;
566 }
567 }
568
569 // equality constraint portion
570 for (i=0; i<numNonlinearEqConstraints; i++) {
571 // convert to h0 = 0
572 Real h0 = fn_vals[numUserPrimaryFns+numNonlinearIneqConstraints+i]
573 - nln_eq_tgts[i];
574 aug_lag += (augLagrangeMult[cntr++] + penaltyParameter*h0)*h0;
575 }
576 return aug_lag;
577 }
578
579
580 void SurrBasedMinimizer::
augmented_lagrangian_gradient(const RealVector & fn_vals,const RealMatrix & fn_grads,const BoolDeque & sense,const RealVector & primary_wts,const RealVector & nln_ineq_l_bnds,const RealVector & nln_ineq_u_bnds,const RealVector & nln_eq_tgts,RealVector & alag_grad)581 augmented_lagrangian_gradient(const RealVector& fn_vals,
582 const RealMatrix& fn_grads,
583 const BoolDeque& sense,
584 const RealVector& primary_wts,
585 const RealVector& nln_ineq_l_bnds,
586 const RealVector& nln_ineq_u_bnds,
587 const RealVector& nln_eq_tgts,
588 RealVector& alag_grad)
589 {
590 size_t i, j, index, cntr = 0;
591
592 // objective function portion
593 objective_gradient(fn_vals, fn_grads, sense, primary_wts, alag_grad);
594
595 // inequality constraint portion
596 Real g0;
597 for (i=0; i<numNonlinearIneqConstraints; i++) {
598 // For the Rockafellar augmented Lagrangian, augLagrangeMult is an
599 // "extended" multiplier vector and includes inactive constraints.
600 index = i + numUserPrimaryFns;
601 const Real& g = fn_vals[index];
602 const Real& l_bnd = nln_ineq_l_bnds[i];
603 const Real& u_bnd = nln_ineq_u_bnds[i];
604 const Real* grad_g = fn_grads[index];
605 if (l_bnd > -bigRealBoundSize) { // g has a lower bound
606 g0 = l_bnd - g; // convert l <= g to l - g <= 0
607 // grad psi = grad g0 if "active", 0 if "inactive"
608 if (g0 >= -augLagrangeMult[cntr]/2./penaltyParameter)
609 for (j=0; j<numContinuousVars; j++)
610 alag_grad[j] -= (augLagrangeMult[cntr] + 2.*penaltyParameter*g0)
611 * grad_g[j]; // l - g <= 0 --> grad g0 = -grad g
612 ++cntr;
613 }
614 if (u_bnd < bigRealBoundSize) { // g has an upper bound
615 g0 = g - u_bnd; // convert g <= u to g - u <= 0
616 // grad psi = grad g0 if "active", 0 if "inactive"
617 if (g0 >= -augLagrangeMult[cntr]/2./penaltyParameter)
618 for (j=0; j<numContinuousVars; j++)
619 alag_grad[j] += (augLagrangeMult[cntr] + 2.*penaltyParameter*g0)
620 * grad_g[j]; // g - u <= 0 --> grad g0 = +grad g
621 ++cntr;
622 }
623 }
624
625 // equality constraint portion
626 for (i=0; i<numNonlinearEqConstraints; i++) {
627 index = i + numUserPrimaryFns + numNonlinearIneqConstraints;
628 Real h0 = fn_vals[index] - nln_eq_tgts[i]; // convert to h0 = 0
629 const Real* grad_h = fn_grads[index];
630 for (j=0; j<numContinuousVars; j++)
631 alag_grad[j] += (augLagrangeMult[cntr] + 2.*penaltyParameter*h0)
632 * grad_h[j];
633 ++cntr;
634 }
635 }
636
637
638 void SurrBasedMinimizer::
augmented_lagrangian_hessian(const RealVector & fn_vals,const RealMatrix & fn_grads,const RealSymMatrixArray & fn_hessians,const BoolDeque & sense,const RealVector & primary_wts,const RealVector & nln_ineq_l_bnds,const RealVector & nln_ineq_u_bnds,const RealVector & nln_eq_tgts,RealSymMatrix & alag_hess)639 augmented_lagrangian_hessian(const RealVector& fn_vals,
640 const RealMatrix& fn_grads,
641 const RealSymMatrixArray& fn_hessians,
642 const BoolDeque& sense,
643 const RealVector& primary_wts,
644 const RealVector& nln_ineq_l_bnds,
645 const RealVector& nln_ineq_u_bnds,
646 const RealVector& nln_eq_tgts,
647 RealSymMatrix& alag_hess)
648 {
649 size_t i, j, k, index, cntr = 0;
650
651 // objective function portion
652 objective_hessian(fn_vals, fn_grads, fn_hessians, sense, primary_wts,
653 alag_hess);
654
655 // inequality constraint portion
656 Real g0;
657 for (i=0; i<numNonlinearIneqConstraints; i++) {
658 // For the Rockafellar augmented Lagrangian, augLagrangeMult is an
659 // "extended" multiplier vector and includes inactive constraints.
660 index = i + numUserPrimaryFns;
661 const Real& g = fn_vals[index];
662 const Real& l_bnd = nln_ineq_l_bnds[i];
663 const Real& u_bnd = nln_ineq_u_bnds[i];
664 const RealSymMatrix& hess_g = fn_hessians[index];
665 if (l_bnd > -bigRealBoundSize) { // g has a lower bound
666 g0 = l_bnd - g; // convert l <= g to l - g <= 0
667 // grad psi = grad g0 if "active", 0 if "inactive"
668 if (g0 >= -augLagrangeMult[cntr]/2./penaltyParameter) {
669 Real term = augLagrangeMult[cntr] + 2.*penaltyParameter*g0;
670 for (j=0; j<numContinuousVars; j++)
671 for (k=0; k<=j; ++k) // l - g <= 0 --> hess g0 = -hess g
672 alag_hess(j,k) -= term * hess_g(j,k);
673 }
674 ++cntr;
675 }
676 if (u_bnd < bigRealBoundSize) { // g has an upper bound
677 g0 = g - u_bnd; // convert g <= u to g - u <= 0
678 // grad psi = grad g0 if "active", 0 if "inactive"
679 if (g0 >= -augLagrangeMult[cntr]/2./penaltyParameter) {
680 Real term = augLagrangeMult[cntr] + 2.*penaltyParameter*g0;
681 for (j=0; j<numContinuousVars; j++)
682 for (k=0; k<=j; ++k) // g - u <= 0 --> hess g0 = +hess g
683 alag_hess(j,k) += term * hess_g(j,k);
684 }
685 ++cntr;
686 }
687 }
688
689 // equality constraint portion
690 for (i=0; i<numNonlinearEqConstraints; i++) {
691 index = i + numUserPrimaryFns + numNonlinearIneqConstraints;
692 const RealSymMatrix& hess_h = fn_hessians[index];
693 Real h0 = fn_vals[index] - nln_eq_tgts[i], // convert to h0 = 0
694 term = augLagrangeMult[cntr] + 2.*penaltyParameter*h0;
695 for (j=0; j<numContinuousVars; j++)
696 for (k=0; k<=j; ++k) // g - u <= 0 --> hess g0 = +hess g
697 alag_hess(j,k) += term * hess_h(j,k);
698 ++cntr;
699 }
700 }
701
702
703 /** The penalty function computation applies a quadratic penalty to
704 any constraint violations and adds this to the objective function(s)
705 p = f + r_p cv. */
706 Real SurrBasedMinimizer::
penalty_merit(const RealVector & fn_vals,const BoolDeque & sense,const RealVector & primary_wts)707 penalty_merit(const RealVector& fn_vals, const BoolDeque& sense,
708 const RealVector& primary_wts)
709 {
710 return objective(fn_vals, sense, primary_wts)
711 + penaltyParameter * constraint_violation(fn_vals, constraintTol);
712 }
713
714
715 void SurrBasedMinimizer::
penalty_gradient(const RealVector & fn_vals,const RealMatrix & fn_grads,const BoolDeque & sense,const RealVector & primary_wts,RealVector & pen_grad)716 penalty_gradient(const RealVector& fn_vals, const RealMatrix& fn_grads,
717 const BoolDeque& sense, const RealVector& primary_wts,
718 RealVector& pen_grad)
719 {
720 size_t i, j, index;
721
722 // objective function portion
723 objective_gradient(fn_vals, fn_grads, sense, primary_wts, pen_grad);
724
725 // inequality constraint portion
726 for (i=0; i<numNonlinearIneqConstraints; i++) {
727 index = i + numUserPrimaryFns;
728 const Real& g = fn_vals[index];
729 const Real& l_bnd = origNonlinIneqLowerBnds[i];
730 const Real& u_bnd = origNonlinIneqUpperBnds[i];
731 const Real* grad_g = fn_grads[index];
732 // Define violation as g0 > c_tol:
733 // g_l - g > c_tol --> grad g0 = -grad g
734 // g - g_u > c_tol --> grad g0 = +grad g
735 if (l_bnd > -bigRealBoundSize) {
736 Real g0_viol = l_bnd - g - constraintTol;
737 if (g0_viol > 0.)
738 for (j=0; j<numContinuousVars; j++)
739 pen_grad[j] -= 2.*penaltyParameter*g0_viol*grad_g[j];
740 }
741 if (u_bnd < bigRealBoundSize) {
742 Real g_viol = g - u_bnd - constraintTol;
743 if (g_viol > 0.)
744 for (j=0; j<numContinuousVars; j++)
745 pen_grad[j] += 2.*penaltyParameter*g_viol*grad_g[j];
746 }
747 }
748
749 // equality constraint portion
750 for (i=0; i<numNonlinearEqConstraints; i++) {
751 index = i + numUserPrimaryFns + numNonlinearIneqConstraints;
752 Real h0 = fn_vals[index] - origNonlinEqTargets[i];
753 const Real* grad_h = fn_grads[index];
754 // Define violation as fabs(h0) > c_tol:
755 // h0 > c_tol --> grad h0 = +grad h
756 // h0 < -c_tol --> grad h0 = +grad h
757 if (h0 > constraintTol) {
758 Real h_viol = h0 - constraintTol;
759 for (j=0; j<numContinuousVars; j++)
760 pen_grad[j] += 2.*penaltyParameter*h_viol*grad_h[j];
761 }
762 else if (h0 < -constraintTol) {
763 Real h_viol = h0 + constraintTol;
764 for (j=0; j<numContinuousVars; j++)
765 pen_grad[j] += 2.*penaltyParameter*h_viol*grad_h[j];
766 }
767 }
768 }
769
770
771 /** Compute the quadratic constraint violation defined as cv = g+^T g+
772 + h+^T h+. This implementation supports equality constraints and
773 2-sided inequalities. The constraint_tol allows for a small
774 constraint infeasibility (used for penalty methods, but not
775 Lagrangian methods). */
776 Real SurrBasedMinimizer::
constraint_violation(const RealVector & fn_vals,const Real & constraint_tol)777 constraint_violation(const RealVector& fn_vals, const Real& constraint_tol)
778 {
779 size_t i;
780 Real constr_viol = 0.0;
781 for (i=0; i<numNonlinearIneqConstraints; i++) { // ineq constraint violations
782 const Real& g = fn_vals[numUserPrimaryFns+i];
783 const Real& l_bnd = origNonlinIneqLowerBnds[i];
784 const Real& u_bnd = origNonlinIneqUpperBnds[i];
785 if (l_bnd > -bigRealBoundSize) { // g has a lower bound
786 Real g0_tol = l_bnd - g - constraint_tol; // l - g <= constraint_tol
787 if (g0_tol > 0.)
788 constr_viol += g0_tol*g0_tol;
789 }
790 if (u_bnd < bigRealBoundSize) { // g has an upper bound
791 Real g0_tol = g - u_bnd - constraint_tol; // g - u <= constraint_tol
792 if (g0_tol > 0.)
793 constr_viol += g0_tol*g0_tol;
794 }
795 }
796 for (i=0; i<numNonlinearEqConstraints; i++) { // eq constraint violations
797 Real abs_h0_tol
798 = std::fabs(fn_vals[numUserPrimaryFns+numNonlinearIneqConstraints+i] -
799 origNonlinEqTargets[i]) - constraint_tol;
800 if (abs_h0_tol > 0.)
801 constr_viol += abs_h0_tol*abs_h0_tol;
802 }
803 return constr_viol;
804 }
805
806
807 /** Redefines default iterator results printing to include
808 optimization results (objective functions and constraints). */
print_results(std::ostream & s,short results_state)809 void SurrBasedMinimizer::print_results(std::ostream& s, short results_state)
810 {
811 size_t i, num_best = bestVariablesArray.size();
812 if (num_best != bestResponseArray.size()) {
813 Cerr << "\nError: mismatch in lengths of bestVariables and bestResponses."
814 << std::endl;
815 abort_handler(-1);
816 }
817
818 const String& interface_id = (methodName == SURROGATE_BASED_LOCAL ||
819 methodName == SURROGATE_BASED_GLOBAL) ?
820 iteratedModel.truth_model().interface_id() : iteratedModel.interface_id();
821 int eval_id;
822 activeSet.request_values(1);
823 // -------------------------------------
824 // Single and Multipoint results summary
825 // -------------------------------------
826 for (i=0; i<num_best; ++i) {
827 // output best variables
828 s << "<<<<< Best parameters ";
829 if (num_best > 1) s << "(set " << i+1 << ") ";
830 s << "=\n" << bestVariablesArray[i];
831 // output best response
832 const RealVector& best_fns = bestResponseArray[i].function_values();
833 if (optimizationFlag) {
834 if (numUserPrimaryFns > 1) s << "<<<<< Best objective functions ";
835 else s << "<<<<< Best objective function ";
836 if (num_best > 1) s << "(set " << i+1 << ") "; s << "=\n";
837 write_data_partial(s, (size_t)0, numUserPrimaryFns, best_fns);
838 }
839 else
840 print_residuals(numUserPrimaryFns, best_fns,
841 RealVector(),
842 num_best, i,
843 s);
844 size_t num_cons = numFunctions - numUserPrimaryFns;
845 if (num_cons) {
846 s << "<<<<< Best constraint values ";
847 if (num_best > 1) s << "(set " << i+1 << ") "; s << "=\n";
848 write_data_partial(s, numUserPrimaryFns, num_cons, best_fns);
849 }
850 // lookup evaluation id where best occurred. This cannot be catalogued
851 // directly because the optimizers track the best iterate internally and
852 // return the best results after iteration completion. Therfore, perform a
853 // search in data_pairs to extract the evalId for the best fn eval.
854 PRPCacheHIter cache_it = lookup_by_val(data_pairs, interface_id,
855 bestVariablesArray[i], activeSet);
856 if (cache_it == data_pairs.get<hashed>().end()) {
857 s << "<<<<< Best data not found in evaluation cache\n\n";
858 eval_id = 0;
859 }
860 else {
861 eval_id = cache_it->eval_id();
862 if (eval_id > 0)
863 s << "<<<<< Best data captured at function evaluation " << eval_id
864 << "\n\n";
865 else // should not occur
866 s << "<<<<< Best data not found in evaluations from current execution,"
867 << "\n but retrieved from restart archive with evaluation id "
868 << -eval_id << "\n\n";
869 }
870 }
871 }
872
873 } // namespace Dakota
874