Automated Planning Domain-Configurable Planning: Hierarchical Task Networks Jonas Kvarnström Automated Planning Group Department of Computer and Information Science Linköping University jonas.kvarnstrom@liu.se – 2015 2 Recall the fundamental assumption that we only specify Structure: Objects and state variables Initial state and goal Physical preconditions and physical effects of actions We only specify what can be done The planner must decide what should be done But even the most sophisticated heuristics and domain analysis methods lack our intuitions and background knowledge… jonkv@ida Assumptions How can we make a planner take advantage of what we know? Planners taking advantage of additional knowledge can be called: ▪ Knowledge-rich ▪ Domain-configurable ▪ (Sometimes incorrectly called “domain-dependent”) 3 jonkv@ida Domain-Configurable Planners 4 jonkv@ida Comparisons (1) Higher performance More effort Domain-specific Must write an entire planner Can specialize the planner for very high performance Domain-configurable High-level (but sometimes complex) domain definition Can provide more information for high performance “Domain-independent” Provide minimal information about actions Less efficient 5 Larger problem classes can be handled efficiently Domain-configurable Easier to improve expressivity and efficiency Often practically useful for a larger set of domains! “Domain-independent” Should be useful for a wide range of domains Domain-specific Only works in a single domain jonkv@ida Comparisons (2) 6 Classical Planning vs. Hierarchical Task Networks: Objective is to achieve a goal: Objective is to perform a task: at(TimesSquare) { on(A,B), on(C,D) } … travel-to(TimesSquare) place-blocks-correctly … Find any sequence of actions that achieves the goal Use "templates" to incrementally refine the task until you reach primitive actions travel-to(TimesSquare) taxi-to(airport); fly-to(JFK); … Provides guidance but still requires planning jonkv@ida HTNs: Ideas A simple form of Hierarchical Task Network, as defined in the book A primitive task is an operator / action As in classical planning, what is primitive depends on the execution system and on how detailed you want your plans to be ▪ For you, fly(here,there) may be a primitive task ▪ For the pilot, it may be decomposed into many smaller steps stack(A,B) load(crane1, loc3, cont5, …) point(camera4, obj4) Dark green (in this presentation): "Done", no need to think further Can be ground or non-ground: stack(A,x) ▪ There is no separate terminology, as in operator/action 8 jonkv@ida Terminology 1: Primitive Task A non-primitive task: Cannot be directly executed Must be decomposed into 0 or more subtasks put-all-blocks-in-place() make-tower(A,B,C,D,E) move-stack-of-blocks(x, y) Should be decomposed to pickup, putdown, stack, unstack actions! Orange: There's a "problem" that we need to solve 9 jonkv@ida Terminology 2: Non-Primitive Task 10 A method specifies one way to decompose a non-primitive task Can have many methods per non-primitive task travel(from, to) travel(from, to) go-by-plane(from, to) foot-travel(from, to) Light green: A potential template for a solution The decomposition is a graph <N,E> (also known as network) Nodes in N correspond to subtasks to perform Edges in E correspond to ordering relations Task: travel(x,y) Decomposition: buy-ticket (airport(x), airport(y)) fly(airport(x), airport(y)) travel (x, airport(x)) travel (airport(y), y) jonkv@ida Terminology 3: Method 11 jonkv@ida Totally Ordered STNs In Totally Ordered Simple Task Networks (STN), each method must specify a sequence of subtasks Can still be modeled as a graph <N,E> buy-ticket (airport(x), airport(y)) travel (x, airport(x)) fly(airport(x), airport(y)) Alternatively: A sequence <t1,…,tk> <buy-ticket (airport(x), airport(y)), travel (x, airport(x)), fly(airport(x), airport(y)), travel (airport(y), y) > travel (airport(y), y) 12 jonkv@ida Totally Ordered STNs (2) We can illustrate the entire decomposition in this way (horizontal arrow sequence) The “travel” task has a method called “go-by-plane” buy-ticket (airport(x), airport(y)) travel (x, airport(x)) travel(x,y) Task go-by-plane(x,y) Method fly(airport(x), airport(y)) Each method can also have a precondition (exemplified later) travel (airport(y), y) 13 Since a non-primitive task can have many methods: You still need to search, to determine which method to use get-taxi-at(x) travel(x,y) Task taxi-travel(x,y) Method ride-taxi(x, y) pay-driver travel(x,y) Task foot-travel(x,y) Method walk(x, y) …and to determine parameters (shown later) Non-primitive subtasks Primitive subtask jonkv@ida Multiple Methods 14 jonkv@ida Composition Properties of these plans: Hierarchical Consist of tasks Based on graphs ≈ networks buy-ticket (airport(x), airport(y)) travel (x, airport(x)) travel(x,y) Task go-by-plane(x,y) Method fly (airport(x), airport(y)) foot-travel(x,y) taxi-travel(x,airport(x)) get-taxi-at(x) ride-taxi(x, airport(x)) travel (airport(y), y) pay-driver walk(x, y) 15 Let’s switch to Dock Worker Robots… c3 c1 p1 p2 loc1 jonkv@ida DWR 16 jonkv@ida Methods To move the topmost container from one pile to another: task: move-topmost-container(pile1, pile2) method: take-and-put(cont, crane, loc, pile1, pile2, c1, c2) precond: attached(pile1, loc), attached(pile2, loc), belong(crane, loc), top(cont, pile1), on(cont, c1), top(c2, pile2) The task has parameters given from above A method can have additional parameters, whose values are chosen by the planner – just as in classical planning! The precond adds constraints: crane must be some crane in the same loc as the piles, cont must be the topmost container of pile1, … Interpretation: If you are asked to move-topmost-container(pile1, pile2), check all possible values for cont, crane, loc, c1, c2 where the preconds are satisfied 17 To move the topmost container from one pile to another: task: move-topmost-container(pile1, pile2) method: take-and-put(cont, crane, loc, pile1, pile2, c1, c2) precond: attached(pile1, loc), attached(pile2, loc), belong(crane, loc), top(cont, pile1), on(cont, c1), top(c2, pile2) subtasks: <take(crane, loc, cont, c1, pile1), put(crane, loc, cont, c2, pile2)> move-topmost-container(pile1, pile2) take-and-put(…) take(…) put(…) cont c1 c2 pile1 pile2 jonkv@ida Methods (2) 18 Suppose want to move three entire stacks of containers But preserve the order of the containers! Call this task move-three-stacks() Initial state, with 3 locations, 3 piles to move Corresponding goal, all piles moved jonkv@ida Example 19 How do we do it? First move all containers to another pile, so they end up in inverse order Then move them to the real destination C3 C1 C3 C2 C2 C2 C1 C3 C1 Pile 1 Pile 2 Pile 3 jonkv@ida Example 20 A total-order method using this technique: Task: ▪ method: ▪ precond: ▪ subtasks: move-three-stacks() move-each-twice() ; no preconditions apart from the subtasks' ; move each stack twice: <move-stack(p1a, p1b), move-stack(p1b, p1c), move-stack(p2a, p2b), move-stack(p2b, p2c), move-stack(p3a, p3b), move-stack(p3b, p3c) > No parameters: Hardcoded which stacks to move Tuple: All subtasks are sequentially ordered jonkv@ida Example 2: move-each-twice 21 jonkv@ida Example 2b: move-each-twice Alternative: Task: ▪ method: ▪ precond: move-three-stacks() move-each-twice-choosing-interm(loc1, interm1, loc2, interm2, …) top(pallet, interm1), top(pallet, interm2), top(pallet, interm3), attached(…), Let the planner choose intermediate piles … (there might be several alternatives)! ▪ subtasks: ; move each stack twice: <move-stack(p1a, interm1), move-stack(interm1, p1c), move-stack(p2a, interm2), move-stack(interm2, p2c), move-stack(p3a, interm3), move-stack(interm3, p3c) > 22 How can we implement the task move-stack(pile1, pile2)? Must move all containers in a stack, but we don’t know how many… HTN planning allows recursion ▪ Move the topmost container (we know how to do that!) ▪ Then move the rest First attempt: ▪ task: ▪ method: ▪ precond: ▪ subtasks: move-stack(pile1, pile2) recursive-move(pile1, pile2) true <move-topmost-container(pile1, pile2), move-stack(pile1, pile2)> move-stack(pile1, pile2) recursive-move(pile1, pile2, …) move-topmost-container(pile1, pile2) move-stack(pile1, pile2) take-and-put(…) recursive-move(pile1, pile2, …) jonkv@ida Recursion (1) 23 But consider the BW and DWR "pile models"… BW A B C DWR clear(A) on(A,B) on(B,C) ontable(C) The bottom block is not "on" anything A B C top(A, pile1) on(A,B) on(B,C) on(C,pallet) The bottom block is "on" the pallet, a "special container" We don't want to move the pallet! jonkv@ida Recursion (2) To fix this: 24 Add two method params Task: move-stack(pile1, pile2) ▪ method: recursive-move(pile1, pile2, cont, x) ▪ precond: top(cont, pile1), on(cont, x) ▪ subtasks: <move-topmost-container(pile1, pile2), move-stack(pile1, pile2)> cont is on top of something (x), so cont can’t be the pallet jonkv@ida Recursion (3) 25 The planner can now create a structure like this: move-stack(pile1, pile2) recursive-move(pile1, pile2, …) move-topmost-container(pile1, pile2) move-stack(pile1, pile2) take-and-put(…) recursive-move(pile1, pile2, …) take(…) put(…) move-topmost-container(pile1, pile2) move-stack(pile1, pile2) take-and-put(…) recursive-move(pile1, pile2, …) take(…) put(…) move-topmost But when will the recursion end? move-stack jonkv@ida Recursion (4) 26 At some point, only the pallet will be left in the stack Then recursive-move will not be applicable But we specified that we must execute some form of move-stack! move-stack(pile1, pile2) recursive-move(pile1, pile2, …) move-topmost-container(pile1, pile2) move-stack(pile1, pile2) take-and-put(…) recursive-move(pile1, pile2, …) take(…) put(…) move-topmost-container(pile1, pile2) take-and-put(…) take(…) put(…) move-stack(pile1, pile2) pile1 is empty! No applicable methods… Planner would backtrack! jonkv@ida Recursion (5) 27 We need a method that terminates the recursion Task: ▪ method: ▪ precond: ▪ subtasks: Unique pallet object – not a variable! move-stack(p, q) already-moved(p, q) move-stack(pile1, pile2) top(pallet, p) <> recursive-move(pile1, pile2, …) move-topmost-container(pile1, pile2) move-stack(pile1, pile2) take-and-put(…) recursive-move(pile1, pile2, …) take(…) put(…) move-topmost-container(pile1, pile2) move-stack(pile1, pile2) take-and-put(…) already-moved(pile1,pile2) take(…) put(…) Method preconds satisfied Zero subtasks! jonkv@ida Recursion (6) An STN planning domain specifies: A set of operators used as primitive tasks A set of methods ▪ These indirectly specify the available non-primitive tasks 28 jonkv@ida Domains, Problems, Solutions General HTNs: Can have additional constraints to be enforced An STN problem instance specifies: An STN planning domain An initial state An initial task network, which should be ground (no variables) ▪ Total Order STN example: <move-three-stacks(); move-stack(p5a, p5c)> ▪ (Can also be represented as a graph with ordering constraints between task nodes) No goals to be achieved! We should perform tasks. A solution is any executable action sequence that can be generated from the initial task network by recursively applying methods to non-primitive tasks operators to primitive tasks The planner uses only the methods specified for a given task Will not try arbitrary actions… For this to be useful, you must have useful “recipes” for all tasks 29 jonkv@ida Domains, Problems, Solutions Added during the lecture… task deliver(package, dest) method: move1(package, packageloc, truckloc, dest) precond: at(truck, truckloc) & at(package, packageloc) & packageloc = truckloc subtasks: <load(package), driveto(dest), unload(package)> task deliver(package, dest) method: move1(package, packageloc, truckloc, dest) precond: at(truck, truckloc) & at(package, packageloc) & packageloc != truckloc subtasks: <driveto(packageloc), load(package), driveto(dest), unload(package)> jonkv@ida 31 task deliver(package, dest) method: move1(package, packageloc, truckloc, dest) precond: at(truck, truckloc) & at(package, packageloc) subtasks: <be-at(packageloc), load(package), driveto(dest), unload(package)> task be-at(loc) method: drive(loc) precond: !at(truck,loc) subtasks: <driveto(loc)> task be-at(loc) method: already-there precond: at(truck,loc) subtasks: <> jonkv@ida 32 Total Order Forward Decomposition: s0 34 move-stack(pile1, pile2) Task to perform, specified in the problem instance recursive-move(pile1, pile2, …) Check preconds in s0 first! jonkv@ida Total Order Forward Decomposition move-stack(pile1, pile2) mtc(pile1, pile2) s0 take-and-put(…) s2 Check preconds… recursive-move(pile1, pile2, …) mtc(pile1, pile2) s0 take(…) Check preconds… s1 put(…) Check preconds… s2 s2 s2 take-and-put(…) take(…) Like forward search, TFD generates actions in the same order in which they’ll be executed When we plan the next task, we know the current state of the world s3 put(…) s4 move-stack(pile1, pile2) s4 recursive-move(pile1, pile2, …) move-topmost move-stack 35 Primitive Tasks vs. Operators: mtc(pile1, pile2) We've said… ▪ A primitive task is an action Primitive task = action The book says… ▪ A primitive task is decomposed to a single action take-and-put(…) take(…) put(…) mtc(pile1, pile2) take-and-put(…) Primitive task take(…) put(…) Action take(…) put(…) Not an essential difference, as long as you are consistent! jonkv@ida Definitions TFD takes an STN problem instance: – the current state <t1,…,tk> – a list of tasks to be achieved in the specified order O – the available operators (with params, preconds, effects) M – the available methods (with params, preconds, subtasks) s Returns: A sequential plan ▪ Loses the hierarchical structure of the final plan ▪ Simplifies the presentation – but the structure could also be kept! TFD(s, <t1,…,tk>, O, M): ▪ // If we have no tasks left to do… if (k = 0) then return the empty plan 36 jonkv@ida Solving Total-Order STN Problems (1) 37 TFD(s, <t1,…,tk>, O, M): For simplicity: The case where all tasks are ground ▪ if (k = 0) then return the empty plan ▪ if (t1 is primitive) then // A primitive task is decomposed into a single action! // May be many to choose from (e.g. method has more params than task). actions ground instances of operators in O candidates { a | a ∈ actions and a is relevant for t1 and // Achieves the task a is applicable in s } if (candidates = ∅) return failure s t1 = take(…) a = take(…) t2 = put(…) Waiting in line to be decomposed in the next step jonkv@ida Solving Total-Order STN Problems (2) TFD(s, <t1,…,tk>, O, M): For simplicity: The case where all tasks are ground ▪ if (k = 0) then return the empty plan ▪ if (t1 is primitive) then // A primitive task is decomposed into a single action! // May be many to choose from (e.g. method has more params than task). actions ground instances of operators in O candidates {a| a ∈ actions and a is relevant for t1 and // Achieves the task a is applicable in s } if (candidates = ∅) return failure ▪ ▪ 38 nondeterministically choose any a ∈ candidates // Or use backtracking newstate γ(s,a) // Apply the action, find the new state remaining <t2,…,tk> π TFD(newstate, remaining, O, M) if (π = failure) return failure else return a.π // Concatenation: a + the rest of the plan s t1 = take(…) a = take(…) newstate t2 = put(…) remaining jonkv@ida Solving Total-Order STN Problems (3) 39 TFD(s, <t1,…,tk>, O, M): The case where tasks are nonground: move(container1,X) ▪ if (k = 0) then return the empty plan ▪ if (t1 is primitive) then // A primitive task is decomposed into a single action! // May be many to choose from (e.g. method has more params than task). actions ground instances of operators in O candidates { (a,σ) | a ∈ actions and σ is a substitution s.t. action a achieves σ(t1) and a is applicable in s } Basically, σ can specify variable if (candidates = ∅) return failure bindings for parameters of t1… (italics = variables) s candidates t1 = take(crane, loc1, cont2, cont, pile8) take(crane1, loc1, cont2, cont5, pile8) take(crane2, loc1, cont2, cont5, pile8) t2=put(crane, …) σ = { crane↦crane1, cont↦cont5 } σ = { crane↦crane2, cont↦cont5 } jonkv@ida Solving Total-Order STN Problems (4) 40 TFD(s, <t1,…,tk>, O, M): ▪ if (k = 0) then return the empty plan ▪ if (t1 is primitive) then actions ground instances of operators in O candidates { (a,σ) | a ∈ actions and σ is a substitution s.t. action a achieves σ(t1) and a is applicable in s } if (candidates = ∅) return failure ▪ ▪ nondeterministically choose any (a,σ) ∈ candidates// Or use backtracking newstate γ(s,a) // Apply the action, find the new state remaining σ(<t2,…,tk>) // Must have the same variable bindings! π TFD(newstate, remaining, O, M) // Handle the remaining tasks if (π = failure) return failure else return a.π σ(t2) = put(crane1, …) (italics = variables) s t1 = take(crane, loc1, cont2, cont, pile8) chosen: a = take(crane1, loc1, cont2, cont5, pile8) take(crane2, loc1, cont2, cont5, pile8) t2=put(crane, …) σ = { crane↦crane1, cont↦cont5 } { crane↦crane2, cont↦cont5 } jonkv@ida Solving Total-Order STN Problems (5) 41 TFD(s, <t1,…,tk>, O, M): ▪ if (k = 0) then return the empty plan ▪ if (t1 is primitive) then … ▪ else // t1 is travel(LiU, Resecentrum), for example // A non-primitive task is decomposed into a new task list. // May have many methods to choose from: taxi-travel, bus-travel, walk, … ground ground instances of methods in M As before, candidates { (m,σ) | m ∈ ground and but σ is a substitution s.t. task(m) = σ(t1) and methods m is applicable in s } // Methods have preconds! instead of if (candidates = ∅) return failure actions nondeterministically choose any (m,σ) ∈ active // Or use backtracking travel(x,y) taxi-travel(x,y) get-taxi-at(x) ride-taxi(x, y) pay-driver jonkv@ida Solving Total-Order STN Problems (6) 42 TFD(s, <t1,…,tk>, O, M): ▪ if (k = 0) then return the empty plan ▪ if (t1 is primitive) then … ▪ else // t1 is travel(LiU, Resecentrum), for example // A non-primitive task is decomposed into a new task list. // May have many methods to choose from: taxi-travel, bus-travel, walk, … ground ground instances of methods in M candidates { (m,σ) | m ∈ ground and σ is a substitution s.t. task(m) = σ(t1) and m is applicable in s } // Methods have preconds! if (candidates = ∅) return failure // Or use backtracking nondeterministically choose any (m,σ) ∈ active ▪ Replace the task by its subtasks // No actions are applied here, so no new state! remaining subtasks(m) . σ(<t2,…,tk>) // Prepend new list! π TFD(s, remaining, O, M) if (π = failure) return failure In TFD else return π the "origin" of a task is discarded: travel(x,y) No longer needed, taxi-travel(x,y) only the subtasks are relevant get-taxi-at(x) ride-taxi(x, y) pay-driver jonkv@ida Solving Total-Order STN Problems (7) 44 TFD requires totally ordered methods Can’t interleave subtasks of different tasks Suppose we want to fetch one object somewhere, then return to where we are now Task: ▪ method: ▪ precond: ▪ subtasks: fetch(obj) get(obj, mypos, objpos) robotat(mypos) & at(obj, objpos) <travel(mypos, objpos), pickup(obj), travel(objpos, mypos)> Task: travel(x, y) ▪ method: walk(x, y) ▪ method: stayat(x) I’m at A, the thing to fetch is at B fetch(p) get(p, a, b) travel(a,b) pickup(p) travel(b,a) jonkv@ida Limitation of Ordered-Task Planning 45 Suppose we want to fetch two objects somewhere, and return (Simplified example – consider “fetching all the objects we need”) One idea: Just “fetch” each object in sequence Task: ▪ method: ▪ precond: ▪ subtasks: fetch-both(obj1, obj2) get-both(obj1, obj2, mypos, objpos1, objpos2) – <fetch(obj1, mypos, objpos1), fetch(obj2, mypos, objpos2)> I’m at A, both objects are at B fetch-both(p, q) get-both(p,q) travel(a,b) fetch(p) fetch(q) get(p) get(q) pickup(p) travel(b,a) Have to start with the first Fetch… travel(a,b) pickup(q) travel(b,a) I’m back at A and have to walk again! jonkv@ida Limitation of Ordered-Task Planning To generate more efficient plans using total-order STNs: Use a different domain model! Task: ▪ method: ▪ precond: ▪ subtasks: fetch-both(obj1, obj2) Task: ▪ method: ▪ precond: ▪ subtasks: fetch-both(obj1, obj2) get-both(obj1, obj2, mypos, objpos1, objpos2) objpos1 != objpos2 & at(obj1, objpos1) & at(obj2, objpos2) <travel(mypos, objpos1), pickup(obj1), travel(objpos1, objpos2), pickup(obj2), travel(objpos2, mypos)> get-both-in-same-place(obj1, obj2, mypos, objpos) robotat(mypos) & at(obj1, objpos) & at(obj2, objpos) <travel(mypos, objpos), pickup(obj1), pickup(obj2), travel(objpos, mypos)> Or: load-all; drive-truck; unload-all 46 jonkv@ida Alternative Methods 48 jonkv@ida Partially Ordered Methods Partially ordered method: The subtasks are a partially ordered set {t1, …, tk} – a network travel(x,y) go-by-plane(x,y) No horizontal arrow ordering all tasks buy-ticket (a(x), a(y)) travel (x, a(x)) fly (a(x), a(y)) travel (a(y), y) method go-by-plane(x,y) task: travel(x,y) precond: long-distance(x,y) network: u1=buy-ticket(a(x),a(y)), u2= travel(x,a(x)), u3= fly(a(x), a(y)) u4= travel(a(y),y), Precedence: u1 before u3, {(u1,u3), (u2,u3), (u3 ,u4)} etc. 49 With partially ordered methods, subtasks can be interleaved fetch-both(p, q) get-both(p,q) travel(a,b) walk(a,b) pickup(p) fetch(p) fetch(q) get(p) get(q) travel(b,b) stay-at(b) pickup(q) travel(b,a) travel(a,a) walk(b,a) stay-at(a) Requires a more complicated planning algorithm: PFD SHOP2: implementation of PFD-like algorithm + generalizations jonkv@ida Partially Ordered Methods 50 Partial-order formulation of move-each-twice: Each stack is moved to the temp pile task: move-all-stacks() before it is moved to its final pile ▪ method: move-each-twice() Otherwise, no ordering constraints precond: ; no preconditions network: u1 = move-stack(p1a, p1b), u2 = move-stack(p1b, p1c), u3 = move-stack(p2a, p2b), u4 = move-stack(p2b, p2c), u5 = move-stack(p3a, p3b), u6 = move-stack(p3b, p3c), {(u1, u2), (u3, u4), (u5, u6)} Old total-order formulation: task: move-all-stacks() ▪ method: move-each-twice() precond: ; no preconditions subtasks: < move-stack(p1a, p1b), move-stack(p1b, p1c), move-stack(p2a, p2b), move-stack(p2b, p2c), move-stack(p3a, p3b), move-stack(p3b, p3c) > jonkv@ida Partial-Order and Total-Order PFD takes four inputs: s w O M – the current state – a partial order of tasks to be achieved – the available operators – the available methods PFD(s, w, O, M): ▪ // If we have no tasks left to do… if (w = emptyset) then return the empty plan 51 jonkv@ida Solving Partial-Order STN Problems (1) 52 jonkv@ida Solving Partial-Order STN Problems (2) PFD(s, w, O, M): ▪ if (w = emptyset) then return the empty plan ▪ nondeterministically choose a task u in w A task that could be first – not that has no predecessors necessarily a unique ”first task”! ▪ if (u is primitive) then actions ground instances of operators in O candidates { (a,σ) | a ∈ actions and σ is a substitution s.t. action a achieves σ(t1) and a is applicable in s } if (candidates = ∅) return failure ▪ nondeterministically choose any (a,σ) ∈ candidates newstate γ(s,a) // Apply the action, find the new state remaining σ(w – {u}) // Must have the same variable bindings! π PFD(newstate, remaining, O, M) // Handle the remaining tasks if (π = failure) return failure else return a.π 53 PFD(s, w, O, M): ▪ if (w = emptyset) then return the empty plan ▪ if (u is primitive) then … ▪ else // u is travel(LiU, Resecentrum), for example ground ground instances of methods in M candidates { (m,σ) | m ∈ ground and σ is a substitution s.t. task(m) = σ(t1) and m is applicable in s } // Methods have preconds! if (candidates = ∅) return failure nondeterministically choose any (m,σ) ∈ active // Or use backtracking ▪ // No actions are applied here! remaining δ(w, u, m, σ) π PFD(s, remaining, O, M) if (π = failure) return failure else return π Replacing the task by its subtasks is more complicated here! travel(x,y) taxi-travel(x,y) get-taxi-at(x) ride-taxi(x, y) pay-driver jonkv@ida Solving Partial-Order STN Problems (3) 54 What does δ(w, u, m, σ) mean? Suppose we started with a network w teach() This task has a method: teach() containing a single task: teach-by-lecture() prepare-lecture() travel (home, liu) do-lecture() Only one task in w applying the method is easy – leaves this network: remaining = δ(w, u, m, σ) prepare-lecture() travel (home, liu) do-lecture() jonkv@ida The Delta Function (1) 55 Now we recurse: prepare-lecture() w= travel (home, liu) do-lecture() travel (home, liu) do-lecture() Two tasks could possibly be first: u is one of… We pick one: u = travel (home, liu) prepare-lecture() jonkv@ida The Delta Function (2) 56 u = task to decompose We have: w= prepare-lecture() Method chosen for this task: travel (home, liu) do-lecture() travel(x,y) bus-travel(x,y) buy-ticket (x, y) travel (x, b(x)) fly (b(x), b(y)) travel (b(y), y) jonkv@ida The Delta Function (3) 57 First, we replace the selected task with its expansion Applying the substitution prepare-lecture() travel (home, liu) do-lecture() bus-travel(x,y) buy-ticket (home, liu) travel (home, b(home)) go(b(home), b(liu)) travel (b(liu), liu) Now the old and new tasks need to be connected – and not by placing all new tasks before all existing ones in w! jonkv@ida The Delta Function (4) 58 Every ordering constraint for the decomposed task now applies to its subtasks Must travel before do-lecture prepare-lecture() travel (home, liu) do-lecture() So we have: prepare-lecture() buy-ticket (home, liu) travel (home, b(home)) do-lecture() go(b(home), b(liu)) travel (b(liu), liu) jonkv@ida The Delta Function (5) 59 And: The method itself can have preconditions We have tested the preconditions, and they hold We must make sure they still hold when the first subtask is executed prepare-lecture() do-lecture() bus-travel buy-ticket (home, liu) travel (home, b(home)) go(b(home), b(liu)) travel (b(liu), liu) Must do u’s first subtask before the first subtask of every ti ≠ u The first subtask of bus-travel before the first subtask of prepare-lecture jonkv@ida The Delta Function (6) 60 Which subtask of bus-travel is first? Partially ordered several alternatives! So δ creates one alternative for each possible “first” subtask of u ▪ Here, buy-ticket or travel (x, b(x)) ▪ Then we nondeterministically choose between these alternatives prepare-lecture() buy-ticket (home, liu) travel (home, b(home)) go(b(home), b(liu)) prepare-lecture() buy-ticket (home, liu) travel (home, b(home)) go(b(home), b(liu)) do-lecture() travel (b(liu), liu) do-lecture() travel (b(liu), liu) jonkv@ida The Delta Function (7) Note that only methods are partially ordered The problem specification does not have to define the exact execution order in advance The final plan is totally ordered! The planner chooses an order 61 jonkv@ida Partially Ordered Methods Any classical problem Polynomial-time transformation 63 Corresponding STN problem For some STN problems, there exists no classical problem with the same set of solutions! Even Simple Task Networks are strictly more expressive than classical planning jonkv@ida Comparison to Classical Planning 64 Artificial example: Two primitive tasks, a and b Two STN methods: a aNbN aNbN continue() terminate() aNbN b s0 = {} Initial task: aNbN a b jonkv@ida Comparison to Classical Planning 65 Possible solutions: {anbn | n > 0} No classical problem has this set of solutions! ▪ Corresponds to a finite-state automaton, which cannot recognize {anbn | n > 0} ▪ STNs can even express undecidable problems aNbN aNbN continue() a aNbN continue() a aNbN a continue() aNbN b a aNbN terminate() terminate() b a b b terminate() b a b jonkv@ida Comparison to Classical Planning