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 – 2016 2 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 4 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 5 jonkv@ida Terminology 2: Non-Primitive Task 6 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 7 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) 8 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) 9 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 10 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) 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 11 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: <travel(home,work); do-work(); travel(work,home)> 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 12 jonkv@ida Domains, Problems, Solutions A simple "template expansion" 14 Let’s switch to Dock Worker Robots… c3 c1 p1 p2 loc1 jonkv@ida DWR 15 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 16 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) Iteration with no predetermined bound 18 How can we implement the task move-stack(pile1, pile2)? Should move all containers in a stack There is no limit on how many there might be… A B C top(A, pile1) on(A,B) on(B,C) on(C,pallet) C B A top(A, pile2) on(C,B) on(B,A) on(A,pallet) jonkv@ida Moving a Stack of Containers 19 We need a loop with a termination condition 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) 20 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" What if the pallet is "topmost"? We don't want to move it! jonkv@ida Recursion (2) To fix this: 21 Add two method params – "non-natural", as in "ordinary" planning; does not give the planner a real choice 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) 22 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) 23 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) 24 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) Letting the planner choose parameters 26 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 Moving Stacks (1) 27 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 Moving Stacks (2) 28 Example: We choose an intermediate pile 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 Moving Stacks (3): We choose intermediate 29 jonkv@ida Moving Stacks (4): Planner Chooses Example: The planner chooses intermediate piles 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) > Why do the intermediate piles have to be empty initially? Because the second move-stack moves all containers from the intermediate pile… Modeling "conditional" actions Delivery: A single truck Pick up a package, drive to its destination, unload Task: ▪ method: ▪ precond: ▪ subtasks: deliver(package, dest) move(package, packageloc, dest) at(package, packageloc) <driveto(packageloc), load(package), driveto(dest), unload(package)> What if the truck is already at the package location? First driveto is unnecessary! 31 jonkv@ida Delivery 1: First Variation Alternative: Two alternative methods for deliver Task: ▪ method: ▪ precond: deliver(package, dest) Task: ▪ method: ▪ precond: deliver(package, dest) move1(package, packageloc, truckloc, dest) at(truck, truckloc), at(package, packageloc), packageloc = truckloc ▪ subtasks: <load(package), driveto(dest), unload(package)> ▪ subtasks: move2(package, packageloc, truckloc, dest) at(truck, truckloc), at(package, packageloc), packageloc != truckloc <driveto(packageloc), load(package), driveto(dest), unload(package)> Do we really have to repeat the entire task? Many "conditional" subtasks combinatorial explosion 32 jonkv@ida Delivery 2: Second Variation 33 Make the choice in the subtask instead! Task: ▪ method: ▪ precond: ▪ subtasks: deliver(package, dest) Task: ▪ method: ▪ precond: ▪ subtasks: be-at(loc) Task: ▪ method: ▪ precond: ▪ subtasks: be-at(loc) move1(package, packageloc, truckloc, dest) at(truck, truckloc), at(package, packageloc) <be-at(packageloc), load(package), be-at(dest), unload(package)> drive(loc) !at(truck,loc) <driveto(loc)> already-there at(truck,loc) <> jonkv@ida Delivery 3: Third variation Total Order Forward Decomposition: s0 35 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 36 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 37 jonkv@ida Solving Total-Order STN Problems (1) 38 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 ▪ ▪ 39 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) 40 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) 41 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) 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 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) 43 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) 45 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 46 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 47 jonkv@ida Alternative Methods 49 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. 50 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 51 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 52 jonkv@ida Solving Partial-Order STN Problems (1) 53 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 σ(u) 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.π 54 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) = σ(u) 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) 55 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) 56 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) 57 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)) bus(b(x), b(y)) travel (b(y), y) jonkv@ida The Delta Function (3) 58 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) 59 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) 60 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) 61 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 62 jonkv@ida Partially Ordered Methods Control Rules or Hierarchical Task Networks? Both can be very efficient and expressive If you have ”recipes” for everything, HTN can be more convenient ▪ Can be modeled with control rules, but not intended for this purpose ▪ You have to forbid everything that is ”outside” the recipe If you have knowledge about ”some things that shouldn’t be done”: ▪ With control rules, the default is to ”try everything” ▪ Can more easily express localized knowledge about what should and shouldn’t be done ▪ Doesn’t require knowledge of all the ways in which the goal can be reached 64 jonkv@ida Conclusion