Skip to content

Commit 2f84be4

Browse files
Chipe1norvig
authored andcommitted
planning.py (aimacode#232)
* Added Action class for PDDL * Added tests for Action class * Changed doc string * Added PDLL class * Added Air-Cargo-problem * Tested Air Cargo Problem
1 parent 464ca5d commit 2f84be4

File tree

2 files changed

+163
-8
lines changed

2 files changed

+163
-8
lines changed

planning.py

Lines changed: 129 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,134 @@
11
"""Planning (Chapters 10-11)
22
"""
33

4-
# flake8: noqa
4+
from utils import Expr, expr, first
5+
from logic import FolKB
56

6-
import agents
7+
class PDLL:
8+
"""
9+
PDLL used to deine a search problem
10+
It stores states in a knowledge base consisting of first order logic statements
11+
The conjunction of these logical statements completely define a state
12+
"""
713

8-
import math
9-
import random
10-
import sys
11-
import time
12-
import bisect
13-
import string
14+
def __init__(self, initial_state, actions, goal_test):
15+
self.kb = FolKB(initial_state)
16+
self.actions = actions
17+
self.goal_test_func = goal_test
18+
19+
def goal_test(self):
20+
return self.goal_test_func(self.kb)
21+
22+
def act(self, action):
23+
"""
24+
Performs the action given as argument
25+
Note that action is an Expr like expr('Remove(Glass, Table)') or expr('Eat(Sandwich)')
26+
"""
27+
action_name = action.op
28+
args = action.args
29+
list_action = first(a for a in self.actions if a.name == action_name)
30+
if list_action is None:
31+
raise Exception("Action '{}' not found".format(action_name))
32+
if not list_action.check_precond(self.kb, args):
33+
raise Exception("Action '{}' pre-conditions not satisfied".format(action))
34+
list_action(self.kb, args)
35+
36+
class Action:
37+
"""
38+
Defines an action schema using preconditions and effects
39+
Use this to describe actions in PDDL
40+
action is an Expr where variables are given as arguments(args)
41+
Precondition and effect are both lists with positive and negated literals
42+
Example:
43+
precond_pos = [expr("Human(person)"), expr("Hungry(Person)")]
44+
precond_neg = [expr("Eaten(food)")]
45+
effect_add = [expr("Eaten(food)")]
46+
effect_rem = [expr("Hungry(person)")]
47+
eat = Action(expr("Eat(person, food)"), [precond_pos, precond_neg], [effect_add, effect_rem])
48+
"""
49+
50+
def __init__(self,action , precond, effect):
51+
self.name = action.op
52+
self.args = action.args
53+
self.precond_pos = precond[0]
54+
self.precond_neg = precond[1]
55+
self.effect_add = effect[0]
56+
self.effect_rem = effect[1]
57+
58+
def __call__(self, kb, args):
59+
return self.act(kb, args)
60+
61+
def substitute(self, e, args):
62+
"""Replaces variables in expression with their respective Propostional symbol"""
63+
new_args = [args[i] for x in e.args for i in range(len(self.args)) if self.args[i]==x]
64+
return Expr(e.op, *new_args)
65+
66+
def check_precond(self, kb, args):
67+
"""Checks if the precondition is satisfied in the current state"""
68+
#check for positive clauses
69+
for clause in self.precond_pos:
70+
if self.substitute(clause, args) not in kb.clauses:
71+
return False
72+
#check for negative clauses
73+
for clause in self.precond_neg:
74+
if self.substitute(clause, args) in kb.clauses:
75+
return False
76+
return True
77+
78+
def act(self, kb, args):
79+
"""Executes the action on the state's kb"""
80+
#check if the preconditions are satisfied
81+
if not self.check_precond(kb, args):
82+
raise Exception("Action pre-conditions not satisfied")
83+
#remove negative literals
84+
for clause in self.effect_rem:
85+
kb.retract(self.substitute(clause, args))
86+
#add positive literals
87+
for clause in self.effect_add:
88+
kb.tell(self.substitute(clause, args))
89+
90+
91+
def air_cargo():
92+
init = [expr('At(C1, SFO)'),
93+
expr('At(C2, JFK)'),
94+
expr('At(P1, SFO)'),
95+
expr('At(P2, JFK)'),
96+
expr('Cargo(C1)'),
97+
expr('Cargo(C2)'),
98+
expr('Plane(P1)'),
99+
expr('Plane(P2)'),
100+
expr('Airport(JFK)'),
101+
expr('Airport(SFO)')]
102+
103+
def goal_test(kb):
104+
required = [expr('At(C1 , JFK)'), expr('At(C2 ,SFO)')]
105+
for q in required:
106+
if kb.ask(q) is False:
107+
return False
108+
return True
109+
110+
## Actions
111+
# Load
112+
precond_pos = [expr("At(c, a)"), expr("At(p, a)"), expr("Cargo(c)"), expr("Plane(p)"), expr("Airport(a)")]
113+
precond_neg = []
114+
effect_add = [expr("In(c, p)")]
115+
effect_rem = [expr("At(c, a)")]
116+
load = Action(expr("Load(c, p, a)"), [precond_pos, precond_neg], [effect_add, effect_rem])
117+
118+
# Unload
119+
precond_pos = [expr("In(c, p)"), expr("At(p, a)"), expr("Cargo(c)"), expr("Plane(p)"), expr("Airport(a)")]
120+
precond_neg = []
121+
effect_add = [expr("At(c, a)")]
122+
effect_rem = [expr("In(c, p)")]
123+
unload = Action(expr("Unload(c, p, a)"), [precond_pos, precond_neg], [effect_add, effect_rem])
124+
125+
# Load
126+
# Used used 'f' instead of 'from' because 'from' is a python keyword and expr uses eval() function
127+
precond_pos = [expr("At(p, f)"), expr("Plane(p)"), expr("Airport(f)"), expr("Airport(to)")]
128+
precond_neg = []
129+
effect_add = [expr("At(p, to)")]
130+
effect_rem = [expr("At(p, f)")]
131+
fly = Action(expr("Fly(p, f, to)"), [precond_pos, precond_neg], [effect_add, effect_rem])
132+
133+
return PDLL(init, [load, unload, fly], goal_test)
134+

tests/test_planning.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from planning import *
2+
from utils import expr
3+
from logic import FolKB
4+
5+
def test_action():
6+
precond = [[expr("P(x)"), expr("Q(y, z)")]
7+
,[expr("Q(x)")]]
8+
effect = [[expr("Q(x)")]
9+
, [expr("P(x)")]]
10+
a=Action(expr("A(x,y,z)"),precond, effect)
11+
args = [expr("A"), expr("B"), expr("C")]
12+
assert a.substitute(expr("P(x, z, y)"), args) == expr("P(A, C, B)")
13+
test_kb = FolKB([expr("P(A)"), expr("Q(B, C)"), expr("R(D)")])
14+
assert a.check_precond(test_kb, args)
15+
a.act(test_kb, args)
16+
assert test_kb.ask(expr("P(A)")) is False
17+
assert test_kb.ask(expr("Q(A)")) is not False
18+
assert test_kb.ask(expr("Q(B, C)")) is not False
19+
assert not a.check_precond(test_kb, args)
20+
21+
def test_air_cargo():
22+
p = air_cargo()
23+
assert p.goal_test() is False
24+
solution =[expr("Load(C1 , P1, SFO)"),
25+
expr("Fly(P1, SFO, JFK)"),
26+
expr("Unload(C1, P1, JFK)"),
27+
expr("Load(C2, P2, JFK)"),
28+
expr("Fly(P2, JFK, SFO)"),
29+
expr("Unload (C2, P2, SFO)")]
30+
31+
for action in solution:
32+
p.act(action)
33+
34+
assert p.goal_test()

0 commit comments

Comments
 (0)