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 #include "ExperimentData.hpp"
10 #include "DataMethod.hpp"
11 #include "ProblemDescDB.hpp"
12
13 namespace Dakota {
14
ExperimentData()15 ExperimentData::ExperimentData():
16 calibrationDataFlag(false), numExperiments(0), numConfigVars(0),
17 covarianceDeterminant(1.0), logCovarianceDeterminant(0.0),
18 scalarDataFormat(TABULAR_EXPER_ANNOT), scalarSigmaPerRow(0),
19 readSimFieldCoords(false), interpolateFlag(false), outputLevel(NORMAL_OUTPUT)
20 { /* empty ctor */ }
21
22
23 ExperimentData::
ExperimentData(const ProblemDescDB & pddb,const SharedResponseData & srd,short output_level)24 ExperimentData(const ProblemDescDB& pddb,
25 const SharedResponseData& srd, short output_level):
26 calibrationDataFlag(pddb.get_bool("responses.calibration_data")),
27 numExperiments(pddb.get_sizet("responses.num_experiments")),
28 numConfigVars(pddb.get_sizet("responses.num_config_vars")),
29 covarianceDeterminant(1.0), logCovarianceDeterminant(0.0),
30 scalarDataFilename(pddb.get_string("responses.scalar_data_filename")),
31 scalarDataFormat(pddb.get_ushort("responses.scalar_data_format")),
32 scalarSigmaPerRow(0),
33 readSimFieldCoords(pddb.get_bool("responses.read_field_coordinates")),
34 interpolateFlag(pddb.get_bool("responses.interpolate")),
35 outputLevel(output_level)
36 {
37 initialize(pddb.get_sa("responses.variance_type"), srd);
38 }
39
40
41 ExperimentData::
ExperimentData(size_t num_experiments,size_t num_config_vars,const boost::filesystem::path & data_prefix,const SharedResponseData & srd,const StringArray & variance_types,short output_level,std::string scalar_data_filename)42 ExperimentData(size_t num_experiments, size_t num_config_vars,
43 const boost::filesystem::path& data_prefix,
44 const SharedResponseData& srd,
45 const StringArray& variance_types,
46 short output_level,
47 std::string scalar_data_filename):
48 calibrationDataFlag(true), numExperiments(num_experiments),
49 numConfigVars(num_config_vars),
50 covarianceDeterminant(1.0), logCovarianceDeterminant(0.0),
51 dataPathPrefix(data_prefix), scalarDataFilename(scalar_data_filename),
52 scalarDataFormat(TABULAR_EXPER_ANNOT), scalarSigmaPerRow(0),
53 readSimFieldCoords(false), interpolateFlag(false), outputLevel(output_level)
54 {
55 initialize(variance_types, srd);
56 }
57
58 ExperimentData::
ExperimentData(size_t num_experiments,const SharedResponseData & srd,const RealMatrix & config_vars,const IntResponseMap & all_responses,short output_level)59 ExperimentData(size_t num_experiments, const SharedResponseData& srd,
60 const RealMatrix& config_vars,
61 const IntResponseMap& all_responses, short output_level):
62 calibrationDataFlag(false), numExperiments(num_experiments),
63 numConfigVars(config_vars.numRows()),
64 covarianceDeterminant(1.0), logCovarianceDeterminant(0.0),
65 scalarDataFormat(TABULAR_EXPER_ANNOT), scalarSigmaPerRow(0),
66 readSimFieldCoords(false), interpolateFlag(false), outputLevel(output_level)
67 {
68 simulationSRD = srd.copy();
69 allConfigVars.resize(numExperiments);
70 for (size_t i=0; i<numExperiments; ++i) {
71 allConfigVars[i] =
72 Teuchos::getCol(Teuchos::Copy, const_cast<RealMatrix&>(config_vars),
73 (int) i);
74 if (outputLevel >= DEBUG_OUTPUT)
75 Cout << " allConfigVars i " << allConfigVars[i] << '\n';
76 }
77 if (outputLevel >= DEBUG_OUTPUT)
78 Cout << "Number of config vars " << numConfigVars << '\n';
79
80 // BMA TODO: This doesn't make an object of type ExperimentResponse!
81 IntRespMCIter resp_it = all_responses.begin();
82 IntRespMCIter resp_end = all_responses.end();
83 for ( ; resp_it != resp_end; ++resp_it)
84 allExperiments.push_back(resp_it->second);
85 }
86
87
initialize(const StringArray & variance_types,const SharedResponseData & srd)88 void ExperimentData::initialize(const StringArray& variance_types,
89 const SharedResponseData& srd)
90 {
91 // only initialize data if needed; TODO: consider always initializing
92 if (calibrationDataFlag || !scalarDataFilename.empty()) {
93
94 if (outputLevel > NORMAL_OUTPUT) {
95 Cout << "Constructing ExperimentData with " << numExperiments
96 << " experiment(s).";
97 if (!scalarDataFilename.empty())
98 Cout << "\n Scalar data file name: '" << scalarDataFilename << "'";
99 Cout << std::endl;
100 }
101
102 if (interpolateFlag) {
103 // can't interpolate if there are no simulation coordinates
104 if (!readSimFieldCoords) {
105 Cerr << "\nError: calibration data 'interpolate' option not available "
106 << "if simulation coordinates are not read in also. "
107 << "Please specify simulation coordinates with read_field_coordinates.\n";
108 abort_handler(-1);
109 }
110
111 // can't use normInf as the vector is a 1 x num_fields matrix
112 bool multiple_coords = false;
113 const IntVector coords_per_field = srd.num_coords_per_field();
114 for (size_t f_ind = 0; f_ind < coords_per_field.length(); ++f_ind)
115 if (coords_per_field[f_ind] > 1) {
116 multiple_coords = true;
117 break;
118 }
119 if (multiple_coords) {
120 Cerr << "\nError: calibration data 'interpolate' option not available "
121 << "for fields with\n more than 1 independent coordinate.\n";
122 abort_handler(-1);
123 }
124 }
125
126 // for now, copy in case any recasting between construct and read;
127 // don't want to share a rep, or do we?
128 simulationSRD = srd.copy();
129
130 parse_sigma_types(variance_types);
131 }
132 else
133 {
134 experimentLengths.sizeUninitialized(1);
135 experimentLengths[0] = srd.num_functions();
136 expOffsets.size(1); // init to 0
137 }
138 }
139
140 /** Validate user-provided sigma specifcation. User can specify 0, 1,
141 or num_response_groups sigmas. If specified, sigma types must be
142 the same for all scalar responses. */
parse_sigma_types(const StringArray & sigma_types)143 void ExperimentData::parse_sigma_types(const StringArray& sigma_types)
144 {
145 // leave array empty if not needed (could have many responses and no sigmas)
146 if (sigma_types.size() == 0)
147 return;
148
149 // valid options for sigma_type, and mapping to enum
150 std::map<String, unsigned short> sigma_map;
151 sigma_map["none"] = NO_SIGMA;
152 sigma_map["scalar"] = SCALAR_SIGMA;
153 sigma_map["diagonal"] = DIAGONAL_SIGMA;
154 sigma_map["matrix"] = MATRIX_SIGMA;
155
156 // expand sigma if 0 or 1 given, without validation
157 size_t num_resp_groups = simulationSRD.num_response_groups();
158 size_t num_scalar = simulationSRD.num_scalar_primary();
159 varianceTypes.resize(num_resp_groups, NO_SIGMA);
160 if (sigma_types.size() == 1) {
161 // assign all sigmas to the specified one
162 if (sigma_map.find(sigma_types[0]) != sigma_map.end())
163 varianceTypes.assign(num_resp_groups, sigma_map[sigma_types[0]]);
164 else {
165 Cerr << "\nError: invalid sigma_type '" << sigma_types[0]
166 << "' specified." << std::endl;
167 abort_handler(PARSE_ERROR);
168 }
169 }
170 else if (sigma_types.size() == num_resp_groups) {
171 // initialize one sigma type per
172 for (size_t resp_ind = 0; resp_ind < num_resp_groups; ++resp_ind) {
173 if (sigma_map.find(sigma_types[resp_ind]) != sigma_map.end())
174 varianceTypes[resp_ind] = sigma_map[sigma_types[resp_ind]];
175 else {
176 Cerr << "\nError: invalid sigma_type '" << sigma_types[resp_ind]
177 << "' specified." << std::endl;
178 abort_handler(PARSE_ERROR);
179 }
180 }
181 }
182 else {
183 Cerr << "\nError: sigma_types must have length 1 or number of "
184 << "calibration_terms." << std::endl;
185 abort_handler(PARSE_ERROR);
186 }
187
188 // when using simple scalar data format, must validate that all
189 // scalar are the same and valid (when using separate files, can
190 // differ)
191
192 // scalar sigma must be 0 or scalar
193 bool scalar_data_file = !scalarDataFilename.empty();
194 for (size_t scalar_ind = 0; scalar_ind < num_scalar; ++scalar_ind) {
195 if (varianceTypes[scalar_ind] != NO_SIGMA &&
196 varianceTypes[scalar_ind] != SCALAR_SIGMA) {
197 Cerr << "\nError: sigma_type must be 'none' or 'scalar' for scalar "
198 << "responses." << std::endl;
199 abort_handler(PARSE_ERROR);
200 }
201 if (scalar_data_file) {
202 if (varianceTypes[scalar_ind] != varianceTypes[0]) {
203 Cerr << "\nError: sigma_type must be the same for all scalar responses "
204 << "when using scalar data file."
205 << std::endl;
206 abort_handler(PARSE_ERROR);
207 }
208 }
209 }
210 // number of sigma to read from simple data file(0 or num_scalar)
211 if (scalar_data_file && varianceTypes.size() > 0 &&
212 varianceTypes[0] == SCALAR_SIGMA)
213 scalarSigmaPerRow = num_scalar;
214
215 }
216
217 void ExperimentData::
add_data(const RealVector & one_configvars,const Response & one_response)218 add_data(const RealVector& one_configvars, const Response& one_response)
219 {
220 // BMA TODO: This doesn't make an object of type ExperimentResponse!
221 numExperiments += 1;
222 if (outputLevel >= DEBUG_OUTPUT)
223 Cout << "numExperiments in add_data " << numExperiments << '\n';
224
225 allConfigVars.push_back(one_configvars);
226 allExperiments.push_back(one_response);
227 }
228
229
load_data(const std::string & context_message)230 void ExperimentData::load_data(const std::string& context_message)
231 {
232 // TODO: complete scalar and field cases
233
234 bool scalar_data_file = !scalarDataFilename.empty();
235 if (!calibrationDataFlag && !scalar_data_file) {
236 Cerr << "\nError: load_data attempted for empty experiment spec."
237 << std::endl;
238 abort_handler(-1);
239 }
240
241 // Get a copy of the simulation SRD to use in contructing the
242 // experiment response; won't be able to share the core data since
243 // different experiments may have different sizes...
244 SharedResponseData exp_srd = simulationSRD.copy();
245
246 // change the type of response
247 // TODO: new ctor for experiment response
248 exp_srd.response_type(EXPERIMENT_RESPONSE);
249 Response exp_resp(exp_srd);
250 if (outputLevel >= DEBUG_OUTPUT) {
251 Cout << "Constructing experiment response; initial response is:"
252 << std::endl;
253 exp_resp.write(Cout);
254 }
255
256 if (numConfigVars > 0)
257 allConfigVars.resize(numExperiments);
258
259 size_t num_scalars = simulationSRD.num_scalar_primary();
260
261 // Count how many fields have each sigma type (for sizing). For
262 // field data, if "scalar" or "none" is specified, need to convert
263 // to a diagonal the length of the actual data
264
265 // only fields can have matrix or diagonal
266 size_t num_field_sigma_matrices =
267 std::count(varianceTypes.begin(), varianceTypes.end(), MATRIX_SIGMA);
268 size_t num_field_sigma_diagonals =
269 std::count(varianceTypes.begin(), varianceTypes.end(), DIAGONAL_SIGMA);
270 // this counts how many fields have scalar or no sigma, as given by user
271 // varianceTypes is either empty or number response groups
272 size_t num_field_sigma_scalars = 0;
273 size_t num_field_sigma_none = 0;
274 if( !varianceTypes.empty() ) {
275 num_field_sigma_scalars =
276 std::count(varianceTypes.begin()+num_scalars, varianceTypes.end(),
277 SCALAR_SIGMA);
278 num_field_sigma_none =
279 std::count(varianceTypes.begin()+num_scalars, varianceTypes.end(),
280 NO_SIGMA);
281 }
282
283 // TODO: temporary duplication until necessary covariance APIs are updated
284
285 // setup for reading historical format, one experiment per line,
286 // each consisting of [ [config_vars] fn values [ fn variance ] ]
287 std::ifstream scalar_data_stream;
288 if (scalar_data_file) {
289 if (outputLevel >= NORMAL_OUTPUT) {
290 Cout << "\nReading scalar experimental data from file "
291 << scalarDataFilename << ":";
292 Cout << "\n " << numExperiments << " experiment(s) for";
293 Cout << "\n " << num_scalars << " scalar responses" << std::endl;
294 }
295 TabularIO::open_file(scalar_data_stream, scalarDataFilename,
296 context_message);
297 TabularIO::read_header_tabular(scalar_data_stream, scalarDataFormat);
298 }
299
300 if (!scalar_data_file && numConfigVars > 0) {
301 // read all experiment config vars from new field data format files at once
302 // TODO: have the user give a name for this file, since should be
303 // the same for all responses. Read from foo.<exp_num>.config.
304 // String config_vars_basename("experiment");
305 boost::filesystem::path config_vars_basepath = dataPathPrefix / "experiment";
306 try {
307 read_config_vars_multifile(config_vars_basepath.string(), numExperiments,
308 numConfigVars, allConfigVars);
309 }
310 catch(std::runtime_error & e)
311 {
312 if( numConfigVars > 0 )
313 throw
314 std::runtime_error("Expected to read " +
315 convert_to_string(numConfigVars) +
316 " experiment config variables, but required file(s) \"" +
317 config_vars_basepath.string() +
318 ".*.config\" not found.");
319 }
320 }
321
322 bool found_error = false;
323 for (size_t exp_index = 0; exp_index < numExperiments; ++exp_index) {
324
325 // conditionally read leading exp_id column
326 // TODO: error handling
327 if (scalar_data_file)
328 TabularIO::read_leading_columns(scalar_data_stream, scalarDataFormat);
329
330 // -----
331 // Read and set the configuration variables
332 // -----
333
334 // Need to decide what to do if both scalar_data_file and "experiment.#" files exist - RWH
335 if ( (numConfigVars > 0) && scalar_data_file ) {
336 allConfigVars[exp_index].sizeUninitialized(numConfigVars);
337 // TODO: try/catch
338 scalar_data_stream >> std::ws;
339 if ( scalar_data_stream.eof() ) {
340 Cerr << "\nError: End of file '" << scalarDataFilename
341 << "' reached before reading "
342 << numExperiments << " sets of values."
343 << std::endl;
344 abort_handler(-1);
345 }
346 scalar_data_stream >> allConfigVars[exp_index];
347 }
348 // TODO: else validate scalar vs. field configs?
349
350 // read one file per field response, resize, and populate the
351 // experiment (values, sigma, coords)
352 load_experiment(exp_index, scalar_data_stream, num_field_sigma_matrices,
353 num_field_sigma_diagonals, num_field_sigma_scalars,
354 num_field_sigma_none, exp_resp);
355
356 if (simulationSRD.field_lengths() != exp_resp.field_lengths() &&
357 !interpolateFlag) {
358 Cerr << "\nError: field lengths of experiment " << exp_index+1
359 << " data:\n " << exp_resp.field_lengths()
360 << "\n differ from simulation field lengths:"
361 << simulationSRD.field_lengths() << "and 'interpolate' not enabled."
362 << std::endl;
363 found_error = true;
364 }
365
366 if (outputLevel >= DEBUG_OUTPUT)
367 Cout << "Values for experiment " << exp_index + 1 << ": \n"
368 << exp_resp.function_values() << std::endl;
369
370 allExperiments.push_back(exp_resp.copy());
371 }
372 if (found_error)
373 abort_handler(-1);
374
375
376 // verify that the two experiments have different data
377 if (outputLevel >= DEBUG_OUTPUT) {
378 Cout << "Experiment data summary:\n\n";
379 for (size_t i=0; i<numExperiments; ++i) {
380 if (numConfigVars > 0)
381 Cout << " Experiment " << i+1 << " configuration variables:"<< "\n"
382 << allConfigVars[i];
383 Cout << " Experiment " << i+1 << " data values:"<< "\n"
384 << allExperiments[i].function_values() << '\n';
385 }
386 }
387
388 // store the lengths (number of functions) of each experiment
389 per_exp_length(experimentLengths);
390 size_t i, num_exp = allExperiments.size();
391 expOffsets.sizeUninitialized(num_exp);
392 expOffsets(0) = 0;
393 for (i=1; i<num_exp; i++)
394 expOffsets(i) = experimentLengths(i-1) + expOffsets(i-1);
395
396 // Precompute and cache experiment determinants
397 covarianceDeterminant = 1.0;
398 logCovarianceDeterminant = 0.0;
399 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind) {
400 covarianceDeterminant *= allExperiments[exp_ind].covariance_determinant();
401 logCovarianceDeterminant +=
402 allExperiments[exp_ind].log_covariance_determinant();
403 }
404
405 // Check and warn if extra data exists in scalar_data_stream
406 if (scalar_data_file) {
407 scalar_data_stream >> std::ws;
408 if ( !scalar_data_stream.eof() )
409 Cout << "\nWarning: Data file '" << scalarDataFilename
410 << "' contains extra data."
411 << std::endl;
412 }
413 }
414
415
416 /** Load an experiment from a mixture of legacy format data and field
417 data format files */
418 void ExperimentData::
load_experiment(size_t exp_index,std::ifstream & scalar_data_stream,size_t num_field_sigma_matrices,size_t num_field_sigma_diagonals,size_t num_field_sigma_scalars,size_t num_field_sigma_none,Response & exp_resp)419 load_experiment(size_t exp_index, std::ifstream& scalar_data_stream,
420 size_t num_field_sigma_matrices,
421 size_t num_field_sigma_diagonals,
422 size_t num_field_sigma_scalars,
423 size_t num_field_sigma_none, Response& exp_resp)
424 {
425 bool scalar_data_file = !scalarDataFilename.empty();
426 size_t num_scalars = simulationSRD.num_scalar_primary();
427 size_t num_fields = simulationSRD.num_field_response_groups();
428 size_t num_resp = num_scalars + num_fields;
429
430
431 // -----
432 // Data to populate from files for a single experiment
433 // -----
434
435 // Since the field lengths aren't known until all reads are
436 // complete, use temporary storage to read them all, then resize the
437 // Response object.
438 //
439 // TODO: May want to have field lengths managed in Response instead
440 // of SharedResponse and generate labels on the fly when needed.
441
442 // scalar or field values; the RealVectors for scalars will have
443 // length 1; the length of RealVectors for fields will be determined
444 // by the file read
445 RealVectorArray exp_values(num_scalars + num_fields);
446
447 // field lengths may differ for each experiment
448 IntVector field_lengths(num_fields, 0);
449
450 // coordinates for fields only
451 RealMatrix exp_coords;
452
453 // Non-field response sigmas; field response sigma scalars later get expanded into diagonals
454 RealVector sigma_scalars;
455 IntVector scalar_map_indices;
456
457 // -----
458 // Read the data
459 // -----
460
461 // populate scalar data function and sigma values; translate data
462 // from historical to new format (later collapse and eliminate
463 // copies) TODO: advance a row of exp data in outer context and pass
464 // in here to simplify these two cases
465 sigma_scalars.resize(num_scalars);
466 scalar_map_indices.resize(num_scalars);
467 if (scalar_data_file) {
468 // Non-field response sigmas; field response sigma scalars later get expanded into diagonals
469 for (size_t fn_index = 0; fn_index < num_scalars; ++fn_index) {
470 exp_values[fn_index].resize(1);
471 scalar_data_stream >> std::ws;
472 if ( scalar_data_stream.eof() ) {
473 Cerr << "\nError: End of file '" << scalarDataFilename
474 << "' reached before reading "
475 << numExperiments << " sets of values."
476 << std::endl;
477 abort_handler(-1);
478 }
479 scalar_data_stream >> exp_values[fn_index];
480 }
481 if (scalarSigmaPerRow > 0)
482 read_scalar_sigma(scalar_data_stream, sigma_scalars, scalar_map_indices);
483 else {
484 sigma_scalars = 1.0; // historically these defaulted to 1.0
485 for (size_t i = 0; i<num_scalars; ++i) {
486 scalar_map_indices[i] = i;
487 }
488 // BMA: in a mixed case we might want these populated with 1.0
489 // even if data missing
490 }
491 }
492 else
493 {
494 RealMatrix working_cov_values;
495 const StringArray& scalar_labels = exp_resp.function_labels();
496 for( size_t i=0; i<num_scalars; ++i ) {
497 //std::cout << "Scalar function " << i << ": veriance_type = " << varianceTypes[i] << ", label = \"" << scalar_labels[i] << "\"" << std::endl;
498 // Read data from file named: scalar_labels.expt_num.dat
499 boost::filesystem::path field_base = dataPathPrefix / scalar_labels[i];
500 read_field_values(field_base.string(), exp_index+1, exp_values[i]);
501
502 // Optionally allow covariance data
503 if (!varianceTypes.empty()) {
504 if( varianceTypes[i] ) {
505 read_covariance(field_base.string(), exp_index+1, working_cov_values);
506 sigma_scalars[i] = working_cov_values(0,0);
507 scalar_map_indices[i] = i;
508 }
509 else {
510 sigma_scalars[i] = 1.0;
511 scalar_map_indices[i] = i;
512 }
513 }
514 }
515
516 }
517
518 // Data for sigma - Note: all fields have matrix, diagonal or none covariance (sigma_ data)
519 // and so we dimension accordingly.
520
521 // For sanity checking
522 size_t count_no_sigmas = 0;
523 size_t count_sigma_scalars = 0;
524 size_t count_sigma_diagonals = 0;
525 size_t count_sigma_matrices = 0;
526
527 std::vector<RealMatrix> sigma_matrices(num_field_sigma_matrices);
528 // for fields we make diagonal covariance for these three types
529 std::vector<RealVector> sigma_diagonals(num_field_sigma_diagonals +
530 num_field_sigma_scalars +
531 num_field_sigma_none);
532 // indices for the entries in the above data structures
533 IntVector matrix_map_indices(num_field_sigma_matrices),
534 diagonal_map_indices(num_field_sigma_diagonals +
535 num_field_sigma_scalars +
536 num_field_sigma_none);
537
538
539 // populate field data, sigma, and coordinates from separate files
540 const StringArray& field_labels = exp_resp.field_group_labels();
541 for (size_t field_index = 0; field_index < num_fields; ++field_index) {
542 size_t fn_index = num_scalars + field_index;
543 const String& field_name = field_labels[field_index];
544
545 // read a column vector of field values for this field from
546 // fn_name.exp_num.dat
547 boost::filesystem::path field_base = dataPathPrefix / field_name;
548 read_field_values(field_base.string(), exp_index+1, exp_values[fn_index]);
549 field_lengths[field_index] = exp_values[fn_index].length();
550
551 // For fields with corresponding coords file, read coordinates
552 // from field_name.exp_num.coords and validate number of rows is
553 // field_lengths[field_index]
554 std::string filename = field_name + "." + Dakota::convert_to_string(exp_index+1) + ".coords";
555 boost::filesystem::path coord_path_and_file = dataPathPrefix / filename;
556 if ( boost::filesystem::is_regular_file(coord_path_and_file) )
557 {
558 boost::filesystem::path coord_base = dataPathPrefix / field_name;
559 read_coord_values(coord_base.string(), exp_index+1, exp_coords);
560 // Sanity check length
561 if( field_lengths[field_index] != exp_coords.numRows() )
562 throw std::runtime_error("Inconsistent lengths of field data and coordinates.");
563 exp_resp.field_coords(exp_coords, field_index);
564 }
565
566 if (!varianceTypes.empty()) {
567 // read sigma 1, N (field_lengths[field_index]), or N^2 values
568 RealMatrix working_cov_values;
569 switch(varianceTypes[fn_index])
570 {
571 case NO_SIGMA:
572 // expand to a diagonal of 1.0 of appropriate length = field_length and add
573 // field_index to diagonals map
574 sigma_diagonals[count_sigma_diagonals].sizeUninitialized(field_lengths[field_index]);
575 for( int i=0; i<field_lengths[field_index]; ++i )
576 sigma_diagonals[count_sigma_diagonals](i) = 1.0;
577 diagonal_map_indices[count_sigma_diagonals++] = fn_index; // or should it be field_index? - RWH
578 count_no_sigmas++;
579 break;
580
581 case SCALAR_SIGMA:
582 // read single value, expand to a diagonal of appropriate length = field_length and add
583 // field_index to diagonals map
584 Cout << "Reading scalar cov from " << field_base.string() << std::endl;
585 read_covariance(field_base.string(), exp_index+1, working_cov_values);
586 sigma_diagonals[count_sigma_diagonals].sizeUninitialized(field_lengths[field_index]);
587 for( int i=0; i<field_lengths[field_index]; ++i )
588 sigma_diagonals[count_sigma_diagonals](i) = working_cov_values(0,0);
589 diagonal_map_indices[count_sigma_diagonals++] = fn_index; // or should it be field_index? - RWH
590 count_sigma_scalars++;
591 break;
592
593 case DIAGONAL_SIGMA:
594 // read N values, add to sigma_diagonals and add num_scalars +
595 // field_index to diagonals map
596 Cout << "Reading diagonal cov from " << field_base.string() << std::endl;
597 read_covariance(field_base.string(), exp_index+1, Dakota::CovarianceMatrix::VECTOR,
598 field_lengths[field_index], working_cov_values);
599 sigma_diagonals[count_sigma_diagonals].sizeUninitialized(field_lengths[field_index]);
600 for( int i=0; i<field_lengths[field_index]; ++i )
601 sigma_diagonals[count_sigma_diagonals](i) = working_cov_values[0][i];
602 diagonal_map_indices[count_sigma_diagonals++] = fn_index; // or should it be field_index? - RWH
603 //sigma_diagonals[count_sigma_diagonals-1].print(Cout);
604 break;
605
606 case MATRIX_SIGMA:
607 // read N^2 values, add to sigma_matrices and add num_scalars +
608 // field_index to matrices map
609 read_covariance(field_base.string(), exp_index+1, Dakota::CovarianceMatrix::MATRIX,
610 field_lengths[field_index], working_cov_values);
611 // Check for symmetry
612 if( !is_matrix_symmetric(working_cov_values) )
613 throw std::runtime_error("Covariance matrix from \""+field_base.string()+"\" is not symmetric.");
614 sigma_matrices[count_sigma_matrices] = working_cov_values;
615 matrix_map_indices[count_sigma_matrices++] = fn_index; // or should it be field_index? - RWH
616 //sigma_matrices[count_sigma_matrices-1].print(Cout);
617 break;
618 }
619 }
620 }
621 // Sanity check consistency
622 if( count_no_sigmas != num_field_sigma_none )
623 throw std::runtime_error("Mismatch between specified and actual fields with no sigma provided.");
624 if( count_sigma_scalars != num_field_sigma_scalars )
625 throw std::runtime_error("Mismatch between specified and actual sigma scalars.");
626 if( count_sigma_diagonals != (num_field_sigma_diagonals +
627 num_field_sigma_scalars + num_field_sigma_none) )
628 throw std::runtime_error("Mismatch between specified and actual sigma diagonals.");
629 if( count_sigma_matrices != num_field_sigma_matrices )
630 throw std::runtime_error("Mismatch between specified and actual sigma matrices.");
631
632
633 // -----
634 // Reshape and map in the data
635 // -----
636
637 // Reshape the experiment, now that we know total size
638 exp_resp.field_lengths(field_lengths);
639
640 for (size_t fn_index = 0; fn_index < num_scalars; ++fn_index)
641 exp_resp.function_value(exp_values[fn_index][0], fn_index);
642
643 for (size_t field_ind = 0; field_ind < num_fields; ++field_ind)
644 exp_resp.field_values(exp_values[num_scalars + field_ind], field_ind);
645
646 //Cout << "Sigma scalars " << sigma_scalars << "\n";
647 //Cout << "Scalar map indices" << scalar_map_indices << "\n";
648
649 exp_resp.set_full_covariance(sigma_matrices, sigma_diagonals, sigma_scalars,
650 matrix_map_indices, diagonal_map_indices,
651 scalar_map_indices);
652
653 }
654
655
656
read_scalar_sigma(std::ifstream & scalar_data_stream,RealVector & sigma_scalars,IntVector & scalar_map_indices)657 void ExperimentData::read_scalar_sigma(std::ifstream& scalar_data_stream,
658 RealVector& sigma_scalars,
659 IntVector& scalar_map_indices)
660 {
661 // currently no longer allow 1 sigma to apply to all scalar responses
662 // always read 0, or N
663 RealVector sigma_row(scalarSigmaPerRow);
664 scalar_data_stream >> sigma_row;
665 for (size_t i = 0; i<scalarSigmaPerRow; ++i) {
666 sigma_scalars[i] = sigma_row[i];
667 scalar_map_indices[i] = i;
668 }
669 }
670
671 size_t ExperimentData::
num_scalar_primary() const672 num_scalar_primary() const
673 {
674 if( simulationSRD.is_null() )
675 throw std::runtime_error("ExperimentData is incorrectly (or not) initialized.");
676
677 return simulationSRD.num_scalar_primary();
678 }
679
680 size_t ExperimentData::
num_fields() const681 num_fields() const
682 {
683 if( simulationSRD.is_null() )
684 throw std::runtime_error("ExperimentData is incorrectly (or not) initialized.");
685
686 return simulationSRD.num_field_response_groups();
687 }
688
689
num_config_vars() const690 size_t ExperimentData::num_config_vars() const
691 {
692 return numConfigVars;
693 }
694
695
config_vars() const696 const std::vector<RealVector>& ExperimentData::config_vars() const
697 {
698 return allConfigVars;
699 }
700
701
per_exp_length(IntVector & per_length) const702 void ExperimentData::per_exp_length(IntVector& per_length) const
703 {
704 per_length.resize(allExperiments.size());
705 //Cout << "num experiments " << num_experiments();
706
707 for (size_t i=0; i<num_experiments(); i++)
708 per_length(i)= allExperiments[i].function_values().length();
709 //Cout << "per length " << per_length;
710 }
711
712
field_lengths(size_t experiment) const713 const IntVector& ExperimentData::field_lengths(size_t experiment) const
714 {
715 return allExperiments[experiment].field_lengths();
716 }
717
all_data(size_t experiment)718 const RealVector& ExperimentData::all_data(size_t experiment)
719 {
720 if (experiment >= allExperiments.size()) {
721 Cerr << "\nError: invalid experiment index " << experiment << std::endl;
722 abort_handler(-1);
723 }
724 return allExperiments[experiment].function_values();
725 }
726
response(size_t experiment)727 const Response& ExperimentData::response(size_t experiment)
728 {
729 if (experiment >= allExperiments.size()) {
730 Cerr << "\nError: invalid experiment index " << experiment << std::endl;
731 abort_handler(-1);
732 }
733 return allExperiments[experiment];
734 }
735
num_total_exppoints() const736 size_t ExperimentData::num_total_exppoints() const
737 {
738 size_t res_size = 0;
739 for (size_t i=0; i<num_experiments(); i++)
740 res_size += allExperiments[i].function_values().length();
741 return res_size;
742 }
743
744 Real ExperimentData::
scalar_data(size_t response,size_t experiment)745 scalar_data(size_t response, size_t experiment)
746 {
747 //if (allExperiments[response].experimentType != SCALAR_DATA) {
748 // Cerr << "Error (ExperimentData): invalid query of scalar data." << std::endl;
749 // abort_handler(-1);
750 //}
751 return(allExperiments[experiment].function_value(response));
752 }
753
754 RealVector ExperimentData::
field_data_view(size_t response,size_t experiment) const755 field_data_view(size_t response, size_t experiment) const
756 {
757 return(allExperiments[experiment].field_values_view(response));
758 }
759
760 RealMatrix ExperimentData::
field_coords_view(size_t response,size_t experiment) const761 field_coords_view(size_t response, size_t experiment) const
762 {
763 return(allExperiments[experiment].field_coords_view(response));
764 }
765
variance_type_active(short variance_type) const766 bool ExperimentData::variance_type_active(short variance_type) const
767 {
768 UShortArray::const_iterator vt_it =
769 std::find(varianceTypes.begin(), varianceTypes.end(), variance_type);
770 return vt_it != varianceTypes.end();
771 }
772
variance_active() const773 bool ExperimentData::variance_active() const
774 {
775 return (variance_type_active(SCALAR_SIGMA) ||
776 variance_type_active(DIAGONAL_SIGMA) ||
777 variance_type_active(MATRIX_SIGMA));
778 }
779
interpolate_flag() const780 bool ExperimentData::interpolate_flag() const
781 {
782 return interpolateFlag;
783 }
784
785 RealVector ExperimentData::
residuals_view(const RealVector & residuals,size_t experiment) const786 residuals_view(const RealVector& residuals, size_t experiment) const
787 {
788 int exp_offset = expOffsets[experiment];
789 RealVector exp_resid(Teuchos::View, residuals.values()+exp_offset,
790 experimentLengths[experiment]);
791 return exp_resid;
792 }
793
794 /// Return a view (to allowing updaing in place) of the gradients associated
795 /// with a given experiment, from a matrix contaning gradients from
796 /// all experiments
797 RealMatrix ExperimentData::
gradients_view(const RealMatrix & gradients,size_t experiment) const798 gradients_view(const RealMatrix &gradients, size_t experiment) const
799 {
800 int exp_offset = expOffsets[experiment];
801 RealMatrix exp_grads(Teuchos::View, gradients, gradients.numRows(),
802 experimentLengths[experiment], 0, exp_offset );
803 return exp_grads;
804 }
805
806 /// Return a view (to allowing updaing in place) of the hessians associated
807 /// with a given experiment, from an array contaning the hessians from
808 /// all experiments
809 RealSymMatrixArray ExperimentData::
hessians_view(const RealSymMatrixArray & hessians,size_t experiment) const810 hessians_view(const RealSymMatrixArray &hessians, size_t experiment) const {
811 int num_hess = experimentLengths[experiment],
812 exp_offset = expOffsets[experiment];
813 RealSymMatrixArray exp_hessians( num_hess );
814 if ( !hessians.empty() ){
815 for (size_t i=0; i<num_hess; ++i,++exp_offset)
816 if (hessians[exp_offset].numRows()) // else leave exp_hessians[i] empty
817 exp_hessians[i] = RealSymMatrix(Teuchos::View, hessians[exp_offset],
818 hessians[exp_offset].numRows());
819 }
820 return exp_hessians;
821 }
822
823 Real ExperimentData::
apply_covariance(const RealVector & residuals,size_t experiment) const824 apply_covariance(const RealVector& residuals, size_t experiment) const
825 {
826 RealVector exp_resid = residuals_view( residuals, experiment );
827 if ( variance_active() )
828 return(allExperiments[experiment].apply_covariance(exp_resid));
829 else
830 return exp_resid.dot( exp_resid );
831 }
832
833 // BMA TODO: These functions don't get called when covariance is
834 // inactive, but if they did, could undesireably resize the outbound
835 // object.
836 void ExperimentData::
apply_covariance_inv_sqrt(const RealVector & residuals,size_t experiment,RealVector & weighted_residuals) const837 apply_covariance_inv_sqrt(const RealVector& residuals, size_t experiment,
838 RealVector& weighted_residuals) const
839 {
840 RealVector exp_resid = residuals_view( residuals, experiment );
841 if ( variance_active() )
842 allExperiments[experiment].apply_covariance_inv_sqrt(exp_resid,
843 weighted_residuals);
844 else{
845 // Return a deep copy
846 weighted_residuals.sizeUninitialized( exp_resid.length() );
847 weighted_residuals.assign( exp_resid );
848 }
849 }
850
851 // BMA TODO: These functions don't get called when covariance is
852 // inactive, but if they did, could undesireably resize the outbound
853 // object.
854 void ExperimentData::
apply_covariance_inv_sqrt(const RealMatrix & gradients,size_t experiment,RealMatrix & weighted_gradients) const855 apply_covariance_inv_sqrt(const RealMatrix& gradients, size_t experiment,
856 RealMatrix& weighted_gradients) const
857 {
858 RealMatrix exp_grads = gradients_view( gradients, experiment );
859 if ( variance_active() )
860 allExperiments[experiment].apply_covariance_inv_sqrt(exp_grads,
861 weighted_gradients);
862 else{
863 // Return a deep copy
864 weighted_gradients.shapeUninitialized( exp_grads.numRows(),
865 exp_grads.numCols() );
866 weighted_gradients.assign( exp_grads );
867 }
868 }
869
870 // BMA TODO: These functions don't get called when covariance is
871 // inactive, but if they did, could undesireably resize the outbound
872 // object.
873 void ExperimentData::
apply_covariance_inv_sqrt(const RealSymMatrixArray & hessians,size_t experiment,RealSymMatrixArray & weighted_hessians) const874 apply_covariance_inv_sqrt(const RealSymMatrixArray& hessians, size_t experiment,
875 RealSymMatrixArray& weighted_hessians) const
876 {
877 RealSymMatrixArray exp_hessians = hessians_view( hessians, experiment );
878 if ( variance_active() )
879 allExperiments[experiment].apply_covariance_inv_sqrt(exp_hessians,
880 weighted_hessians);
881 else{
882 // Return a deep copy
883 size_t num_hess = exp_hessians.size();
884 weighted_hessians.resize( num_hess );
885 if ( !exp_hessians.empty() ){
886 for (size_t i=0; i<num_hess; ++i)
887 if (exp_hessians[i].numRows()) // else leave exp_hessians[i] empty
888 weighted_hessians[i] = RealSymMatrix(Teuchos::Copy,
889 exp_hessians[i],
890 exp_hessians[i].numRows());
891 }
892 }
893
894 }
895
apply_simulation_error(const RealVector & simulation_error,size_t experiment)896 void ExperimentData::apply_simulation_error(const RealVector& simulation_error,
897 size_t experiment)
898 {
899 Response exp_response = allExperiments[experiment];
900 const RealVector& exp_vals = exp_response.function_values();
901 for (size_t i = 0; i < allExperiments[experiment].num_functions(); i++)
902 exp_response.function_value(exp_vals[i] + simulation_error[i], i);
903 }
904
905
906 void ExperimentData::
get_main_diagonal(RealVector & diagonal,size_t experiment) const907 get_main_diagonal(RealVector &diagonal, size_t experiment ) const
908 {
909 if ( !variance_active() )
910 throw( std::runtime_error("ExperimentData::get_main_diagonal - covariance matrix is empty") );
911 allExperiments[experiment].get_covariance_diagonal( diagonal );
912 }
913
cov_std_deviation(RealVectorArray & std_deviations) const914 void ExperimentData::cov_std_deviation(RealVectorArray& std_deviations) const
915 {
916 std_deviations.resize(numExperiments);
917 for (size_t exp_ind=0; exp_ind<numExperiments; ++exp_ind) {
918 RealVector& sd_vec = std_deviations[exp_ind];
919 allExperiments[exp_ind].experiment_covariance().get_main_diagonal(sd_vec);
920 for (size_t i=0; i<sd_vec.length(); ++i)
921 sd_vec[i] = std::sqrt(sd_vec[i]);
922 }
923 }
924
cov_as_correlation(RealSymMatrixArray & corr_matrices) const925 void ExperimentData::cov_as_correlation(RealSymMatrixArray& corr_matrices) const
926 {
927 corr_matrices.resize(numExperiments);
928 for (size_t exp_ind=0; exp_ind<numExperiments; ++exp_ind) {
929 const ExperimentCovariance& exp_cov =
930 allExperiments[exp_ind].experiment_covariance();
931 exp_cov.as_correlation(corr_matrices[exp_ind]);
932 }
933 }
934
covariance(int exp_ind,RealSymMatrix & cov_mat) const935 void ExperimentData::covariance(int exp_ind, RealSymMatrix& cov_mat) const
936 {
937 const ExperimentCovariance& exp_cov =
938 allExperiments[exp_ind].experiment_covariance();
939 exp_cov.dense_covariance(cov_mat);
940 }
941
942
943 /** This assumes the souce gradient/Hessian are size less or equal to
944 the destination response, and that the leading part is to be populated. */
945 void ExperimentData::
form_residuals(const Response & sim_resp,Response & residual_resp) const946 form_residuals(const Response& sim_resp, Response& residual_resp) const
947 {
948 // BMA: perhaps a better name would be per_exp_asv?
949 // BMA TODO: Make this call robust to zero and single experiment cases
950 ShortArray total_asv = determine_active_request(residual_resp);
951
952 IntVector experiment_lengths;
953 per_exp_length(experiment_lengths);
954 size_t residual_resp_offset = 0;
955 for (size_t exp_ind = 0; exp_ind < numExperiments; ++exp_ind){
956 size_t num_fns_exp = experiment_lengths[exp_ind]; // total length this exper
957 form_residuals( sim_resp, exp_ind, total_asv, residual_resp_offset,
958 residual_resp );
959 residual_resp_offset += num_fns_exp;
960 }
961 }
962
963
964 void ExperimentData::
form_residuals(const Response & sim_resp,const size_t curr_exp,Response & residual_resp) const965 form_residuals(const Response& sim_resp, const size_t curr_exp,
966 Response& residual_resp) const
967 {
968 // BMA: perhaps a better name would be per_exp_asv?
969 // BMA TODO: Make this call robust to zero and single experiment cases
970 ShortArray total_asv = determine_active_request(residual_resp);
971
972 IntVector experiment_lengths;
973 per_exp_length(experiment_lengths);
974 size_t residual_resp_offset = 0;
975 for (size_t exp_ind = 0; exp_ind < curr_exp; ++exp_ind){
976 size_t num_fns_exp = experiment_lengths[exp_ind]; // total length this exper
977 residual_resp_offset += num_fns_exp;
978 }
979 form_residuals(sim_resp, curr_exp, total_asv, residual_resp_offset,
980 residual_resp);
981 }
982
983
984 /** This assumes the souce gradient/Hessian are size less or equal to
985 the destination response, and that the leading part is to be populated. */
986 void ExperimentData::
form_residuals(const Response & sim_resp,size_t exp_ind,const ShortArray & total_asv,size_t exp_offset,Response & residual_resp) const987 form_residuals(const Response& sim_resp, size_t exp_ind,
988 const ShortArray &total_asv, size_t exp_offset,
989 Response &residual_resp ) const
990 {
991 // size of the residuals for this one experiment
992 size_t exp_resid_size = allExperiments[exp_ind].function_values().length();
993 // the functions from the simulation
994 RealVector sim_fns = sim_resp.function_values();
995 RealMatrix sim_grads = sim_resp.function_gradients_view();
996 RealSymMatrixArray sim_hessians = sim_resp.function_hessians_view();
997
998 const IntVector& sim_field_lens = sim_resp.field_lengths();
999
1000 short asv = total_asv[exp_ind];
1001 RealVector all_residuals = residual_resp.function_values_view();
1002 RealVector exp_residuals(Teuchos::View, all_residuals.values()+exp_offset,
1003 exp_resid_size);
1004
1005 if (!interpolateFlag) {
1006
1007 for (size_t i=0; i<exp_resid_size; i++){
1008 exp_residuals[i] =
1009 sim_fns[i] - allExperiments[exp_ind].function_value(i);
1010 }
1011 // populate only the part of the gradients/Hessians for this
1012 // experiment, for the active submodel derivative variables
1013 if ( asv & 2 ){
1014 size_t num_sm_cv = sim_grads.numRows();
1015 RealMatrix resid_grads
1016 (gradients_view(residual_resp.function_gradients(), exp_ind));
1017 resid_grads = 0.0;
1018 for (size_t r_ind = 0; r_ind<exp_resid_size; ++r_ind)
1019 for (size_t i=0; i<num_sm_cv; ++i)
1020 resid_grads(i, r_ind) = sim_grads(i, r_ind);
1021 }
1022
1023 if ( asv & 4 ) {
1024 size_t num_sm_cv = sim_grads.numRows();
1025 RealSymMatrixArray resid_hess
1026 (hessians_view(residual_resp.function_hessians(), exp_ind));
1027 for (size_t r_ind = 0; r_ind<exp_resid_size; ++r_ind) {
1028 resid_hess[r_ind] = 0.0;
1029 for (size_t i=0; i<num_sm_cv; ++i)
1030 for (size_t j=0; j<num_sm_cv; ++j)
1031 resid_hess[r_ind](i,j) = sim_hessians[r_ind](i,j);
1032 }
1033 }
1034
1035 } else {
1036
1037 for (size_t i=0; i<num_scalar_primary(); i++) {
1038 exp_residuals[i] = sim_fns[i] - allExperiments[exp_ind].function_value(i);
1039 // BMA: Looked like gradients and Hessians of the scalars weren't
1040 // populated, so added:
1041 if (asv & 2) {
1042 size_t num_sm_cv = sim_grads.numRows();
1043 RealVector resid_grad =
1044 residual_resp.function_gradient_view(exp_offset+i);
1045 resid_grad = 0.0;
1046 for (size_t j=0; j<num_sm_cv; ++j)
1047 resid_grad[j] = sim_grads(j, i);
1048 }
1049 if (asv & 4) {
1050 size_t num_sm_cv = sim_hessians[i].numRows();
1051 RealSymMatrix resid_hess =
1052 residual_resp.function_hessian_view(exp_offset+i);
1053 resid_hess = 0.0;
1054 for (size_t j=0; j<num_sm_cv; ++j)
1055 for (size_t k=0; k<num_sm_cv; ++k)
1056 resid_hess(j,k) = sim_hessians[i](j,k);
1057
1058 }
1059 }
1060
1061 // interpolate the simulation data onto the coordinates of the experiment
1062 // data, populating functions, gradients, Hessians
1063
1064 // I think residuals are stored in continguous order,
1065 // [exp1(scalars,fields),...,exp_n(scalars,fields)
1066 // if so need to pass exp_offset to interpolate function above
1067 // and inside that function set offset = exp_offset and
1068 // then increment in usual way
1069 interpolate_simulation_data(sim_resp, exp_ind, total_asv, exp_offset,
1070 residual_resp);
1071
1072 if (outputLevel >= DEBUG_OUTPUT)
1073 Cout << "interp values" << exp_residuals << '\n';
1074
1075 if (asv & 1) {
1076 // compute the residuals, i.e. subtract the experiment data values
1077 // from the (interpolated) simulation values.
1078 size_t cntr = num_scalar_primary();
1079 for (size_t i=0; i<num_fields(); i++){
1080 size_t num_field_fns = field_data_view(i,exp_ind).length();
1081 for (size_t j=0; j<num_field_fns; j++, cntr++)
1082 exp_residuals[cntr] -= field_data_view(i,exp_ind)[j];
1083 }
1084 if (outputLevel >= DEBUG_OUTPUT)
1085 Cout << "residuals in exp space" << exp_residuals << '\n';
1086 }
1087 }
1088 }
1089
1090 void ExperimentData::
interpolate_simulation_data(const Response & sim_resp,size_t exp_ind,const ShortArray & total_asv,size_t exp_offset,Response & interp_resp) const1091 interpolate_simulation_data(const Response &sim_resp, size_t exp_ind,
1092 const ShortArray &total_asv, size_t exp_offset,
1093 Response &interp_resp ) const
1094 {
1095 size_t offset = exp_offset + num_scalar_primary();
1096 IntVector field_lens = field_lengths(exp_ind);
1097 for (size_t field_num=0; field_num<num_fields(); field_num++){
1098 RealMatrix exp_coords = field_coords_view(field_num,exp_ind);
1099 interpolate_simulation_field_data( sim_resp, exp_coords, field_num,
1100 total_asv[exp_ind],
1101 offset, interp_resp );
1102 offset += field_lens[field_num];
1103 }
1104 }
1105
1106
1107 // BMA TODO: Make this call robust to zero and single experiment cases
1108 ShortArray ExperimentData::
determine_active_request(const Response & resid_resp) const1109 determine_active_request(const Response& resid_resp) const
1110 {
1111 ShortArray total_asv( numExperiments );
1112
1113 // can't apply matrix-valued errors due to possibly incomplete
1114 // dataset when active set vector is in use (missing residuals)
1115 bool interogate_field_data =
1116 variance_type_active(MATRIX_SIGMA) || interpolate_flag();
1117
1118 IntVector experiment_lengths;
1119 per_exp_length(experiment_lengths);
1120
1121
1122 size_t calib_term_ind = 0; // index into the total set of calibration terms
1123 for (size_t exp_ind = 0; exp_ind < numExperiments; ++exp_ind){
1124 // total length this exper
1125 size_t num_fns_exp = experiment_lengths[exp_ind];
1126
1127 // Within a field group, cannot have matrix (off-diagonal)
1128 // covariance and non-uniform ASV
1129 //
1130 // TODO: This is overly conservative; instead check whether
1131 // matrix covariance is active on per-field group basis
1132 const ShortArray& asv = resid_resp.active_set_request_vector();
1133 total_asv[exp_ind] = 0;
1134 if (interogate_field_data) {
1135
1136 size_t num_scalar = num_scalar_primary();
1137 for (size_t sc_ind = 0; sc_ind < num_scalar; ++sc_ind)
1138 total_asv[exp_ind] |= asv[calib_term_ind + sc_ind];
1139
1140 const IntVector& exp_field_lens = field_lengths(exp_ind);
1141 size_t num_field_groups = num_fields();
1142 size_t field_start = calib_term_ind + num_scalar;
1143 for (size_t fg_ind = 0; fg_ind < num_field_groups; ++fg_ind) {
1144
1145 // determine presence and consistency of active set vector
1146 // requests within this field
1147 size_t asv_1 = 0, asv_2 = 0, asv_4 = 0;
1148 size_t num_fns_field = exp_field_lens[fg_ind];
1149 for (size_t fn_ind = 0; fn_ind < num_fns_field; ++fn_ind) {
1150 if (asv[field_start + fn_ind] & 1) ++asv_1;
1151 if (asv[field_start + fn_ind] & 2) ++asv_2;
1152 if (asv[field_start + fn_ind] & 4) ++asv_4;
1153 }
1154
1155 // with matrix covariance, each of fn, grad, Hess must have all
1156 // same asv (either none or all) (within a field response group)
1157 if ( (asv_1 != 0 && asv_1 != num_fns_field) ||
1158 (asv_2 != 0 && asv_2 != num_fns_field) ||
1159 (asv_4 != 0 && asv_4 != num_fns_field)) {
1160 Cerr << "\nError: matrix form of data error covariance cannot be "
1161 << "used with non-uniform\n active set vector; consider "
1162 << "disabling active set vector or specifying no\n , "
1163 << "scalar, or diagonal covariance" << std::endl;
1164 abort_handler(-1);
1165 }
1166 if (asv_1 > 0) total_asv[exp_ind] |= 1;
1167 if (asv_2 > 0) total_asv[exp_ind] |= 2;
1168 if (asv_4 > 0) total_asv[exp_ind] |= 4;
1169 }
1170 }
1171 else {
1172 // compute aggregate ASV over scalars and field data
1173 for (size_t fn_ind = 0; fn_ind < num_fns_exp; ++fn_ind)
1174 total_asv[exp_ind] |= asv[calib_term_ind + fn_ind];
1175 }
1176
1177 calib_term_ind += num_fns_exp;
1178 } // for each experiment
1179 return(total_asv);
1180 }
1181
1182 void ExperimentData::
scale_residuals(const Response & residual_response,RealVector & weighted_resid) const1183 scale_residuals(const Response& residual_response,
1184 RealVector& weighted_resid) const {
1185
1186 ShortArray total_asv = determine_active_request(residual_response);
1187
1188 for (size_t exp_ind = 0; exp_ind < numExperiments; ++exp_ind) {
1189
1190 // apply noise covariance to the residuals for this experiment
1191 // and store in correct place in weighted_residual
1192 if (outputLevel >= DEBUG_OUTPUT && total_asv[exp_ind] > 0)
1193 Cout << "Calibration: weighting residuals for experiment "
1194 << exp_ind + 1 << " with inverse of specified\nerror covariance."
1195 << std::endl;
1196
1197 // apply cov_inv_sqrt to the residual vector
1198 if (total_asv[exp_ind] & 1) {
1199 // BMA: not sure why View didn't work (was disconnected):
1200 //RealVector exp_weighted_resid(residuals_view(weighted_resid, exp_ind));
1201 RealVector exp_weighted_resid;
1202 // takes full list of source residuals, but per-experiment output vector
1203 apply_covariance_inv_sqrt(residual_response.function_values(),
1204 exp_ind, exp_weighted_resid);
1205 size_t calib_term_ind = expOffsets[exp_ind];
1206 copy_data_partial(exp_weighted_resid, weighted_resid, calib_term_ind);
1207 }
1208
1209 }
1210 }
1211
1212
scale_residuals(Response & residual_response) const1213 void ExperimentData::scale_residuals(Response& residual_response) const
1214 {
1215 ShortArray total_asv = determine_active_request(residual_response);
1216
1217 IntVector experiment_lengths;
1218 per_exp_length(experiment_lengths);
1219
1220 size_t calib_term_ind = 0; // index into the total set of calibration terms
1221 for (size_t exp_ind = 0; exp_ind < numExperiments; ++exp_ind){
1222 // total residuals in this exper
1223 size_t num_fns_exp = experiment_lengths[exp_ind];
1224
1225 // apply noise covariance to the residuals for this experiment
1226 // and store in correct place in residual_response
1227 if (outputLevel >= DEBUG_OUTPUT && total_asv[exp_ind] > 0)
1228 Cout << "Calibration: weighting residuals for experiment "
1229 << exp_ind + 1 << " with inverse of\n specified error covariance."
1230 << std::endl;
1231
1232 // apply cov_inv_sqrt to the residual vector
1233 RealVector weighted_resid;
1234 if (total_asv[exp_ind] & 1)
1235 apply_covariance_inv_sqrt(residual_response.function_values(),
1236 exp_ind, weighted_resid);
1237 else
1238 weighted_resid =
1239 residuals_view(residual_response.function_values(), exp_ind);
1240
1241 // apply cov_inv_sqrt to each row of gradient matrix
1242 RealMatrix weighted_grad;
1243 if (total_asv[exp_ind] & 2) {
1244 apply_covariance_inv_sqrt(residual_response.function_gradients(),
1245 exp_ind, weighted_grad);
1246 }
1247 else
1248 weighted_grad =
1249 gradients_view(residual_response.function_gradients(), exp_ind);
1250
1251 // apply cov_inv_sqrt to non-contiguous Hessian matrices
1252 RealSymMatrixArray weighted_hess;
1253 if (total_asv[exp_ind] & 4)
1254 apply_covariance_inv_sqrt(residual_response.function_hessians(),
1255 exp_ind, weighted_hess);
1256 else
1257 weighted_hess = hessians_view(residual_response.function_hessians(),
1258 exp_ind);
1259
1260 copy_field_data(weighted_resid, weighted_grad, weighted_hess,
1261 calib_term_ind, num_fns_exp, residual_response);
1262
1263 calib_term_ind += num_fns_exp;
1264 }
1265 }
1266
1267
1268 /** Add the data back to the residual to recover the model, for use in
1269 surrogated-based LSQ where DB lookup will fail (need approx eval
1270 DB). best_fns contains primary and secondary responses */
1271 void ExperimentData::
recover_model(size_t num_pri_fns,RealVector & best_fns) const1272 recover_model(size_t num_pri_fns, RealVector& best_fns) const
1273 {
1274 if (interpolateFlag) {
1275 Cerr << "Error: cannot recover model from residuals when interpolating.\n";
1276 abort_handler(-1);
1277 }
1278 const Response& experiment0 = allExperiments[0];
1279 if (num_pri_fns != experiment0.num_functions()) {
1280 Cerr << "Error: incompatible sizes in recover_model()\n";
1281 abort_handler(-1);
1282 }
1283 for (size_t i=0; i<num_pri_fns; ++i)
1284 best_fns[i] += experiment0.function_value(i);
1285 }
1286
1287 void ExperimentData::
build_gradient_of_sum_square_residuals(const Response & resp,const ShortArray & asrv,RealVector & ssr_gradient)1288 build_gradient_of_sum_square_residuals( const Response& resp,
1289 const ShortArray& asrv,
1290 RealVector &ssr_gradient )
1291 {
1292 // initialize ssr_gradient to zero, prior to summing over set of experiments
1293 size_t exp_ind, num_v = resp.active_set().derivative_vector().size();
1294 if (ssr_gradient.length() != num_v) ssr_gradient.size(num_v); // init to 0.
1295 else ssr_gradient = 0.;
1296 //size_t residual_resp_offset = 0;
1297 for (exp_ind = 0; exp_ind < numExperiments; ++exp_ind)
1298 // adds to ssr_gradient for each experiment
1299 build_gradient_of_sum_square_residuals_from_response( resp, asrv, exp_ind,
1300 ssr_gradient );
1301 }
1302
1303 void ExperimentData::
build_gradient_of_sum_square_residuals_from_response(const Response & resp,const ShortArray & asrv,int exp_ind,RealVector & ssr_gradient)1304 build_gradient_of_sum_square_residuals_from_response( const Response& resp,
1305 const ShortArray& asrv,
1306 int exp_ind,
1307 RealVector &ssr_gradient)
1308 {
1309 RealVector scaled_residuals = residuals_view(resp.function_values(), exp_ind);
1310 RealMatrix scaled_gradients =
1311 gradients_view(resp.function_gradients(), exp_ind);
1312
1313 /*scaled_residuals.print(std::cout);
1314 scaled_gradients.print(std::cout);*/
1315
1316 build_gradient_of_sum_square_residuals_from_function_data( scaled_gradients,
1317 scaled_residuals,
1318 ssr_gradient,
1319 asrv);
1320 }
1321
1322 void ExperimentData::
build_gradient_of_sum_square_residuals_from_function_data(const RealMatrix & func_gradients,const RealVector & residuals,RealVector & ssr_gradient,const ShortArray & asrv)1323 build_gradient_of_sum_square_residuals_from_function_data(
1324 const RealMatrix &func_gradients,
1325 const RealVector &residuals,
1326 RealVector &ssr_gradient, const ShortArray& asrv )
1327 {
1328 // This function assumes that residuals are r = ( approx - data )
1329 // NOT r = ( data - approx )
1330
1331 // func_gradients is the transpose of the Jacobian of the functions
1332 int v, r, num_v = func_gradients.numRows(),
1333 num_residuals = residuals.length();
1334 for ( r=0; r<num_residuals; ++r )
1335 if ( (asrv[r] & 3) == 3 ) {
1336 Real res = residuals[r]; const Real* func_grad = func_gradients[r];
1337 for ( v=0; v<num_v; ++v )
1338 ssr_gradient[v] += res * func_grad[v];
1339 // we compute gradient of sum square residuals divided by 2 (i.e. r'r/2),
1340 // where r has been scaled by sqrt(inv Gamma_d)
1341 }
1342 }
1343
1344 void ExperimentData::
build_hessian_of_sum_square_residuals(const Response & resp,const ShortArray & asrv,RealSymMatrix & ssr_hessian)1345 build_hessian_of_sum_square_residuals( const Response& resp,
1346 const ShortArray& asrv,
1347 RealSymMatrix &ssr_hessian )
1348 {
1349 // initialize ssr_hessian to zero, prior to summing over set of experiments
1350 size_t exp_ind, num_v = resp.active_set().derivative_vector().size();
1351 if (ssr_hessian.numRows() != num_v) ssr_hessian.shape(num_v); // init to 0.
1352 else ssr_hessian = 0.;
1353 //size_t residual_resp_offset = 0;
1354 for (exp_ind = 0; exp_ind < numExperiments; ++exp_ind)
1355 // adds to ssr_hessian for each experiment
1356 build_hessian_of_sum_square_residuals_from_response( resp, asrv, exp_ind,
1357 ssr_hessian );
1358 }
1359
1360 void ExperimentData::
build_hessian_of_sum_square_residuals_from_response(const Response & resp,const ShortArray & asrv,int exp_ind,RealSymMatrix & ssr_hessian)1361 build_hessian_of_sum_square_residuals_from_response( const Response& resp,
1362 const ShortArray& asrv,
1363 int exp_ind,
1364 RealSymMatrix &ssr_hessian)
1365 {
1366 RealVector scaled_residuals = residuals_view(resp.function_values(), exp_ind );
1367 RealMatrix scaled_gradients =
1368 gradients_view(resp.function_gradients(), exp_ind );
1369 RealSymMatrixArray scaled_hessians =
1370 hessians_view(resp.function_hessians(), exp_ind );
1371
1372 /*scaled_residuals.print(std::cout);
1373 scaled_gradients.print(std::cout);
1374 Cout << "\nSize:" << scaled_hessians.size();
1375 for (size_t j=0;j<scaled_hessians.size();j++)
1376 scaled_hessians[j].print(std::cout);*/
1377
1378 build_hessian_of_sum_square_residuals_from_function_data( scaled_hessians,
1379 scaled_gradients,
1380 scaled_residuals,
1381 ssr_hessian,
1382 asrv);
1383 }
1384
1385 void ExperimentData::
build_hessian_of_sum_square_residuals_from_function_data(const RealSymMatrixArray & func_hessians,const RealMatrix & func_gradients,const RealVector & residuals,RealSymMatrix & ssr_hessian,const ShortArray & asrv)1386 build_hessian_of_sum_square_residuals_from_function_data(
1387 const RealSymMatrixArray &func_hessians,
1388 const RealMatrix &func_gradients,
1389 const RealVector &residuals,
1390 RealSymMatrix &ssr_hessian,
1391 const ShortArray& asrv ){
1392 // This function assumes that residuals are r = ( approx - data )
1393 // NOT r = ( data - approx )
1394
1395 // func_gradients is the transpose of the Jacobian of the functions
1396 int num_v = ssr_hessian.numRows(), num_residuals = residuals.length();
1397 for ( int k=0; k<num_v; k++ ) {
1398 for ( int j=0; j<=k; j++ ) {
1399 Real &hess_jk = ssr_hessian(j,k);
1400 for ( int i=0; i<num_residuals; i++ ) {
1401 short asrv_i = asrv[i];
1402 if (asrv_i & 2) hess_jk += func_gradients(j,i)*func_gradients(k,i);
1403 if ( (asrv_i & 5) == 5 ) hess_jk += residuals[i]*func_hessians[i](j,k);
1404 }
1405 // we adopt convention and compute hessian of sum square residuals
1406 // multiplied by 1/2 e.g. r'r/2. Thus we do not need following
1407 // multiplication
1408 //hess_jk *= 2.;
1409 }
1410 }
1411 }
1412
1413
1414 /** In-place scaling of residual response by hyper-parameter multipliers */
1415 void ExperimentData::
scale_residuals(const RealVector & multipliers,unsigned short multiplier_mode,size_t num_calib_params,Response & residual_response) const1416 scale_residuals(const RealVector& multipliers, unsigned short multiplier_mode,
1417 size_t num_calib_params, Response& residual_response) const
1418 {
1419
1420 // NOTE: In most general case the index into multipliers is exp_ind
1421 // * num_response_groups + resp_ind (never per function ind, e.g.,
1422 // in a field)
1423 size_t num_hyper = multipliers.length();
1424 size_t total_resid = num_total_exppoints();
1425 const ShortArray& asv = residual_response.active_set_request_vector();
1426
1427 switch (multiplier_mode) {
1428
1429 case CALIBRATE_NONE:
1430 // no-op
1431 break;
1432
1433 case CALIBRATE_ONE: {
1434 assert(multipliers.length() == 1);
1435 Real fn_scale = 1.0/sqrt(multipliers[0]);
1436 Real grad_scale = -0.5/multipliers[0];
1437 Real hess_scale = 0.75*std::pow(multipliers[0], -2.0);
1438
1439 for (size_t i=0; i<total_resid; ++i) {
1440
1441 if (asv[i] & 1) {
1442 Real& fn_value = residual_response.function_value_view(i);
1443 fn_value *= fn_scale;
1444 }
1445
1446 if (asv[i] & 2) {
1447 const Real& fn_value = residual_response.function_value(i);
1448 RealVector fn_grad = residual_response.function_gradient_view(i);
1449 // scale all of the gradient (including zeroed entries)
1450 fn_grad *= fn_scale;
1451 // then augment with gradient entry for the hyper-parameter
1452 fn_grad[num_calib_params] = grad_scale * fn_value;
1453 }
1454
1455 if (asv[i] & 4) {
1456 const Real& fn_value = residual_response.function_value(i);
1457 const RealVector fn_grad = residual_response.function_gradient_view(i);
1458 RealSymMatrix fn_hess = residual_response.function_hessian_view(i);
1459 // scale all of the Hessian (including zeroed entries)
1460 fn_hess *= fn_scale;
1461 // then augment with Hessian entries for the hyper-parameter
1462 for (size_t j=0; j<num_calib_params; ++j) {
1463 fn_hess(j, num_calib_params) = grad_scale * fn_grad[j];
1464 fn_hess(num_calib_params, j) = grad_scale * fn_grad[j];
1465 }
1466 fn_hess(num_calib_params, num_calib_params) = hess_scale * fn_value;
1467 }
1468
1469 }
1470 break;
1471 }
1472
1473 case CALIBRATE_PER_EXPER: case CALIBRATE_PER_RESP: case CALIBRATE_BOTH: {
1474 IntVector resid2mult_indices;
1475 resid2mult_map(multiplier_mode, resid2mult_indices);
1476
1477 for (size_t i=0; i<total_resid; ++i) {
1478 // index of multiplier for this residual
1479 int mult_ind = resid2mult_indices[i];
1480 Real fn_scale = 1.0/sqrt(multipliers[mult_ind]);
1481 Real grad_scale = -0.5/multipliers[mult_ind];
1482 Real hess_scale = 0.75*std::pow(multipliers[mult_ind], -2.0);
1483
1484 if (asv[i] & 1) {
1485 Real& fn_value = residual_response.function_value_view(i);
1486 fn_value *= fn_scale;
1487 }
1488
1489 if (asv[i] & 2) {
1490 const Real& fn_value = residual_response.function_value(i);
1491 RealVector fn_grad = residual_response.function_gradient_view(i);
1492 // scale all of the gradient (including zeroed entries)
1493 fn_grad *= fn_scale;
1494 // then augment with gradient entries for the hyper-parameters
1495 // only 1 multiplier can affect a given residual
1496 fn_grad[num_calib_params + mult_ind] = grad_scale * fn_value;
1497 }
1498
1499 if (asv[i] & 4) {
1500 const Real& fn_value = residual_response.function_value(i);
1501 const RealVector fn_grad = residual_response.function_gradient_view(i);
1502 RealSymMatrix fn_hess = residual_response.function_hessian_view(i);
1503 // scale all of the Hessian (including zeroed entries)
1504 fn_hess *= fn_scale;
1505 // augment with Hessian entries for the hyper-parameters
1506 for (size_t j=0; j<num_calib_params; ++j) {
1507 fn_hess(j, num_calib_params + mult_ind) = grad_scale * fn_grad[j];
1508 fn_hess(num_calib_params + mult_ind, j) = grad_scale * fn_grad[j];
1509 }
1510 fn_hess(num_calib_params + mult_ind, num_calib_params + mult_ind) =
1511 hess_scale * fn_value;
1512 }
1513 }
1514 break;
1515 }
1516
1517 default:
1518 // unknown mode
1519 Cerr << "\nError: unknown multiplier mode in scale_residuals().\n";
1520 abort_handler(-1);
1521 break;
1522 }
1523 }
1524
1525 // BMA Reviewed
1526 /** Determinant of the total covariance used in inference, which has
1527 blocks mult_i * I * Cov_i. */
1528 Real ExperimentData::
cov_determinant(const RealVector & multipliers,unsigned short multiplier_mode) const1529 cov_determinant(const RealVector& multipliers,
1530 unsigned short multiplier_mode) const
1531 {
1532 // initialize with product of experiment covariance determinants
1533 Real det = covarianceDeterminant;
1534 size_t total_resid = num_total_exppoints();
1535
1536 switch (multiplier_mode) {
1537
1538 case CALIBRATE_NONE:
1539 // no-op: multiplier is 1.0
1540 break;
1541
1542 case CALIBRATE_ONE:
1543 assert(multipliers.length() == 1);
1544 det *= std::pow(multipliers[0], (double)total_resid);
1545 break;
1546
1547 case CALIBRATE_PER_EXPER: case CALIBRATE_PER_RESP: case CALIBRATE_BOTH: {
1548 RealVector expand_mults;
1549 generate_multipliers(multipliers, multiplier_mode, expand_mults);
1550 // for each experiment, add contribution from mult: det(mult_i*I*Cov_i)
1551 // Don't need to exponentiate to block size as we're multiplying det by
1552 // each multiplier individiually
1553 for (size_t resid_ind = 0; resid_ind < total_resid; ++resid_ind)
1554 det *= expand_mults[resid_ind];
1555 break;
1556 }
1557
1558 default:
1559 // unknown mode
1560 Cerr << "\nError: unknown multiplier mode in cov_determinant().\n";
1561 abort_handler(-1);
1562 break;
1563
1564 } // switch
1565
1566 return det;
1567 }
1568
1569 /** Determinant of half the log of total covariance used in inference,
1570 which has blocks mult_i * I * Cov_i. */
1571 Real ExperimentData::
half_log_cov_determinant(const RealVector & multipliers,unsigned short multiplier_mode) const1572 half_log_cov_determinant(const RealVector& multipliers,
1573 unsigned short multiplier_mode) const
1574 {
1575 // initialize with sum of experiment covariance log determinants
1576 Real log_det = logCovarianceDeterminant;
1577 size_t total_resid = num_total_exppoints();
1578
1579 switch (multiplier_mode) {
1580
1581 case CALIBRATE_NONE:
1582 // no-op: multiplier is 1.0
1583 break;
1584
1585 case CALIBRATE_ONE:
1586 assert(multipliers.length() == 1);
1587 log_det += std::log(multipliers[0]) * (double)total_resid;
1588 break;
1589
1590 case CALIBRATE_PER_EXPER: case CALIBRATE_PER_RESP: case CALIBRATE_BOTH: {
1591 RealVector expand_mults;
1592 generate_multipliers(multipliers, multiplier_mode, expand_mults);
1593 // for each experiment, add contribution from mult: det(mult_i*I*Cov_i)
1594 for (size_t resid_ind = 0; resid_ind < total_resid; ++resid_ind)
1595 log_det += std::log(expand_mults[resid_ind]);
1596 break;
1597 }
1598
1599 default:
1600 // unknown mode
1601 Cerr << "\nError: unknown multiplier mode in log_cov_determinant().\n";
1602 abort_handler(-1);
1603 break;
1604
1605 } // switch
1606
1607 return log_det / 2.0;
1608 }
1609
1610
1611 // BMA Reviewed
1612 /** Compute the gradient of scalar f(m) 0.5*log(det(mult*Cov))
1613 w.r.t. mults. Since this is the only use case, we include the 0.5
1614 factor and perform an update in-place. */
1615 void ExperimentData::
half_log_cov_det_gradient(const RealVector & multipliers,unsigned short multiplier_mode,size_t hyper_offset,RealVector & gradient) const1616 half_log_cov_det_gradient(const RealVector& multipliers,
1617 unsigned short multiplier_mode, size_t hyper_offset,
1618 RealVector& gradient) const
1619 {
1620 switch (multiplier_mode) {
1621
1622 case CALIBRATE_NONE:
1623 // no hyper-parameters being calibrated
1624 break;
1625
1626 case CALIBRATE_ONE: {
1627 // This multiplier affects all functions
1628 assert(multipliers.length() == 1);
1629 Real total_resid = (Real) num_total_exppoints();
1630 gradient[hyper_offset] +=
1631 total_resid / multipliers[0] / 2.0;
1632 break;
1633 }
1634
1635 case CALIBRATE_PER_EXPER: case CALIBRATE_PER_RESP: case CALIBRATE_BOTH: {
1636 SizetArray resid_per_mult = residuals_per_multiplier(multiplier_mode);
1637 assert(multipliers.length() == resid_per_mult.size());
1638 for (size_t i=0; i<multipliers.length(); ++i)
1639 gradient[hyper_offset + i] +=
1640 ((Real) resid_per_mult[i]) / multipliers[i] / 2.0;
1641 break;
1642 }
1643
1644 }
1645
1646 }
1647
1648 // BMA Reviewed
1649 /** Compute the gradient of scalar f(m) log(det(mult*Cov)) w.r.t. mults */
1650 void ExperimentData::
half_log_cov_det_hessian(const RealVector & multipliers,unsigned short multiplier_mode,size_t hyper_offset,RealSymMatrix & hessian) const1651 half_log_cov_det_hessian(const RealVector& multipliers,
1652 unsigned short multiplier_mode, size_t hyper_offset,
1653 RealSymMatrix& hessian) const
1654 {
1655 switch (multiplier_mode) {
1656
1657 case CALIBRATE_NONE:
1658 // no hyper-parameters being calibrated
1659 break;
1660
1661 case CALIBRATE_ONE: {
1662 // This multiplier affects all functions
1663 assert(multipliers.length() == 1);
1664 Real total_resid = (Real) num_total_exppoints();
1665 hessian(hyper_offset, hyper_offset) +=
1666 -total_resid / multipliers[0] / multipliers[0] / 2.0;
1667 break;
1668 }
1669
1670 case CALIBRATE_PER_EXPER: case CALIBRATE_PER_RESP: case CALIBRATE_BOTH: {
1671 SizetArray resid_per_mult = residuals_per_multiplier(multiplier_mode);
1672 assert(multipliers.length() == resid_per_mult.size());
1673 for (size_t i =0; i<multipliers.length(); ++i)
1674 hessian(hyper_offset, hyper_offset) +=
1675 -((Real) resid_per_mult[i]) / multipliers[i] / multipliers[i] / 2.0;
1676 break;
1677 }
1678
1679 }
1680 }
1681
1682
1683 // BMA Reviewed
1684 /** Calculate how many residuals each multiplier affects */
1685 SizetArray ExperimentData::
residuals_per_multiplier(unsigned short multiplier_mode) const1686 residuals_per_multiplier(unsigned short multiplier_mode) const
1687 {
1688 SizetArray resid_per_mult;
1689 switch (multiplier_mode) {
1690
1691 case CALIBRATE_PER_EXPER: {
1692 resid_per_mult.resize(numExperiments, 0);
1693 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind) {
1694 size_t fns_this_exp = allExperiments[exp_ind].num_functions();
1695 resid_per_mult[exp_ind] = fns_this_exp;
1696 }
1697 break;
1698 }
1699
1700 case CALIBRATE_PER_RESP: {
1701 size_t num_scalar = simulationSRD.num_scalar_primary();
1702 size_t num_fields = simulationSRD.num_field_response_groups();
1703 resid_per_mult.resize(num_scalar + num_fields, 0);
1704 // iterate scalar responses, then fields
1705 for (size_t s_ind = 0; s_ind < num_scalar; ++s_ind)
1706 resid_per_mult[s_ind] += numExperiments;
1707
1708 // each experiment can have different field lengths
1709 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind) {
1710 const IntVector& field_lens = allExperiments[exp_ind].field_lengths();
1711 for (size_t f_ind = 0; f_ind < num_fields; ++f_ind)
1712 resid_per_mult[num_scalar + f_ind] += field_lens[f_ind];
1713 }
1714 break;
1715 }
1716
1717 case CALIBRATE_BOTH: {
1718 size_t num_scalar = simulationSRD.num_scalar_primary();
1719 size_t num_fields = simulationSRD.num_field_response_groups();
1720 size_t multiplier_offset = 0;
1721 resid_per_mult.resize(numExperiments*simulationSRD.num_response_groups(), 0);
1722 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind) {
1723
1724 // iterate scalar responses, then fields
1725 for (size_t s_ind = 0; s_ind < num_scalar; ++s_ind)
1726 resid_per_mult[multiplier_offset++] = 1;
1727
1728 // each experiment can have different field lengths
1729 const IntVector& field_lens = allExperiments[exp_ind].field_lengths();
1730 for (size_t f_ind = 0; f_ind < num_fields; ++f_ind) {
1731 resid_per_mult[multiplier_offset++] = field_lens[f_ind];
1732 }
1733 }
1734 break;
1735 }
1736
1737 }
1738
1739 return resid_per_mult;
1740 }
1741
1742
1743 // BMA Reviewed
generate_multipliers(const RealVector & multipliers,unsigned short multiplier_mode,RealVector & expanded_multipliers) const1744 void ExperimentData::generate_multipliers(const RealVector& multipliers,
1745 unsigned short multiplier_mode,
1746 RealVector& expanded_multipliers) const
1747 {
1748 // in most cases, we won't call this function for NONE or ONE cases
1749 expanded_multipliers.resize(num_total_exppoints());
1750
1751 switch (multiplier_mode) {
1752
1753 case CALIBRATE_NONE:
1754 expanded_multipliers = 1.0;
1755 break;
1756
1757 case CALIBRATE_ONE:
1758 assert(multipliers.length() == 1);
1759 expanded_multipliers = multipliers[0];
1760 break;
1761
1762 case CALIBRATE_PER_EXPER: {
1763 assert(multipliers.length() == numExperiments);
1764 size_t resid_offset = 0;
1765 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind) {
1766 size_t fns_this_exp = allExperiments[exp_ind].num_functions();
1767 for (size_t fn_ind = 0; fn_ind < fns_this_exp; ++fn_ind)
1768 expanded_multipliers[resid_offset++] = multipliers[exp_ind];
1769 }
1770 break;
1771 }
1772
1773 case CALIBRATE_PER_RESP: {
1774 assert(multipliers.length() == simulationSRD.num_response_groups());
1775 size_t num_scalar = simulationSRD.num_scalar_primary();
1776 size_t num_fields = simulationSRD.num_field_response_groups();
1777 size_t resid_offset = 0;
1778 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind) {
1779
1780 // each response has a different multiplier, but they don't
1781 // differ across experiments
1782 size_t multiplier_offset = 0;
1783
1784 // iterate scalar responses, then fields
1785 for (size_t s_ind = 0; s_ind < num_scalar; ++s_ind)
1786 expanded_multipliers[resid_offset++] = multipliers[multiplier_offset++];
1787
1788 // each experiment can have different field lengths
1789 const IntVector& field_lens = allExperiments[exp_ind].field_lengths();
1790 for (size_t f_ind = 0; f_ind < num_fields; ++f_ind) {
1791 for (size_t i=0; i<field_lens[f_ind]; ++i)
1792 expanded_multipliers[resid_offset++] = multipliers[multiplier_offset];
1793 // only increment per-top-level response (each field has different mult)
1794 ++multiplier_offset;
1795 }
1796 }
1797 break;
1798 }
1799
1800 case CALIBRATE_BOTH: {
1801 assert(multipliers.length() ==
1802 numExperiments*simulationSRD.num_response_groups());
1803 size_t num_scalar = simulationSRD.num_scalar_primary();
1804 size_t num_fields = simulationSRD.num_field_response_groups();
1805 size_t resid_offset = 0, multiplier_offset = 0;
1806 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind) {
1807
1808 // don't reset the multiplier_offset; each exp, each resp has its own
1809
1810 // iterate scalar responses, then fields
1811 for (size_t s_ind = 0; s_ind < num_scalar; ++s_ind)
1812 expanded_multipliers[resid_offset++] = multipliers[multiplier_offset++];
1813
1814 // each experiment can have different field lengths
1815 const IntVector& field_lens = allExperiments[exp_ind].field_lengths();
1816 for (size_t f_ind = 0; f_ind < num_fields; ++f_ind) {
1817 for (size_t i=0; i<field_lens[f_ind]; ++i)
1818 expanded_multipliers[resid_offset++] = multipliers[multiplier_offset];
1819 // only increment per-top-level response (each field has different mult)
1820 ++multiplier_offset;
1821 }
1822 }
1823 break;
1824 }
1825
1826 default:
1827 // unknown mode
1828 Cerr << "\nError: unknown multiplier mode in generate_multipliers().\n";
1829 abort_handler(-1);
1830 break;
1831
1832 } // switch
1833
1834 }
1835
1836
1837 /// return the index of the multiplier that affects each residual
resid2mult_map(unsigned short multiplier_mode,IntVector & resid2mult_indices) const1838 void ExperimentData::resid2mult_map(unsigned short multiplier_mode,
1839 IntVector& resid2mult_indices) const
1840 {
1841 // in most cases, we won't call this function for NONE or ONE cases
1842 resid2mult_indices.resize(num_total_exppoints());
1843
1844 switch (multiplier_mode) {
1845
1846 case CALIBRATE_NONE:
1847 Cerr << "\nError: cannot generate map for zero multipliers.\n";
1848 abort_handler(-1);
1849 break;
1850
1851 case CALIBRATE_ONE:
1852 resid2mult_indices = 0;
1853 break;
1854
1855 case CALIBRATE_PER_EXPER: {
1856 size_t resid_offset = 0;
1857 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind) {
1858 size_t fns_this_exp = allExperiments[exp_ind].num_functions();
1859 for (size_t fn_ind = 0; fn_ind < fns_this_exp; ++fn_ind)
1860 resid2mult_indices[resid_offset++] = exp_ind;
1861 }
1862 break;
1863 }
1864
1865 case CALIBRATE_PER_RESP: {
1866 size_t num_scalar = simulationSRD.num_scalar_primary();
1867 size_t num_fields = simulationSRD.num_field_response_groups();
1868 size_t resid_offset = 0;
1869 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind) {
1870
1871 // each response has a different multiplier, but they don't
1872 // differ across experiments
1873 size_t multiplier_offset = 0;
1874
1875 // iterate scalar responses, then fields
1876 for (size_t s_ind = 0; s_ind < num_scalar; ++s_ind)
1877 resid2mult_indices[resid_offset++] = multiplier_offset++;
1878
1879 // each experiment can have different field lengths
1880 const IntVector& field_lens = allExperiments[exp_ind].field_lengths();
1881 for (size_t f_ind = 0; f_ind < num_fields; ++f_ind) {
1882 for (size_t i=0; i<field_lens[f_ind]; ++i)
1883 resid2mult_indices[resid_offset++] = multiplier_offset;
1884 // only increment per-top-level response (each field has different mult)
1885 ++multiplier_offset;
1886 }
1887 }
1888 break;
1889 }
1890
1891 case CALIBRATE_BOTH: {
1892 size_t num_scalar = simulationSRD.num_scalar_primary();
1893 size_t num_fields = simulationSRD.num_field_response_groups();
1894 size_t resid_offset = 0, multiplier_offset = 0;
1895 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind) {
1896
1897 // don't reset the multiplier_offset; each exp, each resp has its own
1898
1899 // iterate scalar responses, then fields
1900 for (size_t s_ind = 0; s_ind < num_scalar; ++s_ind)
1901 resid2mult_indices[resid_offset++] = multiplier_offset++;
1902
1903 // each experiment can have different field lengths
1904 const IntVector& field_lens = allExperiments[exp_ind].field_lengths();
1905 for (size_t f_ind = 0; f_ind < num_fields; ++f_ind) {
1906 for (size_t i=0; i<field_lens[f_ind]; ++i)
1907 resid2mult_indices[resid_offset++] = multiplier_offset;
1908 // only increment per-top-level response (each field has different mult)
1909 ++multiplier_offset;
1910 }
1911 }
1912 break;
1913 }
1914
1915 default:
1916 // unknown mode
1917 Cerr << "\nError: unknown multiplier mode in generate_multipliers().\n";
1918 abort_handler(-1);
1919 break;
1920
1921 } // switch
1922
1923 }
1924
1925
1926 StringArray ExperimentData::
hyperparam_labels(unsigned short multiplier_mode) const1927 hyperparam_labels(unsigned short multiplier_mode) const
1928 {
1929 String cm_prefix("CovMult");
1930 StringArray hp_labels;
1931
1932 switch(multiplier_mode) {
1933
1934 case CALIBRATE_NONE:
1935 break;
1936
1937 case CALIBRATE_ONE:
1938 hp_labels.push_back(cm_prefix);
1939 break;
1940
1941 case CALIBRATE_PER_EXPER:
1942 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind)
1943 hp_labels.
1944 push_back(cm_prefix + "Exp" + std::to_string(exp_ind+1));
1945 break;
1946
1947 // BMA TODO: Could use response labels here...
1948 case CALIBRATE_PER_RESP: {
1949 size_t num_resp = simulationSRD.num_scalar_primary() +
1950 simulationSRD.num_field_response_groups();
1951 for (size_t resp_ind=0; resp_ind < num_resp; ++resp_ind)
1952 hp_labels.
1953 push_back(cm_prefix + "Resp" + std::to_string(resp_ind+1));
1954 break;
1955 }
1956
1957 case CALIBRATE_BOTH: {
1958 size_t num_resp = simulationSRD.num_scalar_primary() +
1959 simulationSRD.num_field_response_groups();
1960 for (size_t exp_ind=0; exp_ind < numExperiments; ++exp_ind)
1961 for (size_t resp_ind=0; resp_ind < num_resp; ++resp_ind)
1962 hp_labels.
1963 push_back(cm_prefix + "Exp" + std::to_string(exp_ind+1) +
1964 "Resp" + std::to_string(resp_ind+1));
1965 break;
1966 }
1967
1968 default:
1969 Cerr << "\nError: unkown multiplier mode in hyperparam_labels().\n";
1970 abort_handler(-1);
1971
1972 }
1973
1974 return hp_labels;
1975
1976 }
1977
1978
1979 } // namespace Dakota
1980