diff --git a/planning.ipynb b/planning.ipynb
index ca54bcde2..82be3da14 100644
--- a/planning.ipynb
+++ b/planning.ipynb
@@ -5900,7 +5900,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.6.1"
+ "version": "3.5.3"
}
},
"nbformat": 4,
diff --git a/planning.py b/planning.py
index 9492e2c8b..2913c2c2e 100644
--- a/planning.py
+++ b/planning.py
@@ -1253,7 +1253,7 @@ def act(self, action):
raise Exception("Action '{}' not found".format(action.name))
self.init = list_action.do_action(self.jobs, self.resources, self.init, args).clauses
- def refinements(hla, state, library): # TODO - refinements may be (multiple) HLA themselves ...
+ def refinements(hla, state, library): # refinements may be (multiple) HLA themselves ...
"""
state is a Problem, containing the current state kb
library is a dictionary containing details for every possible refinement. eg:
@@ -1274,40 +1274,32 @@ def refinements(hla, state, library): # TODO - refinements may be (multiple) HL
],
# empty refinements indicate a primitive action
'precond': [
- ['At(Home)', 'Have(Car)'],
+ ['At(Home) & Have(Car)'],
['At(Home)'],
- ['At(Home)', 'Have(Car)'],
+ ['At(Home) & Have(Car)'],
['At(SFOLongTermParking)'],
['At(Home)']
],
'effect': [
- ['At(SFO)', '~At(Home)'],
- ['At(SFO)', '~At(Home)'],
- ['At(SFOLongTermParking)', '~At(Home)'],
- ['At(SFO)', '~At(SFOLongTermParking)'],
- ['At(SFO)', '~At(Home)']
+ ['At(SFO) & ~At(Home)'],
+ ['At(SFO) & ~At(Home)'],
+ ['At(SFOLongTermParking) & ~At(Home)'],
+ ['At(SFO) & ~At(SFOLongTermParking)'],
+ ['At(SFO) & ~At(Home)']
]
}
"""
e = Expr(hla.name, hla.args)
indices = [i for i, x in enumerate(library['HLA']) if expr(x).op == hla.name]
for i in indices:
- # TODO multiple refinements
- precond = []
- for p in library['precond'][i]:
- if p[0] == '~':
- precond.append(expr('Not' + p[1:]))
- else:
- precond.append(expr(p))
- effect = []
- for e in library['effect'][i]:
- if e[0] == '~':
- effect.append(expr('Not' + e[1:]))
- else:
- effect.append(expr(e))
- action = HLA(library['steps'][i][0], precond, effect)
- if action.check_precond(state.init, action.args):
- yield action
+ actions = []
+ for j in range(len(library['steps'][i])):
+ # find the index of the step [j] of the HLA
+ index_step = [k for k,x in enumerate(library['HLA']) if x == library['steps'][i][j]][0]
+ precond = library['precond'][index_step][0] # preconditions of step [j]
+ effect = library['effect'][index_step][0] # effect of step [j]
+ actions.append(HLA(library['steps'][i][j], precond, effect))
+ yield actions
def hierarchical_search(problem, hierarchy):
"""
@@ -1338,13 +1330,164 @@ def hierarchical_search(problem, hierarchy):
print("...")
frontier.append(Node(plan.state, plan.parent, sequence))
- def result(problem, action):
+ def result(state, actions):
"""The outcome of applying an action to the current problem"""
- if action is not None:
- problem.act(action)
- return problem
- else:
- return problem
+ for a in actions:
+ if a.check_precond(state, a.args):
+ state = a(state, a.args).clauses
+ return state
+
+
+ def angelic_search(problem, hierarchy, initialPlan):
+ """
+ [Figure 11.8] A hierarchical planning algorithm that uses angelic semantics to identify and
+ commit to high-level plans that work while avoiding high-level plans that don’t.
+ The predicate MAKING-PROGRESS checks to make sure that we aren’t stuck in an infinite regression
+ of refinements.
+ At top level, call ANGELIC -SEARCH with [Act ] as the initialPlan .
+
+ initialPlan contains a sequence of HLA's with angelic semantics
+
+ The possible effects of an angelic HLA in initialPlan are :
+ ~ : effect remove
+ $+: effect possibly add
+ $-: effect possibly remove
+ $$: possibly add or remove
+ """
+ frontier = deque(initialPlan)
+ while True:
+ if not frontier:
+ return None
+ plan = frontier.popleft() # sequence of HLA/Angelic HLA's
+ opt_reachable_set = Problem.reach_opt(problem.init, plan)
+ pes_reachable_set = Problem.reach_pes(problem.init, plan)
+ if problem.intersects_goal(opt_reachable_set):
+ if Problem.is_primitive( plan, hierarchy ):
+ return ([x for x in plan.action])
+ guaranteed = problem.intersects_goal(pes_reachable_set)
+ if guaranteed and Problem.making_progress(plan, plan):
+ final_state = guaranteed[0] # any element of guaranteed
+ #print('decompose')
+ return Problem.decompose(hierarchy, problem, plan, final_state, pes_reachable_set)
+ (hla, index) = Problem.find_hla(plan, hierarchy) # there should be at least one HLA/Angelic_HLA, otherwise plan would be primitive.
+ prefix = plan.action[:index-1]
+ suffix = plan.action[index+1:]
+ outcome = Problem(Problem.result(problem.init, prefix), problem.goals , problem.actions )
+ for sequence in Problem.refinements(hla, outcome, hierarchy): # find refinements
+ frontier.append(Angelic_Node(outcome.init, plan, prefix + sequence+ suffix, prefix+sequence+suffix))
+
+
+ def intersects_goal(problem, reachable_set):
+ """
+ Find the intersection of the reachable states and the goal
+ """
+ return [y for x in list(reachable_set.keys()) for y in reachable_set[x] if all(goal in y for goal in problem.goals)]
+
+
+ def is_primitive(plan, library):
+ """
+ checks if the hla is primitive action
+ """
+ for hla in plan.action:
+ indices = [i for i, x in enumerate(library['HLA']) if expr(x).op == hla.name]
+ for i in indices:
+ if library["steps"][i]:
+ return False
+ return True
+
+
+
+ def reach_opt(init, plan):
+ """
+ Finds the optimistic reachable set of the sequence of actions in plan
+ """
+ reachable_set = {0: [init]}
+ optimistic_description = plan.action #list of angelic actions with optimistic description
+ return Problem.find_reachable_set(reachable_set, optimistic_description)
+
+
+ def reach_pes(init, plan):
+ """
+ Finds the pessimistic reachable set of the sequence of actions in plan
+ """
+ reachable_set = {0: [init]}
+ pessimistic_description = plan.action_pes # list of angelic actions with pessimistic description
+ return Problem.find_reachable_set(reachable_set, pessimistic_description)
+
+ def find_reachable_set(reachable_set, action_description):
+ """
+ Finds the reachable states of the action_description when applied in each state of reachable set.
+ """
+ for i in range(len(action_description)):
+ reachable_set[i+1]=[]
+ if type(action_description[i]) is Angelic_HLA:
+ possible_actions = action_description[i].angelic_action()
+ else:
+ possible_actions = action_description
+ for action in possible_actions:
+ for state in reachable_set[i]:
+ if action.check_precond(state , action.args) :
+ if action.effect[0] :
+ new_state = action(state, action.args).clauses
+ reachable_set[i+1].append(new_state)
+ else:
+ reachable_set[i+1].append(state)
+ return reachable_set
+
+ def find_hla(plan, hierarchy):
+ """
+ Finds the the first HLA action in plan.action, which is not primitive
+ and its corresponding index in plan.action
+ """
+ hla = None
+ index = len(plan.action)
+ for i in range(len(plan.action)): # find the first HLA in plan, that is not primitive
+ if not Problem.is_primitive(Node(plan.state, plan.parent, [plan.action[i]]), hierarchy):
+ hla = plan.action[i]
+ index = i
+ break
+ return (hla, index)
+
+ def making_progress(plan, initialPlan):
+ """
+ Not correct
+
+ Normally should from infinite regression of refinements
+
+ Only case covered: when plan contains one action (then there is no regression to be done)
+ """
+ if (len(plan.action)==1):
+ return False
+ return True
+
+ def decompose(hierarchy, s_0, plan, s_f, reachable_set):
+ solution = []
+ while plan.action_pes:
+ action = plan.action_pes.pop()
+ i = max(reachable_set.keys())
+ if (i==0):
+ return solution
+ s_i = Problem.find_previous_state(s_f, reachable_set,i, action)
+ problem = Problem(s_i, s_f , plan.action)
+ j=0
+ for x in Problem.angelic_search(problem, hierarchy, [Angelic_Node(s_i, Node(None), [action],[action])]):
+ solution.insert(j,x)
+ j+=1
+ s_f = s_i
+ return solution
+
+
+ def find_previous_state(s_f, reachable_set, i, action):
+ """
+ Given a final state s_f and an action finds a state s_i in reachable_set
+ such that when action is applied to state s_i returns s_f.
+ """
+ s_i = reachable_set[i-1][0]
+ for state in reachable_set[i-1]:
+ if s_f in [x for x in Problem.reach_pes(state, Angelic_Node(state, None, [action],[action]))[1]]:
+ s_i =state
+ break
+ return s_i
def job_shop_problem():
@@ -1419,19 +1562,177 @@ def go_to_sfo():
[]
],
'precond': [
- ['At(Home)', 'Have(Car)'],
+ ['At(Home) & Have(Car)'],
['At(Home)'],
- ['At(Home)', 'Have(Car)'],
+ ['At(Home) & Have(Car)'],
['At(SFOLongTermParking)'],
['At(Home)']
],
'effect': [
- ['At(SFO)', '~At(Home)'],
- ['At(SFO)', '~At(Home)'],
- ['At(SFOLongTermParking)', '~At(Home)'],
- ['At(SFO)', '~At(SFOLongTermParking)'],
- ['At(SFO)', '~At(Home)']
+ ['At(SFO) & ~At(Home)'],
+ ['At(SFO) & ~At(Home)'],
+ ['At(SFOLongTermParking) & ~At(Home)'],
+ ['At(SFO) & ~At(SFOLongTermParking)'],
+ ['At(SFO) & ~At(Home)']
]
}
return Problem(init='At(Home)', goals='At(SFO)', actions=actions), library
+
+
+class Angelic_HLA(HLA):
+ """
+ Define Actions for the real-world (that may be refined further), under angelic semantics
+ """
+
+ def __init__(self, action, precond , effect, duration =0, consume = None, use = None):
+ super().__init__(action, precond, effect, duration, consume, use)
+
+
+ def convert(self, clauses):
+ """
+ Converts strings into Exprs
+ An HLA with angelic semantics can achieve the effects of simple HLA's (add / remove a variable )
+ and furthermore can have following effects on the variables:
+ Possibly add variable ( $+ )
+ Possibly remove variable ( $- )
+ Possibly add or remove a variable ( $$ )
+
+ Overrides HLA.convert function
+ """
+ lib = {'~': 'Not',
+ '$+': 'PosYes',
+ '$-': 'PosNot',
+ '$$' : 'PosYesNot'}
+
+ if isinstance(clauses, Expr):
+ clauses = conjuncts(clauses)
+ for i in range(len(clauses)):
+ for ch in lib.keys():
+ if clauses[i].op == ch:
+ clauses[i] = expr( lib[ch] + str(clauses[i].args[0]))
+
+ elif isinstance(clauses, str):
+ for ch in lib.keys():
+ clauses = clauses.replace(ch, lib[ch])
+ if len(clauses) > 0:
+ clauses = expr(clauses)
+
+ try:
+ clauses = conjuncts(clauses)
+ except AttributeError:
+ pass
+
+ return clauses
+
+
+
+
+ def angelic_action(self):
+ """
+ Converts a high level action (HLA) with angelic semantics into all of its corresponding high level actions (HLA).
+ An HLA with angelic semantics can achieve the effects of simple HLA's (add / remove a variable)
+ and furthermore can have following effects for each variable:
+
+ Possibly add variable ( $+: 'PosYes' ) --> corresponds to two HLAs:
+ HLA_1: add variable
+ HLA_2: leave variable unchanged
+
+ Possibly remove variable ( $-: 'PosNot' ) --> corresponds to two HLAs:
+ HLA_1: remove variable
+ HLA_2: leave variable unchanged
+
+ Possibly add / remove a variable ( $$: 'PosYesNot' ) --> corresponds to three HLAs:
+ HLA_1: add variable
+ HLA_2: remove variable
+ HLA_3: leave variable unchanged
+
+
+ example: the angelic action with effects possibly add A and possibly add or remove B corresponds to the following 6 effects of HLAs:
+
+
+ '$+A & $$B': HLA_1: 'A & B' (add A and add B)
+ HLA_2: 'A & ~B' (add A and remove B)
+ HLA_3: 'A' (add A)
+ HLA_4: 'B' (add B)
+ HLA_5: '~B' (remove B)
+ HLA_6: ' ' (no effect)
+
+ """
+
+ effects=[[]]
+ for clause in self.effect:
+ (n,w) = Angelic_HLA.compute_parameters(clause, effects)
+ effects = effects*n # create n copies of effects
+ it=range(1)
+ if len(effects)!=0:
+ # split effects into n sublists (seperate n copies created in compute_parameters)
+ it = range(len(effects)//n)
+ for i in it:
+ if effects[i]:
+ if clause.args:
+ effects[i] = expr(str(effects[i]) + '&' + str(Expr(clause.op[w:],clause.args[0]))) # make changes in the ith part of effects
+ if n==3:
+ effects[i+len(effects)//3]= expr(str(effects[i+len(effects)//3]) + '&' + str(Expr(clause.op[6:],clause.args[0])))
+ else:
+ effects[i] = expr(str(effects[i]) + '&' + str(expr(clause.op[w:]))) # make changes in the ith part of effects
+ if n==3:
+ effects[i+len(effects)//3] = expr(str(effects[i+len(effects)//3]) + '&' + str(expr(clause.op[6:])))
+
+ else:
+ if clause.args:
+ effects[i] = Expr(clause.op[w:], clause.args[0]) # make changes in the ith part of effects
+ if n==3:
+ effects[i+len(effects)//3] = Expr(clause.op[6:], clause.args[0])
+
+ else:
+ effects[i] = expr(clause.op[w:]) # make changes in the ith part of effects
+ if n==3:
+ effects[i+len(effects)//3] = expr(clause.op[6:])
+ #print('effects', effects)
+
+ return [ HLA(Expr(self.name, self.args), self.precond, effects[i] ) for i in range(len(effects)) ]
+
+
+ def compute_parameters(clause, effects):
+ """
+ computes n,w
+
+ n = number of HLA effects that the anelic HLA corresponds to
+ w = length of representation of angelic HLA effect
+
+ n = 1, if effect is add
+ n = 1, if effect is remove
+ n = 2, if effect is possibly add
+ n = 2, if effect is possibly remove
+ n = 3, if effect is possibly add or remove
+
+ """
+ if clause.op[:9] == 'PosYesNot':
+ # possibly add/remove variable: three possible effects for the variable
+ n=3
+ w=9
+ elif clause.op[:6] == 'PosYes': # possibly add variable: two possible effects for the variable
+ n=2
+ w=6
+ elif clause.op[:6] == 'PosNot': # possibly remove variable: two possible effects for the variable
+ n=2
+ w=3 # We want to keep 'Not' from 'PosNot' when adding action
+ else: # variable or ~variable
+ n=1
+ w=0
+ return (n,w)
+
+
+class Angelic_Node(Node):
+ """
+ Extends the class Node.
+ self.action: contains the optimistic description of an angelic HLA
+ self.action_pes: contains the pessimistic description of an angelic HLA
+ """
+
+ def __init__(self, state, parent=None, action_opt=None, action_pes=None, path_cost=0):
+ super().__init__(state, parent, action_opt , path_cost)
+ self.action_pes = action_pes
+
+
diff --git a/planning_angelic_search.ipynb b/planning_angelic_search.ipynb
new file mode 100644
index 000000000..20400cd49
--- /dev/null
+++ b/planning_angelic_search.ipynb
@@ -0,0 +1,307 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Angelic Search \n",
+ "\n",
+ "Search using angelic semantics (is a hierarchical search), where the agent chooses the implementation of the HLA's.
\n",
+ "The algorithms input is: problem, hierarchy and initialPlan\n",
+ "- problem is of type Problem \n",
+ "- hierarchy is a dictionary consisting of all the actions. \n",
+ "- initialPlan is an approximate description(optimistic and pessimistic) of the agents choices for the implementation.
\n",
+ " It is a nested list, containing sequence a of actions with their optimistic and pessimistic\n",
+ " description "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 66,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from planning import * "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "The Angelic search algorithm consists of three parts. \n",
+ "- Search using angelic semantics\n",
+ "- Decompose\n",
+ "- a search in the space of refinements, in a similar way with hierarchical search\n",
+ "\n",
+ "### Searching using angelic semantics\n",
+ "- Find the reachable set (optimistic and pessimistic) of the sequence of angelic HLA in initialPlan\n",
+ " - If the optimistic reachable set doesn't intersect the goal, then there is no solution\n",
+ " - If the pessimistic reachable set intersects the goal, then we call decompose, in order to find the sequence of actions that lead us to the goal. \n",
+ " - If the optimistic reachable set intersects the goal, but the pessimistic doesn't we do some further refinements, in order to see if there is a sequence of actions that achieves the goal. \n",
+ " \n",
+ "### Search in space of refinements\n",
+ "- Create a search tree, that has root the action and children it's refinements\n",
+ "- Extend frontier by adding each refinement, so that we keep looping till we find all primitive actions\n",
+ "- If we achieve that we return the path of the solution (search tree), else there is no solution and we return None.\n",
+ "\n",
+ " \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "### Decompose \n",
+ "- Finds recursively the sequence of states and actions that lead us from initial state to goal.\n",
+ "- For each of the above actions we find their refinements,if they are not primitive, by calling the angelic_search function. \n",
+ " If there are not refinements return None\n",
+ " \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Example\n",
+ "\n",
+ "Suppose that somebody wants to get to the airport. \n",
+ "The possible ways to do so is either get a taxi, or drive to the airport.
\n",
+ "Those two actions have some preconditions and some effects. \n",
+ "If you get the taxi, you need to have cash, whereas if you drive you need to have a car.
\n",
+ "Thus we define the following hierarchy of possible actions.\n",
+ "\n",
+ "##### hierarchy"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 67,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "library = {\n",
+ " 'HLA': ['Go(Home,SFO)', 'Go(Home,SFO)', 'Drive(Home, SFOLongTermParking)', 'Shuttle(SFOLongTermParking, SFO)', 'Taxi(Home, SFO)'],\n",
+ " 'steps': [['Drive(Home, SFOLongTermParking)', 'Shuttle(SFOLongTermParking, SFO)'], ['Taxi(Home, SFO)'], [], [], []],\n",
+ " 'precond': [['At(Home) & Have(Car)'], ['At(Home)'], ['At(Home) & Have(Car)'], ['At(SFOLongTermParking)'], ['At(Home)']],\n",
+ " 'effect': [['At(SFO) & ~At(Home)'], ['At(SFO) & ~At(Home) & ~Have(Cash)'], ['At(SFOLongTermParking) & ~At(Home)'], ['At(SFO) & ~At(LongTermParking)'], ['At(SFO) & ~At(Home) & ~Have(Cash)']] }\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "the possible actions are the following:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 68,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "go_SFO = HLA('Go(Home,SFO)', precond='At(Home)', effect='At(SFO) & ~At(Home)')\n",
+ "taxi_SFO = HLA('Taxi(Home,SFO)', precond='At(Home)', effect='At(SFO) & ~At(Home) & ~Have(Cash)')\n",
+ "drive_SFOLongTermParking = HLA('Drive(Home, SFOLongTermParking)', 'At(Home) & Have(Car)','At(SFOLongTermParking) & ~At(Home)' )\n",
+ "shuttle_SFO = HLA('Shuttle(SFOLongTermParking, SFO)', 'At(SFOLongTermParking)', 'At(SFO) & ~At(LongTermParking)')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Suppose that (our preconditionds are that) we are Home and we have cash and car and our goal is to get to SFO and maintain our cash, and our possible actions are the above.
\n",
+ "##### Then our problem is: "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 69,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "prob = Problem('At(Home) & Have(Cash) & Have(Car)', 'At(SFO) & Have(Cash)', [go_SFO, taxi_SFO, drive_SFOLongTermParking,shuttle_SFO])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "An agent gives us some approximate information about the plan we will follow:
\n",
+ "(initialPlan is an Angelic Node, where: \n",
+ "- state is the initial state of the problem, \n",
+ "- parent is None \n",
+ "- action: is a list of actions (Angelic HLA's) with the optimistic estimators of effects and \n",
+ "- action_pes: is a list of actions (Angelic HLA's) with the pessimistic approximations of the effects\n",
+ "##### InitialPlan"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 70,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "angelic_opt_description = Angelic_HLA('Go(Home, SFO)', precond = 'At(Home)', effect ='$+At(SFO) & $-At(Home)' ) \n",
+ "angelic_pes_description = Angelic_HLA('Go(Home, SFO)', precond = 'At(Home)', effect ='$+At(SFO) & ~At(Home)' )\n",
+ "\n",
+ "initialPlan = [Angelic_Node(prob.init, None, [angelic_opt_description], [angelic_pes_description])] \n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We want to find the optimistic and pessimistic reachable set of initialPlan when applied to the problem:\n",
+ "##### Optimistic/Pessimistic reachable set"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 71,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[[At(Home), Have(Cash), Have(Car)], [Have(Cash), Have(Car), At(SFO), NotAt(Home)], [Have(Cash), Have(Car), NotAt(Home)], [At(Home), Have(Cash), Have(Car), At(SFO)], [At(Home), Have(Cash), Have(Car)]] \n",
+ "\n",
+ "[[At(Home), Have(Cash), Have(Car)], [Have(Cash), Have(Car), At(SFO), NotAt(Home)], [Have(Cash), Have(Car), NotAt(Home)]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "opt_reachable_set = Problem.reach_opt(prob.init, initialPlan[0])\n",
+ "pes_reachable_set = Problem.reach_pes(prob.init, initialPlan[0])\n",
+ "print([x for y in opt_reachable_set.keys() for x in opt_reachable_set[y]], '\\n')\n",
+ "print([x for y in pes_reachable_set.keys() for x in pes_reachable_set[y]])\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "##### Refinements"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 72,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[HLA(Drive(Home, SFOLongTermParking)), HLA(Shuttle(SFOLongTermParking, SFO))]\n",
+ "[{'consumes': {}, 'effect': [At(SFOLongTermParking), NotAt(Home)], 'uses': {}, 'completed': False, 'precond': [At(Home), Have(Car)], 'args': (Home, SFOLongTermParking), 'name': 'Drive', 'duration': 0}, {'consumes': {}, 'effect': [At(SFO), NotAt(LongTermParking)], 'uses': {}, 'completed': False, 'precond': [At(SFOLongTermParking)], 'args': (SFOLongTermParking, SFO), 'name': 'Shuttle', 'duration': 0}] \n",
+ "\n",
+ "[HLA(Taxi(Home, SFO))]\n",
+ "[{'consumes': {}, 'effect': [At(SFO), NotAt(Home), NotHave(Cash)], 'uses': {}, 'completed': False, 'precond': [At(Home)], 'args': (Home, SFO), 'name': 'Taxi', 'duration': 0}] \n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "for sequence in Problem.refinements(go_SFO, prob, library):\n",
+ " print (sequence)\n",
+ " print([x.__dict__ for x in sequence ], '\\n')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Run the angelic search\n",
+ "##### Top level call"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 73,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[HLA(Drive(Home, SFOLongTermParking)), HLA(Shuttle(SFOLongTermParking, SFO))] \n",
+ "\n",
+ "[{'consumes': {}, 'effect': [At(SFOLongTermParking), NotAt(Home)], 'uses': {}, 'completed': False, 'precond': [At(Home), Have(Car)], 'args': (Home, SFOLongTermParking), 'name': 'Drive', 'duration': 0}, {'consumes': {}, 'effect': [At(SFO), NotAt(LongTermParking)], 'uses': {}, 'completed': False, 'precond': [At(SFOLongTermParking)], 'args': (SFOLongTermParking, SFO), 'name': 'Shuttle', 'duration': 0}]\n"
+ ]
+ }
+ ],
+ "source": [
+ "plan= Problem.angelic_search(prob, library, initialPlan)\n",
+ "print (plan, '\\n')\n",
+ "print ([x.__dict__ for x in plan])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## Example 2"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 74,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "library_2 = {\n",
+ " 'HLA': ['Go(Home,SFO)', 'Go(Home,SFO)', 'Bus(Home, MetroStop)', 'Metro(MetroStop, SFO)' , 'Metro(MetroStop, SFO)', 'Metro1(MetroStop, SFO)', 'Metro2(MetroStop, SFO)' ,'Taxi(Home, SFO)'],\n",
+ " 'steps': [['Bus(Home, MetroStop)', 'Metro(MetroStop, SFO)'], ['Taxi(Home, SFO)'], [], ['Metro1(MetroStop, SFO)'], ['Metro2(MetroStop, SFO)'],[],[],[]],\n",
+ " 'precond': [['At(Home)'], ['At(Home)'], ['At(Home)'], ['At(MetroStop)'], ['At(MetroStop)'],['At(MetroStop)'], ['At(MetroStop)'] ,['At(Home) & Have(Cash)']],\n",
+ " 'effect': [['At(SFO) & ~At(Home)'], ['At(SFO) & ~At(Home) & ~Have(Cash)'], ['At(MetroStop) & ~At(Home)'], ['At(SFO) & ~At(MetroStop)'], ['At(SFO) & ~At(MetroStop)'], ['At(SFO) & ~At(MetroStop)'] , ['At(SFO) & ~At(MetroStop)'] ,['At(SFO) & ~At(Home) & ~Have(Cash)']] \n",
+ " }"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 75,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[HLA(Bus(Home, MetroStop)), HLA(Metro1(MetroStop, SFO))] \n",
+ "\n",
+ "[{'consumes': {}, 'effect': [At(MetroStop), NotAt(Home)], 'uses': {}, 'completed': False, 'precond': [At(Home)], 'args': (Home, MetroStop), 'name': 'Bus', 'duration': 0}, {'consumes': {}, 'effect': [At(SFO), NotAt(MetroStop)], 'uses': {}, 'completed': False, 'precond': [At(MetroStop)], 'args': (MetroStop, SFO), 'name': 'Metro1', 'duration': 0}]\n"
+ ]
+ }
+ ],
+ "source": [
+ "plan_2 = Problem.angelic_search(prob, library_2, initialPlan)\n",
+ "print(plan_2, '\\n')\n",
+ "print([x.__dict__ for x in plan_2])"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.5.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 1
+}
diff --git a/tests/test_planning.py b/tests/test_planning.py
index 5b6943ee3..08e59ae2e 100644
--- a/tests/test_planning.py
+++ b/tests/test_planning.py
@@ -281,19 +281,199 @@ def test_job_shop_problem():
assert p.goal_test()
+# hierarchies
+library_1 = {
+ 'HLA': ['Go(Home,SFO)', 'Go(Home,SFO)', 'Drive(Home, SFOLongTermParking)', 'Shuttle(SFOLongTermParking, SFO)', 'Taxi(Home, SFO)'],
+ 'steps': [['Drive(Home, SFOLongTermParking)', 'Shuttle(SFOLongTermParking, SFO)'], ['Taxi(Home, SFO)'], [], [], []],
+ 'precond': [['At(Home) & Have(Car)'], ['At(Home)'], ['At(Home) & Have(Car)'], ['At(SFOLongTermParking)'], ['At(Home)']],
+ 'effect': [['At(SFO) & ~At(Home)'], ['At(SFO) & ~At(Home) & ~Have(Cash)'], ['At(SFOLongTermParking) & ~At(Home)'], ['At(SFO) & ~At(LongTermParking)'], ['At(SFO) & ~At(Home) & ~Have(Cash)']] }
+
+
+library_2 = {
+ 'HLA': ['Go(Home,SFO)', 'Go(Home,SFO)', 'Bus(Home, MetroStop)', 'Metro(MetroStop, SFO)' , 'Metro(MetroStop, SFO)', 'Metro1(MetroStop, SFO)', 'Metro2(MetroStop, SFO)' ,'Taxi(Home, SFO)'],
+ 'steps': [['Bus(Home, MetroStop)', 'Metro(MetroStop, SFO)'], ['Taxi(Home, SFO)'], [], ['Metro1(MetroStop, SFO)'], ['Metro2(MetroStop, SFO)'],[],[],[]],
+ 'precond': [['At(Home)'], ['At(Home)'], ['At(Home)'], ['At(MetroStop)'], ['At(MetroStop)'],['At(MetroStop)'], ['At(MetroStop)'] ,['At(Home) & Have(Cash)']],
+ 'effect': [['At(SFO) & ~At(Home)'], ['At(SFO) & ~At(Home) & ~Have(Cash)'], ['At(MetroStop) & ~At(Home)'], ['At(SFO) & ~At(MetroStop)'], ['At(SFO) & ~At(MetroStop)'], ['At(SFO) & ~At(MetroStop)'] , ['At(SFO) & ~At(MetroStop)'] ,['At(SFO) & ~At(Home) & ~Have(Cash)']]
+ }
+
+
+# HLA's
+go_SFO = HLA('Go(Home,SFO)', precond='At(Home)', effect='At(SFO) & ~At(Home)')
+taxi_SFO = HLA('Taxi(Home,SFO)', precond='At(Home)', effect='At(SFO) & ~At(Home) & ~Have(Cash)')
+drive_SFOLongTermParking = HLA('Drive(Home, SFOLongTermParking)', 'At(Home) & Have(Car)','At(SFOLongTermParking) & ~At(Home)' )
+shuttle_SFO = HLA('Shuttle(SFOLongTermParking, SFO)', 'At(SFOLongTermParking)', 'At(SFO) & ~At(LongTermParking)')
+
+# Angelic HLA's
+angelic_opt_description = Angelic_HLA('Go(Home, SFO)', precond = 'At(Home)', effect ='$+At(SFO) & $-At(Home)' )
+angelic_pes_description = Angelic_HLA('Go(Home, SFO)', precond = 'At(Home)', effect ='$+At(SFO) & ~At(Home)' )
+
+# Angelic Nodes
+plan1 = Angelic_Node('At(Home)', None, [angelic_opt_description], [angelic_pes_description])
+plan2 = Angelic_Node('At(Home)', None, [taxi_SFO])
+plan3 = Angelic_Node('At(Home)', None, [drive_SFOLongTermParking, shuttle_SFO])
+
+
def test_refinements():
- library = {'HLA': ['Go(Home,SFO)','Taxi(Home, SFO)'],
- 'steps': [['Taxi(Home, SFO)'],[]],
- 'precond': [['At(Home)'],['At(Home)']],
- 'effect': [['At(SFO)'],['At(SFO)'],['~At(Home)'],['~At(Home)']]}
+ prob = Problem('At(Home) & Have(Car)', 'At(SFO)', [go_SFO])
+ result = [i for i in Problem.refinements(go_SFO, prob, library_1)]
+
+ assert(result[0][0].name == drive_SFOLongTermParking.name)
+ assert(result[0][0].args == drive_SFOLongTermParking.args)
+ assert(result[0][0].precond == drive_SFOLongTermParking.precond)
+ assert(result[0][0].effect == drive_SFOLongTermParking.effect)
+
+ assert(result[0][1].name == shuttle_SFO.name)
+ assert(result[0][1].args == shuttle_SFO.args)
+ assert(result[0][1].precond == shuttle_SFO.precond)
+ assert(result[0][1].effect == shuttle_SFO.effect)
+
+
+ assert(result[1][0].name == taxi_SFO.name)
+ assert(result[1][0].args == taxi_SFO.args)
+ assert(result[1][0].precond == taxi_SFO.precond)
+ assert(result[1][0].effect == taxi_SFO.effect)
+
+
+def test_convert_angelic_HLA():
+ """
+ Converts angelic HLA's into expressions that correspond to their actions
+ ~ : Delete (Not)
+ $+ : Possibly add (PosYes)
+ $-: Possibly delete (PosNo)
+ $$: Possibly add / delete (PosYesNo)
+ """
+ ang1 = Angelic_HLA('Test', precond = None, effect = '~A')
+ ang2 = Angelic_HLA('Test', precond = None, effect = '$+A')
+ ang3 = Angelic_HLA('Test', precond = None, effect = '$-A')
+ ang4 = Angelic_HLA('Test', precond = None, effect = '$$A')
+
+ assert(ang1.convert(ang1.effect) == [expr('NotA')])
+ assert(ang2.convert(ang2.effect) == [expr('PosYesA')])
+ assert(ang3.convert(ang3.effect) == [expr('PosNotA')])
+ assert(ang4.convert(ang4.effect) == [expr('PosYesNotA')])
+
+
+def test_is_primitive():
+ """
+ Tests if a plan is consisted out of primitive HLA's (angelic HLA's)
+ """
+ assert(not Problem.is_primitive(plan1, library_1))
+ assert(Problem.is_primitive(plan2, library_1))
+ assert(Problem.is_primitive(plan3, library_1))
+
+
+def test_angelic_action():
+ """
+ Finds the HLA actions that correspond to the HLA actions with angelic semantics
+
+ h1 : precondition positive: B _______ (add A) or (add A and remove B)
+ effect: add A and possibly remove B
+
+ h2 : precondition positive: A _______ (add A and add C) or (delete A and add C) or (add C) or (add A and delete C) or
+ effect: possibly add/remove A and possibly add/remove C (delete A and delete C) or (delete C) or (add A) or (delete A) or []
+
+ """
+ h_1 = Angelic_HLA( expr('h1'), 'B' , 'A & $-B')
+ h_2 = Angelic_HLA( expr('h2'), 'A', '$$A & $$C')
+ action_1 = Angelic_HLA.angelic_action(h_1)
+ action_2 = Angelic_HLA.angelic_action(h_2)
+
+ assert ([a.effect for a in action_1] == [ [expr('A'),expr('NotB')], [expr('A')]] )
+ assert ([a.effect for a in action_2] == [[expr('A') , expr('C')], [expr('NotA'), expr('C')], [expr('C')], [expr('A'), expr('NotC')], [expr('NotA'), expr('NotC')], [expr('NotC')], [expr('A')], [expr('NotA')], [None] ] )
+
+
+def test_optimistic_reachable_set():
+ """
+ Find optimistic reachable set given a problem initial state and a plan
+ """
+ h_1 = Angelic_HLA( 'h1', 'B' , '$+A & $-B ')
+ h_2 = Angelic_HLA( 'h2', 'A', '$$A & $$C')
+ f_1 = HLA('h1', 'B', 'A & ~B')
+ f_2 = HLA('h2', 'A', 'A & C')
+ problem = Problem('B', 'A', [f_1,f_2] )
+ plan = Angelic_Node(problem.init, None, [h_1,h_2], [h_1,h_2])
+ opt_reachable_set = Problem.reach_opt(problem.init, plan )
+ assert(opt_reachable_set[1] == [[expr('A'), expr('NotB')], [expr('NotB')],[expr('B'), expr('A')], [expr('B')]])
+ assert( problem.intersects_goal(opt_reachable_set) )
+
+
+def test_pesssimistic_reachable_set():
+ """
+ Find pessimistic reachable set given a problem initial state and a plan
+ """
+ h_1 = Angelic_HLA( 'h1', 'B' , '$+A & $-B ')
+ h_2 = Angelic_HLA( 'h2', 'A', '$$A & $$C')
+ f_1 = HLA('h1', 'B', 'A & ~B')
+ f_2 = HLA('h2', 'A', 'A & C')
+ problem = Problem('B', 'A', [f_1,f_2] )
+ plan = Angelic_Node(problem.init, None, [h_1,h_2], [h_1,h_2])
+ pes_reachable_set = Problem.reach_pes(problem.init, plan )
+ assert(pes_reachable_set[1] == [[expr('A'), expr('NotB')], [expr('NotB')],[expr('B'), expr('A')], [expr('B')]])
+ assert(problem.intersects_goal(pes_reachable_set))
+
+
+def test_find_reachable_set():
+ h_1 = Angelic_HLA( 'h1', 'B' , '$+A & $-B ')
+ f_1 = HLA('h1', 'B', 'A & ~B')
+ problem = Problem('B', 'A', [f_1] )
+ plan = Angelic_Node(problem.init, None, [h_1], [h_1])
+ reachable_set = {0: [problem.init]}
+ action_description = [h_1]
+
+ reachable_set = Problem.find_reachable_set(reachable_set, action_description)
+ assert(reachable_set[1] == [[expr('A'), expr('NotB')], [expr('NotB')],[expr('B'), expr('A')], [expr('B')]])
+
+
+
+def test_intersects_goal():
+ problem_1 = Problem('At(SFO)', 'At(SFO)', [])
+ problem_2 = Problem('At(Home) & Have(Cash) & Have(Car) ', 'At(SFO) & Have(Cash)', [])
+ reachable_set_1 = {0: [problem_1.init]}
+ reachable_set_2 = {0: [problem_2.init]}
+
+ assert(Problem.intersects_goal(problem_1, reachable_set_1))
+ assert(not Problem.intersects_goal(problem_2, reachable_set_2))
+
+
+def test_making_progress():
+ """
+ function not yet implemented
+ """
+ assert(True)
+
+def test_angelic_search():
+ """
+ Test angelic search for problem, hierarchy, initialPlan
+ """
+ #test_1
+ prob_1 = Problem('At(Home) & Have(Cash) & Have(Car) ', 'At(SFO) & Have(Cash)', [go_SFO, taxi_SFO, drive_SFOLongTermParking,shuttle_SFO])
+
+ angelic_opt_description = Angelic_HLA('Go(Home, SFO)', precond = 'At(Home)', effect ='$+At(SFO) & $-At(Home)' )
+ angelic_pes_description = Angelic_HLA('Go(Home, SFO)', precond = 'At(Home)', effect ='$+At(SFO) & ~At(Home)' )
+
+ initialPlan = [Angelic_Node(prob_1.init, None, [angelic_opt_description], [angelic_pes_description])]
+ solution = Problem.angelic_search(prob_1, library_1, initialPlan)
+
+ assert( len(solution) == 2 )
+
+ assert(solution[0].name == drive_SFOLongTermParking.name)
+ assert(solution[0].args == drive_SFOLongTermParking.args)
+
+ assert(solution[1].name == shuttle_SFO.name)
+ assert(solution[1].args == shuttle_SFO.args)
+
+ #test_2
+ solution_2 = Problem.angelic_search(prob_1, library_2, initialPlan)
+
+ assert( len(solution_2) == 2 )
+
+ assert(solution_2[0].name == 'Bus')
+ assert(solution_2[0].args == (expr('Home'), expr('MetroStop')))
+
+ assert(solution_2[1].name == 'Metro1')
+ assert(solution_2[1].args == (expr('MetroStop'), expr('SFO')))
+
- go_SFO = HLA('Go(Home,SFO)', precond='At(Home)', effect='At(SFO) & ~At(Home)')
- taxi_SFO = HLA('Go(Home,SFO)', precond='At(Home)', effect='At(SFO) & ~At(Home)')
- prob = Problem('At(Home)', 'At(SFO)', [go_SFO, taxi_SFO])
- result = [i for i in Problem.refinements(go_SFO, prob, library)]
- assert(len(result) == 1)
- assert(result[0].name == 'Taxi')
- assert(result[0].args == (expr('Home'), expr('SFO')))
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: