Automaton.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/Automaton.h"
38 #include "ompl/control/planners/ltl/World.h"
39 #include <boost/range/irange.hpp>
40 #include <unordered_map>
41 #include <unordered_set>
42 #include <boost/dynamic_bitset.hpp>
43 #include <ostream>
44 #include <limits>
45 #include <queue>
46 #include <vector>
47 
49 {
50  typedef std::unordered_map<World, unsigned int>::const_iterator DestIter;
51  DestIter d = entries.find(w);
52  if (d != entries.end())
53  return d->second;
54  for (d = entries.begin(); d != entries.end(); ++d)
55  {
56  if (w.satisfies(d->first))
57  {
58  //Since w satisfies another world that leads to d->second,
59  //we can add an edge directly from w to d->second.
60  entries[w] = d->second;
61  return d->second;
62  }
63  }
64  return -1;
65 }
66 
68  numProps_(numProps),
69  numStates_(numStates),
70  startState_(-1),
71  accepting_(numStates_, false),
72  transitions_(numStates_),
73  distances_(numStates_, std::numeric_limits<unsigned int>::max())
74 {
75 }
76 
77 unsigned int ompl::control::Automaton::addState(bool accepting)
78 {
79  ++numStates_;
80  accepting_.resize(numStates_);
81  accepting_[numStates_-1] = accepting;
82  transitions_.resize(numStates_);
83  return numStates_-1;
84 }
85 
86 void ompl::control::Automaton::setAccepting(unsigned int s, bool a)
87 {
88  accepting_[s] = a;
89 }
90 
91 bool ompl::control::Automaton::isAccepting(unsigned int s) const
92 {
93  return accepting_[s];
94 }
95 
97 {
98  startState_ = s;
99 }
100 
102 {
103  return startState_;
104 }
105 
107  unsigned int src,
108  const World& w,
109  unsigned int dest)
110 {
111  TransitionMap& map = transitions_[src];
112  map.entries[w] = dest;
113 }
114 
115 bool ompl::control::Automaton::run(const std::vector<World>& trace) const
116 {
117  int current = startState_;
118  for (std::vector<World>::const_iterator w = trace.begin(); w != trace.end(); ++w)
119  {
120  current = step(current, *w);
121  if (current == -1)
122  return false;
123  }
124  return true;
125 }
126 
127 int ompl::control::Automaton::step(int state, const World& w) const
128 {
129  if (state == -1)
130  return -1;
131  return transitions_[state].eval(w);
132 }
133 
135 {
136  return transitions_[src];
137 }
138 
139 unsigned int ompl::control::Automaton::numStates(void) const
140 {
141  return numStates_;
142 }
143 
145 {
146  unsigned int ntrans = 0;
147  typedef std::vector<TransitionMap>::const_iterator TransIter;
148  for (TransIter i = transitions_.begin(); i != transitions_.end(); ++i)
149  ntrans += i->entries.size();
150  return ntrans;
151 }
152 
153 unsigned int ompl::control::Automaton::numProps(void) const
154 {
155  return numProps_;
156 }
157 
158 void ompl::control::Automaton::print(std::ostream& out) const
159 {
160  out << "digraph automaton {" << std::endl;
161  out << "rankdir=LR" << std::endl;
162  for (unsigned int i = 0; i < numStates_; ++i)
163  {
164  out << i << " [label=\"" << i << "\",shape=";
165  out << (accepting_[i] ? "doublecircle" : "circle") << "]" << std::endl;
166 
167  const TransitionMap& map = transitions_[i];
168  std::unordered_map<World, unsigned int>::const_iterator e;
169  for (e = map.entries.begin(); e != map.entries.end(); ++e)
170  {
171  const World& w = e->first;
172  unsigned int dest = e->second;
173  const std::string formula = w.formula();
174  out << i << " -> " << dest << " [label=\"" << formula << "\"]" << std::endl;
175  }
176  }
177  out << "}" << std::endl;
178 }
179 
180 unsigned int ompl::control::Automaton::distFromAccepting(unsigned int s, unsigned int maxDist) const
181 {
182  if (distances_[s] < std::numeric_limits<unsigned int>::max())
183  return distances_[s];
184  if (accepting_[s])
185  return 0;
186  std::queue<unsigned int> q;
187  std::unordered_set<unsigned int> processed;
188  std::unordered_map<unsigned int, unsigned int> distance;
189 
190  q.push(s);
191  distance[s] = 0;
192  processed.insert(s);
193 
194  while (!q.empty())
195  {
196  unsigned int current = q.front();
197  q.pop();
198  if (accepting_[current])
199  {
200  distances_[s] = distance[current];
201  return distance[current];
202  }
203  const TransitionMap& map = transitions_[current];
204  std::unordered_map<World, unsigned int>::const_iterator e;
205  for (e = map.entries.begin(); e != map.entries.end(); ++e)
206  {
207  unsigned int neighbor = e->second;
208  if (processed.count(neighbor) > 0)
209  continue;
210  q.push(neighbor);
211  processed.insert(neighbor);
212  distance[neighbor] = distance[current]+1;
213  }
214  }
215  return std::numeric_limits<unsigned int>::max();
216 }
217 
219 {
220  AutomatonPtr phi(new Automaton(numProps, 1));
221  World trivial(numProps);
222  phi->addTransition(0, trivial, 0);
223  phi->setStartState(0);
224  phi->setAccepting(0, true);
225  return phi;
226 }
227 
228 ompl::control::AutomatonPtr ompl::control::Automaton::CoverageAutomaton(unsigned int numProps, const std::vector<unsigned int>& covProps)
229 {
230  AutomatonPtr phi(new Automaton(numProps, 1<<covProps.size()));
231  for (unsigned int src = 0; src < phi->numStates(); ++src)
232  {
233  const boost::dynamic_bitset<> state(covProps.size(), src);
234  World loop(numProps);
235  //each value of p is an index of a proposition in covProps
236  for (unsigned int p = 0; p < covProps.size(); ++p)
237  {
238  //if proposition covProps[p] has already been covered at state src, skip it
239  if (state[p])
240  continue;
241  //for each proposition covProps[p] that has not yet been
242  //covered at state src, construct a transition from src to (src|p)
243  //on formula (covProps[p]==true)
244  boost::dynamic_bitset<> target(state);
245  target[p] = true;
246  World nextProp(numProps);
247  nextProp[covProps[p]] = true;
248  phi->addTransition(src, nextProp, target.to_ulong());
249  //also build a loop from src to src on formula with conjunct (covProps[p]==false)
250  loop[covProps[p]] = false;
251  }
252  //now we add a loop from src to src on conjunction of (covProps[p]==false)
253  //for every p such that the pth bit of src is 1
254  phi->addTransition(src, loop, src);
255  }
256  phi->setAccepting(phi->numStates()-1, true);
257  phi->setStartState(0);
258  return phi;
259 }
260 
261 ompl::control::AutomatonPtr ompl::control::Automaton::SequenceAutomaton(unsigned int numProps, const std::vector<unsigned int>& seqProps)
262 {
263  AutomatonPtr seq(new Automaton(numProps, seqProps.size()+1));
264  for (unsigned int state = 0; state < seqProps.size(); ++state)
265  {
266  // loop when next proposition in sequence is not satisfied
267  World loop(numProps);
268  loop[seqProps[state]] = false;
269  seq->addTransition(state, loop, state);
270 
271  // progress forward when next proposition in sequence is satisfied
272  World progress(numProps);
273  progress[seqProps[state]] = true;
274  seq->addTransition(state, progress, state+1);
275  }
276  //loop on all input when in accepting state
277  seq->addTransition(seqProps.size(), World(numProps), seqProps.size());
278  seq->setAccepting(seqProps.size(), true);
279  seq->setStartState(0);
280  return seq;
281 }
282 
283 ompl::control::AutomatonPtr ompl::control::Automaton::DisjunctionAutomaton(unsigned int numProps, const std::vector<unsigned int>& disjProps)
284 {
285  AutomatonPtr disj(new Automaton(numProps, 2));
286  World loop(numProps);
287  for (std::vector<unsigned int>::const_iterator p = disjProps.begin(); p != disjProps.end(); ++p)
288  {
289  World satisfy(numProps);
290  satisfy[*p] = true;
291  loop[*p] = false;
292  disj->addTransition(0, satisfy, 1);
293  }
294  disj->addTransition(0, loop, 0);
295  disj->addTransition(1, World(numProps), 1);
296  disj->setAccepting(1, true);
297  disj->setStartState(0);
298  return disj;
299 }
300 
301 ompl::control::AutomatonPtr ompl::control::Automaton::AvoidanceAutomaton(unsigned int numProps, const std::vector<unsigned int>& avoidProps)
302 {
303  /* An avoidance automaton is simply a disjunction automaton with its acceptance condition flipped. */
304  AutomatonPtr avoid = DisjunctionAutomaton(numProps, avoidProps);
305  avoid->setAccepting(0, true);
306  avoid->setAccepting(1, false);
307  return avoid;
308 }
309 
311 {
312  const boost::integer_range<unsigned int> props = boost::irange(0u,numProps);
313  return CoverageAutomaton(numProps, std::vector<unsigned int>(props.begin(), props.end()));
314 }
315 
317 {
318  const boost::integer_range<unsigned int> props = boost::irange(0u,numProps);
319  return SequenceAutomaton(numProps, std::vector<unsigned int>(props.begin(), props.end()));
320 }
321 
323 {
324  const boost::integer_range<unsigned int> props = boost::irange(0u,numProps);
325  return DisjunctionAutomaton(numProps, std::vector<unsigned int>(props.begin(), props.end()));
326 }
void setAccepting(unsigned int s, bool a)
Sets the accepting status of a given state.
Definition: Automaton.cpp:86
A class to represent an assignment of boolean values to propositions. A World can be partially restri...
Definition: World.h:71
unsigned int numTransitions(void) const
Returns the number of transitions in this automaton.
Definition: Automaton.cpp:144
unsigned int numProps(void) const
Returns the number of propositions used by this automaton.
Definition: Automaton.cpp:153
int step(int state, const World &w) const
Runs the automaton for one step from the given state, using the values of propositions from a given W...
Definition: Automaton.cpp:127
Automaton(unsigned int numProps, unsigned int numStates=0)
Creates an automaton with a given number of propositions and states.
Definition: Automaton.cpp:67
static AutomatonPtr SequenceAutomaton(unsigned int numProps, const std::vector< unsigned int > &seqProps)
Helper function to return a sequence automaton. Assumes all propositions are mutually exclusive...
Definition: Automaton.cpp:261
void setStartState(unsigned int s)
Sets the start state of the automaton.
Definition: Automaton.cpp:96
static AutomatonPtr DisjunctionAutomaton(unsigned int numProps, const std::vector< unsigned int > &disjProps)
Helper function to return a disjunction automaton, which accepts when one of the given propositions b...
Definition: Automaton.cpp:283
bool isAccepting(unsigned int s) const
Returns whether a given state of the automaton is accepting.
Definition: Automaton.cpp:91
Each automaton state has a transition map, which maps from a World to another automaton state...
Definition: Automaton.h:76
bool satisfies(const World &w) const
Returns whether this World propositionally satisfies a given World w. Specifically, returns true iff for every proposition p assigned in w, p is assigned in this World and this[p] == w[p].
Definition: World.cpp:65
int getStartState(void) const
Returns the start state of the automaton. Returns -1 if no start state has been set.
Definition: Automaton.cpp:101
bool run(const std::vector< World > &trace) const
Runs the automaton from its start state, using the values of propositions from a given sequence of Wo...
Definition: Automaton.cpp:115
void print(std::ostream &out) const
Prints the automaton to a given output stream, in Graphviz dot format.
Definition: Automaton.cpp:158
unsigned int addState(bool accepting=false)
Adds a new state to the automaton and returns an ID for it.
Definition: Automaton.cpp:77
int eval(const World &w) const
Returns the automaton state corresponding to a given World in this transition map. Returns -1 if no such transition exists.
Definition: Automaton.cpp:48
unsigned int distFromAccepting(unsigned int s, unsigned int maxDist=std::numeric_limits< unsigned int >::max()) const
Returns the shortest number of transitions from a given state to an accepting state.
Definition: Automaton.cpp:180
void addTransition(unsigned int src, const World &w, unsigned int dest)
Adds a given transition to the automaton.
Definition: Automaton.cpp:106
A shared pointer wrapper for ompl::control::Automaton.
static AutomatonPtr AcceptingAutomaton(unsigned int numProps)
Returns a single-state automaton that accepts on all inputs.
Definition: Automaton.cpp:218
unsigned int numStates(void) const
Returns the number of states in this automaton.
Definition: Automaton.cpp:139
static AutomatonPtr CoverageAutomaton(unsigned int numProps, const std::vector< unsigned int > &covProps)
Helper function to return a coverage automaton. Assumes all propositions are mutually exclusive...
Definition: Automaton.cpp:228
static AutomatonPtr AvoidanceAutomaton(unsigned int numProps, const std::vector< unsigned int > &avoidProps)
Returns an avoidance automaton, which rejects when any one of the given list of propositions becomes ...
Definition: Automaton.cpp:301
A class to represent a deterministic finite automaton, each edge of which corresponds to a World...
Definition: Automaton.h:69
std::string formula(void) const
Returns a formatted string representation of this World, as a conjunction of literals.
Definition: World.cpp:77
TransitionMap & getTransitions(unsigned int src)
Returns the outgoing transition map for a given automaton state.
Definition: Automaton.cpp:134