16. PDDL I/O Example

This notebook will show the possible interations between the pddl language and the unified_planning usage.

Open In GitHub Open In Colab

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))))
)