Concepts and Data Types

Symbolic planning is a general term for approaches to automated planning that describe the environment and its dynamics in terms of high-level symbols. PDDL is one way of representing such symbolic knowledge, but there are many related formalisms which the shared concepts of fluents, states, actions, domains, and problems. Here we provide general definitions of these concepts, and also describe the system of data types in PDDL.jl that mirror these concepts. A graphical overview is shown below.

A graphical overview of concepts in symbolic planning and their corresponding datatypes.

Fluents and Terms

Fluents define (relational) state variables which may (or may not) change over time. A fluent of arity $n$ is a predicate (Boolean-valued) or function (non-Boolean) with $n$ object arguments, which describes some property or relation over those objects. A ground fluent is a fluent defined over particular set of objects (i.e. none of its arguments are free variables). Arguments may optionally be type-restricted.

Example

The fluent (on ?x ?y) is named on, has arity 2, and describes whether some object denoted by the variable ?x is stacked on top of ?y. The ground fluent (on a b) denotes that object a is stacked on top of object b when true.

The Term data type is used to represent fluents, but also object constants, variables, logical formulae, effect formulae, and ground actions. Every Term has a name property, as well as an args property, representing the (potentially empty) list of sub-terms it has as arguments. Terms are inherited from the Julog.jl package for Prolog-style reasoning about first-order logic.

Julog.TermType
Term

Represents a term or expression in first-order logic, including variables, constants, or compound terms.

There are three subtypes of Terms:

  • Const terms, which are used to represent object constants, and have no arguments.
  • Var terms are used to represent variables in the context of first-order expressions.
  • Compound terms are terms with arguments. They can be used to represent fluents, action preconditions or effects, logical expressions, or ground actions.

To construct a Term using PDDL syntax, the @pddl macro or pddl"..." string macro can be used:

julia> pddl"(on a b)" |> dump
Compound
  name: Symbol on
  args: Array{Term}((2,))
    1: Const
      name: Symbol a
    2: Const
      name: Symbol b

Fluent Signatures

In the context of a planning Domain, (lifted) fluents often have specific type signatures. For example, fluent arguments may be restricted to objects of particular types, and their values may be :boolean or :numeric. This type information is stored in the PDDL.Signature data type:

PDDL.SignatureType
Signature

Type signature for a PDDL fluent (i.e. predicate or function).

Fields

  • name: Name of fluent.

  • type: Output datatype of fluent, represented as a Symbol.

  • args: Argument variables.

  • argtypes: Argument datatypes, represented as Symbols.

source
PDDL.arityFunction
arity(signature)

Returns the arity (i.e. number of arguments) of a fluent signature.

source

States

In symbolic planning, states are symbolic descriptions of the environment and its objects at a particular point in time. Formally, given a finite set of fluents $\mathcal{F}$, a state $s$ is composed of a set of (optionally typed) objects $\mathcal{O}$, and valuations of ground fluents $\mathcal{F}(\mathcal{O})$ defined over all objects in $\mathcal{O}$ of the appropriate types. Each ground fluent thus refers to a state variable. For a ground fluent $f \in \mathcal{F}(\mathcal{O})$, we will use the notation $s[f] = v$ to denote that $f$ has value $v$ in state $s$.

Example

Given the fluents (on ?x ?y) and (on-table ?x) that describe a state $s$ with objects a and b, there are six ground fluents whose values are defined in the state: (on a a), (on a b), (on b a), (on b b), (on-table a) and (on-table b). The expression $s[$(on a b)$] =$ true means that object a is on top of b in state $s$.

In PDDL.jl, states are represented by the State abstract type:

PDDL.StateType
State

Abstract supertype for symbolic states. A State is a symbolic description of the environment and its objects at a particular point in time. It consists of a set of objects, and a set of ground fluents (predicates or functions) with values defined over those objects.

source

The following accessor methods are defined for a State:

PDDL.get_objectsMethod
get_objects(state::State, [type::Symbol])
get_objects(domain::Domain, state::State, type::Symbol)

Returns an iterator over objects in the state. If a type is specified, the iterator will contain objects only of that type (but not its subtypes). If a domain is provided, then the iterator will contain all objects of that type or any of its subtypes.

source
PDDL.get_objtypesMethod
get_objtypes(state)

Returns a map (dictionary, named tuple, etc.) from state objects to their types.

source
PDDL.get_factsMethod
get_facts(state)

Returns an iterator over true Boolean predicates in a state.

source
PDDL.get_fluentMethod
get_fluent(state, term)

Gets the value of a (non-derived) fluent.Equivalent to using the index notation state[term].

source
PDDL.set_fluent!Method
set_fluent!(state, val, term)

Sets the value of a (non-derived) fluent. Equivalent to using the index notation state[term] = val.

source
PDDL.get_fluentsMethod
get_fluents(state)

Returns a map from fluent names to values (false predicates may be omitted). Base.pairs is an alias.

source

Actions

As described in the Getting Started, symbolic planning formalisms distinguish between action schemas (also known as operators), which specify the general semantics of an action, and ground actions, which represent instantiations of an action schema for specific objects.

