ProductGraph.cpp
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2012, 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: Matt Maly */
36 
37 #include "ompl/control/planners/ltl/ProductGraph.h"
38 #include "ompl/base/State.h"
39 #include "ompl/control/planners/ltl/Automaton.h"
40 #include "ompl/control/planners/ltl/PropositionalDecomposition.h"
41 #include "ompl/control/planners/ltl/World.h"
42 #include "ompl/util/ClassForward.h"
43 #include "ompl/util/Console.h"
44 #include "ompl/util/Hash.h"
45 #include <algorithm>
46 #include <functional>
47 #include <boost/graph/adjacency_list.hpp>
48 #include <boost/graph/dijkstra_shortest_paths.hpp>
49 #include <unordered_map>
50 #include <unordered_set>
51 #include <map>
52 #include <ostream>
53 #include <queue>
54 #include <stack>
55 #include <vector>
56 
58 {
59  return decompRegion==s.decompRegion
60  && cosafeState==s.cosafeState
61  && safeState==s.safeState;
62 }
63 
65 {
66  return cosafeState != -1 && safeState != -1;
67 }
68 
69 std::size_t ompl::control::ProductGraph::HashState::operator()(const ompl::control::ProductGraph::State &s) const
70 {
71  std::size_t hash = std::hash<int>()(s.decompRegion);
72  hash_combine(hash, s.cosafeState);
73  hash_combine(hash, s.safeState);
74  return hash;
75 }
76 
77 namespace ompl
78 {
79  namespace control
80  {
81  std::ostream& operator<<(std::ostream& out, const ProductGraph::State& s)
82  {
83  out << "(" << s.decompRegion << "," << s.cosafeState << ",";
84  out << s.safeState << ")";
85  return out;
86  }
87  }
88 }
89 
91 {
92  return decompRegion;
93 }
94 
96 {
97  return cosafeState;
98 }
99 
101 {
102  return safeState;
103 }
104 
106  const AutomatonPtr& cosafetyAut, const AutomatonPtr& safetyAut) :
107  decomp_(decomp),
108  cosafety_(cosafetyAut),
109  safety_(safetyAut)
110 {
111 }
112 
114  const AutomatonPtr& cosafetyAut) :
115  decomp_(decomp),
116  cosafety_(cosafetyAut),
117  safety_(Automaton::AcceptingAutomaton(decomp->getNumProps()))
118 {
119 }
120 
121 ompl::control::ProductGraph::~ProductGraph()
122 {
123  clear();
124 }
125 
127 {
128  return decomp_;
129 }
130 
132 {
133  return cosafety_;
134 }
135 
137 {
138  return safety_;
139 }
140 
141 std::vector<ompl::control::ProductGraph::State*>
143  ProductGraph::State* start,
144  const std::function<double(ProductGraph::State*, ProductGraph::State*)>& edgeWeight)
145 {
146  std::vector<GraphType::vertex_descriptor> parents(boost::num_vertices(graph_));
147  std::vector<double> distances(boost::num_vertices(graph_));
148  EdgeIter ei, eend;
149  //first build up the edge weights
150  for (boost::tie(ei,eend) = boost::edges(graph_); ei != eend; ++ei)
151  {
152  GraphType::vertex_descriptor src = boost::source(*ei, graph_);
153  GraphType::vertex_descriptor target = boost::target(*ei, graph_);
154  graph_[*ei].cost = edgeWeight(graph_[src], graph_[target]);
155  }
156  int startIndex = stateToIndex_[start];
157  boost::dijkstra_shortest_paths(graph_, boost::vertex(startIndex,graph_),
158  boost::weight_map(get(&Edge::cost, graph_)).distance_map(
159  boost::make_iterator_property_map(distances.begin(), get(boost::vertex_index, graph_)
160  )).predecessor_map(
161  boost::make_iterator_property_map(parents.begin(), get(boost::vertex_index, graph_))
162  )
163  );
164  //pick state from solutionStates_ such that distance[state] is minimized
165  State* bestSoln = *solutionStates_.begin();
166  double cost = distances[boost::vertex(stateToIndex_[bestSoln], graph_)];
167  for (std::vector<State*>::const_iterator s = solutionStates_.begin()+1; s != solutionStates_.end(); ++s)
168  {
169  if (distances[boost::vertex(stateToIndex_[*s], graph_)] < cost)
170  {
171  cost = distances[boost::vertex(stateToIndex_[*s], graph_)];
172  bestSoln = *s;
173  }
174  }
175  //build lead from bestSoln parents
176  std::stack<State*> leadStack;
177  while (!(bestSoln == start))
178  {
179  leadStack.push(bestSoln);
180  bestSoln = graph_[parents[boost::vertex(stateToIndex_[bestSoln], graph_)]];
181  }
182  leadStack.push(bestSoln);
183 
184  std::vector<State*> lead;
185  while (!leadStack.empty())
186  {
187  lead.push_back(leadStack.top());
188  leadStack.pop();
189  // Truncate the lead as early when it hits the desired automaton states
190  // \todo: more elegant way to do this?
191  if (lead.back()->cosafeState == solutionStates_.front()->cosafeState
192  && lead.back()->safeState == solutionStates_.front()->safeState)
193  break;
194  }
195  return lead;
196 }
197 
199 {
200  solutionStates_.clear();
201  stateToIndex_.clear();
202  startState_ = nullptr;
203  graph_.clear();
204  std::unordered_map<State,State*,HashState>::iterator i;
205  for (i = stateToPtr_.begin(); i != stateToPtr_.end(); ++i)
206  delete i->second;
207  stateToPtr_.clear();
208 }
209 
210 void ompl::control::ProductGraph::buildGraph(State* start, const std::function<void(State*)>& initialize)
211 {
212  graph_.clear();
213  solutionStates_.clear();
214  std::queue<State*> q;
215  std::unordered_set<State*> processed;
216  std::vector<int> regNeighbors;
217  VertexIndexMap index = get(boost::vertex_index, graph_);
218 
219  GraphType::vertex_descriptor next = boost::add_vertex(graph_);
220  startState_ = start;
221  graph_[boost::vertex(next,graph_)] = startState_;
222  stateToIndex_[startState_] = index[next];
223  q.push(startState_);
224  processed.insert(startState_);
225 
226  OMPL_INFORM("Building graph from start state (%u,%u,%u) with index %d",
227  startState_->decompRegion, startState_->cosafeState,
228  startState_->safeState, stateToIndex_[startState_]);
229 
230  while (!q.empty())
231  {
232  State* current = q.front();
233  //Initialize each state using the supplied state initializer function
234  initialize(current);
235  q.pop();
236 
237  if (safety_->isAccepting(current->safeState) && cosafety_->isAccepting(current->cosafeState))
238  {
239  solutionStates_.push_back(current);
240  }
241 
242  GraphType::vertex_descriptor v = boost::vertex(stateToIndex_[current], graph_);
243 
244  //enqueue each neighbor of current
245  decomp_->getNeighbors(current->decompRegion, regNeighbors);
246  for (std::vector<int>::const_iterator r = regNeighbors.begin(); r != regNeighbors.end(); ++r)
247  {
248  State* nextState = getState(current, *r);
249  if (!nextState->isValid())
250  continue;
251  //if this state is newly discovered,
252  //then we can dynamically allocate a copy of it
253  //and add the new pointer to the graph.
254  //either way, we need the pointer
255  if (processed.find(nextState) == processed.end())
256  {
257  const GraphType::vertex_descriptor next = boost::add_vertex(graph_);
258  stateToIndex_[nextState] = index[next];
259  graph_[boost::vertex(next,graph_)] = nextState;
260  q.push(nextState);
261  processed.insert(nextState);
262  }
263 
264  //whether or not the neighbor is newly discovered,
265  //we still need to add the edge to the graph
266  GraphType::edge_descriptor edge;
267  bool ignore;
268  boost::tie(edge,ignore) = boost::add_edge(v, boost::vertex(stateToIndex_[nextState],graph_), graph_);
269  //graph_[edge].src = index[v];
270  //graph_[edge].dest = stateToIndex_[nextState];
271  }
272  regNeighbors.clear();
273  }
274  if (solutionStates_.empty())
275  {
276  OMPL_ERROR("No solution path found in product graph.");
277  }
278 
279  OMPL_INFORM("Number of decomposition regions: %u", decomp_->getNumRegions());
280  OMPL_INFORM("Number of cosafety automaton states: %u", cosafety_->numStates());
281  OMPL_INFORM("Number of safety automaton states: %u", safety_->numStates());
282  OMPL_INFORM("Number of high-level states in abstraction graph: %u", boost::num_vertices(graph_));
283 }
284 
286 {
287  return std::find(solutionStates_.begin(), solutionStates_.end(), s)
288  != solutionStates_.end();
289 }
290 
292 {
293  return startState_;
294 }
295 
297 {
298  return decomp_->getRegionVolume(s->decompRegion);
299 }
300 
302 {
303  return cosafety_->distFromAccepting(s->cosafeState);
304 }
305 
307 {
308  return safety_->distFromAccepting(s->safeState);
309 }
310 
312 {
313  return getState(cs, cosafety_->getStartState(), safety_->getStartState());
314 }
315 
317 {
318  State s;
319  s.decompRegion = decomp_->locateRegion(cs);
320  s.cosafeState = cosafe;
321  s.safeState = safe;
322  State*& ret = stateToPtr_[s];
323  if (ret == nullptr)
324  ret = new State(s);
325  return ret;
326 }
327 
329 {
330  State s;
331  s.decompRegion = nextRegion;
332  const World nextWorld = decomp_->worldAtRegion(nextRegion);
333  s.cosafeState = cosafety_->step(parent->cosafeState, nextWorld);
334  s.safeState = safety_->step(parent->safeState, nextWorld);
335  State*& ret = stateToPtr_[s];
336  if (ret == nullptr)
337  ret = new State(s);
338  return ret;
339 }
340 
342 {
343  return getState(parent, decomp_->locateRegion(cs));
344 }
double getRegionVolume(const State *s)
Helper method to return the volume of the PropositionalDecomposition region corresponding to the give...
A class to represent an assignment of boolean values to propositions. A World can be partially restri...
Definition: World.h:71
A shared pointer wrapper for ompl::control::PropositionalDecomposition.
State * getState(const base::State *cs) const
Returns a ProductGraph State with initial co-safety and safety Automaton states, and the Propositiona...
int getSafeAutDistance(const State *s) const
Helper method to return the distance from a given State's safety state to an accepting state in the s...
State * getStartState(void) const
Returns the initial State of this ProductGraph.
const PropositionalDecompositionPtr & getDecomp() const
Returns the PropositionalDecomposition contained within this ProductGraph.
#define OMPL_ERROR(fmt,...)
Log a formatted error string.
Definition: Console.h:64
int getCosafeAutDistance(const State *s) const
Helper method to return the distance from a given State's co-safety state to an accepting state in th...
std::vector< State * > computeLead(State *start, const std::function< double(State *, State *)> &edgeWeight)
Returns a shortest-path sequence of ProductGraph states, beginning with a given initial State and end...
const AutomatonPtr & getSafetyAutom() const
Returns the safe Automaton contained within this ProductGraph.
A State of a ProductGraph represents a vertex in the graph-based Cartesian product represented by the...
Definition: ProductGraph.h:84
bool isValid(void) const
Returns whether this State is valid. A State is valid if and only if none of its Automaton states are...
Definition of an abstract state.
Definition: State.h:50
void clear()
Clears all memory belonging to this ProductGraph.
int getSafeState(void) const
Returns this State's safe Automaton state component.
bool operator==(const State &s) const
Returns whether this State is equivalent to a given State, by comparing their PropositionalDecomposit...
A shared pointer wrapper for ompl::control::Automaton.
bool isSolution(const State *s) const
Returns whether the given State is an accepting State in this ProductGraph. We call a State accepting...
int getDecompRegion(void) const
Returns this State's PropositionalDecomposition region component.
A class to represent a deterministic finite automaton, each edge of which corresponds to a World...
Definition: Automaton.h:69
int getCosafeState(void) const
Returns this State's co-safe Automaton state component.
ProductGraph(const PropositionalDecompositionPtr &decomp, const AutomatonPtr &cosafetyAut, const AutomatonPtr &safetyAut)
Initializes a ProductGraph with a given PropositionalDecomposition, co-safe Automaton, and safe Automaton.
void buildGraph(State *start, const std::function< void(State *)> &initialize=ProductGraph::noInit)
Constructs this ProductGraph beginning with a given initial State, using a breadth-first search...
const AutomatonPtr & getCosafetyAutom() const
Returns the co-safe Automaton contained within this ProductGraph.
#define OMPL_INFORM(fmt,...)
Log a formatted information string.
Definition: Console.h:68