py2pddl
Write planning task as Python classes, then translate to PDDL. Type checking included.
Science Score: 44.0%
This score indicates how likely this project is to be science-related based on various indicators:
-
✓CITATION.cff file
Found CITATION.cff file -
✓codemeta.json file
Found codemeta.json file -
✓.zenodo.json file
Found .zenodo.json file -
○DOI references
-
○Academic publication links
-
○Academic email domains
-
○Institutional organization owner
-
○JOSS paper metadata
-
○Scientific vocabulary similarity
Low similarity (10.0%) to scientific vocabulary
Repository
Write planning task as Python classes, then translate to PDDL. Type checking included.
Basic Info
Statistics
- Stars: 47
- Watchers: 4
- Forks: 9
- Open Issues: 3
- Releases: 0
Metadata Files
README.md
py2pddl (Python to PDDL)
Write your planning task as Python classes, then translate
them to PDDL files. We will use PDDL 1.2 with (:requirements :strips :typing), i.e.
the actions only use positive preconditions and deterministic effects, and
we use 'types' like in OOP to represent sets of objects.
The library is written with these considerations:
- Type checking
- To define the domain and problem as Python but yet still look similar to PDDL
- To quickly define the domain and problem using some boilerplate
- To define ground predicates cleanly
- To reduce the use of strings while defining ground predicates
Features
This library allows you to define:
- Domain: requirements (strips and typing), types, predicates, action
- Problem: objects, init, goal
Negation is done using the ~ operator.
Requirements
- Python 3.6
- python-fire (
pip install fire)
Installation
bash
pip install git+https://github.com/remykarem/py2pddl#egg=py2pddl
Quick start in 5 steps
We will use the following air cargo problem:

