SOT
optimizer.h
1 
8 #ifndef SOT_OPTIMIZER_H
9 #define SOT_OPTIMIZER_H
10 
11 #include "common.h"
12 #include "utils.h"
13 #include <thread>
14 #include <mutex>
15 
17 namespace sot {
18 
20 
34  class Optimizer {
35  protected:
36  std::shared_ptr<Problem> mData;
37  std::shared_ptr<ExpDesign> mExpDes;
38  std::shared_ptr<Surrogate> mSurf;
39  std::shared_ptr<Sampling> mSampling;
40  const double mSigmaMax = 0.2;
41  const double mSigmaMin = 0.005;
42  int mSuccTol = 3;
43  int mFailTol;
44  int mMaxEvals;
45  int mNumEvals;
47  int mDim;
50  std::string mName = "Surrogate Optimizer";
52  int mEvalCount = 0;
53  std::mutex mMutex;
55 
60  void evalBatch(const mat &batch, vec &funVals) {
61  mMutex.lock();
62  int myEval = mEvalCount;
63  mEvalCount++;
64  mMutex.unlock();
65 
66  while(myEval < batch.n_cols) {
67  vec x = batch.col(myEval);
68  funVals[myEval] = mData->eval(x);
69 
70  mMutex.lock();
71  myEval = mEvalCount;
72  mEvalCount++;
73  mMutex.unlock();
74  }
75  }
76  public:
78 
87  Optimizer(std::shared_ptr<Problem>& data, std::shared_ptr<ExpDesign>& expDes,
88  std::shared_ptr<Surrogate>& surf, std::shared_ptr<Sampling>& sampling,
89  int maxEvals) {
90  mData = std::shared_ptr<Problem>(data);
91  mExpDes = std::shared_ptr<ExpDesign>(expDes);
92  mSurf = std::shared_ptr<Surrogate>(surf);
93  mSampling = std::shared_ptr<Sampling>(sampling);
94  mNumEvals = 0;
95  mMaxEvals = maxEvals;
96  mInitPoints = mExpDes->numPoints();
97  mDim = data->dim();
98  mxLow = data->lBounds();
99  mxUp = data->uBounds();
100  mFailTol = data->dim();
101  mNumThreads = 1;
102 
103  if(mMaxEvals < mInitPoints) {
104  throw std::logic_error("Experimental design larger than evaluation budget");
105  }
106  }
108 
118  Optimizer(std::shared_ptr<Problem>& data, std::shared_ptr<ExpDesign>& expDes,
119  std::shared_ptr<Surrogate>& surf, std::shared_ptr<Sampling>& sampling,
120  int maxEvals, int numThreads) : Optimizer(data, expDes, surf, sampling, maxEvals)
121  {
122  mNumThreads = numThreads;
123  }
124 
126 
129  Result run() {
130  std::vector<std::thread> threads(mNumThreads);
131  Result res(mMaxEvals, mDim);
132  mNumEvals = 0;
133 
134  double fBestLoc = std::numeric_limits<double>::max();
135  vec xBestLoc;
136 
137  start:
138  double sigma = mSigmaMax;
139  int fail = 0;
140  int succ = 0;
141 
142  mat initDes = fromUnitBox(mExpDes->generatePoints(), mxLow, mxUp);
143  vec initFunVal = arma::zeros(mInitPoints);
144 
146 
147  if(mNumThreads > 1) { // Evaluate in synchronous parallel
148  mEvalCount = 0;
149  for(int i=0; i < mNumThreads; i++) {
150  threads[i] = std::thread(&sot::Optimizer::evalBatch, this,
151  std::ref(initDes), std::ref(initFunVal));
152  }
153 
154  for(int i=0; i < mNumThreads; i++) {
155  threads[i].join();
156  }
157  }
158  else { // Evaluate in serial
159  for(int i=0; i < mInitPoints; i++) {
160  vec x = initDes.col(i);
161  initFunVal(i) = mData->eval(x);
162  }
163  }
164 
165  res.addEvals(initDes, initFunVal);
166 
167  int iStart = mNumEvals;
168  int iEnd = std::min<int>(mNumEvals + mInitPoints - 1, mMaxEvals - 1);
169  for(int i=mNumEvals; i <= iEnd ; i++) {
170  vec x = initDes.col(i - iStart);
171  double fx = initFunVal[i - iStart];
172  if(fx < fBestLoc) {
173  xBestLoc = x;
174  fBestLoc = fx;
175  }
176  mNumEvals++;
177  }
178 
180  if(iStart < iEnd) {
181  mSurf->addPoints(res.X().cols(iStart, iEnd), res.fX().rows(iStart, iEnd));
182  }
184  while (mNumEvals < mMaxEvals) {
185  // Fit the RBF
186  mSurf->fit();
187 
188  // Find new points to evaluate
189  mat X = res.X().cols(iStart, mNumEvals - 1);
190  int newEvals = std::min<int>(mNumThreads, mMaxEvals - mNumEvals);
191  mat batch = mSampling->makePoints(xBestLoc, X, sigma*(mxUp - mxLow), newEvals);
192  vec batchVals = arma::zeros(newEvals);
193 
194  if(newEvals > 1) { // Evaluate in synchronous parallel
195  mEvalCount = 0;
196  for(int i=0; i < newEvals; i++) {
197  threads[i] = std::thread(&sot::Optimizer::evalBatch, this,
198  std::ref(batch), std::ref(batchVals));
199  }
200 
201  for(int i=0; i < newEvals; i++) {
202  threads[i].join();
203  }
204  }
205  else { // Evaluate in serial
206  batchVals(0) = mData->eval((vec)batch);
207  }
208 
209  // Update evaluation counter
210  mNumEvals += newEvals;
211 
212  // Add to results
213  res.addEvals(batch, batchVals);
214 
215  // Process evaluations
216  for(int i=0; i < newEvals; i++) {
217  vec newx = batch.col(i);
218  double fVal = batchVals(i);
219 
220  if(fVal < fBestLoc) {
221  if(fVal < fBestLoc - 1e-3 * fabs(fBestLoc)) {
222  fail = 0;
223  succ++;
224  }
225  else {
226  fail++;
227  succ = 0;
228  }
229  fBestLoc= fVal;
230  xBestLoc = newx;
231  }
232  else {
233  fail++;
234  succ = 0;
235  }
236 
237  // Update sigma if necessary
238  if(fail == mFailTol) {
239  fail = 0;
240  succ = 0;
241  sigma /= 2.0;
242  int budget = mMaxEvals - mNumEvals - 1;
243  // Restart if sigma is too small and the budget
244  // is larger than the initial design
245  if (sigma < mSigmaMin and budget > mInitPoints) {
246  fBestLoc = std::numeric_limits<double>::max();
247  mSurf->reset();
248  mSampling->reset(mMaxEvals - mNumEvals - mInitPoints);
249  goto start;
250  }
251  }
252  if(succ == mSuccTol) {
253  fail = 0;
254  succ = 0;
255  sigma = fmin(sigma * 2.0, mSigmaMax);
256  }
257  }
258 
259  // Add to surface
260  if (batch.n_cols > 1) {
261  mSurf->addPoints(batch, batchVals);
262  }
263  else {
264  mSurf->addPoint((vec)batch, batchVals(0));
265  }
266  }
267 
268  return res;
269  }
270  };
271 }
272 
273 #endif
274 
Optimization result class.
Definition: utils.h:138
vec fX() const
Method for getting the values of the finished evaluations.
Definition: utils.h:182
Optimizer(std::shared_ptr< Problem > &data, std::shared_ptr< ExpDesign > &expDes, std::shared_ptr< Surrogate > &surf, std::shared_ptr< Sampling > &sampling, int maxEvals)
Constructor.
Definition: optimizer.h:87
const double mSigmaMax
Definition: optimizer.h:40
std::shared_ptr< Surrogate > mSurf
Definition: optimizer.h:38
vec mxLow
Definition: optimizer.h:48
int mEvalCount
Definition: optimizer.h:52
arma::vec vec
Default (column) vector class.
Definition: common.h:17
void addEvals(const mat &X, const vec &funVals)
Method for adding a finished evaluations.
Definition: utils.h:245
std::string mName
Definition: optimizer.h:50
Optimizer(std::shared_ptr< Problem > &data, std::shared_ptr< ExpDesign > &expDes, std::shared_ptr< Surrogate > &surf, std::shared_ptr< Sampling > &sampling, int maxEvals, int numThreads)
Constructor.
Definition: optimizer.h:118
The surrogate optimization algorithm.
Definition: optimizer.h:34
int mDim
Definition: optimizer.h:47
int mFailTol
Definition: optimizer.h:43
std::shared_ptr< ExpDesign > mExpDes
Definition: optimizer.h:37
std::shared_ptr< Problem > mData
Definition: optimizer.h:36
int mInitPoints
Definition: optimizer.h:46
const double mSigmaMin
Definition: optimizer.h:41
vec fromUnitBox(const vec &x, const vec &xLow, const vec &xUp)
Map one point from the unit box to another hypercube.
Definition: utils.h:94
int mNumEvals
Definition: optimizer.h:45
mat X() const
Method for getting the evaluated points.
Definition: utils.h:193
Result run()
Runs the optimization algorithm.
Definition: optimizer.h:129
int mSuccTol
Definition: optimizer.h:42
SOT namespace.
Definition: sot.h:27
vec mxUp
Definition: optimizer.h:49
arma::mat mat
Default matrix class.
Definition: common.h:16
void evalBatch(const mat &batch, vec &funVals)
Evalaute a batch of points in parallel.
Definition: optimizer.h:60
std::shared_ptr< Sampling > mSampling
Definition: optimizer.h:39
std::mutex mMutex
Definition: optimizer.h:53
int mMaxEvals
Definition: optimizer.h:44
int mNumThreads
Definition: optimizer.h:51