BayesOpt
bayesoptbase.cpp
1 /*
2 -------------------------------------------------------------------------
3  This file is part of BayesOpt, an efficient C++ library for
4  Bayesian optimization.
5 
6  Copyright (C) 2011-2015 Ruben Martinez-Cantin <rmcantin@unizar.es>
7 
8  BayesOpt is free software: you can redistribute it and/or modify it
9  under the terms of the GNU Affero General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  BayesOpt is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU Affero General Public License for more details.
17 
18  You should have received a copy of the GNU Affero General Public License
19  along with BayesOpt. If not, see <http://www.gnu.org/licenses/>.
20 ------------------------------------------------------------------------
21 */
22 
23 #include <ctime>
25 #include "bayesopt/parameters.hpp"
26 
27 #include "log.hpp"
28 #include "posteriormodel.hpp"
29 #include "specialtypes.hpp"
30 #include "bopt_state.hpp"
31 
32 
33 namespace bayesopt
34 {
35  BayesOptBase::BayesOptBase(size_t dim, Parameters parameters):
36  mParameters(parameters), mDims(dim)
37  {
38  // Random seed
39  if (mParameters.random_seed < 0) mParameters.random_seed = std::time(0);
41 
42  // Setting verbose stuff (files, levels, etc.)
43  int verbose = mParameters.verbose_level;
44  if (verbose>=3)
45  {
46  FILE* log_fd = fopen( mParameters.log_filename.c_str() , "w" );
47  Output2FILE::Stream() = log_fd;
48  verbose -= 3;
49  }
50 
51  switch(verbose)
52  {
53  case 0: FILELog::ReportingLevel() = logWARNING; break;
54  case 1: FILELog::ReportingLevel() = logINFO; break;
55  case 2: FILELog::ReportingLevel() = logDEBUG4; break;
56  default:
57  FILELog::ReportingLevel() = logERROR; break;
58  }
59  }
60 
62  { } // Default destructor
63 
64 
65  // OPTIMIZATION INTERFACE
66  void BayesOptBase::optimize(vectord &bestPoint)
67  {
68  assert(mDims == bestPoint.size());
69 
70  // Restore state from file
72  {
73  BOptState state;
74  bool load_succeed = state.loadFromFile(mParameters.load_filename,
75  mParameters);
76  if(load_succeed)
77  {
78  restoreOptimization(state);
79  FILE_LOG(logINFO) << "State succesfully restored from file \""
80  << mParameters.load_filename << "\"";
81  }
82  else
83  {
84  // If load not succeed, print info message and then
85  // initialize a new optimization
86  FILE_LOG(logINFO) << "File \"" << mParameters.load_filename
87  << "\" does not exist,"
88  << " starting a new optimization";
90  }
91  }
92  else
93  {
94  // Initialize a new state
96  }
97 
98  for (size_t ii = mCurrentIter; ii < mParameters.n_iterations; ++ii)
99  {
101  }
102 
103  bestPoint = getFinalResult();
104  } // optimize
105 
106 
108  {
109  // Find what is the next point.
110  vectord xNext = nextPoint();
111  double yNext = evaluateSampleInternal(xNext);
112 
113  // If we are stuck in the same point for several iterations, try a random jump!
115  {
116  if (std::pow(mYPrev - yNext,2) < mParameters.noise)
117  {
118  mCounterStuck++;
119  FILE_LOG(logDEBUG) << "Stuck for "<< mCounterStuck << " steps";
120  }
121  else
122  {
123  mCounterStuck = 0;
124  }
125  mYPrev = yNext;
126 
127  if (mCounterStuck > mParameters.force_jump)
128  {
129  FILE_LOG(logINFO) << "Forced random query!";
130  xNext = samplePoint();
131  yNext = evaluateSampleInternal(xNext);
132  mCounterStuck = 0;
133  }
134  }
135 
136  mModel->addSample(xNext,yNext);
137 
138  // Update surrogate model
139  bool retrain = ((mParameters.n_iter_relearn > 0) &&
140  ((mCurrentIter + 1) % mParameters.n_iter_relearn == 0));
141 
142  if (retrain) // Full update
143  {
144  mModel->updateHyperParameters();
145  mModel->fitSurrogateModel();
146  }
147  else // Incremental update
148  {
149  mModel->updateSurrogateModel();
150  }
151 
152  plotStepData(mCurrentIter,xNext,yNext);
153  mModel->updateCriteria(xNext);
154  mCurrentIter++;
155 
156  // Save state if required
158  {
159  BOptState state;
160  saveOptimization(state);
162  }
163  }
164 
165 
167  {
168  // Posterior surrogate model
169  mModel.reset(PosteriorModel::create(mDims,mParameters,mEngine));
170 
171  // Configure iteration parameters
172  if (mParameters.n_init_samples <= 0)
173  {
175  static_cast<size_t>(ceil(0.1*mParameters.n_iterations));
176  }
177 
178  size_t nSamples = mParameters.n_init_samples;
179 
180  // Generate xPoints for initial sampling
181  matrixd xPoints(nSamples,mDims);
182  vectord yPoints(nSamples,0);
183 
184  // Save generated xPoints before its evaluation
185  generateInitialPoints(xPoints);
186  saveInitialSamples(xPoints);
187  mModel->setSamples(xPoints);
188 
189  // Save on each evaluation for safety reasons
190  for(size_t i=0; i<yPoints.size(); i++)
191  {
192  yPoints[i] = evaluateSampleInternal(row(xPoints,i));
193  //We clear the vector in the first iteration
194  saveResponse(yPoints[i], i==0);
195  }
196 
197  // Put samples into model
198  mModel->setSamples(yPoints);
199 
200  if(mParameters.verbose_level > 0)
201  {
202  mModel->plotDataset(logDEBUG);
203  }
204 
205  mModel->updateHyperParameters();
206  mModel->fitSurrogateModel();
207  mCurrentIter = 0;
208 
209  mCounterStuck = 0;
210  mYPrev = 0.0;
211  }
212 
214  {
215  return remapPoint(getPointAtMinimum());
216  }
217 
218 
219  // SAVE-RESTORE INTERFACE
221  {
222  // BayesOptBase members
223  state.mCurrentIter = mCurrentIter;
224  state.mCounterStuck = mCounterStuck;
225  state.mYPrev = mYPrev;
226 
227  state.mParameters = mParameters;
228 
229  // Samples
230  state.mX = mModel->getData()->mX;
231  state.mY = mModel->getData()->mY;
232  }
233 
235  {
236  // Restore parameters
237  mParameters = state.mParameters;
238 
239  // Posterior surrogate model
240  mModel.reset(PosteriorModel::create(mDims, mParameters, mEngine));
241 
242  // Load samples, putting mX vecOfvec into a matrixd
243  matrixd xPoints(state.mX.size(),state.mX[0].size());
244  vectord yPoints(state.mX.size(),0);
245  for(size_t i=0; i<state.mX.size(); i++)
246  {
247  row(xPoints, i) = state.mX[i];
248  if(i < state.mY.size())
249  {
250  yPoints[i] = state.mY[i];
251  }
252  else
253  {
254  // Generate remaining initial samples saving in each evaluation
255  yPoints[i] = evaluateSampleInternal(row(xPoints,i));
256  saveResponse(yPoints[i], false);
257  }
258  }
259 
260  // Set loaded and generated samples
261  mModel->setSamples(xPoints,yPoints);
262 
263  if(mParameters.verbose_level > 0)
264  {
265  mModel->plotDataset(logDEBUG);
266  }
267 
268  // Calculate the posterior model
269  mModel->updateHyperParameters();
270  mModel->fitSurrogateModel();
271 
272  mCurrentIter = state.mCurrentIter;
273  mCounterStuck = state.mCounterStuck;
274  mYPrev = state.mYPrev;
275 
276  // Check if optimization has already finished
278  {
279  FILE_LOG(logINFO) << "Optimization has already finished, delete \""
281  << "\" or give more n_iterations in parameters.";
282  }
283  }
284 
285 
286  // GETTERS AND SETTERS
287  // Potential inline functions. Moved here to simplify API and header
288  // structure.
289  ProbabilityDistribution* BayesOptBase::getPrediction(const vectord& query)
290  { return mModel->getPrediction(query); };
291 
292  const Dataset* BayesOptBase::getData()
293  { return mModel->getData(); };
294 
295  Parameters* BayesOptBase::getParameters()
296  {return &mParameters;};
297 
298  double BayesOptBase::getValueAtMinimum()
299  { return mModel->getValueAtMinimum(); };
300 
301  double BayesOptBase::evaluateCriteria(const vectord& query)
302  {
303  if (checkReachability(query)) return mModel->evaluateCriteria(query);
304  else return 0.0;
305  }
306 
307  size_t BayesOptBase::getCurrentIter()
308  {return mCurrentIter;};
309 
310 
311 
312  // PROTECTED
314  { return mModel->getPointAtMinimum(); };
315 
316  double BayesOptBase::evaluateSampleInternal( const vectord &query )
317  {
318  const double yNext = evaluateSample(remapPoint(query));
319  if (yNext == HUGE_VAL)
320  {
321  throw std::runtime_error("Function evaluation out of range");
322  }
323  return yNext;
324  };
325 
326 
327 
328 
329 
330  void BayesOptBase::plotStepData(size_t iteration, const vectord& xNext,
331  double yNext)
332  {
334  {
335  FILE_LOG(logINFO) << "Iteration: " << iteration+1 << " of "
336  << mParameters.n_iterations << " | Total samples: "
337  << iteration+1+mParameters.n_init_samples ;
338  FILE_LOG(logINFO) << "Query: " << remapPoint(xNext); ;
339  FILE_LOG(logINFO) << "Query outcome: " << yNext ;
340  FILE_LOG(logINFO) << "Best query: " << getFinalResult();
341  FILE_LOG(logINFO) << "Best outcome: " << getValueAtMinimum();
342  }
343  } //plotStepData
344 
345 
346  void BayesOptBase::saveInitialSamples(matrixd xPoints)
347  {
348  // Save state if required
350  {
351  BOptState state;
352  saveOptimization(state);
353 
354  // Overwrite the state with initial samples so far
355  state.mX.clear();
356  for(size_t i=0; i<xPoints.size1(); i++)
357  {
358  state.mX.push_back(row(xPoints,i));
359  }
361  }
362  }
363 
364 
365  void BayesOptBase::saveResponse(double yPoint, bool clear)
366  {
367  // Save state if required
369  {
370  BOptState state;
371  saveOptimization(state);
372  if (clear)
373  {
374  state.mY.clear();
375  }
376  utils::append(state.mY,yPoint);
378  }
379  }
380 
381 
382 
383 
384 
385 
386  // PRIVATE MEMBERS
388  {
389  //Epsilon-Greedy exploration (see Bull 2011)
390  if ((mParameters.epsilon > 0.0) && (mParameters.epsilon < 1.0))
391  {
392  randFloat drawSample(mEngine,realUniformDist(0,1));
393  double result = drawSample();
394  FILE_LOG(logINFO) << "Trying random jump with prob:" << result;
395  if (mParameters.epsilon > result)
396  {
397  FILE_LOG(logINFO) << "Epsilon-greedy random query!";
398  return samplePoint();
399  }
400  }
401 
402  vectord Xnext(mDims);
403 
404  // GP-Hedge and related algorithms
405  if (mModel->criteriaRequiresComparison())
406  {
407  bool changed = true;
408 
409  mModel->setFirstCriterium();
410  while (changed)
411  {
412  findOptimal(Xnext);
413  changed = mModel->setNextCriterium(Xnext);
414  }
415  std::string name = mModel->getBestCriteria(Xnext);
416  FILE_LOG(logINFO) << name << " was selected.";
417  }
418  else // Standard "Bayesian optimization"
419  {
420  FILE_LOG(logDEBUG) << "------ Optimizing criteria ------";
421  findOptimal(Xnext);
422  }
423  return Xnext;
424  }
425 
426 
427 
428 } //namespace bayesopt
429 
virtual void generateInitialPoints(matrixd &xPoints)=0
Selects the initial set of points to build the surrogate model.
void plotStepData(size_t iteration, const vectord &xNext, double yNext)
Print data for every step according to the verbose level.
Boost vector and matrix types.
double epsilon
For epsilon-greedy exploration.
Definition: parameters.hpp:99
size_t load_save_flag
1-Load data,2-Save data, 3-Load and save data.
Definition: parameters.hpp:80
void saveToFile(std::string filename)
Creates or overwrites the provided file with the state.
Definition: bopt_state.cpp:33
virtual void findOptimal(vectord &xOpt)=0
Call the inner optimization method to find the optimal point acording to the criteria.
Class that represents the state of an optimization.
Definition: bopt_state.hpp:46
Namespace of the library interface.
Definition: using.dox:1
virtual bool checkReachability(const vectord &query)
This function checks if the query is valid or not.
std::string load_filename
Init data file path (if applicable)
Definition: parameters.hpp:82
boost::mt19937 mEngine
Random number generator.
void saveOptimization(BOptState &state)
Saves the current state of the optimization process into a state class.
Parameters mParameters
Configuration parameters.
size_t n_init_samples
Number of samples before optimization.
Definition: parameters.hpp:68
void saveInitialSamples(matrixd xPoints)
Eases the process of saving a state during initial samples.
virtual vectord samplePoint()=0
Sample a single point in the input space.
virtual double evaluateSample(const vectord &query)=0
Function that defines the actual function to be optimized.
size_t mDims
Number of dimensions.
bool loadFromFile(std::string filename, Parameters &program_params)
Loads the state from the provided file and takes program_params values if needed. ...
Definition: bopt_state.cpp:39
size_t n_iterations
Maximum BayesOpt evaluations (budget)
Definition: parameters.hpp:66
void optimize(vectord &bestPoint)
Execute the optimization process of the function defined in evaluateSample.
size_t force_jump
If >0, and the difference between two consecutive observations is pure noise, for n consecutive steps...
Definition: parameters.hpp:100
vectord getFinalResult()
Once the optimization has been perfomed, return the optimal point.
Abstract interface for posterior model/criteria.
Representation of a optimization state.
Dataset model to deal with the vector (real) based datasets.
Definition: dataset.hpp:40
double evaluateSampleInternal(const vectord &query)
Wrapper for the target function adding any preprocessing or constraint.
void initializeOptimization()
Initialize the optimization process.
vectord nextPoint()
Selects the next point to evaluate according to a certain criteria or metacriteria.
std::string log_filename
Log file path (if applicable)
Definition: parameters.hpp:78
void stepOptimization()
Execute ONE step the optimization process of the function defined in evaluateSample.
int verbose_level
Neg-Error,0-Warning,1-Info,2-Debug -> stdout 3-Error,4-Warning,5-Info,>5-Debug -> logfile...
Definition: parameters.hpp:76
double noise
Variance of observation noise (and nugget)
Definition: parameters.hpp:88
Modules and helper macros for logging.
BayesOpt common module for interfaces.
size_t mCurrentIter
Current iteration number.
void restoreOptimization(BOptState state)
Restores the optimization process of a previous execution.
virtual ~BayesOptBase()
Default destructor.
Parameter definitions.
virtual vectord remapPoint(const vectord &x)=0
Remap the point x to the original space (e.g.
std::string save_filename
Sava data file path (if applicable)
Definition: parameters.hpp:83
vectord getPointAtMinimum()
Get optimal point in the inner space (e.g.
int random_seed
>=0 -> Fixed seed, <0 -> Time based (variable).
Definition: parameters.hpp:74
size_t n_iter_relearn
Number of samples before relearn kernel.
Definition: parameters.hpp:69