Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
2 changes: 1 addition & 1 deletion 2 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Here is a table of algorithms, the figure, name of the code in the book and in t
| 10.7 | Cake-Problem | `have_cake_and_eat_cake_too` |[`planning.py`][planning]|
| 10.9 | Graphplan | `GraphPlan` |[`planning.py`][planning]|
| 10.13 | Partial-Order-Planner | |
| 11.1 | Job-Shop-Problem-With-Resources | |
| 11.1 | Job-Shop-Problem-With-Resources | `job_shop_problem` |[`planning.py`][planning]|
| 11.5 | Hierarchical-Search | |
| 11.8 | Angelic-Search | |
| 11.10 | Doubles-tennis | `double_tennis_problem` |[`planning.py`][planning]|
Expand Down
213 changes: 213 additions & 0 deletions 213 planning.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,3 +574,216 @@ def goal_test(kb):
go = Action(expr("Go(actor, to)"), [precond_pos, precond_neg], [effect_add, effect_rem])

return PDLL(init, [hit, go], goal_test)


class HLA(Action):
"""
Define Actions for the real-world (that may be refined further), and satisfy resource
constraints.
"""
unique_group = 1

def __init__(self, action, precond=[None, None], effect=[None, None], duration=0,
consume={}, use={}):
"""
As opposed to actions, to define HLA, we have added constraints.
duration holds the amount of time required to execute the task
consumes holds a dictionary representing the resources the task consumes
uses holds a dictionary representing the resources the task uses
"""
super().__init__(action, precond, effect)
self.duration = duration
self.consumes = consume
self.uses = use
self.completed = False
# self.priority = -1 # must be assigned in relation to other HLAs
# self.job_group = -1 # must be assigned in relation to other HLAs

def do_action(self, job_order, available_resources, kb, args):
"""
An HLA based version of act - along with knowledge base updation, it handles
resource checks, and ensures the actions are executed in the correct order.
"""
# print(self.name)
if not self.has_usable_resource(available_resources):
raise Exception('Not enough usable resources to execute {}'.format(self.name))
if not self.has_consumable_resource(available_resources):
raise Exception('Not enough consumable resources to execute {}'.format(self.name))
if not self.inorder(job_order):
raise Exception("Can't execute {} - execute prerequisite actions first".
format(self.name))
super().act(kb, args) # update knowledge base
for resource in self.consumes: # remove consumed resources
available_resources[resource] -= self.consumes[resource]
self.completed = True # set the task status to complete

def has_consumable_resource(self, available_resources):
"""
Ensure there are enough consumable resources for this action to execute.
"""
for resource in self.consumes:
if available_resources.get(resource) is None:
return False
if available_resources[resource] < self.consumes[resource]:
return False
return True

def has_usable_resource(self, available_resources):
"""
Ensure there are enough usable resources for this action to execute.
"""
for resource in self.uses:
if available_resources.get(resource) is None:
return False
if available_resources[resource] < self.uses[resource]:
return False
return True

def inorder(self, job_order):
"""
Ensure that all the jobs that had to be executed before the current one have been
successfully executed.
"""
for jobs in job_order:
if self in jobs:
for job in jobs:
if job is self:
return True
if not job.completed:
return False
return True


class Problem(PDLL):
"""
Define real-world problems by aggregating resources as numerical quantities instead of
named entities.

This class is identical to PDLL, except that it overloads the act function to handle
resource and ordering conditions imposed by HLA as opposed to Action.
"""
def __init__(self, initial_state, actions, goal_test, jobs=None, resources={}):
super().__init__(initial_state, actions, goal_test)
self.jobs = jobs
self.resources = resources

def act(self, action):
"""
Performs the HLA given as argument.

Note that this is different from the superclass action - where the parameter was an
Expression. For real world problems, an Expr object isn't enough to capture all the
detail required for executing the action - resources, preconditions, etc need to be
checked for too.
"""
args = action.args
list_action = first(a for a in self.actions if a.name == action.name)
if list_action is None:
raise Exception("Action '{}' not found".format(action.name))
list_action.do_action(self.jobs, self.resources, self.kb, args)
# print(self.resources)


