From 5a78850abe75e7e4dca34d29d4fbc96fea0d357f Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Tue, 15 May 2018 14:52:47 +0530 Subject: [PATCH 1/2] PDDLs and Actions can now be defined using Exprs as well as Strings --- planning.py | 55 ++++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/planning.py b/planning.py index d9a152e9a..f51258718 100644 --- a/planning.py +++ b/planning.py @@ -17,16 +17,18 @@ class PDDL: def __init__(self, init, goals, actions): self.init = self.convert(init) - self.goals = expr(goals) + self.goals = self.convert(goals) self.actions = actions - def convert(self, init): + def convert(self, clauses): """Converts strings into exprs""" + if not isinstance(clauses, Expr): + clauses = expr(clauses) try: - init = conjuncts(expr(init)) + clauses = conjuncts(clauses) except AttributeError: - init = expr(init) - return init + clauses = clauses + return clauses def goal_test(self): """Checks if the goals have been reached""" @@ -61,34 +63,35 @@ class Action: """ def __init__(self, action, precond, effect): - action = expr(action) + if isinstance(action, str): + action = expr(action) self.name = action.op self.args = action.args - self.precond, self.effect = self.convert(precond, effect) + self.precond = self.convert(precond) + self.effect = self.convert(effect) def __call__(self, kb, args): return self.act(kb, args) - def convert(self, precond, effect): + def convert(self, clauses): """Converts strings into Exprs""" - - precond = precond.replace('~', 'Not') - if len(precond) > 0: - precond = expr(precond) - effect = effect.replace('~', 'Not') - if len(effect) > 0: - effect = expr(effect) - - try: - precond = conjuncts(precond) - except AttributeError: - pass - try: - effect = conjuncts(effect) - except AttributeError: - pass - - return precond, effect + if isinstance(clauses, Expr): + clauses = conjuncts(clauses) + for i in range(len(clauses)): + if clauses[i].op == '~': + clauses[i] = expr('Not' + str(clauses[i].args[0])) + + elif isinstance(clauses, str): + clauses = clauses.replace('~', 'Not') + if len(clauses) > 0: + clauses = expr(clauses) + + try: + clauses = conjuncts(clauses) + except AttributeError: + pass + + return clauses def substitute(self, e, args): """Replaces variables in expression with their respective Propositional symbol""" From 5dd73240a29bc27f133740352ffff4fc4cffb7ba Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Tue, 15 May 2018 15:19:04 +0530 Subject: [PATCH 2/2] Minor refactors --- planning.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/planning.py b/planning.py index f51258718..e31cd2f87 100644 --- a/planning.py +++ b/planning.py @@ -23,7 +23,10 @@ def __init__(self, init, goals, actions): def convert(self, clauses): """Converts strings into exprs""" if not isinstance(clauses, Expr): - clauses = expr(clauses) + if len(clauses) > 0: + clauses = expr(clauses) + else: + clauses = [] try: clauses = conjuncts(clauses) except AttributeError: @@ -32,7 +35,7 @@ def convert(self, clauses): def goal_test(self): """Checks if the goals have been reached""" - return all(goal in self.init for goal in conjuncts(self.goals)) + return all(goal in self.init for goal in self.goals) def act(self, action): """ @@ -141,10 +144,10 @@ def act(self, kb, args): def air_cargo(): """Air cargo problem""" - return PDDL(init='At(C1, SFO) & At(C2, JFK) & At(P1, SFO) & At(P2, JFK) & Cargo(C1) & Cargo(C2) & Plane(P1) & Plane(P2) & Airport(SFO) & Airport(JFK)', + return PDDL(init='At(C1, SFO) & At(C2, JFK) & At(P1, SFO) & At(P2, JFK) & Cargo(C1) & Cargo(C2) & Plane(P1) & Plane(P2) & Airport(SFO) & Airport(JFK)', goals='At(C1, JFK) & At(C2, SFO)', actions=[Action('Load(c, p, a)', - precond='At(c, a) & At(p, a) & Cargo(c) & Plane(p) & Airport(a)', + precond='At(c, a) & At(p, a) & Cargo(c) & Plane(p) & Airport(a)', effect='In(c, p) & ~At(c, a)'), Action('Unload(c, p, a)', precond='In(c, p) & At(p, a) & Cargo(c) & Plane(p) & Airport(a)', @@ -210,6 +213,25 @@ def shopping_problem(): effect='At(y) & ~At(x)')]) +def socks_and_shoes(): + """Socks and shoes problem""" + + return PDDL(init='', + goals='RightShoeOn & LeftShoeOn', + actions=[Action('RightShoe', + precond='RightSockOn', + effect='RightShoeOn'), + Action('RightSock', + precond='', + effect='RightSockOn'), + Action('LeftShoe', + precond='LeftSockOn', + effect='LeftShoeOn'), + Action('LeftSock', + precond='', + effect='LeftSockOn')]) + + class Level: """ Contains the state of the planning problem @@ -562,6 +584,26 @@ def goal_test(kb, goals): return None +def socks_and_shoes_graphplan(): + pddl = socks_and_shoes() + graphplan = GraphPlan(pddl) + + def goal_test(kb, goals): + return all(kb.ask(q) is not False for q in goals) + + goals = expr('RightShoeOn, LeftShoeOn') + + while True: + if (goal_test(graphplan.graph.levels[-1].kb, goals) and graphplan.graph.non_mutex_goals(goals, -1)): + solution = graphplan.extract_solution(goals, -1) + if solution: + return solution + + graphplan.graph.expand_graph() + if len(graphplan.graph.levels) >= 2 and graphplan.check_leveloff(): + return None + + def linearize(solution): """Converts a level-ordered solution into a linear solution"""