LightningDB.cpp
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2014, JSK, The University of Tokyo.
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 JSK, The University of Tokyo 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: Dave Coleman */
36 
37 // OMPL
38 #include "ompl/tools/lightning/LightningDB.h"
39 #include "ompl/base/ScopedState.h"
40 #include "ompl/util/Time.h"
41 #include "ompl/tools/config/SelfConfig.h"
42 #include "ompl/base/PlannerDataStorage.h"
43 
44 // Boost
45 #include <boost/filesystem.hpp>
46 
48  : numUnsavedPaths_(0)
49 {
50  si_.reset(new base::SpaceInformation(space));
51 
52  // Set nearest neighbor type
54 
55  // Use our custom distance function for nearest neighbor tree
56  nn_->setDistanceFunction(std::bind(&ompl::tools::LightningDB::distanceFunction, this, std::placeholders::_1, std::placeholders::_2));
57 
58  // Load the PlannerData instance to be used for searching
59  nnSearchKey_.reset(new ompl::base::PlannerData(si_));
60 }
61 
63 {
64  if (numUnsavedPaths_)
65  OMPL_WARN("The database is being unloaded with unsaved experiences");
66 }
67 
68 bool ompl::tools::LightningDB::load(const std::string &fileName)
69 {
70  // Error checking
71  if (fileName.empty())
72  {
73  OMPL_ERROR("Empty filename passed to save function");
74  return false;
75  }
76  if ( !boost::filesystem::exists( fileName ) )
77  {
78  OMPL_WARN("Database file does not exist: %s", fileName.c_str());
79  return false;
80  }
81 
82  // Load database from file, track loading time
83  time::point start = time::now();
84 
85  OMPL_INFORM("Loading database from file: %s", fileName.c_str());
86 
87  // Open a binary input stream
88  std::ifstream iStream(fileName.c_str(), std::ios::binary);
89 
90  // Get the total number of paths saved
91  double numPaths = 0;
92  iStream >> numPaths;
93 
94  // Check that the number of paths makes sense
95  if (numPaths < 0 || numPaths > std::numeric_limits<double>::max())
96  {
97  OMPL_WARN("Number of paths to load %d is a bad value", numPaths);
98  return false;
99  }
100 
101  // Start loading all the PlannerDatas
102  for (std::size_t i = 0; i < numPaths; ++i)
103  {
104  // Create a new planner data instance
106 
107  // Note: the StateStorage class checks if the states match for us
108  plannerDataStorage_.load(iStream, *plannerData.get());
109 
110  // Add to nearest neighbor tree
111  nn_->add(plannerData);
112  }
113 
114  // Close file
115  iStream.close();
116 
117  double loadTime = time::seconds(time::now() - start);
118  OMPL_INFORM("Loaded database from file in %f sec with %d paths", loadTime, nn_->size());
119  return true;
120 }
121 
122 void ompl::tools::LightningDB::addPath(ompl::geometric::PathGeometric& solutionPath, double &insertionTime)
123 {
124  // Benchmark runtime
125  time::point startTime = time::now();
126  {
127  addPathHelper(solutionPath);
128  }
129  insertionTime = time::seconds(time::now() - startTime);
130 }
131 
132 void ompl::tools::LightningDB::addPathHelper(ompl::geometric::PathGeometric &solutionPath)
133 {
134  // Create a new planner data instance
136 
137  // Add the states to one nodes files
138  for (std::size_t i = 0; i < solutionPath.getStates().size(); ++i)
139  {
140  ompl::base::PlannerDataVertex vert( solutionPath.getStates()[i] ); // TODO tag?
141 
142  plannerData->addVertex(vert);
143  }
144 
145  // Deep copy the states in the vertices so that when the planner goes out of scope, all data remains intact
146  plannerData->decoupleFromPlanner();
147 
148  // Add to nearest neighbor tree
149  nn_->add(plannerData);
150 
151  numUnsavedPaths_++;
152 }
153 
154 bool ompl::tools::LightningDB::saveIfChanged(const std::string &fileName)
155 {
156  if (numUnsavedPaths_)
157  return save(fileName);
158  else
159  OMPL_INFORM("Not saving because database has not changed");
160  return true;
161 }
162 
163 bool ompl::tools::LightningDB::save(const std::string &fileName)
164 {
165  // Error checking
166  if (fileName.empty())
167  {
168  OMPL_ERROR("Empty filename passed to save function");
169  return false;
170  }
171 
172  // Save database from file, track saving time
173  time::point start = time::now();
174 
175  OMPL_INFORM("Saving database to file: %s", fileName.c_str());
176 
177  // Open a binary output stream
178  std::ofstream outStream(fileName.c_str(), std::ios::binary);
179 
180  // Convert the NN tree to a vector
181  std::vector<ompl::base::PlannerDataPtr> plannerDatas;
182  nn_->list(plannerDatas);
183 
184  // Write the number of paths we will be saving
185  double numPaths = plannerDatas.size();
186  outStream << numPaths;
187 
188  // Start saving each planner data object
189  for (std::size_t i = 0; i < numPaths; ++i)
190  {
191  ompl::base::PlannerData &pd = *plannerDatas[i].get();
192 
193  // Save a single planner data
194  plannerDataStorage_.store(pd, outStream);
195  }
196 
197  // Close file
198  outStream.close();
199 
200  // Benchmark
201  double loadTime = time::seconds(time::now() - start);
202  OMPL_INFORM("Saved database to file in %f sec with %d paths", loadTime, plannerDatas.size());
203 
204  numUnsavedPaths_ = 0;
205 
206  return true;
207 }
208 
209 void ompl::tools::LightningDB::getAllPlannerDatas(std::vector<ompl::base::PlannerDataPtr> &plannerDatas) const
210 {
211  OMPL_DEBUG("LightningDB: getAllPlannerDatas");
212 
213  // Convert the NN tree to a vector
214  nn_->list(plannerDatas);
215 
216  OMPL_DEBUG("Number of paths found: %d", plannerDatas.size());
217 }
218 
219 std::vector<ompl::base::PlannerDataPtr> ompl::tools::LightningDB::findNearestStartGoal(
220  int nearestK, const base::State *start, const base::State *goal)
221 {
222  // Fill in our pre-made PlannerData instance with the new start and goal states to be searched for
223  if (nnSearchKey_->numVertices() == 2)
224  {
225  nnSearchKey_->getVertex(0) = ompl::base::PlannerDataVertex(start);
226  nnSearchKey_->getVertex(1) = ompl::base::PlannerDataVertex(goal);
227  }
228  else
229  {
230  nnSearchKey_->addVertex(ompl::base::PlannerDataVertex(start));
231  nnSearchKey_->addVertex(ompl::base::PlannerDataVertex(goal));
232  }
233  assert( nnSearchKey_->numVertices() == 2);
234 
235  std::vector<ompl::base::PlannerDataPtr> nearest;
236  nn_->nearestK(nnSearchKey_, nearestK, nearest);
237 
238  return nearest;
239 }
240 
241 double ompl::tools::LightningDB::distanceFunction(const ompl::base::PlannerDataPtr a, const ompl::base::PlannerDataPtr b) const
242 {
243  // Bi-directional implementation - check path b from [start, goal] and [goal, start]
244  return std::min(
245  // [ a.start, b.start] + [a.goal + b.goal]
246  si_->distance(a->getVertex(0).getState(), b->getVertex(0).getState()) +
247  si_->distance(a->getVertex(a->numVertices()-1).getState(), b->getVertex(b->numVertices()-1).getState()),
248  // [ a.start, b.goal] + [a.goal + b.start]
249  si_->distance(a->getVertex(0).getState(), b->getVertex(b->numVertices()-1).getState()) +
250  si_->distance(a->getVertex(a->numVertices()-1).getState(), b->getVertex(0).getState()));
251 }
252 
254 {
255  return nn_->size();
256 }
257 
259 {
260  // Loop through every PlannerData and sum the number of states
261  std::size_t statesCount = 0;
262 
263  // Convert the NN tree to a vector
264  std::vector<ompl::base::PlannerDataPtr> plannerDatas;
265  nn_->list(plannerDatas);
266 
267  // Start saving each planner data object
268  for (std::size_t i = 0; i < plannerDatas.size(); ++i)
269  {
270  statesCount += plannerDatas[i]->numVertices();
271  }
272 
273  return statesCount;
274 }
Object containing planner generated vertex and edge data. It is assumed that all vertices are unique...
Definition: PlannerData.h:163
std::size_t getExperiencesCount() const
Get the total number of paths stored in the database.
A shared pointer wrapper for ompl::base::StateSpace.
virtual void load(const char *filename, PlannerData &pd)
Load the PlannerData structure from the given stream. The StateSpace that was used to store the data ...
Base class for a vertex in the PlannerData structure. All derived classes must implement the clone an...
Definition: PlannerData.h:59
duration seconds(double sec)
Return the time duration representing a given number of seconds.
Definition: Time.h:78
A nearest neighbors datastructure that uses linear search. The linear search is done over sqrt(n) ele...
void addPath(geometric::PathGeometric &solutionPath, double &insertionTime)
Add a new solution path to our database. Des not actually save to file so experience will be lost if ...
LightningDB(const base::StateSpacePtr &space)
Constructor needs the state space used for planning.
Definition: LightningDB.cpp:47
std::vector< base::State * > & getStates()
Get the states that make up the path (as a reference, so it can be modified, hence the function is no...
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
std::vector< ompl::base::PlannerDataPtr > findNearestStartGoal(int nearestK, const base::State *start, const base::State *goal)
Find the k nearest paths to our queries one.
The base class for space information. This contains all the information about the space planning is d...
bool load(const std::string &fileName)
Load database from file.
Definition: LightningDB.cpp:68
Definition of an abstract state.
Definition: State.h:50
#define OMPL_WARN(fmt,...)
Log a formatted warning string.
Definition: Console.h:66
ompl::base::PlannerDataStorage plannerDataStorage_
Helper class for storing each plannerData instance.
Definition: LightningDB.h:163
#define OMPL_DEBUG(fmt,...)
Log a formatted debugging string.
Definition: Console.h:70
point now()
Get the current time point.
Definition: Time.h:72
std::size_t getStatesCount() const
Get the total number of states stored in the database, across all paths.
void getAllPlannerDatas(std::vector< ompl::base::PlannerDataPtr > &plannerDatas) const
Get a vector of all the paths in the nearest neighbor tree.
virtual ~LightningDB()
Deconstructor.
Definition: LightningDB.cpp:62
Definition of a geometric path.
Definition: PathGeometric.h:60
base::SpaceInformationPtr si_
The created space information.
Definition: LightningDB.h:160
bool saveIfChanged(const std::string &fileName)
Save loaded database to file, except skips saving if no paths have been added.
std::chrono::system_clock::time_point point
Representation of a point in time.
Definition: Time.h:66
A shared pointer wrapper for ompl::base::PlannerData.
virtual void store(const PlannerData &pd, const char *filename)
Store (serialize) the PlannerData structure to the given filename.
bool save(const std::string &fileName)
Save loaded database to file.
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68