From 43bf3e8d36320ff236b160278ae828621c2a9328 Mon Sep 17 00:00:00 2001 From: Chipe1 Date: Fri, 20 May 2016 01:42:48 +0530 Subject: [PATCH 1/6] Added Action class for PDDL --- planning.py | 57 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/planning.py b/planning.py index 52e4c0b36..f24e93bd5 100644 --- a/planning.py +++ b/planning.py @@ -1,13 +1,54 @@ """Planning (Chapters 10-11) """ -# flake8: noqa +from utils import Expr +from logic import FolKB -import agents +class Action(): + """ + Defines an action schema using preconditions and effects + Use this to describe actions in PDDL + action is an Expr where variables are given as arguments(args) + Precondition and effect are both lists with positive and negated literals + eat = Action([expr("Hungry"), ], [, ]) + """ -import math -import random -import sys -import time -import bisect -import string + def __init__(self,action , precond, effect): + self.name = action.op + self.args = action.args + self.precond_pos = precond[0] + self.precond_neg = precond[1] + self.effect_pos = effect[0] + self.effect_neg = effect[1] + + def __call__(self, kb, args): + return self.act(kb, args) + + def substitute(self, e, args): + """Replaces variables in expression with their respective Propostional symbol""" + new_args = [args[i] for i in range(len(self.args)) for x in e.args if self.args[i]==x] + return Expr(e.op, *new_args) + + def check_precond(self, kb, args): + """Checks if the precondition is satisfied in the current state""" + #check for positive clauses + for clause in self.precond_pos: + if self.substitute(clause, args) not in kb.clauses: + return False + #check for negative clauses + for clause in self.precond_neg: + if self.substitute(clause, args) in kb.clause: + return False + return True + + def act(self, kb): + """Executes the action on the state's kb""" + #check if the preconditions are satisfied + if not self.check_precond(kb): + raise Exception("Action pre-conditions not satisfied") + #remove negative literals + for clause in self.effect_neg: + kb.retract(self.substitute(clause, args)) + #add positive literals + for clause in self.effect_pos: + kb.tell(self.substitute(clause, args)) From 0bd31806bb51baea24adf8f1655c82213c02bc61 Mon Sep 17 00:00:00 2001 From: Chipe1 Date: Fri, 20 May 2016 02:14:35 +0530 Subject: [PATCH 2/6] Added tests for Action class --- planning.py | 8 ++++---- tests/test_planning.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 tests/test_planning.py diff --git a/planning.py b/planning.py index f24e93bd5..8158ae35a 100644 --- a/planning.py +++ b/planning.py @@ -26,7 +26,7 @@ def __call__(self, kb, args): def substitute(self, e, args): """Replaces variables in expression with their respective Propostional symbol""" - new_args = [args[i] for i in range(len(self.args)) for x in e.args if self.args[i]==x] + new_args = [args[i] for x in e.args for i in range(len(self.args)) if self.args[i]==x] return Expr(e.op, *new_args) def check_precond(self, kb, args): @@ -37,14 +37,14 @@ def check_precond(self, kb, args): return False #check for negative clauses for clause in self.precond_neg: - if self.substitute(clause, args) in kb.clause: + if self.substitute(clause, args) in kb.clauses: return False return True - def act(self, kb): + def act(self, kb, args): """Executes the action on the state's kb""" #check if the preconditions are satisfied - if not self.check_precond(kb): + if not self.check_precond(kb, args): raise Exception("Action pre-conditions not satisfied") #remove negative literals for clause in self.effect_neg: diff --git a/tests/test_planning.py b/tests/test_planning.py new file mode 100644 index 000000000..6e05661bd --- /dev/null +++ b/tests/test_planning.py @@ -0,0 +1,19 @@ +from planning import * +from utils import expr +from logic import FolKB + +def test_action(): + precond = [[expr("P(x)"), expr("Q(y, z)")] + ,[expr("Q(x)")]] + effect = [[expr("Q(x)")] + , [expr("P(x)")]] + a=Action(expr("A(x,y,z)"),precond, effect) + args = [expr("A"), expr("B"), expr("C")] + assert a.substitute(expr("P(x, z, y)"), args) == expr("P(A, C, B)") + test_kb = FolKB([expr("P(A)"), expr("Q(B, C)"), expr("R(D)")]) + assert a.check_precond(test_kb, args) + a.act(test_kb, args) + assert test_kb.ask(expr("P(A)")) is False + assert test_kb.ask(expr("Q(A)")) is not False + assert test_kb.ask(expr("Q(B, C)")) is not False + assert not a.check_precond(test_kb, args) From 4d30cc47b0ae482bd086a428eadf465a1b1be6eb Mon Sep 17 00:00:00 2001 From: Chipe1 Date: Fri, 20 May 2016 02:25:39 +0530 Subject: [PATCH 3/6] Changed doc string --- planning.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/planning.py b/planning.py index 8158ae35a..8bc9c4931 100644 --- a/planning.py +++ b/planning.py @@ -10,7 +10,12 @@ class Action(): Use this to describe actions in PDDL action is an Expr where variables are given as arguments(args) Precondition and effect are both lists with positive and negated literals - eat = Action([expr("Hungry"), ], [, ]) + Example: + precond_pos = [expr("Human(person)"), expr("Hungry(Person)")] + precond_neg = [expr("Eaten(food)")] + effect_add = [expr("Eaten(food)")] + effect_rem = [expr("Hungry(person)")] + eat = Action(Eat(person, food), [precond_pos, precond_neg], [effect_add, effect_rem]) """ def __init__(self,action , precond, effect): @@ -18,8 +23,8 @@ def __init__(self,action , precond, effect): self.args = action.args self.precond_pos = precond[0] self.precond_neg = precond[1] - self.effect_pos = effect[0] - self.effect_neg = effect[1] + self.effect_add = effect[0] + self.effect_rem = effect[1] def __call__(self, kb, args): return self.act(kb, args) @@ -47,8 +52,8 @@ def act(self, kb, args): if not self.check_precond(kb, args): raise Exception("Action pre-conditions not satisfied") #remove negative literals - for clause in self.effect_neg: + for clause in self.effect_rem: kb.retract(self.substitute(clause, args)) #add positive literals - for clause in self.effect_pos: + for clause in self.effect_add: kb.tell(self.substitute(clause, args)) From 0cbec43b8e1450511a9930fa7b01736c90a76049 Mon Sep 17 00:00:00 2001 From: Chipe1 Date: Sat, 21 May 2016 23:36:14 +0530 Subject: [PATCH 4/6] Added PDLL class --- planning.py | 38 +++++++++++++++++++++++++++++++++++--- tests/test_planning.py | 2 ++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/planning.py b/planning.py index 8bc9c4931..9fa80d924 100644 --- a/planning.py +++ b/planning.py @@ -1,10 +1,40 @@ """Planning (Chapters 10-11) """ -from utils import Expr +from utils import Expr, expr, first from logic import FolKB -class Action(): +class PDLL: + """ + PDLL used to deine a search problem + It stores states in a knowledge base consisting of first order logic statements + The conjunction of these logical statements completely define a state + """ + + def __init__(self, initial_state, actions, goal_test): + self.kb = FolKB(initial_state) + self.actions = actions + self.goal_test_func = goal_test + + def goal_test(self): + return self.goal_test_func(self.kb) + + def act(self, action): + """ + Performs the action given as argument + Note that action is a string like 'Remove(Glass, Table)' or 'Eat(Sandwich)' + """ + e = expr(action) + action_name = e.op + args = e.args + action = first(a for a in self.actions if a.name == e.op) + if action in None: + raise Exception("Action '{}' not found".format(action_name)) + if not action.check_precond(self.kb, args): + raise Exception("Action pre-conditions not satisfied") + action(self.kb ,args) + +class Action: """ Defines an action schema using preconditions and effects Use this to describe actions in PDDL @@ -15,7 +45,7 @@ class Action(): precond_neg = [expr("Eaten(food)")] effect_add = [expr("Eaten(food)")] effect_rem = [expr("Hungry(person)")] - eat = Action(Eat(person, food), [precond_pos, precond_neg], [effect_add, effect_rem]) + eat = Action(expr("Eat(person, food)"), [precond_pos, precond_neg], [effect_add, effect_rem]) """ def __init__(self,action , precond, effect): @@ -57,3 +87,5 @@ def act(self, kb, args): #add positive literals for clause in self.effect_add: kb.tell(self.substitute(clause, args)) + + diff --git a/tests/test_planning.py b/tests/test_planning.py index 6e05661bd..5f2601c46 100644 --- a/tests/test_planning.py +++ b/tests/test_planning.py @@ -17,3 +17,5 @@ def test_action(): assert test_kb.ask(expr("Q(A)")) is not False assert test_kb.ask(expr("Q(B, C)")) is not False assert not a.check_precond(test_kb, args) + + From c18de7c7ed6906b348a903beaf0e133cc928bff3 Mon Sep 17 00:00:00 2001 From: Chipe1 Date: Sat, 21 May 2016 23:54:27 +0530 Subject: [PATCH 5/6] Added Air-Cargo-problem --- planning.py | 61 +++++++++++++++++++++++++++++++++++------- tests/test_planning.py | 1 - 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/planning.py b/planning.py index 9fa80d924..9e52c839e 100644 --- a/planning.py +++ b/planning.py @@ -22,17 +22,16 @@ def goal_test(self): def act(self, action): """ Performs the action given as argument - Note that action is a string like 'Remove(Glass, Table)' or 'Eat(Sandwich)' + Note that action is an Expr like expr('Remove(Glass, Table)') or expr('Eat(Sandwich)') """ - e = expr(action) - action_name = e.op - args = e.args - action = first(a for a in self.actions if a.name == e.op) - if action in None: + action_name = action.op + args = action.args + list_action = first(a for a in self.actions if a.name == action_name) + if list_action is None: raise Exception("Action '{}' not found".format(action_name)) - if not action.check_precond(self.kb, args): - raise Exception("Action pre-conditions not satisfied") - action(self.kb ,args) + if not list_action.check_precond(self.kb, args): + raise Exception("Action '{}' pre-conditions not satisfied".format(action)) + list_action(self.kb, args) class Action: """ @@ -89,3 +88,47 @@ def act(self, kb, args): kb.tell(self.substitute(clause, args)) +def air_cargo(): + init = [expr('At(C1, SFO)'), + expr('At(C2, JFK)'), + expr('At(P1, SFO)'), + expr('At(P2, JFK)'), + expr('Cargo(C1)'), + expr('Cargo(C2)'), + expr('Plane(P1)'), + expr('Plane(P2)'), + expr('Airport(JFK)'), + expr('Airport(SFO)')] + + def goal_test(kb): + required = [expr('At(C1 , JFK)'), expr('At(C2 ,SFO)')] + for q in required: + if kb.ask(q) is False: + return False + return True + + ## Actions + # Load + precond_pos = [expr("At(c, a)"), expr("At(p, a)"), expr("Cargo(c)"), expr("Plane(p)"), expr("Airport(a)")] + precond_neg = [] + effect_add = [expr("In(c, p)")] + effect_rem = [expr("At(c, a)")] + load = Action(expr("Load(c, p, a)"), [precond_pos, precond_neg], [effect_add, effect_rem]) + + # Unload + precond_pos = [expr("In(c, p)"), expr("At(p, a)"), expr("Cargo(c)"), expr("Plane(p)"), expr("Airport(a)")] + precond_neg = [] + effect_add = [expr("At(c, a)")] + effect_rem = [expr("In(c, p)")] + unload = Action(expr("Unload(c, p, a)"), [precond_pos, precond_neg], [effect_add, effect_rem]) + + # Load + # Used used 'f' instead of 'from' because 'from' is a python keyword and expr uses eval() function + precond_pos = [expr("At(p, f)"), expr("Plane(p)"), expr("Airport(f)"), expr("Airport(to)")] + precond_neg = [] + effect_add = [expr("At(p, to)")] + effect_rem = [expr("At(p, f)")] + fly = Action(expr("Fly(p, f, to)"), [precond_pos, precond_neg], [effect_add, effect_rem]) + + return PDLL(init, [load, unload, fly], goal_test) + diff --git a/tests/test_planning.py b/tests/test_planning.py index 5f2601c46..a93130243 100644 --- a/tests/test_planning.py +++ b/tests/test_planning.py @@ -18,4 +18,3 @@ def test_action(): assert test_kb.ask(expr("Q(B, C)")) is not False assert not a.check_precond(test_kb, args) - From 9b1a036227e8e2258b0d2ed19ca405efeeb86e47 Mon Sep 17 00:00:00 2001 From: Chipe1 Date: Sat, 21 May 2016 23:55:42 +0530 Subject: [PATCH 6/6] Tested Air Cargo Problem --- tests/test_planning.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_planning.py b/tests/test_planning.py index a93130243..aed4812ea 100644 --- a/tests/test_planning.py +++ b/tests/test_planning.py @@ -18,3 +18,17 @@ def test_action(): assert test_kb.ask(expr("Q(B, C)")) is not False assert not a.check_precond(test_kb, args) +def test_air_cargo(): + p = air_cargo() + assert p.goal_test() is False + solution =[expr("Load(C1 , P1, SFO)"), + expr("Fly(P1, SFO, JFK)"), + expr("Unload(C1, P1, JFK)"), + expr("Load(C2, P2, JFK)"), + expr("Fly(P2, JFK, SFO)"), + expr("Unload (C2, P2, SFO)")] + + for action in solution: + p.act(action) + + assert p.goal_test()