-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexample_have_cake.py
241 lines (222 loc) · 9.99 KB
/
example_have_cake.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# Import from Logic the following:
#
# - Expr class - Expressions allowed to be represented as say E
# maths expressions using symbols
# with where E.op and E.args is its operator and arguments
# to form logical sentences.
# Represent as either `~(P & Q) |'==>'| (~P | ~Q)` or
# with expr shortcut `expr('~(P & Q) ==> (~P | ~Q)')`
#
# - PropKB class - Propositional Knowledge Bases used to
# represent a knowledge base (KB) of propositional local sentences.
# using its four methods
# - __init__(self, sentence=None) - create clauses field as
# list of all sentences in knowledge database where each
# sentence comprises just literals and ORs
# - tell(self, sentence) - adds a sentence to the KB by
# converting to Conjunctive Normal Form (CNF), extracting
# all clauses to clauses field
# - ask_generator(self, query) - returns all substitutions
# that make query True (i.e. returns {...} or False).
# - ask_if_true(self, query) - same as ask_generator
# but returns True or False
# - retract(self, sentence) - converts sentences to clauses
# and then removes all clauses from the knowledge base
from aimacode.logic import PropKB
# Import from Action the following:
#
# - Action class - Action Schema used to describe Actions using the
# Expr class including Preconditions and Effects
# where Variables are args using the
# Planning Planning Domain Definition Language (PDDL)
from aimacode.planning import Action
from aimacode.search import (
Node,
breadth_first_search,
astar_search,
depth_first_graph_search,
uniform_cost_search,
greedy_best_first_graph_search,
Problem,
)
from aimacode.utils import expr
from lp_utils import (
FluentState,
encode_state,
decode_state
)
# Import from PlanningGraph the following:
#
# - PgNode class - Planning Graph Nodes base class including:
#
# Properties:
# - Parent - set of parent nodes up a level
# - Child - set of child nodes down a level
# - Mutex - set of sibling nodes mutually exclusive with current node
#
# - PgNode_s class - Planning Graph "State" (Literal Fluent) Nodes:
# (inherits from PgNode)
#
# Properties:
# - Parent - set of parent nodes up at previous Action level (A-level)
# - Child - set of child nodes down at next Action level (A-Level)
# - Mutex - set of sibling State Nodes (S-Nodes) mutex with current node
#
# - PgNode_a class - Planning Graph "Action" (Type Nodes):
# (inherits from PgNode)
#
# Properties:
# - Parent - set of parent Precondition nodes up at previous State level (S-level)
# - Child - set of child Effect nodes down at next State level (S-level)
# - Mutex - set of sibling Action Nodes (A-Nodes) mutex with current node
#
# - PlanningGraph class - Planning Graph is built with alternating Action and State levels until the
# last two State levels contain the same literals
#
# Properties:
# - Problem - i.e. subclass of HaveCakeProblem
# - Serial Planning - Whether one Action may occur at a time
# - Fluent State - fluent states T/F (i.e. string in form 'TFTTFF')
# - Ground Actions - list of valid ground actions and noop actions
# - Noop Actions lists comprise both a positive no-op action
# (literal as positive precondition with literal expression added as output
# effect) and negative no-op action (literal as negative precondition with
# literal expression removed from output effect) for each literal expression that
# they pass through levels of planning graph.
# - State Levels - list of sets of PgNode_s that each represent an S-level
# - Action Levels - list of sets of PgNode_a that each represent an A-level
#
# Functions:
#
# - Noop Actions
# - Planning Graph - create
# - Action - add A-level to Planning Graph
# - State - add S-level (literal) to Planning Graph
# - Mutex - update A-level node mutex for siblings when
# - Serial Planning Graph
# - Action node pairs are non-persistence actions
# - Action node pairs have either Inconsistent Effects, Interference, Competing needs
from my_planning_graph import PlanningGraph
from run_search import run_search
import my_logging
from my_logging import *
my_logging.setup_log_level()
class HaveCakeProblem(Problem):
def __init__(self, initial: FluentState, goal: list):
self.state_map = initial.pos + initial.neg
Problem.__init__(self, encode_state(initial, self.state_map), goal=goal)
self.actions_list = self.get_actions()
# Returns list including Eat Action and Bake Action
def get_actions(self):
precond_pos = [expr("Have(Cake)")]
precond_neg = []
effect_add = [expr("Eaten(Cake)")]
effect_rem = [expr("Have(Cake)")]
eat_action = Action(expr("Eat(Cake)"),
[precond_pos, precond_neg],
[effect_add, effect_rem])
precond_pos = []
precond_neg = [expr("Have(Cake)")]
effect_add = [expr("Have(Cake)")]
effect_rem = []
bake_action = Action(expr("Bake(Cake)"),
[precond_pos, precond_neg],
[effect_add, effect_rem])
return [eat_action, bake_action]
def actions(self, state: str) -> list: # of Action
possible_actions = []
kb = PropKB()
kb.tell(decode_state(state, self.state_map).pos_sentence())
for action in self.actions_list:
is_possible = True
for clause in action.precond_pos:
if clause not in kb.clauses:
is_possible = False
for clause in action.precond_neg:
if clause in kb.clauses:
is_possible = False
if is_possible:
possible_actions.append(action)
return possible_actions
def result(self, state: str, action: Action):
new_state = FluentState([], [])
old_state = decode_state(state, self.state_map)
for fluent in old_state.pos:
if fluent not in action.effect_rem:
new_state.pos.append(fluent)
for fluent in action.effect_add:
if fluent not in new_state.pos:
new_state.pos.append(fluent)
for fluent in old_state.neg:
if fluent not in action.effect_add:
new_state.neg.append(fluent)
for fluent in action.effect_rem:
if fluent not in new_state.neg:
new_state.neg.append(fluent)
return encode_state(new_state, self.state_map)
def goal_test(self, state: str) -> bool:
kb = PropKB()
kb.tell(decode_state(state, self.state_map).pos_sentence())
for clause in self.goal:
if clause not in kb.clauses:
return False
return True
def h_1(self, node: Node):
# note that this is not a true heuristic
h_const = 1
return h_const
def h_pg_levelsum(self, node: Node):
# uses the planning graph level-sum heuristic calculated
# from this node to the goal
# requires implementation in PlanningGraph
pg = PlanningGraph(self, node.state)
pg_levelsum = pg.h_levelsum()
return pg_levelsum
def h_ignore_preconditions(self, node: Node):
count = 0
kb = PropKB()
kb.tell(decode_state(node.state, self.state_map).pos_sentence())
for clause in self.goal:
if clause not in kb.clauses:
count += 1
return count
def have_cake():
def get_init():
pos = [expr('Have(Cake)'),
]
neg = [expr('Eaten(Cake)'),
]
return FluentState(pos, neg)
def get_goal():
return [expr('Have(Cake)'),
expr('Eaten(Cake)'),
]
return HaveCakeProblem(get_init(), get_goal())
if __name__ == '__main__':
p = have_cake()
print("**** Have Cake example problem setup ****")
print("Initial state for this problem is {}".format(p.initial))
print("Actions for this domain are:")
for a in p.actions_list:
print(' {}{}'.format(a.name, a.args))
print("Fluents in this problem are:")
for f in p.state_map:
print(' {}'.format(f))
print("Goal requirement for this problem are:")
for g in p.goal:
print(' {}'.format(g))
print()
print("*** Breadth First Search")
run_search(p, breadth_first_search)
print("*** Depth First Search")
run_search(p, depth_first_graph_search)
print("*** Uniform Cost Search")
run_search(p, uniform_cost_search)
print("*** Greedy Best First Graph Search - null heuristic")
run_search(p, greedy_best_first_graph_search, parameter=p.h_1)
print("*** A-star null heuristic")
run_search(p, astar_search, p.h_1)
print("*** A-star ignore preconditions heuristic")
run_search(p, astar_search, p.h_ignore_preconditions)
print("*** A-star levelsum heuristic")
run_search(p, astar_search, p.h_pg_levelsum)