An action schema comprises:

  • A name that identifies the action.
  • A list of (optionally typed) parameters or arguments that an action operates over.
  • A precondition formula, defined over the parameters, that has to hold true for the action to be executable.
  • An effect formula, defined over the parameters, specifying how the action modifies the state once it is executed.
Example

An example action schema definition in PDDL is shown below:

(:action stack
 :parameters (?x ?y - block)
 :precondition (and (holding ?x) (clear ?y) (not (= ?x ?y)))
 :effect (and (not (holding ?x)) (not (clear ?y)) (clear ?x) (handempty) (on ?x ?y)))

This schema defines the semantics of an action named stack and has two parameters of type block. Its precondition states that block ?x has to be held, block ?y has to be clear (no other block is on top of it), and that?x is not the same as ?y. Its effect states that in the next state, ?x will no longer be held, and that it will be instead be placed on top of block ?y.

In PDDL.jl, action schemas are represented by the Action abstract type:

PDDL.ActionType
Action

Abstract supertype for action schemas, which specify the general semantics of an action parameterized by a set of arguments.

source

The following accessor methods are defined for an Action:

PDDL.get_precondMethod
get_precond(action::Action)
get_precond(action::Action, args)
get_precond(domain::Domain, action::Term)

Returns the precondition of an action schema as a Term, optionally parameterized by args. Alternatively, an action and its arguments can be specified as a Term.

source
PDDL.get_effectMethod
get_effect(action::Action)
get_effect(action::Action, args)
get_effect(domain::Domain, action::Term)

Returns the effect of an action schema as a Term, optionally parameterized by args. Alternatively, an action and its arguments can be specified as a Term.

source

In contrast to action schemas, ground actions are represented with the Term data type. This is because the name property of a Term is sufficient to identify an action schema in the context of a planning domain, and the args property can be used to represent action parameters.

There also exists a special no-op action schema, denoted PDDL.NoOp() in Julia code. The corresponding ground action can be expressed as PDDL.no_op or pddl"(--)".

PDDL.NoOpType
NoOp()

Constructs a no-op action which has no preconditions or effects.

source
PDDL.no_opConstant
PDDL.no_op

An alias for pddl"(–)", the action Term for a NoOp action which has no preconditions or effects.

source

State Differences

For some use cases, such as action grounding or interpreted execution, it can be helpful to more explicitly represent the effects of an action as a difference between States. PDDL.jl uses the PDDL.Diff abstract data type to represent such differences, including PDDL.GenericDiffs and PDDL.ConditionalDiffs.

PDDL.DiffType
Diff

Abstract type representing differences between states.

source
PDDL.GenericDiffType
GenericDiff(add, del, ops)

Generic state difference represented as additions, deletions, and assignments.

Fields

  • add: List of added Terms

  • del: List of deleted Terms

  • ops: Dictionary mapping fluents to their assigned expressions.

source
PDDL.ConditionalDiffType
ConditionalDiff(conds, diffs)

Conditional state difference, represented as paired conditions and sub-diffs.

Fields

  • conds: List of list of condition Terms for each sub-diff.

  • diffs: List of sub-diffs.

source

Multiple PDDL.Diffs can be combined using the PDDL.combine! and PDDL.combine functions:

PDDL.combine!Function
combine!(diff::Diff, ds::Diff...)

Combine state differences, modifying the first Diff in place.

source
PDDL.combineFunction
combine(diff::Diff, ds::Diff...)

Combine state differences, returning a fresh Diff.

source

Domains

A planning domain is a (first-order) symbolic model of the environment, specifying the predicates and functions that can be used to describe the environment, and the actions that can be taken in the environment, including their preconditions and effects. Some domains may also specify the types of objects that exist, or include domain axioms that specify which predicates can be derived from others.

In PDDL.jl, domains are represented by the Domain abstract type:

PDDL.DomainType
Domain

Abstract supertype for planning domains, which specify a symbolic model of the environment and its transition dynamics.

source

The following accessor methods are defined for a Domain:

PDDL.get_subtypesMethod
get_subtypes(domain, type)

Returns an iterator over (immediate) subtypes of type in the domain.

source
PDDL.get_functionMethod
get_function(domain, name)

Returns the signature associated with a function name.

source
PDDL.get_fluentMethod
get_fluent(domain, name)

Returns the signature associated with a fluent name.

source
PDDL.get_axiomsMethod
get_axioms(domain)

Returns a map from names of derived predicates to their corresponding axioms.

source
PDDL.get_axiomMethod
get_axiom(domain, name)

Returns the axiom assocated with a derived predicate name.

source

Problems

A planning problem for a particular domain specifies both the initial state of the environment, and the task specification to be achieved. Typically, the task specification is a goal to be achieved, specified as a logical formula to be satisfied. However, planning problems can also include other specifications, such as a cost metric to minimize, and temporal constraints on the plan or state trajectory.

In PDDL.jl, problems are represented by the Problem abstract type:

PDDL.ProblemType
Problem

Abstract supertype for planning problems, which specify the initial state of a planning domain and the task specification to be achieved.

source

The following accessor methods are defined for a Problem: