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