def job_shop_problem():
"""
[figure 11.1] JOB-SHOP-PROBLEM

A job-shop scheduling problem for assembling two cars,
with resource and ordering constraints.

Example:
>>> from planning import *
>>> p = job_shop_problem()
>>> p.goal_test()
False
>>> p.act(p.jobs[1][0])
>>> p.act(p.jobs[1][1])
>>> p.act(p.jobs[1][2])
>>> p.act(p.jobs[0][0])
>>> p.act(p.jobs[0][1])
>>> p.goal_test()
False
>>> p.act(p.jobs[0][2])
>>> p.goal_test()
True
>>>
"""
init = [expr('Car(C1)'),
expr('Car(C2)'),
expr('Wheels(W1)'),
expr('Wheels(W2)'),
expr('Engine(E2)'),
expr('Engine(E2)')]

def goal_test(kb):
# print(kb.clauses)
required = [expr('Has(C1, W1)'), expr('Has(C1, E1)'), expr('Inspected(C1)'),
expr('Has(C2, W2)'), expr('Has(C2, E2)'), expr('Inspected(C2)')]
for q in required:
# print(q)
# print(kb.ask(q))
if kb.ask(q) is False:
return False
return True

resources = {'EngineHoists': 1, 'WheelStations': 2, 'Inspectors': 2, 'LugNuts': 500}

# AddEngine1
precond_pos = []
precond_neg = [expr("Has(C1,E1)")]
effect_add = [expr("Has(C1,E1)")]
effect_rem = []
add_engine1 = HLA(expr("AddEngine1"),
[precond_pos, precond_neg], [effect_add, effect_rem],
duration=30, use={'EngineHoists': 1})

# AddEngine2
precond_pos = []
precond_neg = [expr("Has(C2,E2)")]
effect_add = [expr("Has(C2,E2)")]
effect_rem = []
add_engine2 = HLA(expr("AddEngine2"),
[precond_pos, precond_neg], [effect_add, effect_rem],
duration=60, use={'EngineHoists': 1})

# AddWheels1
precond_pos = []
precond_neg = [expr("Has(C1,W1)")]
effect_add = [expr("Has(C1,W1)")]
effect_rem = []
add_wheels1 = HLA(expr("AddWheels1"),
[precond_pos, precond_neg], [effect_add, effect_rem],
duration=30, consume={'LugNuts': 20}, use={'WheelStations': 1})

# AddWheels2
precond_pos = []
precond_neg = [expr("Has(C2,W2)")]
effect_add = [expr("Has(C2,W2)")]
effect_rem = []
add_wheels2 = HLA(expr("AddWheels2"),
[precond_pos, precond_neg], [effect_add, effect_rem],
duration=15, consume={'LugNuts': 20}, use={'WheelStations': 1})

# Inspect1
precond_pos = []
precond_neg = [expr("Inspected(C1)")]
effect_add = [expr("Inspected(C1)")]
effect_rem = []
inspect1 = HLA(expr("Inspect1"),
[precond_pos, precond_neg], [effect_add, effect_rem],
duration=10, use={'Inspectors': 1})

# Inspect2
precond_pos = []
precond_neg = [expr("Inspected(C2)")]
effect_add = [expr("Inspected(C2)")]
effect_rem = []
inspect2 = HLA(expr("Inspect2"),
[precond_pos, precond_neg], [effect_add, effect_rem],
duration=10, use={'Inspectors': 1})

job_group1 = [add_engine1, add_wheels1, inspect1]
job_group2 = [add_engine2, add_wheels2, inspect2]

return Problem(init, [add_engine1, add_engine2, add_wheels1, add_wheels2, inspect1, inspect2],
goal_test, [job_group1, job_group2], resources)
17 changes: 17 additions & 0 deletions 17 tests/test_planning.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,20 @@ def test_graph_call():
graph()

assert levels_size == len(graph.levels) - 1


def test_job_shop_problem():
p = job_shop_problem()
assert p.goal_test() is False

solution = [p.jobs[1][0],
p.jobs[0][0],
p.jobs[0][1],
p.jobs[0][2],
p.jobs[1][1],
p.jobs[1][2]]

for action in solution:
p.act(action)

assert p.goal_test()
Morty Proxy This is a proxified and sanitized view of the page, visit original site.