Skip to content

Commit 7de2967

Browse files
kaivalyarnorvig
authored andcommitted
Planning notebook (aimacode#506)
* start planning notebook * reorder cell execution order * incorporating suggestions
1 parent ff8fc03 commit 7de2967

File tree

1 file changed

+312
-3
lines changed

1 file changed

+312
-3
lines changed

planning.ipynb

Lines changed: 312 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,325 @@
11
{
22
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"metadata": {
6+
"collapsed": true
7+
},
8+
"source": [
9+
"# Planning: planning.py; chapters 10-11"
10+
]
11+
},
12+
{
13+
"cell_type": "markdown",
14+
"metadata": {},
15+
"source": [
16+
"This notebook describes the [planning.py](https://github.com/aimacode/aima-python/blob/master/planning.py) module, which covers Chapters 10 (Classical Planning) and 11 (Planning and Acting in the Real World) of *[Artificial Intelligence: A Modern Approach](http://aima.cs.berkeley.edu)*. See the [intro notebook](https://github.com/aimacode/aima-python/blob/master/intro.ipynb) for instructions.\n",
17+
"\n",
18+
"We'll start by looking at `PDDL` and `Action` data types for defining problems and actions. Then, we will see how to use them by trying to plan a trip from *Sibiu* to *Bucharest* across the familiar map of Romania, from [search.ipynb](https://github.com/aimacode/aima-python/blob/master/search.ipynb). Finally, we will look at the implementation of the GraphPlan algorithm.\n",
19+
"\n",
20+
"The first step is to load the code:"
21+
]
22+
},
323
{
424
"cell_type": "code",
5-
"execution_count": null,
25+
"execution_count": 1,
26+
"metadata": {
27+
"collapsed": false
28+
},
29+
"outputs": [],
30+
"source": [
31+
"from planning import *"
32+
]
33+
},
34+
{
35+
"cell_type": "markdown",
36+
"metadata": {},
37+
"source": [
38+
"To be able to model a planning problem properly, it is essential to be able to represent an Action. Each action we model requires at least three things:\n",
39+
"* preconditions that the action must meet\n",
40+
"* the effects of executing the action\n",
41+
"* some expression that represents the action"
42+
]
43+
},
44+
{
45+
"cell_type": "markdown",
46+
"metadata": {},
47+
"source": [
48+
"Planning actions have been modelled using the `Action` class. Let's look at the source to see how the internal details of an action are implemented in Python."
49+
]
50+
},
51+
{
52+
"cell_type": "code",
53+
"execution_count": 2,
654
"metadata": {
755
"collapsed": false
856
},
957
"outputs": [],
1058
"source": [
11-
"import planning"
59+
"%psource Action"
1260
]
1361
},
62+
{
63+
"cell_type": "markdown",
64+
"metadata": {},
65+
"source": [
66+
"It is interesting to see the way preconditions and effects are represented here. Instead of just being a list of expressions each, they consist of two lists - `precond_pos` and `precond_neg`. This is to work around the fact that PDDL doesn't allow for negations. Thus, for each precondition, we maintain a seperate list of those preconditions that must hold true, and those whose negations must hold true. Similarly, instead of having a single list of expressions that are the result of executing an action, we have two. The first (`effect_add`) contains all the expressions that will evaluate to true if the action is executed, and the the second (`effect_neg`) contains all those expressions that would be false if the action is executed (ie. their negations would be true).\n",
67+
"\n",
68+
"The constructor parameters, however combine the two precondition lists into a single `precond` parameter, and the effect lists into a single `effect` parameter."
69+
]
70+
},
71+
{
72+
"cell_type": "markdown",
73+
"metadata": {},
74+
"source": [
75+
"The `PDDL` class is used to represent planning problems in this module. The following attributes are essential to be able to define a problem:\n",
76+
"* a goal test\n",
77+
"* an initial state\n",
78+
"* a set of viable actions that can be executed in the search space of the problem\n",
79+
"\n",
80+
"View the source to see how the Python code tries to realise these."
81+
]
82+
},
83+
{
84+
"cell_type": "code",
85+
"execution_count": 3,
86+
"metadata": {
87+
"collapsed": false
88+
},
89+
"outputs": [],
90+
"source": [
91+
"%psource PDDL"
92+
]
93+
},
94+
{
95+
"cell_type": "markdown",
96+
"metadata": {},
97+
"source": [
98+
"The `initial_state` attribute is a list of `Expr` expressions that forms the initial knowledge base for the problem. Next, `actions` contains a list of `Action` objects that may be executed in the search space of the problem. Lastly, we pass a `goal_test` function as a parameter - this typically takes a knowledge base as a parameter, and returns whether or not the goal has been reached."
99+
]
100+
},
101+
{
102+
"cell_type": "markdown",
103+
"metadata": {},
104+
"source": [
105+
"Now lets try to define a planning problem using these tools. Since we already know about the map of Romania, lets see if we can plan a trip across a simplified map of Romania.\n",
106+
"\n",
107+
"Here is our simplified map definition:"
108+
]
109+
},
110+
{
111+
"cell_type": "code",
112+
"execution_count": 4,
113+
"metadata": {
114+
"collapsed": false
115+
},
116+
"outputs": [],
117+
"source": [
118+
"from utils import *\n",
119+
"# this imports the required expr so we can create our knowledge base\n",
120+
"\n",
121+
"knowledge_base = [\n",
122+
" expr(\"Connected(Bucharest,Pitesti)\"),\n",
123+
" expr(\"Connected(Pitesti,Rimnicu)\"),\n",
124+
" expr(\"Connected(Rimnicu,Sibiu)\"),\n",
125+
" expr(\"Connected(Sibiu,Fagaras)\"),\n",
126+
" expr(\"Connected(Fagaras,Bucharest)\"),\n",
127+
" expr(\"Connected(Pitesti,Craiova)\"),\n",
128+
" expr(\"Connected(Craiova,Rimnicu)\")\n",
129+
" ]"
130+
]
131+
},
132+
{
133+
"cell_type": "markdown",
134+
"metadata": {},
135+
"source": [
136+
"Let us add some logic propositions to complete our knowledge about travelling around the map. These are the typical symmetry and transitivity properties of connections on a map. We can now be sure that our `knowledge_base` understands what it truly means for two locations to be connected in the sense usually meant by humans when we use the term.\n",
137+
"\n",
138+
"Let's also add our starting location - *Sibiu* to the map."
139+
]
140+
},
141+
{
142+
"cell_type": "code",
143+
"execution_count": 5,
144+
"metadata": {
145+
"collapsed": true
146+
},
147+
"outputs": [],
148+
"source": [
149+
"knowledge_base.extend([\n",
150+
" expr(\"Connected(x,y) ==> Connected(y,x)\"),\n",
151+
" expr(\"Connected(x,y) & Connected(y,z) ==> Connected(x,z)\"),\n",
152+
" expr(\"At(Sibiu)\")\n",
153+
" ])"
154+
]
155+
},
156+
{
157+
"cell_type": "markdown",
158+
"metadata": {},
159+
"source": [
160+
"We now have a complete knowledge base, which can be seen like this:"
161+
]
162+
},
163+
{
164+
"cell_type": "code",
165+
"execution_count": 6,
166+
"metadata": {
167+
"collapsed": false
168+
},
169+
"outputs": [
170+
{
171+
"data": {
172+
"text/plain": [
173+
"[Connected(Bucharest, Pitesti),\n",
174+
" Connected(Pitesti, Rimnicu),\n",
175+
" Connected(Rimnicu, Sibiu),\n",
176+
" Connected(Sibiu, Fagaras),\n",
177+
" Connected(Fagaras, Bucharest),\n",
178+
" Connected(Pitesti, Craiova),\n",
179+
" Connected(Craiova, Rimnicu),\n",
180+
" (Connected(x, y) ==> Connected(y, x)),\n",
181+
" ((Connected(x, y) & Connected(y, z)) ==> Connected(x, z)),\n",
182+
" At(Sibiu)]"
183+
]
184+
},
185+
"execution_count": 6,
186+
"metadata": {},
187+
"output_type": "execute_result"
188+
}
189+
],
190+
"source": [
191+
"knowledge_base"
192+
]
193+
},
194+
{
195+
"cell_type": "markdown",
196+
"metadata": {},
197+
"source": [
198+
"We now define possible actions to our problem. We know that we can drive between any connected places. But, as is evident from [this](https://en.wikipedia.org/wiki/List_of_airports_in_Romania) list of Romanian airports, we can also fly directly between Sibiu, Bucharest, and Craiova.\n",
199+
"\n",
200+
"We can define these flight actions like this:"
201+
]
202+
},
203+
{
204+
"cell_type": "code",
205+
"execution_count": 7,
206+
"metadata": {
207+
"collapsed": false
208+
},
209+
"outputs": [],
210+
"source": [
211+
"#Sibiu to Bucharest\n",
212+
"precond_pos = [expr('At(Sibiu)')]\n",
213+
"precond_neg = []\n",
214+
"effect_add = [expr('At(Bucharest)')]\n",
215+
"effect_rem = [expr('At(Sibiu)')]\n",
216+
"fly_s_b = Action(expr('Fly(Sibiu, Bucharest)'), [precond_pos, precond_neg], [effect_add, effect_rem])\n",
217+
"\n",
218+
"#Bucharest to Sibiu\n",
219+
"precond_pos = [expr('At(Bucharest)')]\n",
220+
"precond_neg = []\n",
221+
"effect_add = [expr('At(Sibiu)')]\n",
222+
"effect_rem = [expr('At(Bucharest)')]\n",
223+
"fly_b_s = Action(expr('Fly(Bucharest, Sibiu)'), [precond_pos, precond_neg], [effect_add, effect_rem])\n",
224+
"\n",
225+
"#Sibiu to Craiova\n",
226+
"precond_pos = [expr('At(Sibiu)')]\n",
227+
"precond_neg = []\n",
228+
"effect_add = [expr('At(Craiova)')]\n",
229+
"effect_rem = [expr('At(Sibiu)')]\n",
230+
"fly_s_c = Action(expr('Fly(Sibiu, Craiova)'), [precond_pos, precond_neg], [effect_add, effect_rem])\n",
231+
"\n",
232+
"#Craiova to Sibiu\n",
233+
"precond_pos = [expr('At(Craiova)')]\n",
234+
"precond_neg = []\n",
235+
"effect_add = [expr('At(Sibiu)')]\n",
236+
"effect_rem = [expr('At(Craiova)')]\n",
237+
"fly_c_s = Action(expr('Fly(Craiova, Sibiu)'), [precond_pos, precond_neg], [effect_add, effect_rem])\n",
238+
"\n",
239+
"#Bucharest to Craiova\n",
240+
"precond_pos = [expr('At(Bucharest)')]\n",
241+
"precond_neg = []\n",
242+
"effect_add = [expr('At(Craiova)')]\n",
243+
"effect_rem = [expr('At(Bucharest)')]\n",
244+
"fly_b_c = Action(expr('Fly(Bucharest, Craiova)'), [precond_pos, precond_neg], [effect_add, effect_rem])\n",
245+
"\n",
246+
"#Craiova to Bucharest\n",
247+
"precond_pos = [expr('At(Craiova)')]\n",
248+
"precond_neg = []\n",
249+
"effect_add = [expr('At(Bucharest)')]\n",
250+
"effect_rem = [expr('At(Craiova)')]\n",
251+
"fly_c_b = Action(expr('Fly(Craiova, Bucharest)'), [precond_pos, precond_neg], [effect_add, effect_rem])"
252+
]
253+
},
254+
{
255+
"cell_type": "markdown",
256+
"metadata": {},
257+
"source": [
258+
"And the drive actions like this."
259+
]
260+
},
261+
{
262+
"cell_type": "code",
263+
"execution_count": 8,
264+
"metadata": {
265+
"collapsed": true
266+
},
267+
"outputs": [],
268+
"source": [
269+
"#Drive\n",
270+
"precond_pos = [expr('At(x)')]\n",
271+
"precond_neg = []\n",
272+
"effect_add = [expr('At(y)')]\n",
273+
"effect_rem = [expr('At(x)')]\n",
274+
"drive = Action(expr('Drive(x, y)'), [precond_pos, precond_neg], [effect_add, effect_rem])"
275+
]
276+
},
277+
{
278+
"cell_type": "markdown",
279+
"metadata": {},
280+
"source": [
281+
"Finally, we can define a a function that will tell us when we have reached our destination, Bucharest."
282+
]
283+
},
284+
{
285+
"cell_type": "code",
286+
"execution_count": 9,
287+
"metadata": {
288+
"collapsed": true
289+
},
290+
"outputs": [],
291+
"source": [
292+
"def goal_test(kb):\n",
293+
" return kb.ask(expr(\"At(Bucharest)\"))"
294+
]
295+
},
296+
{
297+
"cell_type": "markdown",
298+
"metadata": {},
299+
"source": [
300+
"Thus, with all the components in place, we can define the planning problem."
301+
]
302+
},
303+
{
304+
"cell_type": "code",
305+
"execution_count": 10,
306+
"metadata": {
307+
"collapsed": false
308+
},
309+
"outputs": [],
310+
"source": [
311+
"prob = PDDL(knowledge_base, [fly_s_b, fly_b_s, fly_s_c, fly_c_s, fly_b_c, fly_c_b, drive], goal_test)"
312+
]
313+
},
314+
{
315+
"cell_type": "code",
316+
"execution_count": null,
317+
"metadata": {
318+
"collapsed": false
319+
},
320+
"outputs": [],
321+
"source": []
322+
},
14323
{
15324
"cell_type": "code",
16325
"execution_count": null,
@@ -37,7 +346,7 @@
37346
"name": "python",
38347
"nbconvert_exporter": "python",
39348
"pygments_lexer": "ipython3",
40-
"version": "3.5.1"
349+
"version": "3.4.3"
41350
}
42351
},
43352
"nbformat": 4,

0 commit comments

Comments
 (0)