Denna handledning innehåller kod för att lösa planeringsproblem med en ängslig algoritm. Ängslig sökning är en bättre version av den hierarkiska sökalgoritmen eftersom den kan hitta en lösning snabbare. Ängslig sökning kommer bara att överväga åtgärder på hög nivå som kan nå målet, algoritmen föredrar åtgärder som har fler förfiningar jämfört med åtgärder med färre förfiningar.
En lösning på ett planeringsproblem är en sekvens av åtgärder som tar en agent från ett initialt tillstånd till ett måltillstånd. Ett planeringsproblem beskrivs med ett PDDL (Planning Domain Definition Language). Ett planeringsproblem har ett initialt tillstånd, ett måltillstånd och möjliga åtgärder att genomföra. Tillstånd och möjliga åtgärder läggs till som meningar i en kunskapsbas. Varje möjlig åtgärd har ett funktionsnamn, variabler, förutsättningar och effekter. Ett planeringsproblem kan lösas med framåtsökning (progression) eller bakåtsökning (regression). Framåtsökning startar från det ursprungliga tillståndet och bakåtsökning startar från måltillståndet.
Ängslig sökning behöver en beskrivning av en hierarki av handlingar, från högnivååtgärder (HLA) till primitiva åtgärder. Åtgärder på hög nivå (HLA) förfinas till mer specifika åtgärder, denna process fortsätter tills vi har primitiva åtgärder som inte har några förfiningar. Primitiva åtgärder kommer att inkluderas som steg i den slutliga lösningen på problemet. Ängslig sökning behöver en optimistisk beskrivning som möjligen överskattar den nåbara uppsättningen och en pessimistisk beskrivning som eventuellt underskattar den nåbara uppsättningen. Förmågan att avvisa åtgärder på hög nivå ger ängslig sökning en beräkningsfördel jämfört med hierarkisk sökning.
Jag använder kod från aima-python i den här handledningen (ladda ner paket), dessa moduler innehåller alla nödvändiga klasser och funktioner för en ängslig sökning i Python.
Problem och lösningar
Koden nedan inkluderar 3 problem som definieras som sökproblem. Utmatningen från en körning visas under koden. Varje problem behöver en hierarki av möjliga åtgärder, en optimistisk beskrivning och en pessimistisk beskrivning.
# Import libraries
import aima.utils
import aima.planning
# Get to SFO problem
def get_to_SFO1():
# Hierarchy of possible actions
library = {
'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)']] }
# Possible actions
go_SFO = aima.planning.HLA('Go(Home,SFO)', precond='At(Home)', effect='At(SFO) & ~At(Home)')
taxi_SFO = aima.planning.HLA('Taxi(Home,SFO)', precond='At(Home)', effect='At(SFO) & ~At(Home) & ~Have(Cash)')
drive_SFOLongTermParking = aima.planning.HLA('Drive(Home, SFOLongTermParking)', 'At(Home) & Have(Car)','At(SFOLongTermParking) & ~At(Home)' )
shuttle_SFO = aima.planning.HLA('Shuttle(SFOLongTermParking, SFO)', 'At(SFOLongTermParking)', 'At(SFO) & ~At(LongTermParking)')
# Create a problem
problem = aima.planning.RealWorldPlanningProblem('At(Home) & Have(Cash) & Have(Car)', 'At(SFO) & Have(Cash)', [go_SFO])
# Create an initial plan
optimistic = aima.planning.AngelicHLA('Go(Home, SFO)', precond = 'At(Home)', effect ='$+At(SFO) & $-At(Home)' )
pessimistic = aima.planning.AngelicHLA('Go(Home, SFO)', precond = 'At(Home)', effect ='$+At(SFO) & ~At(Home)' )
initial_plan = [aima.planning.AngelicNode(problem.initial, None, [optimistic], [pessimistic])]
# Angelic Search
plan = problem.angelic_search(library, initial_plan)
print('--- Get to SFO1 problem ---')
print (plan, '\n')
print ([x.__dict__ for x in plan])
print()
# Get to SFO problem
def get_to_SFO2():
# Hierarchy of possible actions
library = {
'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)'],
['At(SFOLongTermParking) & ~At(Home)'],
['At(SFO) & ~At(SFOLongTermParking)'],
['At(SFO) & ~At(Home)']]}
# Possible actions
go_home_sfo1 = aima.planning.HLA('Go(Home, SFO)', precond='At(Home) & Have(Car)', effect='At(SFO) & ~At(Home)')
go_home_sfo2 = aima.planning.HLA('Go(Home, SFO)', precond='At(Home)', effect='At(SFO) & ~At(Home)')
drive_home_sfoltp = aima.planning.HLA('Drive(Home, SFOLongTermParking)', precond='At(Home) & Have(Car)', effect='At(SFOLongTermParking) & ~At(Home)')
shuttle_sfoltp_sfo = aima.planning.HLA('Shuttle(SFOLongTermParking, SFO)', precond='At(SFOLongTermParking)', effect='At(SFO) & ~At(SFOLongTermParking)')
taxi_home_sfo = aima.planning.HLA('Taxi(Home, SFO)', precond='At(Home)', effect='At(SFO) & ~At(Home)')
actions = [go_home_sfo1, go_home_sfo2, drive_home_sfoltp, shuttle_sfoltp_sfo, taxi_home_sfo]
# Create a problem
problem = aima.planning.RealWorldPlanningProblem(initial='At(Home)', goals='At(SFO)', actions=actions)
# Create an initial plan
optimistic = aima.planning.AngelicHLA('Go(Home, SFO)', precond='At(Home)', effect='$+At(SFO) & $-At(Home)' )
pessimistic = aima.planning.AngelicHLA('Go(Home, SFO)', precond='At(Home)', effect='$+At(SFO) & ~At(Home)' )
initial_plan = [aima.planning.AngelicNode(problem.initial, None, [optimistic], [pessimistic])]
# Angelic Search
plan = problem.angelic_search(library, initial_plan)
print('--- Get to SFO2 problem ---')
print(plan, '\n')
print([x.__dict__ for x in plan])
print()
# Robot delivery problem
def robot_delivery():
# Hierarchy of possible actions (Important that steps have different names, see Go and Walk)
library = {
'HLA': [
'Go(MyOffice, MailOffice)',
'Go(MailOffice, MyOffice)',
'Walk(MyOffice, Floor)',
'Walk(MailOffice, Floor)',
'Walk(Floor, MailOffice)',
'Walk(Floor, MyOffice)',
'Pickup(Letter, MailOffice)',
'Drop(Letter, MyOffice)'
],
'steps': [
['Walk(MyOffice, Floor)', 'Walk(Floor, MailOffice)', 'Pickup(Letter, MailOffice)'],
['Walk(MailOffice, Floor)', 'Walk(Floor, MyOffice)', 'Drop(Letter, MyOffice)'],
[],
[],
[],
[],
[],
[]
],
'precond': [
['At(MyOffice) & ~Has(Letter)'],
['At(MailOffice) & Has(Letter)'],
['At(MyOffice)'],
['At(MailOffice)'],
['At(Floor)'],
['At(Floor)'],
['At(MailOffice) & LetterAt(MailOffice)'],
['At(MyOffice) & Has(Letter)']
],
'effect': [
['At(MailOffice) & ~At(MyOffice) & ~LetterAt(MailOffice)'],
['At(MyOffice) & ~At(MailOffice) & LetterAt(MyOffice)'],
['At(Floor) & ~At(MyOffice)'],
['At(Floor) & ~At(MailOffice)'],
['At(MailOffice) & ~At(Floor)'],
['At(MyOffice) & ~At(Floor)'],
['Has(Letter) & ~LetterAt(MailOffice)'],
['~Has(Letter) & LetterAt(MyOffice)']
]
}
# Possible actions
myoffice_to_mailoffice = aima.planning.HLA('Go(MyOffice, MailOffice)', precond='At(MyOffice) & ~Has(Letter)', effect='At(MailOffice) & ~At(MyOffice) & ~LetterAt(MailOffice)')
mailoffice_to_myoffice = aima.planning.HLA('Go(MailOffice, MyOffice)', precond='At(MailOffice) & Has(Letter)', effect='At(MyOffice) & ~At(MailOffice) & LetterAt(MyOffice)')
myoffice_to_floor = aima.planning.HLA('Walk(MyOffice, Floor)', precond='At(MyOffice)', effect='At(Floor) & ~At(MyOffice)')
mailoffice_to_floor = aima.planning.HLA('Walk(MailOffice, Floor)', precond='At(MailOffice)', effect='At(Floor) & ~At(MailOffice)')
floor_to_mailoffice = aima.planning.HLA('Walk(Floor, MailOffice)', precond='At(Floor)', effect='At(MailOffice) & ~At(Floor)')
floor_to_myoffice = aima.planning.HLA('Walk(Floor, MyOffice)', precond='At(Floor)', effect='At(MyOffice) & ~At(Floor)')
pickup_letter = aima.planning.HLA('Pickup(Letter, MailOffice)', precond='At(MailOffice) & LetterAt(MailOffice)', effect='Has(Letter) & ~LetterAt(MailOffice)')
drop_letter = aima.planning.HLA('Drop(Letter, MyOffice)', precond='At(MyOffice) & Has(Letter)', effect='~Has(Letter) & LetterAt(MyOffice)')
actions = [myoffice_to_mailoffice, mailoffice_to_myoffice, myoffice_to_floor, mailoffice_to_floor, floor_to_mailoffice, floor_to_myoffice, pickup_letter, drop_letter]
# Angelic Search (Part 1)
problem = aima.planning.RealWorldPlanningProblem(initial='At(MyOffice) & LetterAt(MailOffice) & ~Has(Letter)', goals='At(MailOffice)', actions=actions)
optimistic = aima.planning.AngelicHLA('Go(MyOffice, MailOffice)', precond='At(MyOffice)', effect ='$+At(MailOffice) & $-At(MyOffice)')
pessimistic = aima.planning.AngelicHLA('Go(MyOffice, MailOffice)', precond='At(MyOffice)', effect ='$+At(MailOffice) & ~At(MyOffice)')
initial_plan = [aima.planning.AngelicNode(problem.initial, None, [optimistic], [pessimistic])]
plan = problem.angelic_search(library, initial_plan)
print('--- Robot delivery part 1 ---')
print(plan, '\n')
print([x.__dict__ for x in plan])
print()
# Angelic Search (Part 2)
problem = aima.planning.RealWorldPlanningProblem(initial='At(MailOffice) & Has(Letter)', goals='At(MyOffice)', actions=actions)
optimistic = aima.planning.AngelicHLA('Go(MailOffice, MyOffice)', precond='At(MailOffice)', effect='$+At(MyOffice) & $-At(MailOffice)')
pessimistic = aima.planning.AngelicHLA('Go(MailOffice, MyOffice)', precond='At(MailOffice)', effect='$+At(MyOffice) & ~At(MailOffice)')
initial_plan = [aima.planning.AngelicNode(problem.initial, None, [optimistic], [pessimistic])]
plan = problem.angelic_search(library, initial_plan)
print('--- Robot delivery part 2 ---')
print(plan, '\n')
print([x.__dict__ for x in plan])
print()
# The main entry point for this module
def main():
# Get to SFO1 problem
get_to_SFO1()
# Get to SFO2 problem
get_to_SFO2()
# Robot delivery problem
robot_delivery()
# Tell python to run main method
if __name__ == "__main__": main()
--- Get to SFO1 problem ---
[Drive(Home, SFOLongTermParking), Shuttle(SFOLongTermParking, SFO)]
[{'name': 'Drive', 'args': (Home, SFOLongTermParking), 'precond': [At(Home), Have(Car)], 'effect': [At(SFOLongTermParking), NotAt(Home)], 'domain': None, 'duration': 0, 'consumes': {}, 'uses': {}, 'completed': False}, {'name': 'Shuttle', 'args': (SFOLongTermParking, SFO), 'precond': [At(SFOLongTermParking)], 'effect': [At(SFO), NotAt(LongTermParking)], 'domain': None, 'duration': 0, 'consumes': {}, 'uses': {}, 'completed': False}]
--- Get to SFO2 problem ---
[Taxi(Home, SFO)]
[{'name': 'Taxi', 'args': (Home, SFO), 'precond': [At(Home)], 'effect': [At(SFO), NotAt(Home)], 'domain': None, 'duration': 0, 'consumes': {}, 'uses': {}, 'completed': False}]
--- Robot delivery part 1 ---
[Walk(MyOffice, Floor), Walk(Floor, MailOffice), Pickup(Letter, MailOffice)]
[{'name': 'Walk', 'args': (MyOffice, Floor), 'precond': [At(MyOffice)], 'effect': [At(Floor), NotAt(MyOffice)], 'domain': None, 'duration': 0, 'consumes': {}, 'uses': {}, 'completed': False}, {'name': 'Walk', 'args': (Floor, MailOffice), 'precond': [At(Floor)], 'effect': [At(MailOffice), NotAt(Floor)], 'domain': None, 'duration': 0, 'consumes': {}, 'uses': {}, 'completed': False}, {'name': 'Pickup', 'args': (Letter, MailOffice), 'precond': [At(MailOffice), LetterAt(MailOffice)], 'effect': [Has(Letter), NotLetterAt(MailOffice)], 'domain': None, 'duration': 0, 'consumes': {}, 'uses': {}, 'completed': False}]
--- Robot delivery part 2 ---
[Walk(MailOffice, Floor), Walk(Floor, MyOffice), Drop(Letter, MyOffice)]
[{'name': 'Walk', 'args': (MailOffice, Floor), 'precond': [At(MailOffice)], 'effect': [At(Floor), NotAt(MailOffice)], 'domain': None, 'duration': 0, 'consumes': {}, 'uses': {}, 'completed': False}, {'name': 'Walk', 'args': (Floor, MyOffice), 'precond': [At(Floor)], 'effect': [At(MyOffice), NotAt(Floor)], 'domain': None, 'duration': 0, 'consumes': {}, 'uses': {}, 'completed': False}, {'name': 'Drop', 'args': (Letter, MyOffice), 'precond': [At(MyOffice), Has(Letter)], 'effect': [NotHas(Letter), LetterAt(MyOffice)], 'domain': None, 'duration': 0, 'consumes': {}, 'uses': {}, 'completed': False}]