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:       SurrogateModel
10 //- Description: Implementation code for the SurrogateModel class
11 //- Owner:       Mike Eldred
12 //- Checked by:
13 
14 #include "dakota_system_defs.hpp"
15 #include "SurrogateModel.hpp"
16 #include "ProblemDescDB.hpp"
17 #include "ParallelLibrary.hpp"
18 #include "ParamResponsePair.hpp"
19 #include "PRPMultiIndex.hpp"
20 #include "dakota_data_io.hpp"
21 #include "SurrogateData.hpp"
22 
23 static const char rcsId[]="@(#) $Id: SurrogateModel.cpp 7024 2010-10-16 01:24:42Z mseldre $";
24 
25 //#define DEBUG
26 
27 
28 namespace Dakota {
29 
SurrogateModel(ProblemDescDB & problem_db)30 SurrogateModel::SurrogateModel(ProblemDescDB& problem_db):
31   Model(BaseConstructor(), problem_db),
32   surrogateFnIndices(problem_db.get_is("model.surrogate.function_indices")),
33   corrType(problem_db.get_short("model.surrogate.correction_type")),
34   surrModelEvalCntr(0), approxBuilds(0)
35 {
36   // assign default responseMode based on correction specification;
37   // NO_CORRECTION (0) is default
38   responseMode = (corrType) ? AUTO_CORRECTED_SURROGATE : UNCORRECTED_SURROGATE;
39 
40   // process surrogateFnIndices. IntSets are sorted and unique.
41   if (surrogateFnIndices.empty()) // default: all fns are approximated
42     for (int i=0; i<numFns; ++i)
43       surrogateFnIndices.insert(i);
44   else {
45     // check for out of range values
46     if (*surrogateFnIndices.begin() < 0 ||
47 	*(--surrogateFnIndices.end()) >= numFns) {
48       Cerr << "Error: id_surrogates out of range." << std::endl;
49       abort_handler(-1);
50     }
51   }
52 }
53 
54 
55 SurrogateModel::
SurrogateModel(ProblemDescDB & problem_db,ParallelLibrary & parallel_lib,const SharedVariablesData & svd,bool share_svd,const SharedResponseData & srd,bool share_srd,const ActiveSet & set,short corr_type,short output_level)56 SurrogateModel(ProblemDescDB& problem_db, ParallelLibrary& parallel_lib,
57 	       const SharedVariablesData& svd, bool share_svd,
58 	       const SharedResponseData&  srd, bool share_srd,
59 	       const ActiveSet& set, short corr_type, short output_level):
60   // Allow DFSModel to employ sizing differences (e.g., consuming aggregations)
61   Model(LightWtBaseConstructor(), problem_db, parallel_lib, svd, share_svd,
62 	srd, share_srd, set, output_level),
63   corrType(corr_type), surrModelEvalCntr(0), approxBuilds(0)
64 {
65   modelType = "surrogate";
66 
67   // assign default responseMode based on correction specification;
68   // NO_CORRECTION (0) is default
69   responseMode = (corrType) ? AUTO_CORRECTED_SURROGATE : UNCORRECTED_SURROGATE;
70 
71   // set up surrogateFnIndices to use default (all fns are approximated)
72   for (int i=0; i<numFns; ++i)
73     surrogateFnIndices.insert(i);
74 }
75 
76 
check_submodel_compatibility(const Model & sub_model)77 void SurrogateModel::check_submodel_compatibility(const Model& sub_model)
78 {
79   // Check for compatible array sizing between sub_model and currentVariables,
80   // accounting for the use of different views in different variables sets:
81   // > common case for local/multipoint/hierarchical: the variables view in
82   //   sub_model and currentVariables are the same.
83   // > common case for global: sub_model has an "All" vars view due to DACE
84   //   usage and the currentVariables view may vary depending on the type
85   //   of iterator interfaced with this SurrogateModel.  Enforcing an "all"
86   //   view in the data returned from currentVariables ensures consistency.
87   short active_view = currentVariables.view().first,
88      sm_active_view = sub_model.current_variables().view().first;
89   bool error_flag = false;
90   if ( active_view == sm_active_view ) {
91     // common cases: Distinct on Distinct (e.g., opt/UQ on local/multipt/hier)
92     //               All on All           (e.g., DACE/PStudy on global)
93     size_t sm_cv = sub_model.cv(),  sm_div = sub_model.div(),
94       sm_dsv = sub_model.dsv(),     sm_drv = sub_model.drv(),
95       cv  = currentVariables.cv(),  div = currentVariables.div(),
96       dsv = currentVariables.dsv(), drv = currentVariables.drv();
97     if (sm_cv != cv || sm_div != div || sm_dsv != dsv || sm_drv != drv) {
98       Cerr << "Error: incompatibility between approximate and actual model "
99 	   << "variable sets within SurrogateModel:\n       Active approximate "
100 	   << "= " << cv << " continuous, " << div << " discrete int, " << dsv
101 	   << " discrete string, and " << drv << " discrete real.\n       "
102 	   << "Active      actual = " << sm_cv << " continuous, " << sm_div
103 	   << " discrete int, " << sm_dsv << " discrete string, and " << sm_drv
104 	   << " discrete real.\n       Check consistency of variables "
105 	   << "specifications." << std::endl;
106       error_flag = true;
107     }
108   }
109   else if ( ( sm_active_view == RELAXED_ALL || sm_active_view == MIXED_ALL ) &&
110 	    active_view >= RELAXED_DESIGN ) {
111     // common case: Distinct on All (e.g., opt/UQ on global surrogate)
112     size_t sm_cv  = sub_model.cv(),   sm_div = sub_model.div(),
113       sm_dsv  = sub_model.dsv(),      sm_drv = sub_model.drv(),
114       acv  = currentVariables.acv(),  adiv = currentVariables.adiv(),
115       adsv = currentVariables.adsv(), adrv = currentVariables.adrv();
116     if (sm_cv != acv || sm_div != adiv || sm_dsv != adsv || sm_drv != adrv) {
117       Cerr << "Error: incompatibility between approximate and actual model "
118 	   << "variable sets within SurrogateModel:\n       Active "
119 	   << "approximate = " << acv << " continuous, " << adiv
120 	   << " discrete int, " << adsv << " discrete string, and " << adrv
121 	   << " discrete real (All view).\n       Active      actual = "
122 	   << sm_cv << " continuous, " << sm_div << " discrete int, " << sm_dsv
123 	   << " discrete string, and " << sm_drv << " discrete real.\n       "
124 	   << "Check consistency of variables specifications."<< std::endl;
125       error_flag = true;
126     }
127   }
128   else if ( ( active_view == RELAXED_ALL || active_view == MIXED_ALL ) &&
129 	    sm_active_view >= RELAXED_DESIGN ) {
130     // common case: All on Distinct (e.g., DACE/PStudy on local/multipt/hier)
131     // Note: force_rebuild() is critical for this case (prevents interrogation
132     // of a surrogate for inconsistent values for vars not included in build)
133     size_t sm_acv = sub_model.acv(), sm_adiv = sub_model.adiv(),
134       sm_adsv = sub_model.adsv(),    sm_adrv = sub_model.adrv(),
135       cv  = currentVariables.cv(),   div = currentVariables.div(),
136       dsv = currentVariables.dsv(),  drv = currentVariables.drv();
137     if (sm_acv != cv || sm_adiv != div || sm_adsv != dsv || sm_adrv != drv) {
138       Cerr << "Error: incompatibility between approximate and actual model "
139 	   << "variable sets within SurrogateModel:\n       Active "
140 	   << "approximate = " << cv << " continuous, " << div
141 	   << " discrete int, " << dsv << " discrete string, and " << drv
142 	   << " discrete real.\n       Active      actual = " << sm_acv
143 	   << " continuous, " << sm_adiv << " discrete int, " << sm_adsv
144 	   << " discrete string, and " << sm_adrv << " discrete real (All "
145 	   << "view).\n       Check consistency of variables specifications."
146 	   << std::endl;
147       error_flag = true;
148     }
149   }
150   //else: other options are specific to DataFit and Hierarch surrogates
151 
152   if (error_flag)
153     abort_handler(-1);
154 }
155 
156 
157 /** This function forces a rebuild of the approximation according to
158     the sub-model variables view, the approximation type, and whether
159     the active approximation bounds or inactive variable values have
160     changed since the last approximation build. */
force_rebuild()161 bool SurrogateModel::force_rebuild()
162 {
163   // force rebuild for change in inactive vars based on sub-model view.  It
164   // is assumed that any recastings within Model recursions do not affect the
165   // inactive variables (while RecastModel::variablesMapping has access to
166   // all of the vars, the convention is to modify only the active vars).
167 
168   // for global surrogates, force rebuild for change in active bounds
169 
170   Model& actual_model = truth_model();
171 
172   // Don't force rebuild for active subspace model:
173   // JAM TODO: There is probably a more elegant way to accomodate subspace models
174   if (actual_model.model_type() == "active_subspace")
175     return false;
176 
177   short approx_active_view = currentVariables.view().first;
178   if (actual_model.is_null()) {
179     // compare reference vars against current inactive top-level data
180     if ( referenceICVars  != currentVariables.inactive_continuous_variables() ||
181 	 referenceIDIVars !=
182 	 currentVariables.inactive_discrete_int_variables()                   ||
183 	 referenceIDSVars !=
184 	 currentVariables.inactive_discrete_string_variables()                ||
185 	 referenceIDRVars !=
186 	 currentVariables.inactive_discrete_real_variables() )
187       return true;
188 
189     if ( strbegins(surrogateType, "global_") &&
190 	 // compare reference bounds against current active top-level data
191 	 ( referenceCLBnds != userDefinedConstraints.continuous_lower_bounds()||
192 	   referenceCUBnds != userDefinedConstraints.continuous_upper_bounds()||
193 	   referenceDILBnds !=
194 	   userDefinedConstraints.discrete_int_lower_bounds()                 ||
195 	   referenceDIUBnds !=
196 	   userDefinedConstraints.discrete_int_upper_bounds()                 ||
197 	   // no discrete string bounds
198 	   referenceDRLBnds !=
199 	   userDefinedConstraints.discrete_real_lower_bounds()                ||
200 	   referenceDRUBnds !=
201 	   userDefinedConstraints.discrete_real_upper_bounds() ) )
202 	return true;
203   }
204   else { // actual_model is defined
205     const Variables& actual_vars = actual_model.current_variables();
206     short sub_model_active_view  = actual_vars.view().first;
207 
208     // compare reference vars against current inactive top-level data
209     if ( approx_active_view == sub_model_active_view  &&
210 	 approx_active_view >= RELAXED_DESIGN &&
211 	 // compare inactive top-level data against inactive sub-model data
212 	 ( referenceICVars != currentVariables.inactive_continuous_variables()||
213 	   referenceIDIVars !=
214 	   currentVariables.inactive_discrete_int_variables()                 ||
215 	   referenceIDSVars !=
216 	   currentVariables.inactive_discrete_string_variables()              ||
217 	   referenceIDRVars !=
218 	   currentVariables.inactive_discrete_real_variables() ) )
219       return true;
220     else if ( ( approx_active_view == RELAXED_ALL ||
221 		approx_active_view == MIXED_ALL ) &&
222 	      sub_model_active_view >= RELAXED_DESIGN ) {
223       // coerce top level data to sub-model view, but don't update sub-model
224       if (truthModelVars.is_null())
225 	truthModelVars = actual_vars.copy();
226       truthModelVars.all_continuous_variables(
227         currentVariables.continuous_variables());
228       truthModelVars.all_discrete_int_variables(
229         currentVariables.discrete_int_variables());
230       truthModelVars.all_discrete_string_variables(
231         currentVariables.discrete_string_variables());
232       truthModelVars.all_discrete_real_variables(
233         currentVariables.discrete_real_variables());
234       // perform check on inactive data at sub-model level
235       if ( referenceICVars  != truthModelVars.inactive_continuous_variables() ||
236 	   referenceIDIVars !=
237 	   truthModelVars.inactive_discrete_int_variables() ||
238 	   referenceIDSVars !=
239 	   truthModelVars.inactive_discrete_string_variables() ||
240 	   referenceIDRVars !=
241 	   truthModelVars.inactive_discrete_real_variables() )
242 	return true;
243     }
244     // TO DO: extend for aleatory/epistemic uncertain views
245     /*
246     Model sub_model = actual_model.subordinate_model();
247     while (sub_model.model_type() == "recast")
248       sub_model = sub_model.subordinate_model();
249     if ( referenceICVars  != sub_model.inactive_continuous_variables()      ||
250          referenceIDIVars != sub_model.inactive_discrete_int_variables()    ||
251          referenceIDSVars != sub_model.inactive_discrete_string_variables() ||
252          referenceIDRVars != sub_model.inactive_discrete_real_variables() )
253       return true;
254     */
255 
256     // compare reference bounds against current active top-level data
257     if ( strbegins(surrogateType, "global_") ) {
258 
259       if (actual_model.model_type() == "recast") {
260 	// check for internal changes within subModel definition since the
261 	// SurrogateModel may be in a standard variable space (such that the
262 	// outer level values/bounds do not reflect inner level updates).
263 
264 	// force_rebuild() is called within the context of an approximate
265 	// derived_evaluate(), whereas update_actual_model() and update_global()
266 	// are called w/i the context of build_approximation().  Therefore, one
267 	// must be cautious with assuming that top-level updates have propagated
268 	// to lower levels.  (The only current use case involves
269 	// uSpaceModel.force_rebuild() w/i NonDExpansion::compute_expansion(),
270 	// although it may prove useful for other u-space approximations within
271 	// PCE/SC and local/global reliability).
272 
273 	// Dive through Model recursion to bypass recasting. This is not readily
274 	// handled within new Model virtual fns since the type of approximation
275 	// (known here, but not w/i virtual fns) could dictate different checks.
276 	Model sub_model = actual_model.subordinate_model();
277 	while (sub_model.model_type() == "recast")
278 	  sub_model = sub_model.subordinate_model();
279 
280 	if (referenceCLBnds  != sub_model.continuous_lower_bounds()    ||
281 	    referenceCUBnds  != sub_model.continuous_upper_bounds()    ||
282 	    referenceDILBnds != sub_model.discrete_int_lower_bounds()  ||
283 	    referenceDIUBnds != sub_model.discrete_int_upper_bounds()  ||
284 	    // no discrete string bounds
285 	    referenceDRLBnds != sub_model.discrete_real_lower_bounds() ||
286 	    referenceDRUBnds != sub_model.discrete_real_upper_bounds())
287 	  return true;
288       }
289       else if ( approx_active_view == sub_model_active_view &&
290 		// compare active top-level data against active sub-model data
291 		( referenceCLBnds !=
292 		  userDefinedConstraints.continuous_lower_bounds()    ||
293 		  referenceCUBnds !=
294 		  userDefinedConstraints.continuous_upper_bounds()    ||
295 		  referenceDILBnds !=
296 		  userDefinedConstraints.discrete_int_lower_bounds()  ||
297 		  referenceDIUBnds !=
298 		  userDefinedConstraints.discrete_int_upper_bounds()  ||
299 		  // no discrete string bounds
300 		  referenceDRLBnds !=
301 		  userDefinedConstraints.discrete_real_lower_bounds() ||
302 		  referenceDRUBnds !=
303 		  userDefinedConstraints.discrete_real_upper_bounds() ) )
304 	return true;
305       else if ( approx_active_view >= RELAXED_DESIGN &&
306 		( sub_model_active_view == RELAXED_ALL ||
307 		  sub_model_active_view == MIXED_ALL ) &&
308 		// compare top-level data in All view w/ active sub-model data
309 		( referenceCLBnds !=
310 		  userDefinedConstraints.all_continuous_lower_bounds()     ||
311 		  referenceCUBnds !=
312 		  userDefinedConstraints.all_continuous_upper_bounds()     ||
313 		  referenceDILBnds !=
314 		  userDefinedConstraints.all_discrete_int_lower_bounds()   ||
315 		  referenceDIUBnds !=
316 		  userDefinedConstraints.all_discrete_int_upper_bounds()   ||
317 		  // no discrete string bounds
318 		  referenceDRLBnds !=
319 		  userDefinedConstraints.all_discrete_real_lower_bounds()  ||
320 		  referenceDRUBnds !=
321 		  userDefinedConstraints.all_discrete_real_upper_bounds() ) )
322 	return true;
323       else if ( ( approx_active_view  == RELAXED_ALL ||
324 		  approx_active_view  == MIXED_ALL ) &&
325 		sub_model_active_view >= RELAXED_DESIGN ) {
326 	// coerce top level data to sub-model view, but don't update sub-model
327 	if (truthModelCons.is_null())
328 	  truthModelCons = actual_model.user_defined_constraints().copy();
329 	truthModelCons.all_continuous_lower_bounds(
330 	  userDefinedConstraints.continuous_lower_bounds());
331 	truthModelCons.all_continuous_upper_bounds(
332 	  userDefinedConstraints.continuous_upper_bounds());
333 	truthModelCons.all_discrete_int_lower_bounds(
334 	  userDefinedConstraints.discrete_int_lower_bounds());
335 	truthModelCons.all_discrete_int_upper_bounds(
336 	  userDefinedConstraints.discrete_int_upper_bounds());
337 	// no discrete string bounds
338 	truthModelCons.all_discrete_real_lower_bounds(
339 	  userDefinedConstraints.discrete_real_lower_bounds());
340 	truthModelCons.all_discrete_real_upper_bounds(
341 	  userDefinedConstraints.discrete_real_upper_bounds());
342 	// perform check on active data at sub-model level
343 	if ( referenceCLBnds  != truthModelCons.continuous_lower_bounds()    ||
344 	     referenceCUBnds  != truthModelCons.continuous_upper_bounds()    ||
345 	     referenceDILBnds != truthModelCons.discrete_int_lower_bounds()  ||
346 	     referenceDIUBnds != truthModelCons.discrete_int_upper_bounds()  ||
347 	     // no discrete string bounds
348 	     referenceDRLBnds != truthModelCons.discrete_real_lower_bounds() ||
349 	     referenceDRUBnds != truthModelCons.discrete_real_upper_bounds() )
350 	  return true;
351       }
352 
353       /*
354       // -----------------------COLLAPSED----------------------------------
355       if ( // SBO: rebuild over {d} for each new TR of {d}
356 	   // OUU All view: rebuild over {u}+{d} for each new TR of {d}
357 	   active_bounds_differ ||
358 	   // OUU Distinct view: rebuild over {u} for each new instance of {d}
359 	   ( sub_model_active_view >= RELAXED_DESIGN &&
360 	     inactive_values_differ ) )
361         return true;
362 
363       // -----------------------EXPANDED-----------------------------------
364       if (approx_active_view == sub_model_active_view &&
365 	  approx_active_view >= RELAXED_DESIGN) { // Distinct to Distinct
366         // SBO: rebuild over {d} for each new TR of {d}
367         // OUU: force rebuild over {u} for each new instance of {d}
368         // inactive bounds are irrelevant
369         if (active_bounds_differ || inactive_values_differ)
370 	  return true;
371       }
372       else if ( approx_active_view   == sub_model_active_view &&
373                 ( approx_active_view == RELAXED_ALL ||
374 	          approx_active_view == MIXED_ALL ) ) { // All to All
375         // unusual case: Surrogate-based DACE,PStudy
376         // there are no inactive vars/bounds
377         if (active_bounds_differ)
378 	  return true;
379       }
380       else if ( approx_active_view >= RELAXED_DESIGN &&
381                 ( sub_model_active_view == RELAXED_ALL ||
382 	          sub_model_active_view == MIXED_ALL ) ) { // Distinct to All
383         // OUU: force rebuild over {u}+{d} for each new TR of {d}
384         if (active_bounds_differ)
385 	  return true;
386       }
387       else if ( ( approx_active_view  == RELAXED_ALL ||
388                   approx_active_view  == MIXED_ALL ) &&
389 	        sub_model_active_view >= RELAXED_DESIGN ) { // All->Distinct
390         // unusual case: approx over subset of active top-level vars
391         if (active_bounds_differ || inactive_values_differ)
392 	  return true;
393       }
394       */
395     }
396     /*
397     else { // local, multipoint, hierarchical
398 
399       // For local/multipoint/hierarchical, the approximation is not dependent
400       // on the bounds.  For an "All" sub-model view, the surrogate accounts for
401       // _all_ continuous variables and a rebuild never needs to be forced
402       // (although many surrogate-based algorithms will rebuild for each new
403       // approx region).  For a "Distinct" view, a rebuild is required for any
404       // change in inactive variable values.
405 
406       // -------------------------COLLAPSED------------------------------
407       if ( // OUU Distinct view: rebuild over {u} for each new instance of {d}
408 	   sub_model_active_view >= RELAXED_DESIGN &&
409 	   inactive_values_differ )
410         return true;
411 
412       // -------------------------EXPANDED-------------------------------
413       if (approx_active_view == sub_model_active_view &&
414 	  approx_active_view >= RELAXED_DESIGN) { // Distinct to Distinct
415         // SBO: rebuild over {d} for each new TR of {d}
416         // OUU: force rebuild over {u} for each new instance of {d}
417         // inactive bounds are irrelevant
418         if (inactive_values_differ)
419 	  return true;
420       }
421       else if ( approx_active_view   == sub_model_active_view &&
422                 ( approx_active_view == RELAXED_ALL ||
423 	          approx_active_view == MIXED_ALL ) ) { // All to All
424         // unusual case: Surrogate-based DACE,PStudy
425         // there are no inactive vars
426       }
427       else if ( approx_active_view >= RELAXED_DESIGN &&
428                 ( sub_model_active_view == RELAXED_ALL ||
429 	          sub_model_active_view == MIXED_ALL ) ) { // Distinct to All
430         // OUU: force rebuild over {u}+{d} for each new TR of {d}
431       }
432       else if ( ( approx_active_view  == RELAXED_ALL ||
433                   approx_active_view  == MIXED_ALL ) &&
434 	        sub_model_active_view >= RELAXED_DESIGN ) { // All->Distinct
435         // unusual case: approx over subset of active top-level vars
436         if (inactive_values_differ)
437 	  return true;
438       }
439     }
440     */
441   }
442 
443   return false; // no rebuild required
444 }
445 
446 
447 void SurrogateModel::
asv_split(const ShortArray & orig_asv,ShortArray & actual_asv,ShortArray & approx_asv,bool build_flag)448 asv_split(const ShortArray& orig_asv, ShortArray& actual_asv,
449 	  ShortArray& approx_asv, bool build_flag)
450 {
451   size_t i, num_qoi = qoi();
452   switch (responseMode) {
453   case AGGREGATED_MODELS: {
454     // split actual & approx asv (can ignore build_flag)
455     if (orig_asv.size() != 2*num_qoi) {
456       Cerr << "Error: ASV not aggregated for AGGREGATED_MODELS mode in "
457 	   << "SurrogateModel::asv_split()." << std::endl;
458       abort_handler(MODEL_ERROR);
459     }
460     approx_asv.resize(num_qoi); actual_asv.resize(num_qoi);
461     // aggregated response uses {HF,LF} order:
462     for (i=0; i<num_qoi; ++i)
463       actual_asv[i] = orig_asv[i];
464     for (i=0; i<num_qoi; ++i)
465       approx_asv[i] = orig_asv[i+num_qoi];
466     break;
467   }
468   default: // non-aggregated modes have consistent ASV request vector lengths
469     if (surrogateFnIndices.size() == num_qoi) {
470       if (build_flag) actual_asv = orig_asv;
471       else            approx_asv = orig_asv;
472     }
473     // else response set is mixed:
474     else if (build_flag) { // construct mode: define actual_asv
475       actual_asv.assign(num_qoi, 0);
476       for (ISIter it=surrogateFnIndices.begin();
477 	   it!=surrogateFnIndices.end(); ++it)
478 	actual_asv[*it] = orig_asv[*it];
479     }
480     else { // eval mode: define actual_asv & approx_asv contributions
481       for (i=0; i<num_qoi; ++i) {
482 	short orig_asv_val = orig_asv[i];
483 	if (orig_asv_val) {
484 	  if (surrogateFnIndices.count(i)) {
485 	    if (approx_asv.empty()) // keep empty if no active requests
486 	      approx_asv.assign(num_qoi, 0);
487 	    approx_asv[i] = orig_asv_val;
488 	  }
489 	  else {
490 	    if (actual_asv.empty()) // keep empty if no active requests
491 	      actual_asv.assign(num_qoi, 0);
492 	    actual_asv[i] = orig_asv_val;
493 	  }
494 	}
495       }
496     }
497     break;
498   }
499 }
500 
501 
502 void SurrogateModel::
asv_combine(const ShortArray & actual_asv,const ShortArray & approx_asv,ShortArray & combined_asv)503 asv_combine(const ShortArray& actual_asv, const ShortArray& approx_asv,
504 	    ShortArray& combined_asv)
505 {
506   if (actual_asv.empty())
507     combined_asv = approx_asv;
508   else if (approx_asv.empty())
509     combined_asv = actual_asv;
510   else {
511     combined_asv.resize(numFns);
512     for (size_t i=0; i<numFns; ++i)
513       combined_asv[i] = (surrogateFnIndices.count(i)) ?
514 	approx_asv[i] : actual_asv[i];
515   }
516 }
517 
518 
519 void SurrogateModel::
response_combine(const Response & actual_response,const Response & approx_response,Response & combined_response)520 response_combine(const Response& actual_response,
521 		 const Response& approx_response, Response& combined_response)
522 {
523   const ShortArray& actual_asv = actual_response.active_set_request_vector();
524   const ShortArray& approx_asv = approx_response.active_set_request_vector();
525   ShortArray combined_asv;
526   if (combined_response.is_null()) {
527     combined_response = currentResponse.copy();
528     asv_combine(actual_asv, approx_asv, combined_asv);
529     combined_response.active_set_request_vector(combined_asv);
530   }
531   else
532     combined_asv = combined_response.active_set_request_vector();
533 
534   if (approx_asv.empty())
535     combined_response.update(actual_response);
536   else if (actual_asv.empty())
537     combined_response.update(approx_response);
538   else { // combined
539     const RealVector& actual_fns   = actual_response.function_values();
540     const RealVector& approx_fns   = approx_response.function_values();
541     //const RealMatrix& actual_grads = actual_response.function_gradients();
542     //const RealMatrix& approx_grads = approx_response.function_gradients();
543     const RealSymMatrixArray& actual_hessians
544       = actual_response.function_hessians();
545     const RealSymMatrixArray& approx_hessians
546       = approx_response.function_hessians();
547     for (size_t i=0; i<numFns; i++) {
548       if (surrogateFnIndices.count(i)) {
549 	if (combined_asv[i] & 1)
550 	  combined_response.function_value(approx_fns[i], i);
551 	if (combined_asv[i] & 2)
552 	  combined_response.function_gradient(
553 	    approx_response.function_gradient_view(i), i);
554 	if (combined_asv[i] & 4)
555 	  combined_response.function_hessian(approx_hessians[i], i);
556       }
557       else {
558 	if (combined_asv[i] & 1)
559 	  combined_response.function_value(actual_fns[i], i);
560 	if (combined_asv[i] & 2)
561 	  combined_response.function_gradient(
562 	   actual_response.function_gradient_view(i), i);
563 	if (combined_asv[i] & 4)
564 	  combined_response.function_hessian(actual_hessians[i], i);
565       }
566     }
567   }
568 }
569 
570 
571 void SurrogateModel::
aggregate_response(const Response & hf_resp,const Response & lf_resp,Response & agg_resp)572 aggregate_response(const Response& hf_resp, const Response& lf_resp,
573 		   Response& agg_resp)
574 {
575   if (agg_resp.is_null())
576     agg_resp = currentResponse.copy(); // resized to 2x in resize_response()
577 
578   const ShortArray& lf_asv =  lf_resp.active_set_request_vector();
579   const ShortArray& hf_asv =  hf_resp.active_set_request_vector();
580   ShortArray       agg_asv = agg_resp.active_set_request_vector();
581   size_t i, offset_i, num_lf_fns = lf_asv.size(), num_hf_fns = hf_asv.size();
582   short asv_i;
583 
584   // Order with HF first since it corresponds to the active model key
585   for (i=0; i<num_hf_fns; ++i) {
586     agg_asv[i] = asv_i = hf_asv[i];
587     if (asv_i & 1) agg_resp.function_value(hf_resp.function_value(i), i);
588     if (asv_i & 2)
589       agg_resp.function_gradient(hf_resp.function_gradient_view(i), i);
590     if (asv_i & 4)
591       agg_resp.function_hessian(hf_resp.function_hessian(i), i);
592   }
593 
594   // Order with LF second since it corresponds to a previous/decremented key
595   for (i=0; i<num_lf_fns; ++i) {
596     offset_i = i + num_hf_fns;
597     agg_asv[offset_i] = asv_i = lf_asv[i];
598     if (asv_i & 1) agg_resp.function_value(lf_resp.function_value(i), offset_i);
599     if (asv_i & 2)
600       agg_resp.function_gradient(lf_resp.function_gradient_view(i), offset_i);
601     if (asv_i & 4)
602       agg_resp.function_hessian(lf_resp.function_hessian(i), offset_i);
603   }
604 
605   agg_resp.active_set_request_vector(agg_asv);
606 }
607 
608 } // namespace Dakota
609