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