1. Set up boilerplate
Create an aircargo.py file by running:
text
python -m py2pddl.init aircargo.py
and enter the following:
text
Name: AirCargo
Types (separated by space): cargo airport plane
Predicates (separated by space): plane_at cargo_at in_
Actions (separated by space): load unload fly
2. Define the domain
In the aircargo.py source file, the AirCargoDomain class has been created.
The structure of the class is similar to how a PDDL domain should be defined.
- Name of the domain is the name of the Python class (
AirCargoDomain). - Types are defined as class variables at the top (
Plane,Cargo,Airport). - Predicates are defined as instance methods decorated with
@predicate. - Actions are defined as instance methods decorated with
@action.
Now, complete the class definition such that it looks like this:
```python from py2pddl import Domain, create_type from py2pddl import predicate, action
class AirCargoDomain(Domain):
Plane = create_type("Plane")
Cargo = create_type("Cargo")
Airport = create_type("Airport")
@predicate(Cargo, Airport)
def cargo_at(self, c, a):
"""Complete the method signature and specify
the respective types in the decorator"""
@predicate(Plane, Airport)
def plane_at(self, p, a):
"""Complete the method signature and specify
the respective types in the decorator"""
@predicate(Cargo, Plane)
def in_(self, c, p):
"""Complete the method signature and specify
the respective types in the decorator"""
@action(Cargo, Plane, Airport)
def load(self, c, p, a):
precond = [self.cargo_at(c, a), self.plane_at(p, a)]
effect = [~self.cargo_at(c, a), self.in_(c, p)]
return precond, effect
@action(Cargo, Plane, Airport)
def unload(self, c, p, a):
precond = [self.in_(c, p), self.plane_at(p, a)]
effect = [self.cargo_at(c, a), ~self.in_(c, p)]
return precond, effect
@action(Plane, Airport, Airport)
def fly(self, p, orig, dest):
precond = [self.plane_at(p, orig)]
effect = [~self.plane_at(p, orig), self.plane_at(p, dest)]
return precond, effect
```
Note:
- To create a new type
Car, simply addCar = create_type("Car")at the top of the class definition. - The positional arguments of
@predicateand@actiondecorators are the types of the respective arguments. - Methods decorated with
@predicateshould have empty bodies. - Methods decorated with
@actionreturn a tuple of two lists.
3. Define the problem
At the bottom part of aircargo.py, there is another class called AirCargoProblem.
Again, the structure of the class is similar to how a PDDL problem should be defined.
- Name of the domain is the name of the Python class (
AirCargoProblem). - Objects are defined as the instance attributes in the
__init__method. - Initial states are defined as a methods decorated with
@init. - Goal is defined as an instance methods decorated with
@goal.
Complete the class definition as follows:
```python from py2pddl import goal, init
class AirCargoProblem(AirCargoDomain):
def __init__(self):
super().__init__()
self.cargos = AirCargoDomain.Cargo.create_objs([1, 2], prefix="c")
self.planes = AirCargoDomain.Plane.create_objs([1, 2], prefix="p")
self.airports = AirCargoDomain.Airport.create_objs(["sfo", "jfk"])
@init
def init(self):
at = [self.cargo_at(self.cargos[1], self.airports["sfo"]),
self.cargo_at(self.cargos[2], self.airports["jfk"]),
self.plane_at(self.planes[1], self.airports["sfo"]),
self.plane_at(self.planes[2], self.airports["jfk"]),]
return at
@goal
def goal(self):
return [self.cargo_at(self.cargos[1], self.airports["jfk"]),
self.cargo_at(self.cargos[2], self.airports["sfo"])]
```
Note:
- The Python objects (
cargos,planesandairports) are created using the respective types defined in theAirCargoDomain. For example,AirCargoDomain.Cargo.create_objs([1, 2], None, "c")will create a Python dictionary{1: AirCargoDomain.Plane("p1"), 2: AirCargoDomain.Plane("p2")}. This allows cleaner access to these objects while defining initial state and goal, which usually can get pretty messy. - The PDDL objects defined in the
__init__are meant to be used across the 2 instance methods. - Any method decorated with
@initmust return a list. - Any method decorated with
@goalmust return a list.
4. Parse
Generate the PDDL files from the command line by runnning
text python -m py2pddl.parse aircargo.pyYou can also import the parsing function from the module
python from py2pddl import parse parse("aircargo.py")The class itself also contains methods to generate the domain and problem PDDL files separately. These methods were inherited from
Domain.```python from aircargo import AirCargoProblem
problem = AirCargoProblem() problem.generatedomainpddl() problem.generateproblempddl() ```
Here is the generated domain.pddl file.
text
(define
(domain somedomain)
(:requirements :strips :typing)
(:types
airport
cargo
plane
)
(:predicates
(cargo_at ?c - cargo ?a - airport)
(in_ ?c - cargo ?p - plane)
(plane_at ?p - plane ?a - airport)
)
(:action fly
:parameters (?p - plane ?orig - airport ?dest - airport)
:precondition (plane_at ?p ?orig)
:effect (and (not (plane_at ?p ?orig)) (plane_at ?p ?dest))
)
(:action load
:parameters (?c - cargo ?p - plane ?a - airport)
:precondition (and (cargo_at ?c ?a) (plane_at ?p ?a))
:effect (and (not (cargo_at ?c ?a)) (in_ ?c ?p))
)
(:action unload
:parameters (?c - cargo ?p - plane ?a - airport)
:precondition (and (in_ ?c ?p) (plane_at ?p ?a))
:effect (and (cargo_at ?c ?a) (not (in_ ?c ?p)))
)
)
And here is the generated problem.pddl file.
text
(define
(problem someproblem)
(:domain somedomain)
(:objects
sfo jfk - airport
c1 c2 - cargo
p1 p2 - plane
)
(:init (cargo_at c1 sfo) (cargo_at c2 jfk) (plane_at p1 sfo) (plane_at p2 jfk))
(:goal (and (cargo_at c1 jfk) (cargo_at c2 sfo)))
)
Then use your favourite planner like Fast Downward. To output a plan. Here's the plan generated from the above PDDL:
text
(load c1 p1 sfo)
(fly p1 sfo jfk)
(load c2 p1 jfk)
(unload c1 p1 jfk)
(fly p1 jfk sfo)
(unload c2 p1 sfo)
; cost = 6 (unit cost)
See more examples in the pddl/ folder.
5. Generate the problems dynamically
If you want the problem PDDL to be more dynamic if you have
changing inits and goals, you could use dictionaries and
specify in the init or goal keyword argument.
python
p.generate_problem_pddl(
goal={"cargo": "C2"})
Examples
Below are several example domains. The respective Python files, PDDL files and sas_plan files (generated using Fast Downward) can be found in the pddl/ folder here.
Resources
If you use this software for your work, please cite us as follows:
@article{bin_Karim_py2pddl_2020,
author = {bin Karim, Raimi},
journal = {https://github.com/remykarem/py2pddl},
month = {11},
title = {{py2pddl}},
year = {2020}
}
Owner
- Name: Remy
- Login: remykarem
- Kind: user
- Location: Singapore
- Company: GovTech
- Website: remykarem.medium.com
- Twitter: remykarem
- Repositories: 4
- Profile: https://github.com/remykarem
Software Engineer
Citation (CITATION.cff)
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "bin Karim"
given-names: "Raimi"
title: "py2pddl"
date-released: 2020-11-22
url: "https://github.com/remykarem/py2pddl"
preferred-citation:
type: article
authors:
- family-names: "bin Karim"
given-names: "Raimi"
month: 11
journal: "https://github.com/remykarem/py2pddl"
title: "py2pddl"
year: 2020
GitHub Events
Total
- Watch event: 7
Last Year
- Watch event: 7
Issues and Pull Requests
Last synced: over 1 year ago
All Time
- Total issues: 6
- Total pull requests: 0
- Average time to close issues: 1 day
- Average time to close pull requests: N/A
- Total issue authors: 6
- Total pull request authors: 0
- Average comments per issue: 0.0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0
Past Year
- Issues: 2
- Pull requests: 0
- Average time to close issues: about 17 hours
- Average time to close pull requests: N/A
- Issue authors: 2
- Pull request authors: 0
- Average comments per issue: 0.0
- Average comments per pull request: 0
- Merged pull requests: 0
- Bot issues: 0
- Bot pull requests: 0
Top Authors
Issue Authors
- Martin36 (1)
- mikunakanobestwaifu (1)
- LucasJoseph (1)
- nik7273 (1)
- williamn25 (1)
- RodrigoFBernardo (1)
Pull Request Authors
Top Labels
Issue Labels
Pull Request Labels
Dependencies
- fire ==0.3.1
- fire ==0.3.1