NOMAD Source  Version 4.0.0 Beta
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Algorithm.cpp
Go to the documentation of this file.
1 
2 #include <signal.h>
3 
4 #include "../Algos/EvcInterface.hpp"
5 #include "../Algos/Algorithm.hpp"
6 
7 #include "../Math/RNG.hpp"
8 
9 #include "../Param/AllParameters.hpp" // Used for hot restart only.
10 
11 #include "../Util/fileutils.hpp"
12 
13 
14 void NOMAD::Algorithm::init()
15 {
16 
17  _name = "AGenericAlgorithmHasNoName";
18 
19  // Comment to appear at the end of stats lines
20  // By default, nothing is added
21  NOMAD::MainStep::setAlgoComment("");
22 
23  // Verifications that throw Exceptions to the Constructor if not validated.
24  verifyParentNotNull();
25 
26  if (nullptr == _runParams)
27  {
28  throw NOMAD::Exception(__FILE__, __LINE__,
29  "A valid RunParameters must be provided to an Algorithm constructor.");
30  }
31 
32  if (nullptr == _pbParams)
33  {
34  throw NOMAD::Exception(__FILE__, __LINE__,
35  "A valid PbParameters must be provided to the Algorithm constructor.");
36  }
37 
38  if ( nullptr == _stopReasons )
39  throw NOMAD::Exception(__FILE__, __LINE__,
40  "Valid stop reasons must be provided to the Algorithm constructor.");
41 
42  // Instanciate generic algorithm termination
43  _termination = std::make_unique<NOMAD::Termination>( this );
44 
45 
46  // step::userInterrupt() will be called if ctrl-c is pressed:
47  // TODO adjust for threads.
48  // - Should all threads be interrupted? Or it does not matter
49  // as long as the user can continue ? But the latter could cause damage.
50  // Better ensure all threads get interrupted.
51  signal(SIGINT, userInterrupt);
52 
53 }
54 
55 
56 NOMAD::Algorithm::~Algorithm()
57 {
58 
59 }
60 
61 
62 void NOMAD::Algorithm::start()
63 {
64  defaultStart();
65 
66  // All stop reasons are reset.
67  _stopReasons->setStarted();
68 
69 
70  // Update hot restart info
71  readInformationForHotRestart();
72 
73  // By default reset the lap counter for BbEval and set the lap maxBbEval to INF
74  NOMAD::EvcInterface::getEvaluatorControl()->resetLapBbEval();
75  NOMAD::EvcInterface::getEvaluatorControl()->setLapMaxBbEval( NOMAD::INF_SIZE_T );
76 
77  if (nullptr == _megaIteration)
78  {
79  // Default behavior - not hot restart.
80  // Clear cache hits.
81  // Initialization.
82  // Eval X0s.
83 
84  // Ensure we do not count cache hits which may have been read in the cache.
85  NOMAD::CacheBase::resetNbCacheHits();
86 
87  _initialization->start();
88  _initialization->run();
89  _initialization->end();
90 
91  }
92  else
93  {
94  // Hot restart situation.
95  // We will not need Initialization.
96  auto barrier = _megaIteration->getBarrier();
97 
98  // Update X0s
99  // Use best feasible, or best infeasible points.
100  auto bestFeasPoints = barrier->getAllXFeas();
101  auto bestInfPoints = barrier->getAllXInf();
102 
104  if (!bestFeasPoints.empty())
105  {
106  std::transform(bestFeasPoints.begin(), bestFeasPoints.end(), std::back_inserter(x0s),
107  [](NOMAD::EvalPointPtr evalPointPtr) -> NOMAD::EvalPoint { return *evalPointPtr; });
108 
109  }
110  else if (!bestInfPoints.empty())
111  {
112  std::transform(bestInfPoints.begin(), bestInfPoints.end(), std::back_inserter(x0s),
113  [](NOMAD::EvalPointPtr evalPointPtr) -> NOMAD::EvalPoint { return *evalPointPtr; });
114  }
115  _pbParams->setAttributeValue<NOMAD::ArrayOfPoint>("X0", x0s);
116  _pbParams->checkAndComply();
117  }
118 }
119 
120 
121 void NOMAD::Algorithm::end()
122 {
123  if ( _endDisplay )
124  {
125  displayBestSolutions();
126  displayEvalCounts();
127  }
128 
129  // By default reset the lap counter for BbEval and set the lap maxBbEval to INF
130  NOMAD::EvcInterface::getEvaluatorControl()->resetLapBbEval();
131  NOMAD::EvcInterface::getEvaluatorControl()->setLapMaxBbEval( NOMAD::INF_SIZE_T );
132 
133  saveInformationForHotRestart();
134 
135  defaultEnd();
136 
137 
138 }
139 
140 /// CT Not sure if readInformation can be general (avalaible in Mads
141 //void NOMAD::Algorithm::readInformationForHotRestart()
142 //{
143 //// // Restart from where we were before.
144 //// // For this, we need to read some files.
145 //// // Note: Cache file is treated independently from hot restart file.
146 ////
147 //// if (_runParams->getAttributeValue<bool>("HOT_RESTART_READ_FILES"))
148 //// {
149 //// // Verify the files exist and are readable.
150 //// std::string hotRestartFile = _runParams->getAttributeValue<std::string>("HOT_RESTART_FILE");
151 //// if (NOMAD::checkReadFile(hotRestartFile))
152 //// {
153 //// std::cout << "Read hot restart file " << hotRestartFile << std::endl;
154 ////
155 //// // Create a GMesh and an MadsMegaIteration with default values, to be filled
156 //// // by istream is.
157 //// auto barrier = std::make_shared<NOMAD::Barrier>();
158 //// int k = 0;
159 //// NOMAD::SuccessType success = NOMAD::SuccessType::NOT_EVALUATED;
160 //// _megaIteration = std::make_shared<NOMAD::MegaIteration>(this, k, barrier, success);
161 ////
162 //// // Here we use Mads::operator>>
163 //// NOMAD::read(*this, hotRestartFile);
164 //// }
165 //// }
166 //}
167 
168 
169 void NOMAD::Algorithm::hotRestartOnUserInterrupt()
170 {
171  hotRestartBeginHelper();
172 
173  // TODO: To investigate: Reset mesh because parameters have changed.
174  // - Maybe already done as a side effect of calling parent step and
175  // resetting parameters.
176  // - Maybe should be done at another level, Iteration or MegaIteration.
177  // - Useful code:
178  /*
179  std::stringstream ss;
180  auto mesh = getIterationMesh();
181  ss << *mesh;
182  // Reset pointer
183  mesh.reset();
184  mesh = std::make_shared<NOMAD::GMesh>(_pbParams);
185  // Get old mesh values
186  ss >> *mesh;
187  */
188 
189  hotRestartEndHelper();
190 }
191 
192 
193 void NOMAD::Algorithm::saveInformationForHotRestart() const
194 {
195  // If we want to stop completely and then be able to restart
196  // from where we were, we need to save some information on file.
197  //
198  // TODO: Maybe we need to write current parameters. If we write them,
199  // ignore initial values, only take latest values down the Parameter tree.
200  // For now, using initial parameters.
201 
202  // Cache file is treated independently from hot restart file.
203  // As long as the cache file name is set, it is written.
204  // This is the behavior of NOMAD 3.
205  std::string cacheFile = NOMAD::CacheBase::getInstance()->getFileName();
206  if (!cacheFile.empty())
207  {
208  NOMAD::CacheBase::getInstance()->write();
209  }
210  if ( _runParams->getAttributeValue<bool>("HOT_RESTART_WRITE_FILES"))
211  {
212  std::cout << "Save information for hot restart." << std::endl;
213  std::cout << "Write hot restart file." << std::endl;
214  NOMAD::write(*this, _runParams->getAttributeValue<std::string>("HOT_RESTART_FILE"));
215  }
216 }
217 
218 
219 void NOMAD::Algorithm::displayBestSolutions() const
220 {
221  std::vector<NOMAD::EvalPoint> evalPointList;
222  // Display best feasible solutions.
223  std::string sFeas;
224  // Output level is very high if there are no parent algorithm
225  // Output level is info if this algorithm is a sub part of another algorithm.
226  NOMAD::OutputLevel outputLevel = isSubAlgo() ? NOMAD::OutputLevel::LEVEL_INFO
227  : NOMAD::OutputLevel::LEVEL_VERY_HIGH;
228  NOMAD::OutputInfo displaySolFeas(_name, sFeas, outputLevel);
229 
230  sFeas = "Best feasible solution";
231  size_t nbBestFeas = NOMAD::CacheBase::getInstance()->findBestFeas(evalPointList, getSubFixedVariable());
232  if (0 == nbBestFeas)
233  {
234  sFeas += ": Undefined.";
235  displaySolFeas.addMsg(sFeas);
236  }
237  else if (1 == nbBestFeas)
238  {
239  sFeas += ": ";
240  displaySolFeas.addMsgAndSol(sFeas, *evalPointList.begin());
241  }
242  else
243  {
244  sFeas += "s: ";
245  displaySolFeas.addMsgAndSol(sFeas, *evalPointList.begin());
246  }
247 
248 
249  const size_t maxSolCount = 8;
250  if (nbBestFeas > 1)
251  {
252  std::vector<NOMAD::EvalPoint>::const_iterator it;
253  size_t solCount = 0;
254  for (it = evalPointList.begin(); it != evalPointList.end(); ++it)
255  {
256  solCount++;
257  if (evalPointList.begin() == it)
258  {
259  continue; // First element already added
260  }
261  displaySolFeas.addMsgAndSol(" ",*it);
262  if (solCount >= maxSolCount)
263  {
264  // We printed enough solutions already.
265  displaySolFeas.addMsg("... A total of " + std::to_string(evalPointList.size()) + " feasible solutions were found.");
266  break;
267  }
268  }
269  }
270 
271  NOMAD::OutputQueue::Add(std::move(displaySolFeas));
272 
273  evalPointList.clear();
274 
275 
276  // Display best infeasible solutions.
277  std::string sInf;
278  auto hMax = _runParams->getAttributeValue<NOMAD::Double>("H_MAX_0");
279  if (nullptr != _megaIteration)
280  {
281  hMax = _megaIteration->getBarrier()->getHMax();
282  }
283  NOMAD::OutputInfo displaySolInf(_name, sInf, outputLevel);
284  sInf = "Best infeasible solution";
285  size_t nbBestInf = NOMAD::CacheBase::getInstance()->findBestInf(evalPointList, hMax, getSubFixedVariable());
286  if (0 == nbBestInf)
287  {
288  sInf += ": Undefined.";
289  displaySolInf.addMsg(sInf);
290  }
291  else if (1 == nbBestInf)
292  {
293  sInf += ": ";
294  displaySolInf.addMsgAndSol(sInf, *evalPointList.begin());
295  }
296  else
297  {
298  sInf += "s: ";
299  displaySolInf.addMsgAndSol(sInf, *evalPointList.begin());
300  }
301 
302  if (nbBestInf > 1)
303  {
304  size_t solCount = 0;
305  std::vector<NOMAD::EvalPoint>::const_iterator it;
306  for (it = evalPointList.begin(); it != evalPointList.end(); ++it)
307  {
308  solCount++;
309  if (evalPointList.begin() == it)
310  {
311  continue; // First element already added
312  }
313  displaySolInf.addMsgAndSol(" ",(*it));
314  if (solCount >= maxSolCount)
315  {
316  // We printed enough solutions already.
317  displaySolInf.addMsg("... A total of " + std::to_string(evalPointList.size()) + " infeasible solutions were found.");
318  break;
319  }
320  }
321  }
322 
323  NOMAD::OutputQueue::Add(std::move(displaySolInf));
324 }
325 
326 
327 void NOMAD::Algorithm::displayEvalCounts() const
328 {
329  // Compute outputLevel
330 
331  // Display evaluation information
332  size_t bbEval = NOMAD::EvcInterface::getEvaluatorControl()->getBbEval();
333  size_t lapBbEval = NOMAD::EvcInterface::getEvaluatorControl()->getLapBbEval();
334  size_t nbEval = NOMAD::EvcInterface::getEvaluatorControl()->getNbEval();
335  size_t nbCacheHits = NOMAD::CacheBase::getNbCacheHits();
336  int nbEvalNoCount = static_cast<int>(nbEval - bbEval - nbCacheHits);
337  bool showNbEvalNoCount = (nbEvalNoCount > 0);
338  bool showNbCacheHits = (nbCacheHits > 0);
339  bool showNbEval = (nbEval > bbEval);
340  bool showLapBbEval = ( bbEval > lapBbEval && lapBbEval > 0 );
341  // Padding for nice presentation
342  std::string sFeedBbEval, sFeedNbEvalNoCount, sFeedCacheHits, sFeedNbEval;
343  if (showNbEval)
344  {
345  if (showNbEvalNoCount)
346  {
347  sFeedBbEval = " ";
348  sFeedCacheHits = " ";
349  sFeedNbEval = " ";
350  }
351  else
352  {
353  sFeedBbEval = " ";
354  sFeedCacheHits = " ";
355  }
356  }
357  std::string sLapBbEval = "Sub-optimization blackbox evaluations: " + sFeedBbEval + NOMAD::itos(lapBbEval);
358  std::string sBbEval = "Total blackbox evaluations: " + sFeedBbEval + NOMAD::itos(bbEval);
359  std::string sNbEvalNoCount = "Total blackbox evaluation (not counting): " + sFeedNbEvalNoCount + NOMAD::itos(nbEvalNoCount);
360  std::string sCacheHits = "Cache hits: " + sFeedCacheHits + NOMAD::itos(nbCacheHits);
361  std::string sNbEval = "Total number of evaluations: " + sFeedNbEval + NOMAD::itos(nbEval);
362 
363  // Output levels will be modulated depending on the counts and on the Algorithm level.
364  NOMAD::OutputLevel outputLevelHigh = isSubAlgo() ? NOMAD::OutputLevel::LEVEL_INFO
365  : NOMAD::OutputLevel::LEVEL_HIGH;
366  NOMAD::OutputLevel outputLevelNormal = isSubAlgo() ? NOMAD::OutputLevel::LEVEL_INFO
367  : NOMAD::OutputLevel::LEVEL_NORMAL;
368 
369  AddOutputInfo("", outputLevelHigh); // skip line
370  if ( showLapBbEval )
371  AddOutputInfo(sLapBbEval);
372  AddOutputInfo(sBbEval, outputLevelHigh);
373  AddOutputInfo(sNbEvalNoCount, showNbEvalNoCount ? outputLevelNormal : NOMAD::OutputLevel::LEVEL_INFO);
374  AddOutputInfo(sCacheHits, showNbCacheHits ? outputLevelNormal : NOMAD::OutputLevel::LEVEL_INFO);
375  AddOutputInfo(sNbEval, showNbEval ? outputLevelNormal : NOMAD::OutputLevel::LEVEL_INFO);
376 }
377 
378 
379 bool NOMAD::Algorithm::isSubAlgo() const
380 {
381  bool isSub = false;
382 
383  // Get Parent algorithm. No need to cast: We only want to know if it exists.
384  const NOMAD::Step* parentAlgo = getParentOfType<NOMAD::Algorithm*>();
385  if (nullptr != parentAlgo)
386  {
387  isSub = true;
388  }
389 
390  return isSub;
391 }
392 
393 
394 void NOMAD::Algorithm::display ( std::ostream& os ) const
395 {
396 
397  os << "MEGA_ITERATION " << std::endl;
398  os << *_megaIteration << std::endl;
399  os << "NB_EVAL " << NOMAD::EvcInterface::getEvaluatorControl()->getNbEval() << std::endl;
400  os << "NB_BB_EVAL " << NOMAD::EvcInterface::getEvaluatorControl()->getBbEval() << std::endl;
401  uint32_t x, y, z;
402  NOMAD::RNG::getPrivateSeed(x, y, z);
403  os << "RNG " << x << " " << y << " " << z << std::endl;
404 
405 }
406 
407 
408 std::ostream& NOMAD::operator<<(std::ostream& os, const NOMAD::Algorithm & mads)
409 {
410  mads.display(os);
411  return os;
412 }
413 
414 
415 void NOMAD::Algorithm::read( std::istream& is )
416 {
417  // Read line by line
418  std::string name;
419 
420  int nbEval = 0, nbBbEval = 0;
421  uint32_t x, y, z;
422 
423  while (is >> name && is.good() && !is.eof())
424  {
425  if ("MEGA_ITERATION" == name)
426  {
427  is >> *_megaIteration;
428  }
429  else if ("NB_EVAL" == name)
430  {
431  is >> nbEval;
432  }
433  else if ("NB_BB_EVAL" == name)
434  {
435  is >> nbBbEval;
436  }
437  else if ("RNG" == name)
438  {
439  is >> x >> y >> z;
440  NOMAD::RNG::setPrivateSeed(x, y, z);
441  }
442  else
443  {
444  // Put back name to istream. Maybe there is a simpler way.
445  for (unsigned i = 0; i < name.size(); i++)
446  {
447  is.unget();
448  }
449  break;
450  }
451  }
452 
453  NOMAD::EvcInterface::getEvaluatorControl()->setBbEval(nbBbEval);
454  NOMAD::EvcInterface::getEvaluatorControl()->setNbEval(nbEval);
455 
456 }
457 
458 
459 std::istream& NOMAD::operator>>(std::istream& is, NOMAD::Algorithm& algo)
460 {
461  algo.read( is );
462  return is;
463 
464 }
std::string itos(const int i)
Transform an integer into a string.
Definition: utils.cpp:15
std::shared_ptr< EvalPoint > EvalPointPtr
Definition for eval point pointer.
Definition: EvalPoint.hpp:284
unsigned int uint32_t
Definition: defines.hpp:43
std::istream & operator>>(std::istream &is, Algorithm &algo)
Operator to read parameters used for hot restart.
std::vector< Point > ArrayOfPoint
Representation of a vector of points.
bool read(T &info, const std::string &filename)
Definition: fileutils.hpp:140
OutputLevel
Definition: OutputInfo.hpp:12
bool write(const T &info, const std::string &filename)
Definition: fileutils.hpp:102
std::ostream & operator<<(std::ostream &os, const Algorithm &algo)
Operator to write parameters used for hot restart.
const size_t INF_SIZE_T
The infinity for size_t.
Definition: defines.hpp:84