Commit 335fa53c authored by Alejandro Suarez Hernandez's avatar Alejandro Suarez Hernandez
Browse files

Instructions on how to use the interface

parent 19041469
# ReadMe
# IMAGINE Planner interface
To do...
## Introduction
The software in this repository is a planning interface specifically designed
for the IMAGINE project, although it can be used in other applications provided
that the [ADES language][1] is suitable for specifying the actions.
The key feature of this interface is that the domain operators are specified as
[ADES][1] databases. Each action description contains its symbolic specification
together with the motion sequences. The symbolic part of the description consists
of a list of preconditions and a list of effects in Prolog style.
## How to build
The only external dependences of this project are:
* The [ADES][1] library
* The following BOOST components (tested with BOOST 1.58): System, Serialization,
FileSystem, Unit Test Framework (the last one is required exclusively for the
test programs, so it's optional).
Of course, the ADES dependencies have to be installed as well.
In order to compile, go to the build directory (or create one if it does not
exist) and run `cmake` and `make`:
cd build # Create the directory first if it does not exist
cmake ..
make -j4 # Feel free to change the number of make processes
Make sure that LibADES is being found. If necessary, set the `CMAKE_MODULE_PATH`
variable in `src/CMakeLists.txt` to the appropriate value so the
`Findlibimagine-ades.cmake` can be
located by CMake.
## Practical considerations
From a user point of view, one should be concerned exclusively with the `Planner`
abstract class declared in `src/imagine-planner.h`:
struct Problem
PlanState start;
GoalSpecification goal;
Domain domain;
typedef std::pair<Operator,PlanState> Step;
typedef std::vector<Step> Plan;
class Planner
virtual Plan operator()(const Problem& problem) =0;
virtual ~Planner() {}
Planner implementations inherit from the `Planner` abstract class, providing a
definition for the function operator. A `Plan` consists of several `Step`s, being
one `Step` an operator and the state that is expected after the execution of this operator.
`PlanState` and `GoalSpecification` objects are simply collection of Predicates.
The `Domain`'s instances are created providing the path and version of an ADES
database. Internally, the constructor method will read the whole database and
translate the operators' specifications into the internal representation of the
interface. Currently, only one entry of the preconditions and effects maps are
read. This means that action's preconditions and effects should be entirely
expressed in the tail of the first entry of the corresponding maps. E.g.,
imagine the following operator:
Pre: at(boat,Side),at(What,Side),side(Side),!=(What,boat),\+loaded(_)
Post: \+at(What,Side),loaded(What)
The symbolic specification of the ADES should look like this:
<preconditions_ class_id="1" tracking_level="0" version="0">
<item class_id="2" tracking_level="0" version="0">
<second>at(boat,Side), at(What,Side), side(Side), !=(What,boat), \+loaded(_)</second>
<second>\+at(What,Side), loaded(What)</second>
### Expressivity
Here we discuss which sort of expressions are allowed in the preconditions
and post-conditions. The examples presented in this section come from the
[wolf, sheep and cabbage puzzle (also wolf, bunny and lettuce)][2]
#### Preconditions
Currently, the following queries are allowed in the preconditions:
* **Predicates**: `<literal> '(' (<arg>(, <arg>)*)? ')'`. There is at least
one predicate in the state that unifies with the given predicate. Capitalized
symbols and symbols that start with an underscore (_) are considered variables
and are instantiated via unification with some of the object of the state, if
possible. There may be several possible instantiations, and if the whole precondition
validates, each set of assignment is considered a different parametrization of
the operator. E.g., in a state like this: `{p(x,y), p(y, x), p(w, y)}`,
`p(First,y)` can be instantiated with `First -> x` and with `First -> w`.
If the precondition is more complex than checking the existence of a single
predicate (see `AND` queries), the occurrences of the variables that have
already been given a value will be substituted by this value in the rest of
the precondition.
* **Comparisons**: `["==", "!=", "<", ">", "=<", ">="]'('<arg>, <arg>')'`.
These have a special meaning. The arguments should be both ground by the time
this query is being processed (i.e. if any of the arguments is a variable, it
should be instantiated previously in the query).
* **Negated queries**: `\+<query>`. If there is a single predicate in the
state that unifies with the negated predicate, the precondition fails. Contrarily
to the non-negated predicate, these kind of queries are not able to come with
instantiations for any variable. This means that they should be only used to
check that there is not any instantiation of a given query that validates in
the state. E.g., in a state like this: `{p(x,y), p(y, x), p(w, y)}`,
`p(First, y)` would fail because, as we have seen previously, there are two
possible instantiations. However, `p(First, w)` and `p(x, w)` would success
* **AND queries**: `<query>,<query>`. Separated by commas, in a
Prolog-familiar way, these kind of queries means that both sides of the
comma should be validated in the state. Moreover, the variables that have
been instantiated through the LHS of the comma propagate to the RHS.
E.g., in a state like this: `{p(x,y), q(y), p(x,w)}`, the query
`p(x,Second),q(Second)` can be instantiated with `Second -> y`. However,
`Second -> w` is not a valid instantiation because `q(w)` is not present
in the state.
* **Parenthesized expressions**: `'(' <query> ')'`. It is worth noting
that queries can be parenthesized to express even more complex conditions.
This may be useful, for instance, to express total (*for all* or &#8704;)
conditions. E.g., to express the precondition &#8704;`X, C(X)` &#8594; `G(X)`,
let us notice that this is equivalent to &#8708; `X, C(X)` &#8743; `¬G(X)`,
and this can be expressed as a query as follows: `\+(C(X),\+G(X))`. Notice
that the instantiation of X via C will be effective inside the parenthesis,
but it will not be propagated outside. OR queries can be also expressed
this way.
Some examples:
*An object can be loaded into the boat if the boat and the object are at
the same side, if the object being loaded is not the boat itself and if
the boat is not already loaded:*
load_pre(Side,What) :- at(boat,Side), at(What,Side), side(Side),
!=(What,boat), \+loaded(_)
*The boat can switch sides if it does not leave behind any pair of forbidden
objects and if the destiny is diferrent from the origin*:
switch_sides_pre(From,To) :- at(boat,From), side(From), side(To),
!=(From,To), \+(forbidden(Obj1, Obj2),
at(Obj1,From), at(Obj2,From))
*To unload an object in one of the sides of the river, the object has to
be loaded into the boat and the boa has to be at that side*:
unload_pre(Side,What) :- at(boat,Side), side(Side), loaded(What)
#### Effects
At the moment, effects should consists exclusively of a list of predicates
and negated predicates (\+). Predicates that appear without the negation
symbol are added to the state, while negated predicates are removed from
the state. The substitution found in the precondition is also applied
to the effects. Examples:
*After loading an object, the object should be inside the boat:*
load_post(Side,What) :- \+at(What,Side), loaded(What)
*After switching sides, the boat should be at the destination:*
switch_sides_post(From,To) :- \+at(boat,From), at(boat,To)
*After unloading an object, the object is no longer at the boat and
should be located at the side where the boat is:*
unload_post(Side,What) :- at(What,Side), \+loaded(What)
In the future it may be useful to add a `forall(C(X)`&#8594;`G(X))`
function so the effects of `G(X)` are applied for every instantiation
of `C(X)`.
### Usage example
The source `src/examples/imagine-planner_test.cpp` contains an example
planner implementation that adheres to the interface presented before.
Moreover, it shows how to specify several domains using ADES. Here is
an example on how to specify and solve problem:
problem.start = PlanState({Predicate("at", "lettuce", "east"),
Predicate("at", "wolf", "east"),
Predicate("at", "bunny", "east"),
Predicate("at", "boat", "east"),
Predicate("side", "east"),
Predicate("side", "west"),
Predicate("forbidden", "lettuce", "bunny"),
Predicate("forbidden", "wolf", "bunny")});
problem.goal = GoalSpecification({Predicate("at", "lettuce", "west"),
Predicate("at", "bunny", "west"),
Predicate("at", "wolf", "west"),
Predicate("at", "boat", "west")}, {});
problem.domain = Domain(std::string(std::getenv("HOME"))+"/BOAT_DOMAIN");
Plan plan = planner(problem);
## To-do:
* Doxygen documentation
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment