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