Benchmark.cpp
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2010, Rice University
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the Rice University nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34 
35 /* Author: Ioan Sucan, Luis G. Torres */
36 
37 #include "ompl/tools/benchmark/Benchmark.h"
38 #include "ompl/tools/benchmark/MachineSpecs.h"
39 #include "ompl/util/Time.h"
40 #include "ompl/config.h"
41 #include <boost/scoped_ptr.hpp>
42 #include <boost/progress.hpp>
43 #include <thread>
44 #include <mutex>
45 #include <condition_variable>
46 #include <fstream>
47 #include <sstream>
48 
50 namespace ompl
51 {
52  namespace tools
53  {
55  static std::string getResultsFilename(const Benchmark::CompleteExperiment &exp)
56  {
57  return "ompl_" + exp.host + "_" + time::as_string(exp.startTime) + ".log";
58  }
59 
61  static std::string getConsoleFilename(const Benchmark::CompleteExperiment &exp)
62  {
63  return "ompl_" + exp.host + "_" + time::as_string(exp.startTime) + ".console";
64  }
65 
66  static bool terminationCondition(const machine::MemUsage_t maxMem, const time::point &endTime)
67  {
68  if (time::now() < endTime && machine::getProcessMemoryUsage() < maxMem)
69  return false;
70  return true;
71  }
72 
73  class RunPlanner
74  {
75  public:
76 
77  RunPlanner(const Benchmark *benchmark, bool useThreads)
78  : benchmark_(benchmark), timeUsed_(0.0), memUsed_(0), useThreads_(useThreads)
79  {
80  }
81 
82  void run(const base::PlannerPtr &planner, const machine::MemUsage_t memStart, const machine::MemUsage_t maxMem, const double maxTime, const double timeBetweenUpdates)
83  {
84  // if (!useThreads_)
85  // {
86  runThread(planner, memStart + maxMem, time::seconds(maxTime), time::seconds(timeBetweenUpdates));
87  // return;
88  // }
89 
90  // std::thread t(std::bind(&RunPlanner::runThread, this, planner, memStart + maxMem, time::seconds(maxTime), time::seconds(timeBetweenUpdates)));
91 
92  // allow 25% more time than originally specified, in order to detect planner termination
93  // if (!t.try_join_for(time::seconds(maxTime * 1.25)))
94  // {
95  // status_ = base::PlannerStatus::CRASH;
96  //
97  // std::stringstream es;
98  // es << "Planner " << benchmark_->getStatus().activePlanner << " did not complete run " << benchmark_->getStatus().activeRun
99  // << " within the specified amount of time (possible crash). Attempting to force termination of planning thread ..." << std::endl;
100  // std::cerr << es.str();
101  // OMPL_ERROR(es.str().c_str());
102  //
103  // t.interrupt();
104  // t.join();
105  //
106  // std::string m = "Planning thread cancelled";
107  // std::cerr << m << std::endl;
108  // OMPL_ERROR(m.c_str());
109  // }
110 
111  // if (memStart < memUsed_)
112  // memUsed_ -= memStart;
113  // else
114  // memUsed_ = 0;
115  }
116 
117  double getTimeUsed() const
118  {
119  return timeUsed_;
120  }
121 
122  machine::MemUsage_t getMemUsed() const
123  {
124  return memUsed_;
125  }
126 
127  base::PlannerStatus getStatus() const
128  {
129  return status_;
130  }
131 
132  const Benchmark::RunProgressData& getRunProgressData() const
133  {
134  return runProgressData_;
135  }
136 
137  private:
138 
139  void runThread(const base::PlannerPtr &planner, const machine::MemUsage_t maxMem, const time::duration &maxDuration, const time::duration &timeBetweenUpdates)
140  {
141  time::point timeStart = time::now();
142 
143  try
144  {
145  base::PlannerTerminationConditionFn ptc = std::bind(&terminationCondition, maxMem, time::now() + maxDuration);
146  solved_ = false;
147  // Only launch the planner progress property
148  // collector if there is any data for it to report
149  //
150  // \todo issue here is that at least one sample
151  // always gets taken before planner even starts;
152  // might be worth adding a short wait time before
153  // collector begins sampling
154  boost::scoped_ptr<std::thread> t;
155  if (planner->getPlannerProgressProperties().size() > 0)
156  t.reset(new std::thread(std::bind(&RunPlanner::collectProgressProperties, this,
157  planner->getPlannerProgressProperties(),
158  timeBetweenUpdates)));
159  status_ = planner->solve(ptc, 0.1);
160  solvedFlag_.lock();
161  solved_ = true;
162  solvedCondition_.notify_all();
163  solvedFlag_.unlock();
164  if (t)
165  t->join(); // maybe look into interrupting even if planner throws an exception
166  }
167  catch(std::runtime_error &e)
168  {
169  std::stringstream es;
170  es << "There was an error executing planner " << benchmark_->getStatus().activePlanner << ", run = " << benchmark_->getStatus().activeRun << std::endl;
171  es << "*** " << e.what() << std::endl;
172  std::cerr << es.str();
173  OMPL_ERROR(es.str().c_str());
174  }
175 
176  timeUsed_ = time::seconds(time::now() - timeStart);
177  memUsed_ = machine::getProcessMemoryUsage();
178  }
179 
180  void collectProgressProperties(const base::Planner::PlannerProgressProperties& properties,
181  const time::duration &timePerUpdate)
182  {
183  time::point timeStart = time::now();
184 
185  std::unique_lock<std::mutex> ulock(solvedFlag_);
186  while (!solved_)
187  {
188  if (solvedCondition_.wait_for(ulock, timePerUpdate) == std::cv_status::no_timeout)
189  return;
190  else
191  {
192  double timeInSeconds = time::seconds(time::now() - timeStart);
193  std::string timeStamp = std::to_string(timeInSeconds);
194  std::map<std::string, std::string> data;
195  data["time REAL"] = timeStamp;
196  for (base::Planner::PlannerProgressProperties::const_iterator item = properties.begin();
197  item != properties.end();
198  ++item)
199  {
200  data[item->first] = item->second();
201  }
202  runProgressData_.push_back(data);
203  }
204  }
205  }
206 
207  const Benchmark *benchmark_;
208  double timeUsed_;
209  machine::MemUsage_t memUsed_;
210  base::PlannerStatus status_;
211  bool useThreads_;
212  Benchmark::RunProgressData runProgressData_;
213 
214  // variables needed for progress property collection
215  bool solved_;
216  std::mutex solvedFlag_;
217  std::condition_variable solvedCondition_;
218  };
219 
220  }
221 }
223 
224 bool ompl::tools::Benchmark::saveResultsToFile(const char *filename) const
225 {
226  bool result = false;
227 
228  std::ofstream fout(filename);
229  if (fout.good())
230  {
231  result = saveResultsToStream(fout);
232  OMPL_INFORM("Results saved to '%s'", filename);
233  }
234  else
235  {
236  // try to save to a different file, if we can
237  if (getResultsFilename(exp_) != std::string(filename))
238  result = saveResultsToFile();
239 
240  OMPL_ERROR("Unable to write results to '%s'", filename);
241  }
242  return result;
243 }
244 
246 {
247  std::string filename = getResultsFilename(exp_);
248  return saveResultsToFile(filename.c_str());
249 }
250 
251 bool ompl::tools::Benchmark::saveResultsToStream(std::ostream &out) const
252 {
253  if (exp_.planners.empty())
254  {
255  OMPL_WARN("There is no experimental data to save");
256  return false;
257  }
258 
259  if (!out.good())
260  {
261  OMPL_ERROR("Unable to write to stream");
262  return false;
263  }
264 
265  out << "OMPL version " << OMPL_VERSION << std::endl;
266  out << "Experiment " << (exp_.name.empty() ? "NO_NAME" : exp_.name) << std::endl;
267 
268  out << exp_.parameters.size() << " experiment properties" << std::endl;
269  for(std::map<std::string, std::string>::const_iterator it = exp_.parameters.begin(); it != exp_.parameters.end(); ++it)
270  out << it->first << " = " << it->second << std::endl;
271 
272  out << "Running on " << (exp_.host.empty() ? "UNKNOWN" : exp_.host) << std::endl;
273  out << "Starting at " << time::as_string(exp_.startTime) << std::endl;
274  out << "<<<|" << std::endl << exp_.setupInfo << "|>>>" << std::endl;
275  out << "<<<|" << std::endl << exp_.cpuInfo << "|>>>" << std::endl;
276 
277  out << exp_.seed << " is the random seed" << std::endl;
278  out << exp_.maxTime << " seconds per run" << std::endl;
279  out << exp_.maxMem << " MB per run" << std::endl;
280  out << exp_.runCount << " runs per planner" << std::endl;
281  out << exp_.totalDuration << " seconds spent to collect the data" << std::endl;
282 
283  // change this if more enum types are added
284  out << "1 enum type" << std::endl;
285  out << "status";
286  for (unsigned int i = 0 ; i < base::PlannerStatus::TYPE_COUNT ; ++i)
287  out << '|' << base::PlannerStatus(static_cast<base::PlannerStatus::StatusType>(i)).asString();
288  out << std::endl;
289 
290  out << exp_.planners.size() << " planners" << std::endl;
291 
292  for (unsigned int i = 0 ; i < exp_.planners.size() ; ++i)
293  {
294  out << exp_.planners[i].name << std::endl;
295 
296  // get names of common properties
297  std::vector<std::string> properties;
298  for (std::map<std::string, std::string>::const_iterator mit = exp_.planners[i].common.begin() ;
299  mit != exp_.planners[i].common.end() ; ++mit)
300  properties.push_back(mit->first);
301  std::sort(properties.begin(), properties.end());
302 
303  // print names & values of common properties
304  out << properties.size() << " common properties" << std::endl;
305  for (unsigned int k = 0 ; k < properties.size() ; ++k)
306  {
307  std::map<std::string, std::string>::const_iterator it = exp_.planners[i].common.find(properties[k]);
308  out << it->first << " = " << it->second << std::endl;
309  }
310 
311  // construct the list of all possible properties for all runs
312  std::map<std::string, bool> propSeen;
313  for (unsigned int j = 0 ; j < exp_.planners[i].runs.size() ; ++j)
314  for (std::map<std::string, std::string>::const_iterator mit = exp_.planners[i].runs[j].begin() ;
315  mit != exp_.planners[i].runs[j].end() ; ++mit)
316  propSeen[mit->first] = true;
317 
318  properties.clear();
319 
320  for (std::map<std::string, bool>::iterator it = propSeen.begin() ; it != propSeen.end() ; ++it)
321  properties.push_back(it->first);
322  std::sort(properties.begin(), properties.end());
323 
324  // print the property names
325  out << properties.size() << " properties for each run" << std::endl;
326  for (unsigned int j = 0 ; j < properties.size() ; ++j)
327  out << properties[j] << std::endl;
328 
329  // print the data for each run
330  out << exp_.planners[i].runs.size() << " runs" << std::endl;
331  for (unsigned int j = 0 ; j < exp_.planners[i].runs.size() ; ++j)
332  {
333  for (unsigned int k = 0 ; k < properties.size() ; ++k)
334  {
335  std::map<std::string, std::string>::const_iterator it = exp_.planners[i].runs[j].find(properties[k]);
336  if (it != exp_.planners[i].runs[j].end())
337  out << it->second;
338  out << "; ";
339  }
340  out << std::endl;
341  }
342 
343  // print the run progress data if it was reported
344  if (exp_.planners[i].runsProgressData.size() > 0)
345  {
346  // Print number of progress properties
347  out << exp_.planners[i].progressPropertyNames.size() << " progress properties for each run" << std::endl;
348  // Print progress property names
349  for (std::vector<std::string>::const_iterator iter =
350  exp_.planners[i].progressPropertyNames.begin();
351  iter != exp_.planners[i].progressPropertyNames.end();
352  ++iter)
353  {
354  out << *iter << std::endl;
355  }
356  // Print progress properties for each run
357  out << exp_.planners[i].runsProgressData.size() << " runs" << std::endl;
358  for (std::size_t r = 0; r < exp_.planners[i].runsProgressData.size(); ++r)
359  {
360  // For each time point
361  for (std::size_t t = 0; t < exp_.planners[i].runsProgressData[r].size(); ++t)
362  {
363  // Print each of the properties at that time point
364  for (std::map<std::string, std::string>::const_iterator iter =
365  exp_.planners[i].runsProgressData[r][t].begin();
366  iter != exp_.planners[i].runsProgressData[r][t].end();
367  ++iter)
368  {
369  out << iter->second << ",";
370  }
371 
372  // Separate time points by semicolons
373  out << ";";
374  }
375 
376  // Separate runs by newlines
377  out << std::endl;
378  }
379  }
380 
381  out << '.' << std::endl;
382  }
383  return true;
384 }
385 
387 {
388  // sanity checks
389  if (gsetup_)
390  {
391  if (!gsetup_->getSpaceInformation()->isSetup())
392  gsetup_->getSpaceInformation()->setup();
393  }
394  else
395  {
396  if (!csetup_->getSpaceInformation()->isSetup())
397  csetup_->getSpaceInformation()->setup();
398  }
399 
400  if (!(gsetup_ ? gsetup_->getGoal() : csetup_->getGoal()))
401  {
402  OMPL_ERROR("No goal defined");
403  return;
404  }
405 
406  if (planners_.empty())
407  {
408  OMPL_ERROR("There are no planners to benchmark");
409  return;
410  }
411 
412  status_.running = true;
413  exp_.totalDuration = 0.0;
414  exp_.maxTime = req.maxTime;
415  exp_.maxMem = req.maxMem;
416  exp_.runCount = req.runCount;
417  exp_.host = machine::getHostname();
418  exp_.cpuInfo = machine::getCPUInfo();
419  exp_.seed = RNG::getSeed();
420 
421  exp_.startTime = time::now();
422 
423  OMPL_INFORM("Configuring planners ...");
424 
425  // clear previous experimental data
426  exp_.planners.clear();
427  exp_.planners.resize(planners_.size());
428 
429  const base::ProblemDefinitionPtr &pdef = gsetup_ ? gsetup_->getProblemDefinition() : csetup_->getProblemDefinition();
430  // set up all the planners
431  for (unsigned int i = 0 ; i < planners_.size() ; ++i)
432  {
433  // configure the planner
434  planners_[i]->setProblemDefinition(pdef);
435  if (!planners_[i]->isSetup())
436  planners_[i]->setup();
437  exp_.planners[i].name = (gsetup_ ? "geometric_" : "control_") + planners_[i]->getName();
438  OMPL_INFORM("Configured %s", exp_.planners[i].name.c_str());
439  }
440 
441  OMPL_INFORM("Done configuring planners.");
442  OMPL_INFORM("Saving planner setup information ...");
443 
444  std::stringstream setupInfo;
445  if (gsetup_)
446  gsetup_->print(setupInfo);
447  else
448  csetup_->print(setupInfo);
449  setupInfo << std::endl << "Properties of benchmarked planners:" << std::endl;
450  for (unsigned int i = 0 ; i < planners_.size() ; ++i)
451  planners_[i]->printProperties(setupInfo);
452 
453  exp_.setupInfo = setupInfo.str();
454 
455  OMPL_INFORM("Done saving information");
456 
457  OMPL_INFORM("Beginning benchmark");
459  boost::scoped_ptr<msg::OutputHandlerFile> ohf;
460  if (req.saveConsoleOutput)
461  {
462  ohf.reset(new msg::OutputHandlerFile(getConsoleFilename(exp_).c_str()));
463  msg::useOutputHandler(ohf.get());
464  }
465  else
467  OMPL_INFORM("Beginning benchmark");
468 
469  boost::scoped_ptr<boost::progress_display> progress;
470  if (req.displayProgress)
471  {
472  std::cout << "Running experiment " << exp_.name << "." << std::endl;
473  std::cout << "Each planner will be executed " << req.runCount << " times for at most " << req.maxTime << " seconds. Memory is limited at "
474  << req.maxMem << "MB." << std::endl;
475  progress.reset(new boost::progress_display(100, std::cout));
476  }
477 
479  machine::MemUsage_t maxMemBytes = (machine::MemUsage_t)(req.maxMem * 1024 * 1024);
480 
481  for (unsigned int i = 0 ; i < planners_.size() ; ++i)
482  {
483  status_.activePlanner = exp_.planners[i].name;
484  // execute planner switch event, if set
485  try
486  {
487  if (plannerSwitch_)
488  {
489  OMPL_INFORM("Executing planner-switch event for planner %s ...", status_.activePlanner.c_str());
490  plannerSwitch_(planners_[i]);
491  OMPL_INFORM("Completed execution of planner-switch event");
492  }
493  }
494  catch(std::runtime_error &e)
495  {
496  std::stringstream es;
497  es << "There was an error executing the planner-switch event for planner " << status_.activePlanner << std::endl;
498  es << "*** " << e.what() << std::endl;
499  std::cerr << es.str();
500  OMPL_ERROR(es.str().c_str());
501  }
502  if (gsetup_)
503  gsetup_->setup();
504  else
505  csetup_->setup();
506  planners_[i]->params().getParams(exp_.planners[i].common);
507  planners_[i]->getSpaceInformation()->params().getParams(exp_.planners[i].common);
508 
509  // Add planner progress property names to struct
510  exp_.planners[i].progressPropertyNames.push_back("time REAL");
511  base::Planner::PlannerProgressProperties::const_iterator iter;
512  for (iter = planners_[i]->getPlannerProgressProperties().begin();
513  iter != planners_[i]->getPlannerProgressProperties().end();
514  ++iter)
515  {
516  exp_.planners[i].progressPropertyNames.push_back(iter->first);
517  }
518  std::sort(exp_.planners[i].progressPropertyNames.begin(),
519  exp_.planners[i].progressPropertyNames.end());
520 
521  // run the planner
522  for (unsigned int j = 0 ; j < req.runCount ; ++j)
523  {
524  status_.activeRun = j;
525  status_.progressPercentage = (double)(100 * (req.runCount * i + j)) / (double)(planners_.size() * req.runCount);
526 
527  if (req.displayProgress)
528  while (status_.progressPercentage > progress->count())
529  ++(*progress);
530 
531  OMPL_INFORM("Preparing for run %d of %s", status_.activeRun, status_.activePlanner.c_str());
532 
533  // make sure all planning data structures are cleared
534  try
535  {
536  planners_[i]->clear();
537  if (gsetup_)
538  {
539  gsetup_->getProblemDefinition()->clearSolutionPaths();
540  gsetup_->getSpaceInformation()->getMotionValidator()->resetMotionCounter();
541  }
542  else
543  {
544  csetup_->getProblemDefinition()->clearSolutionPaths();
545  csetup_->getSpaceInformation()->getMotionValidator()->resetMotionCounter();
546  }
547  }
548  catch(std::runtime_error &e)
549  {
550  std::stringstream es;
551  es << "There was an error while preparing for run " << status_.activeRun << " of planner " << status_.activePlanner << std::endl;
552  es << "*** " << e.what() << std::endl;
553  std::cerr << es.str();
554  OMPL_ERROR(es.str().c_str());
555  }
556 
557  // execute pre-run event, if set
558  try
559  {
560  if (preRun_)
561  {
562  OMPL_INFORM("Executing pre-run event for run %d of planner %s ...", status_.activeRun, status_.activePlanner.c_str());
563  preRun_(planners_[i]);
564  OMPL_INFORM("Completed execution of pre-run event");
565  }
566  }
567  catch(std::runtime_error &e)
568  {
569  std::stringstream es;
570  es << "There was an error executing the pre-run event for run " << status_.activeRun << " of planner " << status_.activePlanner << std::endl;
571  es << "*** " << e.what() << std::endl;
572  std::cerr << es.str();
573  OMPL_ERROR(es.str().c_str());
574  }
575 
576  RunPlanner rp(this, req.useThreads);
577  rp.run(planners_[i], memStart, maxMemBytes, req.maxTime, req.timeBetweenUpdates);
578  bool solved = gsetup_ ? gsetup_->haveSolutionPath() : csetup_->haveSolutionPath();
579 
580  // store results
581  try
582  {
583  RunProperties run;
584 
585  run["time REAL"] = std::to_string(rp.getTimeUsed());
586  run["memory REAL"] = std::to_string((double)rp.getMemUsed() / (1024.0 * 1024.0));
587  run["status ENUM"] = std::to_string((int)static_cast<base::PlannerStatus::StatusType>(rp.getStatus()));
588  if (gsetup_)
589  {
590  run["solved BOOLEAN"] = std::to_string(gsetup_->haveExactSolutionPath());
591  run["valid segment fraction REAL"] = std::to_string(gsetup_->getSpaceInformation()->getMotionValidator()->getValidMotionFraction());
592  }
593  else
594  {
595  run["solved BOOLEAN"] = std::to_string(csetup_->haveExactSolutionPath());
596  run["valid segment fraction REAL"] = std::to_string(csetup_->getSpaceInformation()->getMotionValidator()->getValidMotionFraction());
597  }
598 
599  if (solved)
600  {
601  if (gsetup_)
602  {
603  run["approximate solution BOOLEAN"] = std::to_string(gsetup_->getProblemDefinition()->hasApproximateSolution());
604  run["solution difference REAL"] = std::to_string(gsetup_->getProblemDefinition()->getSolutionDifference());
605  run["solution length REAL"] = std::to_string(gsetup_->getSolutionPath().length());
606  run["solution smoothness REAL"] = std::to_string(gsetup_->getSolutionPath().smoothness());
607  run["solution clearance REAL"] = std::to_string(gsetup_->getSolutionPath().clearance());
608  run["solution segments INTEGER"] = std::to_string(gsetup_->getSolutionPath().getStateCount() - 1);
609  run["correct solution BOOLEAN"] = std::to_string(gsetup_->getSolutionPath().check());
610 
611  unsigned int factor = gsetup_->getStateSpace()->getValidSegmentCountFactor();
612  gsetup_->getStateSpace()->setValidSegmentCountFactor(factor * 4);
613  run["correct solution strict BOOLEAN"] = std::to_string(gsetup_->getSolutionPath().check());
614  gsetup_->getStateSpace()->setValidSegmentCountFactor(factor);
615 
616  if (req.simplify)
617  {
618  // simplify solution
619  time::point timeStart = time::now();
620  gsetup_->simplifySolution();
621  double timeUsed = time::seconds(time::now() - timeStart);
622  run["simplification time REAL"] = std::to_string(timeUsed);
623  run["simplified solution length REAL"] = std::to_string(gsetup_->getSolutionPath().length());
624  run["simplified solution smoothness REAL"] = std::to_string(gsetup_->getSolutionPath().smoothness());
625  run["simplified solution clearance REAL"] = std::to_string(gsetup_->getSolutionPath().clearance());
626  run["simplified solution segments INTEGER"] = std::to_string(gsetup_->getSolutionPath().getStateCount() - 1);
627  run["simplified correct solution BOOLEAN"] = std::to_string(gsetup_->getSolutionPath().check());
628  gsetup_->getStateSpace()->setValidSegmentCountFactor(factor * 4);
629  run["simplified correct solution strict BOOLEAN"] = std::to_string(gsetup_->getSolutionPath().check());
630  gsetup_->getStateSpace()->setValidSegmentCountFactor(factor);
631  }
632  }
633  else
634  {
635  run["approximate solution BOOLEAN"] = std::to_string(csetup_->getProblemDefinition()->hasApproximateSolution());
636  run["solution difference REAL"] = std::to_string(csetup_->getProblemDefinition()->getSolutionDifference());
637  run["solution length REAL"] = std::to_string(csetup_->getSolutionPath().length());
638  run["solution clearance REAL"] = std::to_string(csetup_->getSolutionPath().asGeometric().clearance());
639  run["solution segments INTEGER"] = std::to_string(csetup_->getSolutionPath().getControlCount());
640  run["correct solution BOOLEAN"] = std::to_string(csetup_->getSolutionPath().check());
641  }
642  }
643 
644  base::PlannerData pd (gsetup_ ? gsetup_->getSpaceInformation() : csetup_->getSpaceInformation());
645  planners_[i]->getPlannerData(pd);
646  run["graph states INTEGER"] = std::to_string(pd.numVertices());
647  run["graph motions INTEGER"] = std::to_string(pd.numEdges());
648 
649  for (std::map<std::string, std::string>::const_iterator it = pd.properties.begin() ; it != pd.properties.end() ; ++it)
650  run[it->first] = it->second;
651 
652  // execute post-run event, if set
653  try
654  {
655  if (postRun_)
656  {
657  OMPL_INFORM("Executing post-run event for run %d of planner %s ...", status_.activeRun, status_.activePlanner.c_str());
658  postRun_(planners_[i], run);
659  OMPL_INFORM("Completed execution of post-run event");
660  }
661  }
662  catch(std::runtime_error &e)
663  {
664  std::stringstream es;
665  es << "There was an error in the execution of the post-run event for run " << status_.activeRun << " of planner " << status_.activePlanner << std::endl;
666  es << "*** " << e.what() << std::endl;
667  std::cerr << es.str();
668  OMPL_ERROR(es.str().c_str());
669  }
670 
671  exp_.planners[i].runs.push_back(run);
672 
673  // Add planner progress data from the planner progress
674  // collector if there was anything to report
675  if (planners_[i]->getPlannerProgressProperties().size() > 0)
676  {
677  exp_.planners[i].runsProgressData.push_back(rp.getRunProgressData());
678  }
679  }
680  catch(std::runtime_error &e)
681  {
682  std::stringstream es;
683  es << "There was an error in the extraction of planner results: planner = " << status_.activePlanner << ", run = " << status_.activePlanner << std::endl;
684  es << "*** " << e.what() << std::endl;
685  std::cerr << es.str();
686  OMPL_ERROR(es.str().c_str());
687  }
688  }
689  }
690 
691  status_.running = false;
692  status_.progressPercentage = 100.0;
693  if (req.displayProgress)
694  {
695  while (status_.progressPercentage > progress->count())
696  ++(*progress);
697  std::cout << std::endl;
698  }
699 
700  exp_.totalDuration = time::seconds(time::now() - exp_.startTime);
701 
702  OMPL_INFORM("Benchmark complete");
704  OMPL_INFORM("Benchmark complete");
705 }
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique...
Definition: PlannerData.h:163
std::string getCPUInfo()
Get information about the CPU of the machine in use.
A shared pointer wrapper for ompl::base::ProblemDefinition.
double maxMem
the maximum amount of memory a planner is allowed to use (MB); 4096.0 by default
Definition: Benchmark.h:178
double timeBetweenUpdates
When collecting time-varying data from a planner during its execution, the planner&#39;s progress will be...
Definition: Benchmark.h:184
bool saveResultsToFile() const
Save the results of the benchmark to a file. The name of the file is the current date and time...
Definition: Benchmark.cpp:245
std::map< std::string, std::string > RunProperties
The data collected from a run of a planner is stored as key-value pairs.
Definition: Benchmark.h:80
std::function< bool()> PlannerTerminationConditionFn
Signature for functions that decide whether termination conditions have been met for a planner...
void noOutputHandler()
This function instructs ompl that no messages should be outputted. Equivalent to useOutputHandler(nul...
Definition: Console.cpp:95
std::string as_string(const point &p)
Return string representation of point in time.
Definition: Time.h:92
bool displayProgress
flag indicating whether progress is to be displayed or not; true by default
Definition: Benchmark.h:187
unsigned int runCount
the number of times to run each planner; 100 by default
Definition: Benchmark.h:181
bool saveConsoleOutput
flag indicating whether console output is saved (in an automatically generated filename); true by def...
Definition: Benchmark.h:190
duration seconds(double sec)
Return the time duration representing a given number of seconds.
Definition: Time.h:78
bool useThreads
flag indicating whether planner runs should be run in a separate thread. It is advisable to set this ...
Definition: Benchmark.h:193
Main namespace. Contains everything in this library.
Definition: Cost.h:42
std::string asString() const
Return a string representation.
The number of possible status values.
Definition: PlannerStatus.h:72
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
double maxTime
the maximum amount of time a planner is allowed to run (seconds); 5.0 by default
Definition: Benchmark.h:175
std::chrono::system_clock::duration duration
Representation of a time duration.
Definition: Time.h:69
A class to store the exit status of Planner::solve()
Definition: PlannerStatus.h:48
Generic class to handle output from a piece of code.
Definition: Console.h:103
static std::uint_fast32_t getSeed()
Get the seed used to generate the seeds of each RNG instance. Passing the returned value to setSeed()...
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition: Console.h:66
MemUsage_t getProcessMemoryUsage()
Get the amount of memory the current process is using. This should work on major platforms (Windows...
Representation of a benchmark request.
Definition: Benchmark.h:157
bool simplify
flag indicating whether simplification should be applied to path; true by default ...
Definition: Benchmark.h:196
point now()
Get the current time point.
Definition: Time.h:72
OutputHandler * getOutputHandler()
Get the instance of the OutputHandler currently used. This is nullptr in case there is no output hand...
Definition: Console.cpp:115
virtual bool saveResultsToStream(std::ostream &out=std::cout) const
Save the results of the benchmark to a stream.
Definition: Benchmark.cpp:251
Implementation of OutputHandler that saves messages in a file.
Definition: Console.h:135
unsigned long long MemUsage_t
Amount of memory used, in bytes.
Definition: MachineSpecs.h:50
std::string getHostname()
Get the hostname of the machine in use.
std::chrono::system_clock::time_point point
Representation of a point in time.
Definition: Time.h:66
void useOutputHandler(OutputHandler *oh)
Specify the instance of the OutputHandler to use. By default, this is OutputHandlerSTD.
Definition: Console.cpp:108
std::map< std::string, PlannerProgressProperty > PlannerProgressProperties
A dictionary which maps the name of a progress property to the function to be used for querying that ...
Definition: Planner.h:357
virtual void benchmark(const Request &req)
Benchmark the added planners on the defined problem. Repeated calls clear previously gathered data...
Definition: Benchmark.cpp:386
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68