# GraphPlan algorithm in Python

I am going to use a GraphPlan algorithm to solve classical planning problems in this tutorial. A solution to a planning problem is a sequence of actions that results in a goal state (a plan). Automated planning is important in the field of artificial intelligence.

An intelligent agent must be able to create plans to reach a goal from an initial state. A planning problem is described with a Planning Domain Definition Language (PDDL), a problem has an initial state, a goal state and possible actions. States and possible actions are added as sentences to a knowledge base. Each possible action has a function name, variables, preconditions and effects. A planning problem can be solved with forward (progression) search or backward (regression) search. Forward search starts from the initial state and backward search starts from the goal state.

A GraphPlan algorithm repeteadly adds a level to a planning graph, a solution can be found if all of the goals in a level not is mutual exclusive. The algorithm terminates if it finds a solution that reaches the goal state or if no more levels can be added to the planning graph. A planning graph is a directed graph that is organized into levels. Two goals is mutual exclusive if they take out each other, you can not both eat the cake and have the cake.

I am using code from aima-python in this tutorial (download package), these modules include all the necessary classes and functions for the GraphPlan algorithm in python.

## Problems and solutions

This example code includes three planning problems and their GraphPlan solutions. I had to split up the robot delivery problem in two parts as it was to difficult for the algorithm to get a letter from the mail office and bring it back to my office: `At(Robert, MyOffice) & At(Letter, MyOffice)`. It is also a problem to get the algorithm to drop the letter in my office in the second part (it finds a solution but not in the correct order).

``````# Import libraries
import aima.utils
import aima.planning

# Air cargo problem
def air_cargo_problem():

# Planning problem
problem = aima.planning.PlanningProblem(initial='At(C1, SFO) & At(C2, JFK) & At(P1, SFO) & At(P2, JFK)',
goals='At(C1, JFK) & At(C2, SFO)',
precond='At(c, a) & At(p, a)',
effect='In(c, p) & ~At(c, a)',
domain='Cargo(c) & Plane(p) & Airport(a)'),
precond='In(c, p) & At(p, a)',
effect='At(c, a) & ~In(c, p)',
domain='Cargo(c) & Plane(p) & Airport(a)'),
aima.planning.Action('Fly(p, f, to)',
precond='At(p, f)',
effect='At(p, to) & ~At(p, f)',
domain='Plane(p) & Airport(f) & Airport(to)')],
domain='Cargo(C1) & Cargo(C2) & Plane(P1) & Plane(P2) & Airport(SFO) & Airport(JFK)')

# Graph plan solution
solution = aima.planning.GraphPlan(problem).execute()
print("Air Cargo: {0}".format(aima.planning.linearize(solution)))
print()

# Drive in romania
def romania():

# Create a knowledge base
knowledge_base = [
aima.utils.expr("Connected(Bucharest,Pitesti)"),
aima.utils.expr("Connected(Pitesti,Rimnicu)"),
aima.utils.expr("Connected(Rimnicu,Sibiu)"),
aima.utils.expr("Connected(Sibiu,Fagaras)"),
aima.utils.expr("Connected(Fagaras,Bucharest)"),
aima.utils.expr("Connected(Pitesti,Craiova)"),
aima.utils.expr("Connected(Craiova,Rimnicu)")
]

# Add rules to knowledge base
knowledge_base.extend([
aima.utils.expr("Connected(x,y) ==> Connected(y,x)"),
aima.utils.expr("At(Sibiu)")
])

# Create a drive action
drive = aima.planning.Action('Drive(x, y)', precond='At(x) & Connected(x,y)', effect='At(y) & ~At(x)')

# Create a goal
goals = 'At(Bucharest)'

# Graph plan solution
problem = aima.planning.PlanningProblem(knowledge_base, goals, [drive])
solution = aima.planning.GraphPlan(problem).execute()
print("Romania: {0}".format(aima.planning.linearize(solution)))
print()

# Robot delivery problem
def robot_delivery_problem(initial_state, goals):

