16. PDDL I/O Example
This notebook will show the possible interations between the pddl language and the unified_planning usage.
16.1. Setup
We start by installing the library with PIP
[1]:
!pip install --pre unified-planning
16.2. Sample pddl domain and problem
Using wget, download from the unified_planning repository the pddl files we need for testing.
[2]:
!wget https://raw.githubusercontent.com/aiplan4eu/unified-planning/master/unified_planning/test/pddl/counters/domain.pddl -O /tmp/counters_domain.pddl
[3]:
!wget https://raw.githubusercontent.com/aiplan4eu/unified-planning/master/unified_planning/test/pddl/counters/problem.pddl -O /tmp/counters_problem.pddl
16.3. PDDL Reader
As the name suggests, the PDDLReader offers the capability of parsing a problem from a pddl file and creates a semantically equivalent problem in the unified_planning
framework.
There are 2 possible usages: - the first one is parsing both a domain file and a problem file. - the second one is parsing only a domain file and then populate the problem using the unified_planning
capabilities.
16.3.1. Parsing domain and problem files
In the following example the PDDLReader
will be used to parse a complete PDDL problem; so it will need a domain.pddl
file and a problem.pddl
file.
[4]:
# Import the PDDLReader and PDDLWriter classes
from unified_planning.io import PDDLReader, PDDLWriter
reader = PDDLReader()
pddl_problem = reader.parse_problem('/tmp/counters_domain.pddl', '/tmp/counters_problem.pddl')
print(pddl_problem)
problem name = instance_4
types = [counter]
fluents = [
real value[c=counter]
real max_int
]
actions = [
action increment(counter c) {
preconditions = [
((value(c) + 1) <= max_int)
]
effects = [
value(c) += 1
]
}
action decrement(counter c) {
preconditions = [
(1 <= value(c))
]
effects = [
value(c) -= 1
]
}
]
objects = [
counter: [c0, c1, c2, c3]
]
initial fluents default = [
]
initial values = [
max_int := 10
value(c0) := 0
value(c1) := 0
value(c2) := 0
value(c3) := 0
]
goals = [
(((value(c0) + 1) <= value(c1)) and ((value(c1) + 1) <= value(c2)) and ((value(c2) + 1) <= value(c3)))
]
16.3.2. Parsing only a domain file
In the following example the PDDLReader
will be used to parse only a domain file, and then the problem will be populated using the unified_planning
framework.
[5]:
# Domain is a up.model.Problem that contains only the pddl domain.
domain = reader.parse_problem('/tmp/counters_domain.pddl', None) # None is the default, so it can be avoided
counter_type = domain.user_type("counter") # get the counter type
domain.set_initial_value(domain.fluent("max_int"), 10) # initialize the fluent "max_int"
value_fluent = domain.fluent("value") # get the "value" fluent
for i in range(4, 6):
problem = domain.clone() # Clone the parsed domain, then populate it
# Populate the problem. "j" iterates in [0, i], creates an object of type
# "counter", sets it's initial value to 0, and then sets the goal:
# "value(c{j-1}) + 1 <= value(c{j})".
# This means that every value of the added objects must be
# at least 1 bigger than the object added before.
for j in range(i + 1):
object_j = problem.add_object(f"c{str(j)}", counter_type) # Create and add object
problem.set_initial_value(value_fluent(object_j), 0) # Set the initial value of "value(object)" to 0
if j > 0:
previous_object = problem.object(f"c{str(j-1)}") # Get previous object
problem.add_goal( # Add the goal "value(c{j-1})+1 <= value(c{j})"
value_fluent(previous_object)+1 <= value_fluent(object_j),
)
print(problem)
problem name = fn-counters
types = [counter]
fluents = [
real value[c=counter]
real max_int
]
actions = [
action increment(counter c) {
preconditions = [
((value(c) + 1) <= max_int)
]
effects = [
value(c) += 1
]
}
action decrement(counter c) {
preconditions = [
(1 <= value(c))
]
effects = [
value(c) -= 1
]
}
]
objects = [
counter: [c0, c1, c2, c3, c4]
]
initial fluents default = [
]
initial values = [
max_int := 10
value(c0) := 0
value(c1) := 0
value(c2) := 0
value(c3) := 0
value(c4) := 0
]
goals = [
((value(c0) + 1) <= value(c1))
((value(c1) + 1) <= value(c2))
((value(c2) + 1) <= value(c3))
((value(c3) + 1) <= value(c4))
]
problem name = fn-counters
types = [counter]
fluents = [
real value[c=counter]
real max_int
]
actions = [
action increment(counter c) {
preconditions = [
((value(c) + 1) <= max_int)
]
effects = [
value(c) += 1
]
}
action decrement(counter c) {
preconditions = [
(1 <= value(c))
]
effects = [
value(c) -= 1
]
}
]
objects = [
counter: [c0, c1, c2, c3, c4, c5]
]
initial fluents default = [
]
initial values = [
max_int := 10
value(c0) := 0
value(c1) := 0
value(c2) := 0
value(c3) := 0
value(c4) := 0
value(c5) := 0
]
goals = [
((value(c0) + 1) <= value(c1))
((value(c1) + 1) <= value(c2))
((value(c2) + 1) <= value(c3))
((value(c3) + 1) <= value(c4))
((value(c4) + 1) <= value(c5))
]
16.4. PDDL Writer
As the PDDLReader
allows a unified_planning user to parse a problem from pddl, the PDDLWriter offers the capability of dumping a unified_planning Problem
in pddl.
There are 3 possible usages of the PDDLWriter
: - printing pddl domain and problem to a file - getting pddl domain and problem as a python str - printing pddl domain and problem to STDOUT
16.4.1. Writing to file
To write the pddl equivalent of a unified_planning Problem
to a file we use the PDDLWriter.write_domain
and PDDLWriter.write_problem
methods.
[6]:
w = PDDLWriter(problem)
w.write_domain('/tmp/written_counters_domain.pddl')
w.write_problem('/tmp/written_counters_problem.pddl')
16.4.2. Getting domain and problem as a python string
To get the pddl equivalent of a unified_planning Problem
as a python string we use the PDDLWriter.get_domain
and PDDLWriter.get_problem
methods.
[7]:
print(w.get_domain())
print(w.get_problem())
(define (domain fn_counters-domain)
(:requirements :strips :typing :numeric-fluents)
(:types counter)
(:functions (value ?c - counter) (max_int))
(:action increment
:parameters ( ?c - counter)
:precondition (and (<= (+ 1 (value ?c)) (max_int)))
:effect (and (increase (value ?c) 1)))
(:action decrement
:parameters ( ?c - counter)
:precondition (and (<= 1 (value ?c)))
:effect (and (decrease (value ?c) 1)))
)
(define (problem fn_counters-problem)
(:domain fn_counters-domain)
(:objects
c0 c1 c2 c3 c4 c5 - counter
)
(:init (= (max_int) 10) (= (value c0) 0) (= (value c1) 0) (= (value c2) 0) (= (value c3) 0) (= (value c4) 0) (= (value c5) 0))
(:goal (and (<= (+ 1 (value c0)) (value c1)) (<= (+ 1 (value c1)) (value c2)) (<= (+ 1 (value c2)) (value c3)) (<= (+ 1 (value c3)) (value c4)) (<= (+ 1 (value c4)) (value c5))))
)
16.4.3. Printing domain and problem to STDOUT
To print the pddl equivalent of a unified_planning Problem
to STDOUT
we use the PDDLWriter.print_domain
and PDDLWriter.print_problem
methods.
[8]:
w.print_domain()
w.print_problem()
(define (domain fn_counters-domain)
(:requirements :strips :typing :numeric-fluents)
(:types counter)
(:functions (value ?c - counter) (max_int))
(:action increment
:parameters ( ?c - counter)
:precondition (and (<= (+ 1 (value ?c)) (max_int)))
:effect (and (increase (value ?c) 1)))
(:action decrement
:parameters ( ?c - counter)
:precondition (and (<= 1 (value ?c)))
:effect (and (decrease (value ?c) 1)))
)
(define (problem fn_counters-problem)
(:domain fn_counters-domain)
(:objects
c0 c1 c2 c3 c4 c5 - counter
)
(:init (= (max_int) 10) (= (value c0) 0) (= (value c1) 0) (= (value c2) 0) (= (value c3) 0) (= (value c4) 0) (= (value c5) 0))
(:goal (and (<= (+ 1 (value c0)) (value c1)) (<= (+ 1 (value c1)) (value c2)) (<= (+ 1 (value c2)) (value c3)) (<= (+ 1 (value c3)) (value c4)) (<= (+ 1 (value c4)) (value c5))))
)