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: DataTransformModel
10 //- Description: Implementation code for the DataTransformModel class
11 //- Owner: Brian Adams
12 //- Checked by:
13
14 #include "DataTransformModel.hpp"
15 #include "ExperimentData.hpp"
16 #include "DakotaMinimizer.hpp"
17 #include "PRPMultiIndex.hpp"
18 #include "ResultsManager.hpp"
19
20 static const char rcsId[]="@(#) $Id$";
21
22 namespace Dakota {
23
24 extern PRPCache data_pairs; // global container
25
26 /// initialization of static needed by RecastModel
27 DataTransformModel* DataTransformModel::dtModelInstance(NULL);
28
29 // BMA TODO:
30 // * Construct with the Iterator's verbosity or the Model's? Models
31 // default to same as their Iterator...
32 // * Verify that the default variables, active set, and secondary
33 // response mapping suffice Need test with data and constraints
34 // * Don't want to output message during recast retrieve... (or do we?)
35
36 /** This constructor computes various indices and mappings, then
37 updates the properties of the RecastModel. Hyper-parameters are
38 assumed to trail the active continuous variables when presented to
39 this RecastModel */
40 DataTransformModel::
DataTransformModel(const Model & sub_model,const ExperimentData & exp_data,size_t num_hyper,unsigned short mult_mode,short recast_resp_deriv_order)41 DataTransformModel(const Model& sub_model, const ExperimentData& exp_data,
42 size_t num_hyper, unsigned short mult_mode,
43 short recast_resp_deriv_order):
44 // BMA TODO: should the BitArrays be empty or same as submodel?
45 // recast_secondary_offset is the index to the equality constraints within
46 // the secondary responses
47 RecastModel(sub_model, variables_expand(sub_model, num_hyper),
48 BitArray(), BitArray(), exp_data.num_total_exppoints(),
49 sub_model.num_secondary_fns(),
50 sub_model.num_nonlinear_ineq_constraints(),
51 response_order(sub_model, recast_resp_deriv_order)),
52 expData(exp_data), numHyperparams(num_hyper), obsErrorMultiplierMode(mult_mode)
53 {
54 modelId = RecastModel::recast_model_id(root_model_id(), "DATA_TRANSFORM");
55 // register state variables as inactive vars if config vars are present
56 // BMA TODO: correctly manage the view if relaxed, also review recursion
57
58 // BMA NOTE: This will change the inactive view of any Variables object
59 // sharing the same SharedVariables data as the subModel's Variables
60 size_t num_config_vars = expData.num_config_vars();
61 if (num_config_vars > 0) {
62 subModel.inactive_view(MIXED_STATE);
63 int num_state_vars =
64 subModel.icv() + subModel.idiv() + subModel.idsv() + subModel.idrv();
65 if (num_state_vars != num_config_vars) {
66 Cerr << "\nError: (DataTransformModel) Number of state "
67 << "variables = " << num_state_vars << " must match\n number "
68 << "of configuration variables = " << num_config_vars << "\n";
69 abort_handler(-1);
70 }
71 }
72
73 size_t num_submodel_primary = sub_model.num_primary_fns();
74 // the RecastModel will have one residual per experiment datum
75 size_t num_recast_primary = expData.num_total_exppoints(),
76 num_secondary = sub_model.num_secondary_fns(),
77 num_recast_fns = num_recast_primary + num_secondary;
78
79 // ---
80 // Variables mapping (one-to-one), truncating trailing hyper-parameters
81 // ---
82
83 // BMA TODO: what variables subset and ordering convention do we
84 // want to assume here? Do we need to iterate sub-types, or just the
85 // containers...Assuming active for now.
86
87 // For now, we assume that any hyper-parameters are appended to the
88 // active continuous variables, and that active discrete int,
89 // string, real follow in both the recast and sub-model
90 size_t submodel_cv = sub_model.cv();
91 size_t submodel_dv = sub_model.div() + sub_model.dsv() + sub_model.drv();
92 Sizet2DArray vars_map_indices(submodel_cv + submodel_dv);
93 for (size_t i=0; i<submodel_cv; ++i) {
94 vars_map_indices[i].resize(1);
95 vars_map_indices[i][0] = i;
96 }
97 // skip the trailing continuous hyper-parameters
98 size_t recast_dv_start = submodel_cv + numHyperparams;
99 for (size_t i=0; i<submodel_dv; ++i) {
100 vars_map_indices[submodel_cv + i].resize(1);
101 vars_map_indices[submodel_cv + i][0] = recast_dv_start + i;
102 }
103 bool nonlinear_vars_mapping = false;
104
105 // mappings from the submodel Response to residual Response
106 BoolDequeArray nonlinear_resp_mapping(num_recast_fns);
107
108 // ---
109 // Primary mapping
110 // ---
111 Sizet2DArray primary_resp_map_indices(num_recast_primary);
112 const Response& curr_resp = sub_model.current_response();
113 const SharedResponseData& srd = curr_resp.shared_data();
114 gen_primary_resp_map(srd, primary_resp_map_indices, nonlinear_resp_mapping);
115
116 // ---
117 // Secondary mapping (one-to-one)
118 // ---
119 Sizet2DArray secondary_resp_map_indices(num_secondary);
120 for (size_t i=0; i<num_secondary; i++) {
121 secondary_resp_map_indices[i].resize(1);
122 // the recast constraints just depend on the simulation
123 // constraints, which start at num_submodel_primary
124 secondary_resp_map_indices[i][0] = num_submodel_primary + i;
125 nonlinear_resp_mapping[num_recast_primary + i].resize(1);
126 nonlinear_resp_mapping[num_recast_primary + i][0] = false;
127 }
128
129 // callbacks for RecastModel transformations: default maps for all but primary
130 void (*variables_map) (const Variables&, Variables&) =
131 (numHyperparams > 0) ? vars_mapping : NULL;
132 void (*set_map) (const Variables&, const ActiveSet&, ActiveSet&) =
133 (numHyperparams > 0) ? set_mapping : NULL;
134 void (*primary_resp_map) (const Variables&, const Variables&, const Response&,
135 Response&) = primary_resp_differencer;
136 void (*secondary_resp_map) (const Variables&, const Variables&,
137 const Response&, Response&) = NULL;
138 RecastModel::
139 init_maps(vars_map_indices, nonlinear_vars_mapping, variables_map, set_map,
140 primary_resp_map_indices, secondary_resp_map_indices,
141 nonlinear_resp_mapping, primary_resp_map, secondary_resp_map);
142
143
144 // ---
145 // Expand currentVariables values/labels to account for hyper-parameters
146 // ---
147 init_continuous_vars();
148
149
150 // ---
151 // Expand any submodel Response data to the expanded residual size
152 // ---
153
154 // NOTE: RecastModel pulls ScalingOpts from subModel
155 //
156 // * CV scales don't change in this recasting; base RecastModel captures them
157 // BMA TODO: What if there are hyper-parameters active?
158 // * Overrides are not needed for nonlinear or linear constraints;
159 // they aren't affected by data transforms
160
161 // NOTE: The following expansions are conservative. Could be skipped when
162 // only scalar data present and no replicates.
163 //
164 // For all primary should be able to just leave as 1 or num_elts
165
166 // Preserve weights through data transformations
167 // Weights are always by group, expanded in DakotaModel; just need to replicate
168 expand_primary_array(sub_model.primary_response_fn_weights().length(),
169 sub_model.primary_response_fn_weights(),
170 num_recast_primary, primaryRespFnWts);
171
172 // TODO: Sense is 1 or group, and NOT currently properly expanded for fields in DakotaModel
173 // Preserve sense through data transformations
174 expand_primary_array(sub_model.primary_response_fn_sense().size(),
175 sub_model.primary_response_fn_sense(),
176 num_recast_primary, primaryRespFnSense);
177
178 // Adjust each scaling type to right size, leaving as length 1 if needed
179 expand_primary_array(sub_model.scaling_options().priScaleTypes.size(),
180 sub_model.scaling_options().priScaleTypes,
181 num_recast_primary, scalingOpts.priScaleTypes);
182 expand_primary_array(sub_model.scaling_options().priScales.length(),
183 sub_model.scaling_options().priScales,
184 num_recast_primary, scalingOpts.priScales);
185
186 // For this derivation of RecastModel, all resizing can occur at construct
187 // time --> Variables/Response are up to date for estimate_message_lengths()
188 // within Model::init_communicators().
189 }
190
191
~DataTransformModel()192 DataTransformModel::~DataTransformModel()
193 { /* empty dtor */}
194
195
196 void DataTransformModel::
data_transform_response(const Variables & sub_model_vars,const Response & sub_model_resp,Response & residual_resp)197 data_transform_response(const Variables& sub_model_vars,
198 const Response& sub_model_resp,
199 Response& residual_resp)
200 {
201 // A DataTransform doesn't map variables, but we map them here to
202 // avoid introducing a latent bug
203 Variables recast_vars(current_variables().copy());
204 inverse_transform_variables(sub_model_vars, recast_vars);
205 transform_response(recast_vars, sub_model_vars, sub_model_resp, residual_resp);
206 }
207
208
data_resize()209 void DataTransformModel::data_resize()
210 {
211 // Actions from ctor chain to check:
212 // RecastModel ctor
213 // nonlinear_resp_mapping
214 // update primary resp mapping
215 // update secondary resp mapping, nonlinear resp mapping
216
217 // RecastModel init_maps
218
219 // expand arrays
220 if (numHyperparams > 0 || obsErrorMultiplierMode > CALIBRATE_NONE) {
221 // TODO: We could support update without size change, or even with
222 // size change in the case of only one multiplier. Or later could
223 // allow updates including the whole parameter domain change.
224 Cerr << "\nError (DataTransformModel): data updates not supported when "
225 << "calibrating\nhyper-parameters.";
226 abort_handler(-1);
227 }
228
229 // there is no change in variables or derivatives for now
230 reshape_response(expData.num_total_exppoints(),
231 subModel.num_secondary_fns());
232
233 }
234
235
236
237 /** Incorporate the hyper parameters into Variables, assuming they are at the
238 end of the active continuous variables. For example, append them to
239 continuous design or continuous aleatory uncertain. */
240 SizetArray DataTransformModel::
variables_expand(const Model & sub_model,size_t num_hyper)241 variables_expand(const Model& sub_model, size_t num_hyper)
242 {
243 SizetArray vc_totals; // default is no size change
244 if (num_hyper > 0) {
245 const SharedVariablesData& svd = sub_model.current_variables().shared_data();
246 vc_totals = svd.components_totals();
247 vc_totals[get_hyperparam_vc_index(sub_model)] += num_hyper;
248 }
249 return vc_totals;
250 }
251
252
get_hyperparam_vc_index(const Model & sub_model)253 int DataTransformModel::get_hyperparam_vc_index(const Model& sub_model)
254 {
255 int vc_index = TOTAL_CDV;
256
257 const SharedVariablesData& svd = sub_model.current_variables().shared_data();
258 const SizetArray& vc_totals = svd.components_totals();
259 short active_view = sub_model.current_variables().view().first;
260 switch (active_view) {
261
262 case MIXED_DESIGN: case RELAXED_DESIGN:
263 // append to end of continuous design
264 vc_index = TOTAL_CDV;
265
266 case MIXED_ALEATORY_UNCERTAIN: case RELAXED_ALEATORY_UNCERTAIN:
267 // append to end of continuous aleatory
268 vc_index = TOTAL_CAUV;
269 break;
270
271 case MIXED_UNCERTAIN: case RELAXED_UNCERTAIN:
272 case MIXED_EPISTEMIC_UNCERTAIN: case RELAXED_EPISTEMIC_UNCERTAIN:
273 // append to end of continuous epistemic (note there may not actually be
274 // any epistemic variables in the *_UNCERTAIN cases)
275 vc_index = TOTAL_CEUV;
276 break;
277
278 case MIXED_ALL: case RELAXED_ALL: case MIXED_STATE: case RELAXED_STATE:
279 // append to end of continuous state
280 vc_index = TOTAL_CSV;
281 break;
282
283 default:
284 Cerr << "\nError: invalid active variables view " << active_view
285 << " in DataTransformModel.\n";
286 abort_handler(-1);
287 break;
288
289 }
290
291 return vc_index;
292 }
293
294
295 short DataTransformModel::
response_order(const Model & sub_model,short recast_resp_order)296 response_order(const Model& sub_model, short recast_resp_order)
297 {
298 const Response& curr_resp = sub_model.current_response();
299
300 // recast resp order at least as much as original response
301 if (!curr_resp.function_gradients().empty()) recast_resp_order |= 2;
302 if (!curr_resp.function_hessians().empty()) recast_resp_order |= 4;
303
304 return recast_resp_order;
305 }
306
307 void DataTransformModel::
gen_primary_resp_map(const SharedResponseData & srd,Sizet2DArray & primary_resp_map_indices,BoolDequeArray & nonlinear_resp_map) const308 gen_primary_resp_map(const SharedResponseData& srd,
309 Sizet2DArray& primary_resp_map_indices,
310 BoolDequeArray& nonlinear_resp_map) const
311 {
312 size_t num_scalar = srd.num_scalar_primary(),
313 num_field_groups = srd.num_field_response_groups();
314 const IntVector& sim_field_lens = srd.field_lengths();
315 size_t num_experiments = expData.num_experiments(), calib_term_ind = 0;
316
317 for (size_t exp_ind = 0; exp_ind < num_experiments; ++exp_ind) {
318 // field lengths can be different per experiment
319 const IntVector& exp_field_lens = expData.field_lengths(exp_ind);
320 for (size_t scalar_ind = 0; scalar_ind < num_scalar; ++scalar_ind) {
321 // simulation scalars inform calibration terms 1 to 1
322 // (no correlation or interpolation allowed)
323 primary_resp_map_indices[calib_term_ind].resize(1);
324 primary_resp_map_indices[calib_term_ind][0] = scalar_ind; //=calib_term_ind
325 nonlinear_resp_map[calib_term_ind].resize(1);
326 nonlinear_resp_map[calib_term_ind][0] = false;
327 ++calib_term_ind;
328 }
329 // base index for simulation in current field group
330 size_t sim_ind_offset = num_scalar;
331 for (size_t fg_ind = 0; fg_ind < num_field_groups; ++fg_ind) {
332 // each field calibration term depends on all simulation field
333 // entries for this field, due to correlation or interpolation
334 // if no matrix correlation, no interpolation, could skip
335 for (size_t z=0; z<exp_field_lens[fg_ind]; ++z) {
336 primary_resp_map_indices[calib_term_ind].resize(sim_field_lens[fg_ind]);
337 nonlinear_resp_map[calib_term_ind].resize(sim_field_lens[fg_ind]);
338 // this residual depends on all other simulation data for this field
339 for (size_t sim_ind = 0; sim_ind<sim_field_lens[fg_ind]; ++sim_ind) {
340 primary_resp_map_indices[calib_term_ind][sim_ind] =
341 sim_ind_offset + sim_ind;
342 nonlinear_resp_map[calib_term_ind][sim_ind] = false;
343 }
344 ++calib_term_ind;
345 }
346 sim_ind_offset += sim_field_lens[fg_ind];
347 }
348 }
349 }
350
351
352 /** Blocking evaluation over all experiment configurations to compute
353 a single set of expanded residuals. If the subModel supports
354 asynchronous evaluate_nowait(), do the configuration evals
355 concurrently and then synchronize. */
derived_evaluate(const ActiveSet & set)356 void DataTransformModel::derived_evaluate(const ActiveSet& set)
357 {
358 // If no configuration variables, use base class implementation
359 if (expData.config_vars().empty())
360 RecastModel::derived_evaluate(set);
361 else {
362 ++recastModelEvalCntr;
363
364 // transform from recast (Iterator) to sub-model (user) variables;
365 // NOTE: these are the same for all configurations
366 transform_variables(currentVariables, subModel.current_variables());
367
368 // the incoming set is for the recast problem, which must be converted
369 // back to the underlying response set for evaluation by the subModel.
370 ActiveSet sub_model_set;
371 transform_set(currentVariables, set, sub_model_set);
372 // update currentResponse early as it's used in form_residuals
373 currentResponse.active_set(set);
374
375 if (outputLevel >= VERBOSE_OUTPUT) {
376 Cout << "\n------------------------------------";
377 Cout << "\nEvaluating model for each experiment";
378 Cout << "\n------------------------------------" << std::endl;
379 }
380
381 size_t num_exp = expData.num_experiments();
382 for (size_t i=0; i<num_exp; ++i) {
383 // augment the active variables with the configuration variables
384 Model::inactive_variables(expData.config_vars()[i], subModel);
385
386 if (subModel.asynch_flag()) {
387 subModel.evaluate_nowait(sub_model_set);
388 // be able to map the subModel's evalID back to the right
389 // recastModel eval and omit evals we didn't schedule
390 // Don't need to cache ActiveSet or Variables
391 recastIdMap[subModel.evaluation_id()] = recastModelEvalCntr;
392 }
393 else {
394 // No need to cache when the subModel is synchronous; populate
395 // a subset of residuals for each subModel eval
396 subModel.evaluate(sub_model_set);
397 // recast the subModel response ("user space") into the currentResponse
398 // ("iterator space"); populate one experiment's residuals
399 expData.form_residuals(subModel.current_response(), i, currentResponse);
400 }
401 }
402
403 if (subModel.asynch_flag()) {
404 // Synchronize and map all configurations to the single eval's residuals
405 // BMA TODO ask MSE: Will these always be in the right order (seems so);
406 // it's important to map the responses in the right order (not
407 // checking yet)
408
409 // In this case we don't need a mapping to a recast ID, just an
410 // IntResponseMap with the subset of evals we own; filter in-place
411 const IntResponseMap& submodel_resp = filter_submodel_responses();
412 transform_response_map(submodel_resp, currentVariables, currentResponse);
413 }
414 else {
415 // BMA TODO: doesn't need submodel vars...
416 scale_response(subModel.current_variables(), currentVariables,
417 currentResponse);
418 }
419
420 print_residual_response(currentResponse);
421
422 // BMA TODO:
423 // We know that DataTransformModel didn't register a secondary
424 // transformation, but what if the submodel constraints differ per
425 // configuration? Need to aggregate/expand the constraints!
426 //RecastModel::transform_secondary_response();
427 }
428 }
429
430
431 /** Non-blocking evaluation (scheduling) over all experiment
432 configurations. Assumes that if this model supports nowait, its
433 subModel does too and schedules them all. */
derived_evaluate_nowait(const ActiveSet & set)434 void DataTransformModel::derived_evaluate_nowait(const ActiveSet& set)
435 {
436 // BMA ask MSE: Is it possible RecastModel is asynch, but not subModel?
437
438 // If no configuration variables, use base class implementation
439 if (expData.config_vars().empty())
440 RecastModel::derived_evaluate_nowait(set);
441 else {
442 ++recastModelEvalCntr;
443
444 // transform from recast (Iterator) to sub-model (user) variables
445 // NOTE: these are the same for all configurations
446 transform_variables(currentVariables, subModel.current_variables());
447
448 // the incoming set is for the recast problem, which must be converted
449 // back to the underlying response set for evaluation by the subModel.
450 ActiveSet sub_model_set;
451 transform_set(currentVariables, set, sub_model_set);
452
453 if (outputLevel >= VERBOSE_OUTPUT) {
454 Cout << "\n------------------------------------";
455 Cout << "\nEvaluating model for each experiment";
456 Cout << "\n------------------------------------" << std::endl;
457 }
458
459 size_t num_exp = expData.num_experiments();
460 for (size_t i=0; i<num_exp; ++i) {
461 // augment the active variables with the configuration variables
462 Model::inactive_variables(expData.config_vars()[i], subModel);
463
464 subModel.evaluate_nowait(sub_model_set);
465
466 // be able to map the subModel's evalID back to the right
467 // recastModel eval
468 recastIdMap[subModel.evaluation_id()] = recastModelEvalCntr;
469 }
470
471 // bookkeep variables for use in primaryRespMapping/secondaryRespMapping
472 //if (primaryRespMapping || secondaryRespMapping) {
473 recastSetMap[recastModelEvalCntr] = set;
474 recastVarsMap[recastModelEvalCntr] = currentVariables.copy();
475 // This RecastModel doens't map variables in a way that needs these
476 // if (variablesMapping)
477 // subModelVarsMap[recastModelEvalCntr] =
478 // subModel.current_variables().copy();
479 //}
480 }
481 }
482
483
484 /** Collect all the subModel evals and build the residual sets for all
485 evaluations. Like rekey functions in DakotaModel, but many
486 sub-model to one recast-model. For the blocking synchronize case,
487 we force the subModel to synch and have all needed data. */
derived_synchronize()488 const IntResponseMap& DataTransformModel::derived_synchronize()
489 {
490 if (expData.config_vars().empty())
491 return RecastModel::derived_synchronize();
492 else {
493 // We don't even really need the recast ID lookup nor the cached
494 // data structure, but want to be tolerant of evals that this
495 // Model didn't schedule (and it simplifies the indexing logic).
496 bool deep_copy = false;
497 cache_submodel_responses(subModel.synchronize(), deep_copy);
498
499 // populate recastResponseMap with all evals
500 bool collect_all = true;
501 collect_residuals(collect_all);
502 }
503
504 return recastResponseMap;
505 }
506
507
508 /** Collect any completed subModel evals and build the residual sets
509 for any fully completed evaluations. Like rekey functions in
510 DakotaModel, but many sub-model to one recast-model. We do not
511 force the subModel to synch. */
derived_synchronize_nowait()512 const IntResponseMap& DataTransformModel::derived_synchronize_nowait()
513 {
514 if (expData.config_vars().empty())
515 return RecastModel::derived_synchronize_nowait();
516 else {
517 // need deep copy in case all the configurations aren't yet complete
518 bool deep_copy = true;
519 cache_submodel_responses(subModel.synchronize_nowait(), deep_copy);
520
521 // populate recastResponseMap with any fully completed evals
522 bool collect_all = false;
523 collect_residuals(collect_all);
524 }
525
526 return recastResponseMap;
527 }
528
529
vars_mapping(const Variables & recast_vars,Variables & submodel_vars)530 void DataTransformModel::vars_mapping(const Variables& recast_vars,
531 Variables& submodel_vars)
532 {
533 // Forward the calibration parameters, omitting the hyper-parameters, which
534 // are assumed to trail the active continuous variables.
535 RealVector sm_cv = submodel_vars.continuous_variables_view();
536 copy_data_partial(recast_vars.continuous_variables(), 0,
537 (int)submodel_vars.cv(), sm_cv);
538 }
539
540
541 /** RecastModel sets up a default set mapping before calling this
542 update, so focus on updating the derivative variables vector */
set_mapping(const Variables & recast_vars,const ActiveSet & recast_set,ActiveSet & sub_model_set)543 void DataTransformModel::set_mapping(const Variables& recast_vars,
544 const ActiveSet& recast_set,
545 ActiveSet& sub_model_set)
546 {
547 // This transformation should forward DVV requests for derivatives
548 // w.r.t. calibration parameters and discard requests for
549 // derivatives w.r.t. hyper-parameters.
550
551 // The sub-model should be working with variable IDs from 1 to
552 // number of active continuous variables
553 SizetArray sub_model_dvv;
554 const SizetArray& recast_dvv = recast_set.derivative_vector();
555 size_t max_sm_id = dtModelInstance->subordinate_model().cv();
556 for (size_t i=0; i<recast_dvv.size(); ++i)
557 if (1 <= recast_dvv[i] && recast_dvv[i] <= max_sm_id)
558 sub_model_dvv.push_back(recast_dvv[i]);
559 sub_model_set.derivative_vector(sub_model_dvv);
560
561 // When calibrating hyper-parameters in a MAP solve, requests for
562 // gradients and Hessians require lower-order data to be present. This
563 // could be relaxed depending on which derivative vars are requested.
564 if (dtModelInstance->numHyperparams > 0) {
565 ShortArray sub_model_asv = sub_model_set.request_vector();
566 for (size_t i=0; i<sub_model_asv.size(); ++i) {
567 if (sub_model_asv[i] & 4)
568 sub_model_asv[i] |= 2;
569 if (sub_model_asv[i] & 2)
570 sub_model_asv[i] |= 1;
571 }
572 sub_model_set.request_vector(sub_model_asv);
573 }
574 }
575
576
577 void DataTransformModel::
primary_resp_differencer(const Variables & submodel_vars,const Variables & recast_vars,const Response & submodel_response,Response & recast_response)578 primary_resp_differencer(const Variables& submodel_vars,
579 const Variables& recast_vars,
580 const Response& submodel_response,
581 Response& recast_response)
582 {
583 // BMA REVIEW: data differencing doesn't affect gradients and
584 // Hessians, as long as they use the updated residual in their
585 // computation. They probably don't!
586
587 if (dtModelInstance->outputLevel >= VERBOSE_OUTPUT) {
588 Cout << "\n-----------------------------------------------------------";
589 Cout << "\nPost-processing Function Evaluation: Data Transformation";
590 Cout << "\n-----------------------------------------------------------"
591 << std::endl;
592 }
593
594 // form residuals (and gradients/Hessians) from the simulation
595 // response this call has to be careful not to resize gradients and
596 // Hessians in a way that tramples hyper-parameters: only update
597 // submodel cv entries, leaving objects sized
598 dtModelInstance->expData.form_residuals(submodel_response, recast_response);
599
600 // scale by covariance, including hyper-parameter multipliers
601 dtModelInstance->scale_response(submodel_vars, recast_vars, recast_response);
602
603 if (dtModelInstance->outputLevel >= VERBOSE_OUTPUT &&
604 dtModelInstance->subordinate_model().num_primary_fns() > 0) {
605 Cout << "Calibration data transformation; residuals:\n";
606 write_data(Cout, recast_response.function_values(),
607 recast_response.function_labels());
608 Cout << std::endl;
609 }
610 if (dtModelInstance->outputLevel >= DEBUG_OUTPUT &&
611 dtModelInstance->subordinate_model().num_primary_fns() > 0) {
612 Cout << "Calibration data transformation; full response:\n"
613 << recast_response << std::endl;
614 }
615
616 }
617
618
619 /** (We don't quite want the rekey behavior since multiple subModel
620 evals map to one recast eval.) */
filter_submodel_responses()621 const IntResponseMap& DataTransformModel::filter_submodel_responses()
622 {
623 const IntResponseMap& sm_resp_map = subModel.synchronize();
624 // Not using BOOST_FOREACH due to potential for iterator invalidation in
625 // erase in cache_unmatched_response (shouldn't be a concern with map?)
626 IntRespMCIter sm_r_it = sm_resp_map.begin();
627 IntRespMCIter sm_r_end = sm_resp_map.end();
628 while (sm_r_it != sm_r_end) {
629 int sm_id = sm_r_it->first;
630 IntIntMIter id_it = recastIdMap.find(sm_id);
631 if (id_it != recastIdMap.end()) {
632 // no need to cache, just leave in the subModel's IntResponseMap
633 recastIdMap.erase(sm_id);
634 ++sm_r_it;
635 }
636 else {
637 ++sm_r_it; // prior to invalidation from erase()
638 subModel.cache_unmatched_response(sm_id);
639 }
640 }
641 return sm_resp_map;
642 }
643
644
645 void DataTransformModel::
cache_submodel_responses(const IntResponseMap & sm_resp_map,bool deep_copy)646 cache_submodel_responses(const IntResponseMap& sm_resp_map, bool deep_copy)
647 {
648 // Not using BOOST_FOREACH due to potential for iterator invalidation in
649 // erase in cache_unmatched_response (shouldn't be a concern with map?)
650 IntRespMCIter sm_r_it = sm_resp_map.begin();
651 IntRespMCIter sm_r_end = sm_resp_map.end();
652 while (sm_r_it != sm_r_end) {
653 int sm_id = sm_r_it->first;
654 IntIntMIter id_it = recastIdMap.find(sm_id);
655 if (id_it != recastIdMap.end()) {
656 int recast_id = id_it->second;
657
658 // this is our eval, cache it as <recast_id, <sm_id, Response> >
659 if (cachedResp.find(recast_id) == cachedResp.end()) {
660 // insert a new recast_id instance
661 cachedResp[recast_id] = IntResponseMap();
662 cachedResp[recast_id][sm_id] = deep_copy ? sm_r_it->second.copy() :
663 sm_r_it->second;
664 }
665 else
666 cachedResp[recast_id][sm_id] = deep_copy ? sm_r_it->second.copy() :
667 sm_r_it->second;
668 recastIdMap.erase(sm_id);
669 ++sm_r_it;
670 }
671 else {
672 ++sm_r_it;
673 subModel.cache_unmatched_response(sm_id);
674 }
675 }
676 }
677
678
collect_residuals(bool collect_all)679 void DataTransformModel::collect_residuals(bool collect_all)
680 {
681 recastResponseMap.clear();
682
683 //BOOST_FOREACH(IntIntResponseMapMap::value_type& cr_pair, cachedResp)
684 IntIntResponseMapMap::iterator cr_pair = cachedResp.begin();
685 while(cr_pair != cachedResp.end()) {
686 int recast_id = cr_pair->first; // (.second is a subModel IntResponseMap)
687 size_t num_exp = expData.num_experiments();
688
689 // the blocking synch case requires all data present
690 if (collect_all && cr_pair->second.size() != num_exp) {
691 Cerr << "\nError (DataTransformModel): Sub-model returned "
692 << cr_pair->second.size() << "evaluations,\n but have " << num_exp
693 << " experiment configurations.\n";
694 abort_handler(-1);
695 }
696
697 // populate recastResponseMap with any recast evals that have all
698 // their configs complete (only complete/clear those with finished
699 // experiment configs)
700 if (cr_pair->second.size() == num_exp) {
701
702 IntASMIter s_it = recastSetMap.find(recast_id);
703 IntVarsMIter v_it = recastVarsMap.find(recast_id);
704
705 recastResponseMap[recast_id] = currentResponse.copy();
706 recastResponseMap[recast_id].active_set(s_it->second);
707
708 transform_response_map(cr_pair->second, v_it->second,
709 recastResponseMap[recast_id]);
710
711 // cleanup (could do clear() at end)
712 recastVarsMap.erase(v_it);
713 recastSetMap.erase(s_it);
714 // BMA TODO:
715 //RecastModel::transform_secondary_response();
716 // BMA TODO: consider iterator here instead of value?
717 cr_pair++;
718 cachedResp.erase(recast_id);
719 print_residual_response(recastResponseMap[recast_id]);
720 } else {
721 cr_pair++;
722 }
723 }
724 }
725
726
727 /** This transformation assumes the residuals are in submodel eval_id
728 order. */
729 void DataTransformModel::
transform_response_map(const IntResponseMap & submodel_resp,const Variables & recast_vars,Response & residual_resp)730 transform_response_map(const IntResponseMap& submodel_resp,
731 const Variables& recast_vars,
732 Response& residual_resp)
733 {
734 size_t num_exp = expData.num_experiments();
735 if (submodel_resp.size() != num_exp) {
736 // unsupported case: could (shouldn't) happen in complex MF workflows
737 Cerr << "\nError (DataTransformModel): sub model evals wrong size.\n";
738 abort_handler(-1);
739 }
740 IntRespMCIter sm_eval_it = submodel_resp.begin();
741 for (size_t i=0; i<num_exp; ++i, ++sm_eval_it)
742 expData.form_residuals(sm_eval_it->second, i, residual_resp);
743
744 // scale by covariance, including hyper-parameter multipliers
745 // BMA TODO: doesn't need submodel vars...
746 scale_response(subModel.current_variables(), recast_vars, residual_resp);
747 }
748
749 void DataTransformModel::
scale_response(const Variables & submodel_vars,const Variables & recast_vars,Response & recast_response)750 scale_response(const Variables& submodel_vars,
751 const Variables& recast_vars,
752 Response& recast_response)
753 {
754 // scale by (error covariance)^{-1/2}
755 if (expData.variance_active())
756 expData.scale_residuals(recast_response);
757
758 // TODO: may need to scale by hyperparameters in Covariance as well
759 if (obsErrorMultiplierMode > CALIBRATE_NONE) {
760 // r <- r ./ mult, where mult might be per-block
761 // scale by mult, whether 1, per-experiment, per-response, or both
762 // for now, the multiplier calibration mode is a method-related
763 // parameter; could instead have a by-index interface here...
764
765 // For now, assume only continuous variables, with hyper at end
766
767 // extract hyperparams; now both raw and sub vars have size total params
768 // hyper are the last numHyperParams
769 size_t num_calib_params = submodel_vars.cv();
770 RealVector hyper_params;
771 hyper_params.sizeUninitialized(numHyperparams);
772 copy_data_partial(recast_vars.continuous_variables(), num_calib_params,
773 numHyperparams, hyper_params);
774
775 // Apply hyperparameter multipliers to all fn, grad, Hess,
776 // including derivative contributions from hyperparmeters
777 // BMA TODO: Model after the above scale_residuals, operating block-wise
778 // and accounting for ASV
779 expData.
780 scale_residuals(hyper_params, obsErrorMultiplierMode, num_calib_params,
781 recast_response);
782 }
783 }
784
785
786 /** Pull up the continuous variable values and labels into the
787 RecastModel, inserting the hyper-parameter values/labels */
init_continuous_vars()788 void DataTransformModel::init_continuous_vars()
789 {
790 const SharedVariablesData& svd = subModel.current_variables().shared_data();
791 const SizetArray& sm_vc_totals = svd.components_totals();
792 const RealVector& sm_acv = subModel.all_continuous_variables();
793 StringMultiArrayConstView sm_acvl = subModel.all_continuous_variable_labels();
794 const RealVector & sm_aclb = subModel.all_continuous_lower_bounds();
795 const RealVector & sm_acub = subModel.all_continuous_upper_bounds();
796
797 int continuous_vc_inds[4] = {TOTAL_CDV, TOTAL_CAUV, TOTAL_CEUV, TOTAL_CSV};
798 int hyperparam_vc_ind = get_hyperparam_vc_index(subModel);
799
800 size_t sm_offset = 0;
801 size_t dtm_offset = 0;
802
803 for(const int& vci : continuous_vc_inds) {
804
805 size_t num_cvars = sm_vc_totals[vci];
806 for (size_t i=0; i<num_cvars; ++i) {
807 all_continuous_variable(sm_acv[sm_offset], dtm_offset);
808 all_continuous_variable_label(sm_acvl[sm_offset], dtm_offset);
809 all_continuous_lower_bound(sm_aclb[sm_offset], dtm_offset);
810 all_continuous_upper_bound(sm_acub[sm_offset], dtm_offset);
811 ++sm_offset;
812 ++dtm_offset;
813 }
814 if (vci == hyperparam_vc_ind) {
815 // insert the hyper-parameter values/labels
816 const StringArray& hyper_labels =
817 expData.hyperparam_labels(obsErrorMultiplierMode);
818 for (size_t i=0; i<numHyperparams; ++i) {
819 all_continuous_variable(1.0, dtm_offset);
820 all_continuous_variable_label(hyper_labels[i], dtm_offset);
821 all_continuous_lower_bound(0.0, dtm_offset);
822 all_continuous_upper_bound(std::numeric_limits<double>::infinity(),
823 dtm_offset);
824 ++dtm_offset;
825 }
826 }
827
828 }
829
830 }
831
832
833 /** Passing the inbound array size so we can use one function for
834 Teuchos and std containers (size vs. length) */
835 template<typename T>
836 void DataTransformModel::
expand_primary_array(size_t submodel_size,const T & submodel_array,size_t recast_size,T & recast_array) const837 expand_primary_array(size_t submodel_size, const T& submodel_array,
838 size_t recast_size, T& recast_array) const
839 {
840 // Assume that coefficients have been expanded for fields (weights, scales, sense)
841 // So each is either len1 or num_elements
842 if (submodel_size == 1)
843 // this copy may not be needed, depends on ctor behavior
844 recast_array = submodel_array;
845 else if (submodel_size > 1) {
846
847 // TODO: convenience function to do this
848 // For num_elements case, just fill the recast_array with copies
849 size_t num_exp = expData.num_experiments();
850
851 assert(submodel_size * num_exp == recast_size);
852
853 recast_array.resize(recast_size);
854 size_t calib_term_ind = 0;
855 for (size_t exp_ind = 0; exp_ind < num_exp; ++exp_ind)
856 for (size_t sma_ind = 0; sma_ind < submodel_size; ++sma_ind)
857 recast_array[calib_term_ind++] = submodel_array[sma_ind];
858 }
859 // else leave recast_array empty
860 }
861
862
print_residual_response(const Response & resid_resp)863 void DataTransformModel::print_residual_response(const Response& resid_resp)
864 {
865 if (outputLevel >= VERBOSE_OUTPUT) {
866 Cout << "\n-----------------------------------------------------------";
867 Cout << "\nPost-processing Function Evaluation: Data Transformation";
868 Cout << "\n-----------------------------------------------------------"
869 << std::endl;
870 }
871
872 if (outputLevel >= VERBOSE_OUTPUT &&
873 subordinate_model().num_primary_fns() > 0) {
874 Cout << "Calibration data transformation; residuals:\n";
875 write_data(Cout, resid_resp.function_values(),
876 resid_resp.function_labels());
877 Cout << std::endl;
878 }
879 if (outputLevel >= DEBUG_OUTPUT &&
880 subordinate_model().num_primary_fns() > 0) {
881 Cout << "Calibration data transformation; full response:\n"
882 << resid_resp << std::endl;
883 }
884 }
885
886
887 void DataTransformModel::
print_best_responses(std::ostream & s,const Variables & best_submodel_vars,const Response & best_submodel_resp,size_t num_best,size_t best_ind)888 print_best_responses(std::ostream& s,
889 const Variables& best_submodel_vars,
890 const Response& best_submodel_resp,
891 size_t num_best, size_t best_ind)
892 {
893 // BMA TODO: Why copying the response, why not just update dataTransformModel?
894 Response residual_resp(current_response().copy());
895 // only transform residuals, not derivatives
896 ActiveSet fn_only_as = residual_resp.active_set();
897 fn_only_as.request_values(1);
898 residual_resp.active_set(fn_only_as);
899
900 s << "Original (as-posed) response:\n";
901
902 // print the original userModel Responses
903 if (expData.config_vars().size() == 0) {
904 const RealVector& best_fns = best_submodel_resp.function_values();
905 Minimizer::print_model_resp(subModel.num_primary_fns(), best_fns, num_best,
906 best_ind, s);
907 // form residuals from model responses and apply covariance
908 short dt_verbosity = output_level();
909 output_level(SILENT_OUTPUT);
910 data_transform_response(best_submodel_vars, best_submodel_resp,
911 residual_resp);
912 output_level(dt_verbosity);
913 }
914 else {
915 recover_submodel_responses(s, best_submodel_vars,
916 num_best, best_ind, residual_resp);
917 }
918
919 const RealVector& resid_fns = residual_resp.function_values();
920 // The data transformation may be weighting by variance; remind the user:
921 if (expData.variance_active())
922 s << "Variance-weighted original (as-posed) residuals:\n";
923 else
924 s << "Original (as-posed) residuals:\n";
925 Minimizer::print_residuals(num_primary_fns(), resid_fns, RealVector(),
926 num_best, best_ind, s);
927
928 // must use the expanded weight set from the data difference model
929 const RealVector& lsq_weights = Model::primary_response_fn_weights();
930 Minimizer::print_residuals(num_primary_fns(), resid_fns, lsq_weights,
931 num_best, best_ind, s);
932 }
933
934
935 // The core of this can be modular on the Model (and static)
936 void DataTransformModel::
recover_submodel_responses(std::ostream & s,const Variables & best_submodel_vars,size_t num_best,size_t best_ind,Response & residual_resp)937 recover_submodel_responses(std::ostream& s,
938 const Variables& best_submodel_vars,
939 size_t num_best, size_t best_ind,
940 Response& residual_resp)
941 {
942 if (subModel.num_primary_fns() > 1 || expData.config_vars().size() > 1)
943 s << "<<<<< Best model responses ";
944 else
945 s << "<<<<< Best model response ";
946 if (num_best > 1) s << "(set " << best_ind+1 << ") "; s << "\n";
947
948 // first try cache lookup
949
950 // Have to make sure the Variables object is in the right mode for
951 // inactive operations. Make a deep copy of the SharedVariablesData
952 // to avoid corrupting any inbound Variables information... (const
953 // doesn't protect the SVD)
954 Variables lookup_vars = best_submodel_vars.copy(true);
955 lookup_vars.inactive_view(MIXED_STATE);
956
957 String interface_id = subModel.interface_id();
958 Response lookup_resp = subModel.current_response().copy();
959 ActiveSet lookup_as = lookup_resp.active_set();
960 lookup_as.request_values(1);
961 lookup_resp.active_set(lookup_as);
962 ParamResponsePair lookup_pr(lookup_vars, interface_id, lookup_resp);
963
964 // use model_resp to populate residuals as we go
965 Response model_resp;
966 size_t num_exp = expData.num_experiments();
967 for (size_t i=0; i<num_exp; ++i) {
968
969 // Model::inactive_variables(expData.config_vars()[i], subModel);
970 Model::inactive_variables(expData.config_vars()[i], subModel, lookup_vars);
971
972 // TODO: use user-provided experiment numbers if given
973 s << "<<<<< Best configuration variables (experiment " << i+1 << ") =\n";
974 lookup_vars.write(s, INACTIVE_VARS); // vars object writes labels
975
976 bool lookup_failure = false;
977 // BMA: why is this necessary? Should have a reference to same object as PRP
978 lookup_pr.variables(lookup_vars);
979 PRPCacheHIter cache_it = lookup_by_val(data_pairs, lookup_pr);
980 if (cache_it == data_pairs.get<hashed>().end()) {
981
982 // If model is a data fit surrogate, re-evaluate it if needed.
983 // Didn't use != "hierarchical" in case other surrogate types are added.
984 if ( subModel.model_type() == "surrogate" &&
985 (strbegins(subModel.surrogate_type(), "global_") ||
986 strbegins(subModel.surrogate_type(), "local_") ||
987 strbegins(subModel.surrogate_type(), "multipoint_")) ) {
988 // TODO: Want to make this a quiet evaluation, but not easy to
989 // propagate to the interface?!?
990 //subModel.ouput_level(SILENT_OUTPUT); // and need restore
991 subModel.current_variables() = lookup_vars;
992 subModel.evaluate(lookup_resp.active_set());
993 model_resp = subModel.current_response();
994
995 // TODO: There are other cases where re-evaluate would be
996 // safe, like a recast of a simulation model that has caching
997 // enabled, but don't treat that for now.
998 }
999 else {
1000 // BMA TODO: Consider NaN so downstream output isn't misleading
1001 lookup_failure = true;
1002 s << "Could not retrieve best model responses (experiment " << i+1
1003 << ")";
1004 }
1005 }
1006 else {
1007 model_resp = cache_it->response();
1008 }
1009
1010 if (!lookup_failure) {
1011 expData.form_residuals(model_resp, i, residual_resp);
1012
1013 // By including labels, this deviates from other summary function output
1014 if (subModel.num_primary_fns() > 1)
1015 s << "<<<<< Best model responses (experiment " << i+1 << ") =\n";
1016 else
1017 s << "<<<<< Best model response (experiment " << i+1 << ") =\n";
1018 write_data_partial(s, (size_t)0, subModel.num_primary_fns(),
1019 model_resp.function_values(),
1020 model_resp.function_labels());
1021 }
1022 }
1023
1024 // TODO: this won't scale properly if hyper-parameters are
1025 // calibrated; would need the error multipliers, which are in the
1026 // recast variables space
1027 scale_response(subModel.current_variables(), currentVariables, residual_resp);
1028 }
1029
1030 void DataTransformModel::
archive_best_residuals(const ResultsManager & results_db,const StrStrSizet & iterator_id,const int num_terms,const RealVector & best_terms,const Real wssr,const int num_points,const int point_index)1031 archive_best_residuals(const ResultsManager &results_db,
1032 const StrStrSizet &iterator_id,
1033 const int num_terms,
1034 const RealVector &best_terms,
1035 const Real wssr, const int num_points,
1036 const int point_index) {
1037 if(!results_db.active()) return;
1038
1039 StringArray residuals_location;
1040 StringArray norm_location;
1041 if(num_points > 1) {
1042 String set_string = String("set:") + std::to_string(point_index+1);
1043 residuals_location.push_back(set_string);
1044 norm_location.push_back(set_string);
1045 }
1046 residuals_location.push_back("best_residuals");
1047 norm_location.push_back("best_norm");
1048 Teuchos::SerialDenseVector<int, Real> residuals(Teuchos::View,
1049 const_cast<Real*>(&best_terms[0]),
1050 num_terms);
1051 results_db.insert(iterator_id, residuals_location, residuals);
1052 results_db.insert(iterator_id, norm_location, wssr);
1053 }
1054
archive_best_original(const ResultsManager & results_db,const StrStrSizet & iterator_id,const RealVector & function_values,const int & exp_index,const int & num_best,const int & best_index)1055 void DataTransformModel::archive_best_original(const ResultsManager &results_db,
1056 const StrStrSizet &iterator_id,
1057 const RealVector &function_values,
1058 const int &exp_index,
1059 const int &num_best,
1060 const int &best_index) {
1061 if(!results_db.active()) return;
1062
1063 DimScaleMap scales;
1064 scales.emplace(0, StringScale("responses", subModel.response_labels(),
1065 ScaleScope::SHARED)
1066 );
1067
1068 StringArray location;
1069 if(num_best > 1)
1070 location.push_back(String("set:") + std::to_string(best_index+1));
1071 location.push_back("best_model_responses");
1072 if(expData.num_config_vars()) {
1073 location.push_back(String("experiment:") + std::to_string(exp_index+1));
1074 location.push_back("responses");
1075 }
1076
1077 results_db.insert(iterator_id, location, function_values, scales);
1078 }
1079
1080
1081 void DataTransformModel::
archive_submodel_responses(const ResultsManager & results_db,const StrStrSizet & iterator_id,const Variables & best_submodel_vars,size_t num_best,size_t best_ind,Response & residual_resp)1082 archive_submodel_responses(const ResultsManager &results_db,
1083 const StrStrSizet &iterator_id,
1084 const Variables& best_submodel_vars,
1085 size_t num_best, size_t best_ind,
1086 Response& residual_resp)
1087 {
1088 // first try cache lookup
1089
1090 // Have to make sure the Variables object is in the right mode for
1091 // inactive operations. Make a deep copy of the SharedVariablesData
1092 // to avoid corrupting any inbound Variables information... (const
1093 // doesn't protect the SVD)
1094
1095
1096 Variables lookup_vars = best_submodel_vars.copy(true);
1097 lookup_vars.inactive_view(MIXED_STATE);
1098
1099 String interface_id = subModel.interface_id();
1100 Response lookup_resp = subModel.current_response().copy();
1101 ActiveSet lookup_as = lookup_resp.active_set();
1102 lookup_as.request_values(1);
1103 lookup_resp.active_set(lookup_as);
1104 ParamResponsePair lookup_pr(lookup_vars, interface_id, lookup_resp);
1105
1106 // use model_resp to populate residuals as we go
1107 Response model_resp;
1108 size_t num_exp = expData.num_experiments();
1109 for (size_t i=0; i<num_exp; ++i) {
1110
1111 // Model::inactive_variables(expData.config_vars()[i], subModel);
1112 Model::inactive_variables(expData.config_vars()[i], subModel, lookup_vars);
1113
1114 bool lookup_failure = false;
1115 // BMA: why is this necessary? Should have a reference to same object as PRP
1116 lookup_pr.variables(lookup_vars);
1117 PRPCacheHIter cache_it = lookup_by_val(data_pairs, lookup_pr);
1118 if (cache_it == data_pairs.get<hashed>().end()) {
1119
1120 // If model is a data fit surrogate, re-evaluate it if needed.
1121 // Didn't use != "hierarchical" in case other surrogate types are added.
1122 if ( subModel.model_type() == "surrogate" &&
1123 (strbegins(subModel.surrogate_type(), "global_") ||
1124 strbegins(subModel.surrogate_type(), "local_") ||
1125 strbegins(subModel.surrogate_type(), "multipoint_")) ) {
1126 // TODO: Want to make this a quiet evaluation, but not easy to
1127 // propagate to the interface?!?
1128 //subModel.ouput_level(SILENT_OUTPUT); // and need restore
1129 subModel.current_variables() = lookup_vars;
1130 subModel.evaluate(lookup_resp.active_set());
1131 model_resp = subModel.current_response();
1132
1133 // TODO: There are other cases where re-evaluate would be
1134 // safe, like a recast of a simulation model that has caching
1135 // enabled, but don't treat that for now.
1136 }
1137 }
1138 else {
1139 model_resp = cache_it->response();
1140 }
1141
1142 if (!lookup_failure) {
1143 expData.form_residuals(model_resp, i, residual_resp);
1144 archive_best_original(results_db, iterator_id,
1145 model_resp.function_values(),
1146 i, num_best, best_ind);
1147 if(expData.num_config_vars() != 0)
1148 archive_best_config_variables(results_db, iterator_id,
1149 lookup_vars, i,
1150 num_best, best_ind);
1151
1152 }
1153 }
1154
1155 // TODO: this won't scale properly if hyper-parameters are
1156 // calibrated; would need the error multipliers, which are in the
1157 // recast variables space
1158 scale_response(subModel.current_variables(), currentVariables, residual_resp);
1159 }
1160
1161 /// archive best responses
1162 void DataTransformModel::
archive_best_responses(const ResultsManager & results_db,const StrStrSizet iterator_id,const Variables & best_submodel_vars,const Response & best_submodel_resp,size_t num_best,size_t best_ind)1163 archive_best_responses(const ResultsManager &results_db,
1164 const StrStrSizet iterator_id,
1165 const Variables& best_submodel_vars,
1166 const Response& best_submodel_resp,
1167 size_t num_best, size_t best_ind) {
1168
1169 // BMA TODO: Why copying the response, why not just update dataTransformModel?
1170 Response residual_resp(current_response().copy());
1171 // only transform residuals, not derivatives
1172 ActiveSet fn_only_as = residual_resp.active_set();
1173 fn_only_as.request_values(1);
1174 residual_resp.active_set(fn_only_as);
1175
1176 // print the original userModel Responses
1177 if (expData.config_vars().size() == 0) {
1178 const RealVector& best_fns = best_submodel_resp.function_values();
1179 archive_best_original(results_db, iterator_id, best_fns, 0, num_best, best_ind);
1180 // form residuals from model responses and apply covariance
1181 short dt_verbosity = output_level();
1182 output_level(SILENT_OUTPUT);
1183 data_transform_response(best_submodel_vars, best_submodel_resp,
1184 residual_resp);
1185 output_level(dt_verbosity);
1186 }
1187 else {
1188 archive_submodel_responses(results_db, iterator_id, best_submodel_vars,
1189 num_best, best_ind, residual_resp);
1190 }
1191
1192 const RealVector& resid_fns = residual_resp.function_values();
1193 // print_residuals will archive the residuals. for now, skip archiving the un-weighted
1194 // ones by passing in an uninitialized ResultsManager.
1195 const RealVector& lsq_weights = Model::primary_response_fn_weights();
1196 Real wssr = std::sqrt(Minimizer::sum_squared_residuals(num_primary_fns(),
1197 resid_fns, lsq_weights)
1198 );
1199 // Currently only the weighted residuals are archived. This differs from what's printed
1200 // to the console (see print_best_responses() above).
1201 archive_best_residuals(results_db, iterator_id, num_primary_fns(),
1202 resid_fns, wssr, num_best, best_ind);
1203
1204 }
1205
1206 /// Archive best configuration variables
1207 void DataTransformModel::
archive_best_config_variables(const ResultsManager & results_db,const StrStrSizet & iterator_id,const Variables & vars,const int & exp_index,const int & num_best,const int & best_index)1208 archive_best_config_variables(const ResultsManager &results_db,
1209 const StrStrSizet &iterator_id,
1210 const Variables &vars,
1211 const int &exp_index,
1212 const int &num_best,
1213 const int &best_index) {
1214
1215 if(!results_db.active()) return;
1216 // When using configuration variables, all inactive variables are
1217 // expected to be config vars.
1218
1219 const auto & cv_labels = vars.inactive_continuous_variable_labels();
1220 const auto & div_labels = vars.inactive_discrete_int_variable_labels();
1221 const auto & dsv_labels = vars.inactive_discrete_string_variable_labels();
1222 const auto & drv_labels = vars.inactive_discrete_real_variable_labels();
1223
1224 StringArray location;
1225 size_t d_index = 2;
1226 if(num_best > 1) {
1227 location.push_back(String("set:") + std::to_string(best_index+1));
1228 d_index = 3;
1229
1230 }
1231 location.push_back("best_model_responses");
1232 location.push_back(String("experiment:") + std::to_string(exp_index+1));
1233 location.push_back(""); // placeholder for variable domain
1234 if(cv_labels.size()) {
1235 location[d_index] = "continuous_config_variables";
1236 DimScaleMap scales;
1237 scales.emplace(0, StringScale("variables", cv_labels));
1238 results_db.insert(iterator_id, location,
1239 vars.inactive_continuous_variables(), scales);
1240 }
1241 if(div_labels.size()) {
1242 location[d_index] = "discrete_integer_config_variables";
1243 DimScaleMap scales;
1244 scales.emplace(0, StringScale("variables", div_labels));
1245 results_db.insert(iterator_id, location,
1246 vars.inactive_discrete_int_variables(), scales);
1247 }
1248 if(dsv_labels.size()) {
1249 location[d_index] = "discrete_string_config_variables";
1250 DimScaleMap scales;
1251 scales.emplace(0, StringScale("variables", dsv_labels));
1252 results_db.insert(iterator_id, location,
1253 vars.inactive_discrete_string_variables(), scales);
1254 }
1255 if(drv_labels.size()) {
1256 location[d_index] = "discrete_real_config_variables";
1257 DimScaleMap scales;
1258 scales.emplace(0, StringScale("variables", drv_labels));
1259 results_db.insert(iterator_id, location,
1260 vars.inactive_discrete_real_variables(), scales);
1261 }
1262 }
1263
num_config_vars() const1264 int DataTransformModel::num_config_vars() const {
1265 return expData.num_config_vars();
1266 }
1267
1268 } // namespace Dakota
1269