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: SurrBasedGlobalMinimizer
10 //- Description: Implementation code for the SurrBasedGlobalMinimizer class
11 //- Owner: John Eddy, Laura Swiler
12 //- Checked by:
13
14 #include "SurrBasedGlobalMinimizer.hpp"
15 #include "ProblemDescDB.hpp"
16 #include "ParallelLibrary.hpp"
17 #include "ParamResponsePair.hpp"
18 #include "DakotaGraphics.hpp"
19 #include "dakota_system_defs.hpp"
20 #include "DakotaInterface.hpp"
21 #include "DakotaApproximation.hpp"
22 #include <algorithm>
23 #include <iostream>
24 #include <string>
25
26 static const char rcsId[]="@(#) $Id: SurrBasedGlobalMinimizer.cpp 7031 2010-10-22 16:23:52Z mseldre $";
27
28 namespace Dakota {
29
30
31 SurrBasedGlobalMinimizer::
SurrBasedGlobalMinimizer(ProblemDescDB & problem_db,Model & model)32 SurrBasedGlobalMinimizer(ProblemDescDB& problem_db, Model& model):
33 SurrBasedMinimizer(problem_db, model, std::shared_ptr<TraitsBase>(new SurrBasedGlobalTraits())),
34 replacePoints(probDescDB.get_bool("method.sbg.replace_points"))
35 {
36 // Verify that iteratedModel is a surrogate model so that
37 // approximation-related functions are defined.
38 if (iteratedModel.model_type() != "surrogate") {
39 Cerr << "Error: SurrBasedGlobalMinimizer::iteratedModel must be a "
40 << "surrogate model." << std::endl;
41 abort_handler(-1);
42 }
43
44 // historical default convergence tolerance
45 if (convergenceTol < 0.0) convergenceTol = 1.0e-4;
46
47 // While this copy will be replaced in best update, initialize here
48 // since relied on in Minimizer::initialize_run when a sub-iterator
49 bestVariablesArray.push_back(
50 iteratedModel.truth_model().current_variables().copy());
51
52 // Instantiate the approximate sub-problem minimizer
53 const String& approx_method_ptr
54 = probDescDB.get_string("method.sub_method_pointer");
55 const String& approx_method_name
56 = probDescDB.get_string("method.sub_method_name");
57 if (!approx_method_ptr.empty()) {
58 // Approach 1: method spec support for approxSubProbMinimizer
59 const String& model_ptr = probDescDB.get_string("method.model_pointer");
60 size_t method_index = probDescDB.get_db_method_node(); // for restoration
61 probDescDB.set_db_method_node(approx_method_ptr); // method only
62 // sub-problem minimizer will use shallow copy of iteratedModel
63 // (from problem_db.get_model())
64 approxSubProbMinimizer = probDescDB.get_iterator();//(iteratedModel);
65 // suppress DB ctor default and don't output summary info
66 approxSubProbMinimizer.summary_output(false);
67 // verify approx method's modelPointer is empty or consistent
68 const String& am_model_ptr = probDescDB.get_string("method.model_pointer");
69 if (!am_model_ptr.empty() && am_model_ptr != model_ptr)
70 Cerr << "Warning: SBO approx_method_pointer specification includes an\n"
71 << " inconsistent model_pointer that will be ignored."
72 << std::endl;
73 probDescDB.set_db_method_node(method_index); // restore method only
74 }
75 else if (!approx_method_name.empty())
76 // Approach 2: instantiate on-the-fly w/o method spec support
77 approxSubProbMinimizer
78 = probDescDB.get_iterator(approx_method_name, iteratedModel);
79 }
80
81
~SurrBasedGlobalMinimizer()82 SurrBasedGlobalMinimizer::~SurrBasedGlobalMinimizer()
83 { }
84
85
86 /** This just specializes the Iterator implementation to perform
87 default tabulation on the truth model instead of surrogate
88 model. */
initialize_graphics(int iterator_server_id)89 void SurrBasedGlobalMinimizer::initialize_graphics(int iterator_server_id)
90 {
91 Model& truth_model = iteratedModel.truth_model();
92 OutputManager& mgr = parallelLib.output_manager();
93 Graphics& dakota_graphics = mgr.graphics();
94 const Variables& vars = truth_model.current_variables();
95 const Response& resp = truth_model.current_response();
96 bool auto_log = false;
97
98 // For graphics, limit (currently) to server id 1, for both ded master
99 // (parent partition rank 1) and peer partitions (parent partition rank 0)
100 if (mgr.graph2DFlag && iterator_server_id == 1) { // initialize the 2D plots
101 dakota_graphics.create_plots_2d(vars, resp);
102 auto_log = true;
103 }
104
105 // For output/restart/tabular data, all Iterator masters stream output
106 if (mgr.tabularDataFlag) { // initialize data tabulation
107 mgr.create_tabular_datastream(vars, resp);
108 auto_log = true;
109 }
110
111 if (auto_log)
112 truth_model.auto_graphics(true);
113 }
114
115
core_run()116 void SurrBasedGlobalMinimizer::core_run()
117 {
118 // Extract subIterator/subModel(s) from the SurrogateModel
119 Model& truth_model = iteratedModel.truth_model();
120 Model& approx_model = iteratedModel.surrogate_model();
121 Iterator& dace_iterator = iteratedModel.subordinate_iterator();
122
123 // This flag controls the method by which we introduce new results data
124 // into the surrogate for updating. Right now, there are two methods
125 // supported. The first is to create each subsequent surrogate using every
126 // truth point evaluated in all previous iterations. This is identified with
127 // the string "append". The second is to rebuild the surrogate at each
128 // iteration using the original truth samples and the truth solutions from
129 // the previous iteration only. This is identified with the string "replace".
130
131 // Update DACE settings for global approximations. Check that dace_iterator
132 // is defined (a dace_iterator specification is not required when the data
133 // samples are read in from a file rather than obtained from sampling).
134 if (!dace_iterator.is_null())
135 dace_iterator.active_set_request_values(1);
136
137 // get data points using sampling, file read, or whatever.
138 iteratedModel.build_approximation();
139
140 // The points obtained using sampling will not change from here on out nor
141 // will the arrays that store them. We will keep them here for use in
142 // rebuilding the surrogate if using "replace" for example.
143
144 bool returns_multipoint = approxSubProbMinimizer.returns_multiple_points(),
145 accepts_multipoint = approxSubProbMinimizer.accepts_multiple_points(),
146 truth_asynch_flag = truth_model.asynch_flag();
147
148 // This flag will be used to indicate when we are finished iterating. An
149 // iteration is a solution of the approximate model followed by an update
150 // of the surrogate.
151 while (globalIterCount < maxIterations) {
152
153 // Test how well the surrogate matches up with the truth model. For this
154 // test, we currently use R-squared as a measure of goodness of fit,
155 // although we can easily generalize it. Also, currently we state that if
156 // R-squared is < 0.5 or > 1.1 (R-squared can be greater than one in
157 // abnormal cases for surrogates that are not polynomial regression models),
158 // we stop the surrogate-based global minimization process because it will
159 // not work with such an inaccurate model.
160 std::vector<Approximation>& approxs
161 = approx_model.derived_interface().approximations();
162 std::vector<Approximation>::iterator it;
163 for (it=approxs.begin(); it!=approxs.end(); ++it) {
164 if (it->diagnostics_available()) {
165
166 // Start the check with the r-squared value.
167 Real r2_diagnostic = it->diagnostic("rsquared");
168 if (outputLevel > NORMAL_OUTPUT)
169 Cout << "R-squared = " << r2_diagnostic << std::endl;
170
171 // If outside of tolerable range, report and abort.
172 // TODO: report function index along with diagnostic.
173 if (r2_diagnostic < 0.5 || r2_diagnostic > 1.1) {
174 Cerr << "Surrogate approximation is not accurate enough for "
175 << "the surrogate-based global minimization.\n"
176 << "The minimization has quit before the requested number "
177 << "of iterations due to poor surrogate fit." << std::endl;
178 abort_handler(-1);
179 }
180
181 // Add some additional diagnostics?
182 }
183 }
184
185 // use the iterator to solve the approximate subproblem. On the first
186 // iteration, the surrogate is built using only the original truth
187 // samples. At each subsequent iteration, the surrogate includes
188 // additional truth samples from validation of subproblem solutions.
189 ParLevLIter pl_iter = methodPCIter->mi_parallel_level_iterator(miPLIndex);
190 approxSubProbMinimizer.run(pl_iter);
191
192 // Get the results from the iterator execution.
193 VariablesArray vars_results;
194 if (returns_multipoint)
195 vars_results = approxSubProbMinimizer.variables_array_results();
196 else
197 vars_results.push_back(approxSubProbMinimizer.variables_results());
198 size_t i, num_results = vars_results.size();
199
200 // Variable/response results were generated using the current approximate
201 // model. For appending to the current approximate model, we must evaluate
202 // the variable results with the truth model.
203 iteratedModel.component_parallel_mode(TRUTH_MODEL_MODE);
204 IntResponseMap truth_resp_results;
205 for (i=0; i<num_results; i++) {
206 // set the current values of the active variables in the truth model
207 truth_model.active_variables(vars_results[i]);
208
209 // request the evaluation in synchronous or asyncronous mode.
210 if (truth_asynch_flag)
211 truth_model.evaluate_nowait();
212 else {
213 truth_model.evaluate();
214 truth_resp_results[truth_model.evaluation_id()]
215 = truth_model.current_response().copy();
216 }
217 }
218 // If we did our evaluations asynchronously, use synchronize to block
219 // until all the results are available and then store these responses.
220 if (truth_asynch_flag)
221 truth_resp_results = truth_model.synchronize();
222
223 // Beyond this point, we will want to know if this is the last iteration.
224 // We will use this information to prevent updating of the surrogate since
225 // it will not be used again.
226 bool last_iter = ++globalIterCount >= maxIterations;
227
228 if (outputLevel > QUIET_OUTPUT) {
229 // In here we want to write the truth values into a simple tab delimited
230 // file so that we can easily compare them with the surrogate values of
231 // the points returned by the iterator.
232 std::string ofname("finaldatatruth" + std::to_string(globalIterCount) +
233 ".dat");
234 std::ofstream ofile(ofname);
235 ofile.precision(12);
236 IntRespMCIter it = truth_resp_results.begin();
237 for (i=0; i<num_results; ++i, ++it) {
238 const RealVector& c_vars = vars_results[i].continuous_variables();
239 const IntVector& di_vars = vars_results[i].discrete_int_variables();
240 const RealVector& dr_vars = vars_results[i].discrete_real_variables();
241 const RealVector& fn_vals = it->second.function_values();
242 std::copy(c_vars.values(), c_vars.values() + c_vars.length(),
243 std::ostream_iterator<Real>(ofile,"\t"));
244 std::copy(di_vars.values(), di_vars.values() + di_vars.length(),
245 std::ostream_iterator<int>(ofile,"\t"));
246 std::copy(dr_vars.values(), dr_vars.values() + dr_vars.length(),
247 std::ostream_iterator<Real>(ofile,"\t"));
248 std::copy(fn_vals.values(), fn_vals.values() + fn_vals.length(),
249 std::ostream_iterator<Real>(ofile,"\t"));
250 ofile << '\n';
251 }
252 ofile.close();
253 }
254
255 // See if we are done
256 if (last_iter) { // catalogue final results in best{Variables,Response}Array
257 if (returns_multipoint) {
258 bestVariablesArray = vars_results;
259 copy_data(truth_resp_results, bestResponseArray);
260 }
261 else {
262 bestVariablesArray.front().active_variables(vars_results.front());
263 bestResponseArray.front().function_values(
264 approxSubProbMinimizer.response_results().function_values());
265 }
266 }
267 else {
268 // restore state prior to previous append_approximation()
269 if (replacePoints && globalIterCount > 1)
270 approx_model.pop_approximation(false);// don't store SDP set; no restore
271 // update the data set and rebuild the approximation
272 approx_model.append_approximation(vars_results, truth_resp_results, true);
273
274 // pass iterator's final vars for use as next set of initial points
275 if (accepts_multipoint)
276 approxSubProbMinimizer.initial_points(vars_results);
277 else
278 approx_model.active_variables(vars_results.front());
279 }
280 }
281 }
282
283 } // namespace Dakota
284