MueLu Version of the Day
Loading...
Searching...
No Matches
MueLu_AvatarInterface.cpp
Go to the documentation of this file.
1// @HEADER
2// *****************************************************************************
3// MueLu: A package for multigrid based preconditioning
4//
5// Copyright 2012 NTESS and the MueLu contributors.
6// SPDX-License-Identifier: BSD-3-Clause
7// *****************************************************************************
8// @HEADER
9
11
12#include <string>
13#include <fstream>
14#include <sstream>
15#include <vector>
16#include "Teuchos_Array.hpp"
17#include "Teuchos_CommHelpers.hpp"
18#include "MueLu_BaseClass.hpp"
19#include "Teuchos_RawParameterListHelpers.hpp"
20
21// ***********************************************************************
22/* Notional Parameterlist Structure
23 "avatar: filestem" "{'mystem1','mystem2'}"
24 "avatar: decision tree files" "{'mystem1.trees','mystem2.trees'}"
25 "avatar: names files" "{'mystem1.names','mystem2.names'}"
26 "avatar: good class" "1"
27 "avatar: heuristic" "1"
28 "avatar: bounds file" "{'bounds.data'}"
29 "avatar: muelu parameter mapping"
30 - "param0'
31 - "muelu parameter" "aggregation: threshold"
32 - "avatar parameter" "DROP_TOL"
33 - "muelu values" "{0,1e-4,1e-3,1e-2}"
34 - "avatar values" "{1,2,3,4}
35 - "param1'
36 - "muelu parameter" "smoother: sweeps"
37 - "avatar parameter" "SWEEPS"
38 - "muelu values" "{1,2,3}"
39 - "avatar values" "{1,2,3}"
40
41
42 Notional SetMueLuParameters "problemFeatures" Structure
43 "my feature #1" "246.01"
44 "my feature #2" "123.45"
45
46 */
47
48/*
49TODO List:
50Modify MueLu
51 Parameter name checking (make sure names match between Avatar’s names file and the parameter / feature names that MueLu sees).
52*/
53
54#ifdef HAVE_MUELU_AVATAR
55
56extern "C" {
57#include "avatar_api.h"
58}
59
60namespace MueLu {
61
62// ***********************************************************************
63RCP<const ParameterList> AvatarInterface::GetValidParameterList() const {
64 RCP<ParameterList> validParamList = rcp(new ParameterList());
65
66 Teuchos::ParameterList pl_dummy;
67 Teuchos::Array<std::string> ar_dummy;
68 int int_dummy;
69
70 // Files from which to read Avatar trees
71 validParamList->set<Teuchos::Array<std::string>>("avatar: decision tree files", ar_dummy, "Names of Avatar decision tree files");
72
73 // Strings from which to read Avatar trees
74 validParamList->set<Teuchos::Array<std::string>>("avatar: decision tree strings", ar_dummy, "Avatar decision tree strings");
75
76 // Files from which to read Avatar names
77 validParamList->set<Teuchos::Array<std::string>>("avatar: names files", ar_dummy, "Names of Avatar decision names files");
78
79 // Strings from which to read Avatar names
80 validParamList->set<Teuchos::Array<std::string>>("avatar: names strings", ar_dummy, "Avatar decision names strings");
81
82 // Filestem arg for Avatar
83 validParamList->set<Teuchos::Array<std::string>>("avatar: filestem", ar_dummy, "Filestem for the files Avatar requires");
84
85 // This should be a MueLu parameter-to-Avatar parameter mapping (e.g. if Avatar doesn't like spaces)
86 validParamList->set<Teuchos::ParameterList>("avatar: muelu parameter mapping", pl_dummy, "Mapping of MueLu to Avatar Parameters");
87
88 // "Good" Class ID for Avatar
89 validParamList->set<int>("avatar: good class", int_dummy, "Numeric code for class Avatar considers to be good");
90
91 // Which drop tol choice heuristic to use
92 validParamList->set<int>("avatar: heuristic", int_dummy, "Numeric code for which heuristic we want to use");
93
94 // Bounds file for extrapolation risk
95 validParamList->set<Teuchos::Array<std::string>>("avatar: bounds file", ar_dummy, "Bounds file for Avatar extrapolation risk");
96
97 // Add dummy variables at the start
98 validParamList->set<int>("avatar: initial dummy variables", int_dummy, "Number of dummy variables to add at the start");
99
100 // Add dummy variables before the class
101 validParamList->set<int>("avatar: pre-class dummy variables", int_dummy, "Number of dummy variables to add at the before the class");
102
103 // Value of the dummy variables
104 validParamList->set<int>("avatar: dummy value", int_dummy, "Value of the dummy variables to add at the before/after the class");
105
106 return validParamList;
107}
108
109// ***********************************************************************
110Teuchos::ArrayRCP<std::string> AvatarInterface::ReadFromFiles(const char* paramName) const {
111 // const Teuchos::Array<std::string> & tf = params_.get<const Teuchos::Array<std::string> >(paramName);
112 Teuchos::Array<std::string>& tf = params_.get<Teuchos::Array<std::string>>(paramName);
113 Teuchos::ArrayRCP<std::string> treelist;
114 // Only Proc 0 will read the files and print the strings
115 if (comm_->getRank() == 0) {
116 treelist.resize(tf.size());
117 for (Teuchos_Ordinal i = 0; i < tf.size(); i++) {
118 std::fstream file;
119 std::stringstream ss;
120 file.open(tf[i]);
121 ss << file.rdbuf();
122 treelist[i] = ss.str();
123 file.close();
124 }
125 }
126 return treelist;
127}
128
129// ***********************************************************************
130void AvatarInterface::Setup() {
131 // Sanity check
132 if (comm_.is_null()) throw std::runtime_error("MueLu::AvatarInterface::Setup(): Communicator cannot be null");
133
134 // Get the avatar strings (NOTE: Only exist on proc 0)
135 if (params_.isParameter("avatar: decision tree strings"))
136 avatarStrings_ = Teuchos::arcpFromArray(params_.get<Teuchos::Array<std::string>>("avatar: decision tree strings"));
137 else
138 avatarStrings_ = ReadFromFiles("avatar: decision tree files");
139
140 if (params_.isParameter("avatar: names strings"))
141 namesStrings_ = Teuchos::arcpFromArray(params_.get<Teuchos::Array<std::string>>("avatar: names strings"));
142 else
143 namesStrings_ = ReadFromFiles("avatar: names files");
144
145 if (params_.isParameter("avatar: bounds file"))
146 boundsString_ = ReadFromFiles("avatar: bounds file");
147
148 filestem_ = params_.get<Teuchos::Array<std::string>>("avatar: filestem");
149
150 if (comm_->getRank() == 0) {
151 // Now actually set up avatar - Avatar's cleanup routine will free the pointer
152 // Avatar_handle* avatar_train(int argc, char **argv, char* names_file, int names_file_is_a_string, char* train_file, int train_file_is_a_string);
153 const int namesfile_is_a_string = 1;
154 const int treesfile_is_a_string = 1;
155 avatarHandle_ = avatar_load(const_cast<char*>(filestem_[0].c_str()), const_cast<char*>(namesStrings_[0].c_str()), namesfile_is_a_string, const_cast<char*>(avatarStrings_[0].c_str()), treesfile_is_a_string);
156 }
157
158 // Which class does Avatar consider "good"
159 avatarGoodClass_ = params_.get<int>("avatar: good class");
160
161 heuristicToUse_ = params_.get<int>("avatar: heuristic");
162
163 // Unpack the MueLu Mapping into something actionable
164 UnpackMueLuMapping();
165}
166
167// ***********************************************************************
168void AvatarInterface::Cleanup() {
169 avatar_cleanup(avatarHandle_);
170 avatarHandle_ = 0;
171}
172
173// ***********************************************************************
174void AvatarInterface::GenerateFeatureString(const Teuchos::ParameterList& problemFeatures, std::string& featureString) const {
175 // NOTE: Assumes that the features are in the same order Avatar wants them.
176 std::stringstream ss;
177
178 // Initial Dummy Variables
179 if (params_.isParameter("avatar: initial dummy variables")) {
180 int num_dummy = params_.get<int>("avatar: initial dummy variables");
181 int dummy_value = params_.get("avatar: dummy value", 666);
182
183 for (int i = 0; i < num_dummy; i++)
184 ss << dummy_value << ",";
185 }
186
187 for (Teuchos::ParameterList::ConstIterator i = problemFeatures.begin(); i != problemFeatures.end(); i++) {
188 // const std::string& name = problemFeatures.name(i);
189 const Teuchos::ParameterEntry& entry = problemFeatures.entry(i);
190 if (i != problemFeatures.begin()) ss << ",";
191 entry.leftshift(ss, false); // Because ss<<entry prints out '[unused]' and we don't want that.
192 }
193 featureString = ss.str();
194}
195
196// ***********************************************************************
197void AvatarInterface::UnpackMueLuMapping() {
198 const Teuchos::ParameterList& mapping = params_.get<Teuchos::ParameterList>("avatar: muelu parameter mapping");
199 // Each MueLu/Avatar parameter pair gets its own sublist. These must be numerically ordered with no gap
200
201 bool done = false;
202 int idx = 0;
203 int numParams = mapping.numParams();
204
205 mueluParameterName_.resize(numParams);
206 avatarParameterName_.resize(numParams);
207 mueluParameterValues_.resize(numParams);
208 avatarParameterValues_.resize(numParams);
209
210 while (!done) {
211 std::stringstream ss;
212 ss << "param" << idx;
213 if (mapping.isSublist(ss.str())) {
214 const Teuchos::ParameterList& sublist = mapping.sublist(ss.str());
215
216 // Get the names
217 mueluParameterName_[idx] = sublist.get<std::string>("muelu parameter");
218 avatarParameterName_[idx] = sublist.get<std::string>("avatar parameter");
219
220 // Get the values
221 // FIXME: For now we assume that all of these guys are doubles and their Avatar analogues are doubles
222 mueluParameterValues_[idx] = sublist.get<Teuchos::Array<double>>("muelu values");
223 avatarParameterValues_[idx] = sublist.get<Teuchos::Array<double>>("avatar values");
224
225 idx++;
226 } else {
227 done = true;
228 }
229 }
230
231 if (idx != numParams)
232 throw std::runtime_error("MueLu::AvatarInterface::UnpackMueLuMapping(): 'avatar: muelu parameter mapping' has unknown fields");
233}
234// ***********************************************************************
235std::string AvatarInterface::ParamsToString(const std::vector<int>& indices) const {
236 std::stringstream ss;
237 for (Teuchos_Ordinal i = 0; i < avatarParameterValues_.size(); i++) {
238 ss << "," << avatarParameterValues_[i][indices[i]];
239 }
240
241 // Pre-Class dummy variables
242 if (params_.isParameter("avatar: pre-class dummy variables")) {
243 int num_dummy = params_.get<int>("avatar: pre-class dummy variables");
244 int dummy_value = params_.get("avatar: dummy value", 666);
245 for (int i = 0; i < num_dummy; i++)
246 ss << "," << dummy_value;
247 }
248
249 return ss.str();
250}
251
252// ***********************************************************************
253void AvatarInterface::SetIndices(int id, std::vector<int>& indices) const {
254 // The combo numbering here starts with the first guy
255 int numParams = (int)avatarParameterValues_.size();
256 int curr_id = id;
257 for (int i = 0; i < numParams; i++) {
258 int div = avatarParameterValues_[i].size();
259 int mod = curr_id % div;
260 indices[i] = mod;
261 curr_id = (curr_id - mod) / div;
262 }
263}
264
265// ***********************************************************************
266void AvatarInterface::GenerateMueLuParametersFromIndex(int id, Teuchos::ParameterList& pl) const {
267 // The combo numbering here starts with the first guy
268 int numParams = (int)avatarParameterValues_.size();
269 int curr_id = id;
270 for (int i = 0; i < numParams; i++) {
271 int div = avatarParameterValues_[i].size();
272 int mod = curr_id % div;
273 pl.set(mueluParameterName_[i], mueluParameterValues_[i][mod]);
274 curr_id = (curr_id - mod) / div;
275 }
276}
277
278// ***********************************************************************
279void AvatarInterface::SetMueLuParameters(const Teuchos::ParameterList& problemFeatures, Teuchos::ParameterList& mueluParams, bool overwrite) const {
280 Teuchos::ParameterList avatarParams;
281 std::string paramString;
282
283 if (comm_->getRank() == 0) {
284 // Only Rank 0 calls Avatar
285 if (!avatarHandle_) throw std::runtime_error("MueLu::AvatarInterface::SetMueLuParameters(): Setup has not been run");
286
287 // Turn the problem features into a "trial" string for Avatar
288 std::string trialString;
289 GenerateFeatureString(problemFeatures, trialString);
290
291 // Compute the number of things we need to test
292 int numParams = (int)avatarParameterValues_.size();
293 std::vector<int> indices(numParams);
294 std::vector<int> sizes(numParams);
295 int num_combos = 1;
296 for (int i = 0; i < numParams; i++) {
297 sizes[i] = avatarParameterValues_[i].size();
298 num_combos *= avatarParameterValues_[i].size();
299 }
300 GetOStream(Runtime0) << "MueLu::AvatarInterface: Testing " << num_combos << " option combinations" << std::endl;
301
302 // For each input parameter to avatar we iterate over its allowable values and then compute the list of options which Avatar
303 // views as acceptable
304 // FIXME: Find alternative to hard coding malloc size (input deck?)
305 int num_classes = avatar_num_classes(avatarHandle_);
306 std::vector<int> predictions(num_combos, 0);
307 std::vector<float> probabilities(num_classes * num_combos, 0);
308
309 std::string testString;
310 for (int i = 0; i < num_combos; i++) {
311 SetIndices(i, indices);
312 // Now we add the MueLu parameters into one, enormous Avatar trial string and run avatar once
313 testString += trialString + ParamsToString(indices) + ",0\n";
314 }
315
316 std::cout << "** Avatar TestString ***\n"
317 << testString << std::endl; // DEBUG
318
319 int bound_check = true;
320 if (params_.isParameter("avatar: bounds file"))
321 bound_check = checkBounds(testString, boundsString_);
322
323 // FIXME: Only send in first tree's string
324 // int* avatar_test(Avatar_handle* a, char* test_data_file, int test_data_is_a_string);
325 const int test_data_is_a_string = 1;
326 avatar_test(avatarHandle_, const_cast<char*>(testString.c_str()), test_data_is_a_string, predictions.data(), probabilities.data());
327
328 // Look at the list of acceptable combinations of options
329 std::vector<int> acceptableCombos;
330 acceptableCombos.reserve(100);
331 for (int i = 0; i < num_combos; i++) {
332 if (predictions[i] == avatarGoodClass_) acceptableCombos.push_back(i);
333 }
334 GetOStream(Runtime0) << "MueLu::AvatarInterface: " << acceptableCombos.size() << " acceptable option combinations found" << std::endl;
335
336 // Did we have any good combos at all?
337 int chosen_option_id = 0;
338 if (acceptableCombos.size() == 0) {
339 GetOStream(Runtime0) << "WARNING: MueLu::AvatarInterface: found *no* combinations of options which it believes will perform well on this problem" << std::endl
340 << " An arbitrary set of options will be chosen instead" << std::endl;
341 } else {
342 // If there is only one acceptable combination, use it;
343 // otherwise, find the parameter choice with the highest
344 // probability of success
345 if (acceptableCombos.size() == 1) {
346 chosen_option_id = acceptableCombos[0];
347 } else {
348 switch (heuristicToUse_) {
349 case 1:
350 chosen_option_id = hybrid(probabilities.data(), acceptableCombos);
351 break;
352 case 2:
353 chosen_option_id = highProb(probabilities.data(), acceptableCombos);
354 break;
355 case 3:
356 // Choose the first option in the list of acceptable
357 // combinations; the lowest drop tolerance among the
358 // acceptable combinations
359 chosen_option_id = acceptableCombos[0];
360 break;
361 case 4:
362 chosen_option_id = lowCrash(probabilities.data(), acceptableCombos);
363 break;
364 case 5:
365 chosen_option_id = weighted(probabilities.data(), acceptableCombos);
366 break;
367 }
368 }
369 }
370
371 // If mesh parameters are outside bounding box, set drop tolerance
372 // to 0, otherwise use avatar recommended drop tolerance
373 if (bound_check == 0) {
374 GetOStream(Runtime0) << "WARNING: Extrapolation risk detected, setting drop tolerance to 0" << std::endl;
375 GenerateMueLuParametersFromIndex(0, avatarParams);
376 } else {
377 GenerateMueLuParametersFromIndex(chosen_option_id, avatarParams);
378 }
379 }
380
381 Teuchos::updateParametersAndBroadcast(outArg(avatarParams), outArg(mueluParams), *comm_, 0, overwrite);
382}
383
384int AvatarInterface::checkBounds(std::string trialString, Teuchos::ArrayRCP<std::string> boundsString) const {
385 std::stringstream ss(trialString);
386 std::vector<double> vect;
387
388 double b;
389 while (ss >> b) {
390 vect.push_back(b);
391 if (ss.peek() == ',') ss.ignore();
392 }
393
394 std::stringstream ssBounds(boundsString[0]);
395 std::vector<double> boundsVect;
396
397 while (ssBounds >> b) {
398 boundsVect.push_back(b);
399 if (ssBounds.peek() == ',') ssBounds.ignore();
400 }
401
402 int min_idx = (int)std::min(vect.size(), boundsVect.size() / 2);
403
404 bool inbounds = true;
405 for (int i = 0; inbounds && i < min_idx; i++)
406 inbounds = boundsVect[2 * i] <= vect[i] && vect[i] <= boundsVect[2 * i + 1];
407
408 return (int)inbounds;
409}
410
411int AvatarInterface::hybrid(float* probabilities, std::vector<int> acceptableCombos) const {
412 float low_crash = probabilities[0];
413 float best_prob = probabilities[2];
414 float diff;
415 int this_combo;
416 int chosen_option_id = acceptableCombos[0];
417 for (int x = 0; x < (int)acceptableCombos.size(); x++) {
418 this_combo = acceptableCombos[x] * 3;
419 diff = probabilities[this_combo] - low_crash;
420 // If this parameter combination has a crash
421 // probability .2 lower than the current "best", we
422 // will use this drop tolerance
423 if (diff < -.2) {
424 low_crash = probabilities[this_combo];
425 best_prob = probabilities[this_combo + 2];
426 chosen_option_id = acceptableCombos[x];
427 }
428 // If this parameter combination has the same
429 // or slightly lower crash probability than the
430 // current best, we compare their "GOOD" probabilities
431 else if (diff <= 0 && probabilities[this_combo + 2] > best_prob) {
432 low_crash = probabilities[this_combo];
433 best_prob = probabilities[this_combo + 2];
434 chosen_option_id = acceptableCombos[x];
435 }
436 }
437 return chosen_option_id;
438}
439
440int AvatarInterface::highProb(float* probabilities, std::vector<int> acceptableCombos) const {
441 float high_prob = probabilities[2];
442 int this_combo;
443 int chosen_option_id = acceptableCombos[0];
444 for (int x = 0; x < (int)acceptableCombos.size(); x++) {
445 this_combo = acceptableCombos[x] * 3;
446 // If this parameter combination has a higher "GOOD"
447 // probability, use this combination
448 if (probabilities[this_combo + 2] > high_prob) {
449 high_prob = probabilities[this_combo + 2];
450 chosen_option_id = acceptableCombos[x];
451 }
452 }
453 return chosen_option_id;
454}
455
456int AvatarInterface::lowCrash(float* probabilities, std::vector<int> acceptableCombos) const {
457 float low_crash = probabilities[0];
458 int this_combo;
459 int chosen_option_id = acceptableCombos[0];
460 for (int x = 0; x < (int)acceptableCombos.size(); x++) {
461 this_combo = acceptableCombos[x] * 3;
462 // If this parameter combination has a lower "CRASH"
463 // probability, use this combination
464 if (probabilities[this_combo] < low_crash) {
465 low_crash = probabilities[this_combo];
466 chosen_option_id = acceptableCombos[x];
467 }
468 }
469 return chosen_option_id;
470}
471
472int AvatarInterface::weighted(float* probabilities, std::vector<int> acceptableCombos) const {
473 float low_crash = probabilities[0];
474 float best_prob = probabilities[2];
475 float diff;
476 int this_combo;
477 int chosen_option_id = acceptableCombos[0];
478 for (int x = 0; x < (int)acceptableCombos.size(); x++) {
479 this_combo = acceptableCombos[x] * 3;
480 diff = probabilities[this_combo] - low_crash;
481 // If this parameter combination has a crash
482 // probability .2 lower than the current "best", we
483 // will use this drop tolerance
484 if (diff < -.2) {
485 low_crash = probabilities[this_combo];
486 best_prob = probabilities[this_combo + 2];
487 chosen_option_id = acceptableCombos[x];
488 }
489 // If this parameter combination is within .1
490 // or has a slightly lower crash probability than the
491 // current best, we compare their "GOOD" probabilities
492 else if (diff <= .1 && probabilities[this_combo + 2] > best_prob) {
493 low_crash = probabilities[this_combo];
494 best_prob = probabilities[this_combo + 2];
495 chosen_option_id = acceptableCombos[x];
496 }
497 }
498 return chosen_option_id;
499}
500
501} // namespace MueLu
502
503#endif // HAVE_MUELU_AVATAR
Namespace for MueLu classes and methods.
@ Runtime0
One-liner description of what is happening.