# Create a knowledge base
kb = [aima.utils.expr("Connected(MyOffice,Floor)"),
aima.utils.expr("Connected(Floor,MailOffice)"),
aima.utils.expr("Connected(Floor,Fikarum)"),
aima.utils.expr("Connected(Fikarum,MailOffice)"),
aima.utils.expr("Connected(Floor,MyOffice)"),
aima.utils.expr("Connected(MailOffice,Floor)"),
aima.utils.expr("Connected(Fikarum,Floor)"),
aima.utils.expr("Connected(MailOffice,Fikarum)")
]

# Add initial state to the knowledge base
kb.extend(initial_state)

# Create actions
actions = [aima.planning.Action("Pickup(b, p, r)",
precond="At(b, r) & At(p, r) & ~Has(b, p)",
effect="Has(b, p) & ~At(p, r)",
domain="Robot(b) & Packet(p) & Room(r)"),
aima.planning.Action("Drop(b, p, r)",
precond="At(b, r) & Has(b, p)",
effect="At(p, r) & ~Has(b, p)",
domain="Robot(b) & Packet(p) & Room(r)"),
aima.planning.Action("Move(b, f, t)",
precond="At(b, f) & Connected(f, t)",
effect="At(b, t) & ~At(b, f)",
domain="Robot(b) & Room(f) & Room(t)")
]

# Create domains
domains = [aima.utils.expr("Robot(Robert)"),
aima.utils.expr("Packet(Letter)"),
aima.utils.expr("Room(MyOffice)"),
aima.utils.expr("Room(Floor)"),
aima.utils.expr("Room(MailOffice)"),
aima.utils.expr("Room(Fikarum)")
]

# Create a planning problem
problem = aima.planning.PlanningProblem(kb, goals, actions, domains)

# Return the problem
return problem

# The main entry point for this module
def main():

# Air cargo problem
air_cargo_problem()

# Romania problem
romania()

# Robot delivery: first part
initial_state = [
aima.utils.expr("At(Robert, MyOffice)"),
aima.utils.expr("At(Letter, MailOffice)"),
aima.utils.expr("~Has(Robert, Letter)")
]
goals = [aima.utils.expr("At(Robert, MailOffice)"), aima.utils.expr("Has(Robert, Letter)")]
problem = robot_delivery_problem(initial_state, goals)
solution = aima.planning.GraphPlan(problem).execute()
print("Robot delivery, part 1: {0}".format(aima.planning.linearize(solution)))
print()

# Robot delivery: second part
initial_state = [
aima.utils.expr("At(Robert, MailOffice)"),
aima.utils.expr("Has(Robert, Letter)")
]
goals = [aima.utils.expr("At(Robert, MyOffice)")]
problem = robot_delivery_problem(initial_state, goals)
solution = aima.planning.GraphPlan(problem).execute()
print("Robot delivery, part 2: {0}".format(aima.planning.linearize(solution)))
print()

# Tell python to run main method
if __name__ == "__main__": main()``````
``````Air Cargo: [Load(C1, P1, SFO), Load(C2, P2, JFK), Fly(P1, SFO, JFK), Fly(P2, JFK, SFO), Unload(C1, P1, JFK), Unload(C2, P2, SFO)]

Romania: [Drive(Sibiu, Fagaras), Drive(Fagaras, Bucharest)]

Robot delivery, part 1: [Move(Robert, MyOffice, Floor), Move(Robert, Floor, MailOffice), Pickup(Robert, Letter, MailOffice)]

Robot delivery, part 2: [Move(Robert, MailOffice, Floor), Move(Robert, Floor, MyOffice)]``````
Tags:

## 2 thoughts on “GraphPlan algorithm in Python”

1. Hi!
Super work! I am using the same Graphplan library and am also struggling with getting the correct solution, i.e. the solution in correct order. Do you have any idea what the reason might be?

1. Hi,

thank you for your comment. I think that it might be some bug in the library (aima-python), maybe in the `linearize` method.