Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Artificial Intelligence: Search and Mining Lecture 2 Masashi Shimbo 2016-04-19 Today’s agenda ▶ ▶ Brief review of the last class More on tree search: ▶ ▶ ▶ Depth-first search Iterative deepening depth-first search Uniform-cost tree search 2 / 76 Review (+ Supplement) What we learned in the last class (1) ▶ State space = (possibly huge) graph ▶ “Search” is a task of finding a path in state space, from the initial state to a goal state — preferably the one with the least cost (shortest path) 4 / 76 State space is a labeled graph (V, A, c) where ▶ V = set of nodes/vertices (states) ▶ |V| may be huge (even infinite) ▶ A ⊂ V × V = set of edges/arcs (actions) ▶ c : A 7→ R+ = label (cost) function (i.e., label = cost) with ▶ a specific node s ∈ V called initial state, and ▶ a set G ⊂ V of goal states. 5 / 76 Tree graphs with identical costs For the ease of discussion, we assumed: ▶ ▶ all action costs are identical the state space is a tree rooted at the initial state A (rooted) tree is a graph such that: ▶ every node in the graph has exactly one path from the root (initial) node. 6 / 76 Rooted tree: example s root (initial) node 7 / 76 Tree with uniform branching factor b = 2 s root (initial) node 8 / 76 What we learned in the last class (2) Difference in many search algorithms can be accounted for by the order in which nodes are expanded. In a tree-like state space, each can be regarded as a variation of the General Tree Search algorithm… 9 / 76 General Tree Search algorithm template 1 2 3 4 5 6 7 8 OPEN ← new List Insert(OPEN, s) loop do if OPEN is empty then return “failure” v ← RemoveOne(OPEN) if IsGoal(v) then return Solution(v, s) foreach u ∈ Expand(v) do Insert(OPEN, u) Different implementations of data structure List and its associated functions RemoveOne() and Insert() lead to different search algorithms 10 / 76 What is the role of the list OPEN? OPEN maintains the frontier nodes of the explored state space In the beginning: ▶ OPEN contains s only In each iteration: ▶ A state is picked up (and removed) from OPEN (with function RemoveOne) ▶ It is Expanded → all successor states are “generated” ▶ The generated states are added to OPEN (with function Insert) 11 / 76 OPEN and expanded nodes during search 12 / 76 OPEN and expanded nodes during search OPEN = nodes at the frontier 13 / 76 OPEN and expanded nodes during search OPEN = nodes at the frontier expanded nodes 14 / 76 What we learned in the last class (3) Using different data structure List, together with functions RemoveOne() and Insert(), leads to different search strategies. Setting List = Queue RemoveOne = Dequeue Insert = Enqueue will make the breadth-first search algorithm. 15 / 76 Breadth-first search Input : initial state s Output : a solution path, if found, or “failure” 1 2 3 4 5 6 7 8 OPEN ← new List Queue Insert Enqueue(OPEN, s) loop do if OPEN = ∅ then return “failure” v ← RemoveOne Dequeue(OPEN) if IsGoal(v) then return Solution(v, s) foreach u ∈ Expand(v) do Insert Enqueue(OPEN, u) 16 / 76 Breadth-first search: properties BFS is complete BFS never fails to find a goal, even if the state space is infinite. BFS is not admissible BFS returns the first shallowest solution path it finds. In general, the shallowest solution path might not be the one with the least cost. It is, however, admissible if all actions have an equal cost. 17 / 76 Breadth-first search: worst-case complexity in a tree with branching factor b and shallowest goal depth d Time complexity In the worst case, all nodes up to depth d, and some nodes at depth (d + 1) are generated. O(bd+1 ) Space complexity Same as the time complexity, because BFS maintains information (Parent[]) of all generated nodes on memory. Hence O(bd+1 ) 18 / 76 Depth-first search Problem with breadth-first search Memory inefficient Space complexity in a tree with branching factor b: O(bd+1 ) because BFS maintains all generated nodes on memory ▶ b: branching factor of the tree ▶ d: depth of the shallowest goal state 20 / 76 Depth-first search Uses Stack (LIFO list; “last-in first-out” list) to order node expansion 21 / 76 Stack LIFO (“Last-in first-out”) buffer Functions for manipulating stacks: ▶ ▶ Push(S , v) — Insert item v at the beginning of list S Pop(S ) — Remove the first item in the list S and return it 22 / 76 Stack: examples S : S : Push(S , 1 2 a b 1 2 3 c a b 1 S : d c ) S : Push(S , d ) 2 3 4 c a b v ← Pop(S ) 1 2 3 c a b 1 S : d 2 3 4 c a b v: d S : 1 2 3 c a b 23 / 76 Stack: examples S : S : Push(S , 1 2 a b 1 2 3 c a b 1 S : d c ) S : Push(S , d ) 2 3 4 c a b v ← Pop(S ) 1 2 3 c a b 1 S : d 2 3 4 c a b v: d S : 1 2 3 c a b 24 / 76 Stack: examples S : S : Push(S , 1 2 a b 1 2 3 c a b 1 S : d c ) S : Push(S , d ) 2 3 4 c a b v ← Pop(S ) 1 2 3 c a b 1 S : d 2 3 4 c a b v: d S : 1 2 3 c a b 25 / 76 Stack: examples S : S : Push(S , 1 2 a b 1 2 3 c a b 1 S : d c ) S : Push(S , d ) 2 3 4 c a b v ← Pop(S ) 1 2 3 c a b 1 S : d 2 3 4 c a b v: d S : 1 2 3 c a b 26 / 76 Stack: examples S : S : Push(S , 1 2 a b 1 2 3 c a b 1 S : d c ) S : Push(S , d ) 2 3 4 c a b v ← Pop(S ) 1 2 3 c a b 1 S : d 2 3 4 c a b v: d S : 1 2 3 c a b 27 / 76 Stack: examples S : S : Push(S , 1 2 a b 1 2 3 c a b 1 S : d c ) S : Push(S , d ) 2 3 4 c a b v ← Pop(S ) 1 2 3 c a b 1 S : d 2 3 4 c a b v: d S : 1 2 3 c a b 28 / 76 Stack: examples S : S : Push(S , 1 2 a b 1 2 3 c a b 1 S : d c ) S : Push(S , d ) 2 3 4 c a b v ← Pop(S ) 1 2 3 c a b 1 S : d 2 3 4 c a b v: d S : 1 2 3 c a b 29 / 76 Stack: examples S : S : Push(S , 1 2 a b 1 2 3 c a b 1 S : d c ) S : Push(S , d ) 2 3 4 c a b v ← Pop(S ) 1 2 3 c a b 1 S : d 2 3 4 c a b v: d S : 1 2 3 c a b 30 / 76 Stack: examples S : S : Push(S , 1 2 a b 1 2 3 c a b 1 S : d c ) S : Push(S , d ) 2 3 4 c a b v ← Pop(S ) 1 2 3 c a b 1 S : d 2 3 4 c a b v: d S : 1 2 3 c a b 31 / 76 Stack: examples S : S : Push(S , 1 2 a b 1 2 3 c a b 1 S : d c ) S : Push(S , d ) 2 3 4 c a b v ← Pop(S ) 1 2 3 c a b 1 S : d 2 3 4 c a b v: d S : 1 2 3 c a b 32 / 76 General tree search algorithm Input : initial state s Output : a solution path, if found, or “failure” 1 2 3 4 5 6 7 8 OPEN ← new List Insert(OPEN, s) loop do if OPEN is empty then return “failure” v ← RemoveOne(OPEN) if IsGoal(v) then return Solution(v, s) foreach u ∈ Expand(v) do Insert(OPEN, u) 33 / 76 Depth-first search Input : initial state s Output : a solution path, if found, or “failure” 1 2 3 4 5 6 7 8 OPEN ← new List Stack Insert Push(OPEN, s) loop do if OPEN is empty then return “failure” v ← RemoveOne Pop(OPEN) if IsGoal(v) then return Solution(v, s) foreach u ∈ Expand(v) do Insert Push(OPEN, u) 34 / 76 Depth-first search: a sample run initial state s Expanded state: a c OPEN: s b e t d goal state f g h i 35 / 76 Depth-first search: a sample run initial state s Expanded state: s a c OPEN: b e t d goal state f g h i 36 / 76 Depth-first search: a sample run initial state s Expanded state: a c OPEN: a b b e t d goal state f g h i 37 / 76 Depth-first search: a sample run initial state s Expanded state: a a c OPEN: b b e t d goal state f g h i 38 / 76 Depth-first search: a sample run initial state s Expanded state: a c OPEN: c d b b e t d goal state f g h i 39 / 76 Depth-first search: a sample run initial state s Expanded state: c a c OPEN: d b b e t d goal state f g h i 40 / 76 Depth-first search: a sample run initial state s Expanded state: a c OPEN: d b b e t d goal state f g h i 41 / 76 Depth-first search: a sample run initial state s Expanded state: d a c OPEN: b b e t d goal state f g h i 42 / 76 Depth-first search: a sample run initial state s Expanded state: a c OPEN: f g b b e t d goal state f g h i 43 / 76 Depth-first search: a sample run initial state s Expanded state: f a c OPEN: g b b e t d goal state f g h i 44 / 76 Depth-first search: a sample run initial state s Expanded state: a c OPEN: g b b e t d goal state f g h i 45 / 76 Depth-first search: a sample run initial state s Expanded state: g a c OPEN: b b e t d goal state f g h i 46 / 76 Depth-first search: a sample run initial state s Expanded state: a c OPEN: b b e t d goal state f g h i 47 / 76 Depth-first search: a sample run initial state s Expanded state: b a c OPEN: b e t d goal state f g h i 48 / 76 Depth-first search: a sample run initial state s Expanded state: a c OPEN: t e b e t d goal state f g h i 49 / 76 Depth-first search: a sample run initial state s Expanded state: t a c OPEN: e b e t d goal state f g h i 50 / 76 Depth-first search is not complete, nor is admissible May not terminate if the state space is infinite It returns the first (leftmost) solution however bad its quality is. 51 / 76 Depth-first search Additional improvement ▶ As soon as all descendants of a node have been expanded, the node can be removed from memory å required memory is linear in the number of nodes in OPEN ▶ For a uniform tree with branching factor b, when depth-first search expands a node at depth k, at most O(bk) nodes are stored in OPEN å required memory is linear in the depth of the state space 52 / 76 Depth-first search Memory-efficient, recursive implementation Main function: Input : initial state s 1 2 function DFS(s) return RecursiveDFS(s, s) Simply call function RecursiveDFS(s, s), which is to be explained in the next slide… 53 / 76 Depth-first search Memory-efficient, recursive implementation (continued) Sub-routine: RecursiveDFS Input : state v to start depth-first search Input : initial state s Output : a solution, if found, or “failure” 1 2 3 4 5 6 7 function RecursiveDFS(v, s) if IsGoal(v) then return Solution(v, s) foreach u ∈ Succ(v) do Parent[u] ← v # memorize parent of u result ← RecursiveDFS(u, s) # search below u if result , “failure” then return result remove information associated with u from memory # such as Parent[u], which is no longer needed 8 return “failure” 54 / 76 Worst case complexity Time complexity In the worst case all nodes in the state space will be expanded. Hence, O(bm ) where m is the maximum depth of any node. Space complexity O(bm) 55 / 76 Iterative deepening depth-first search How to overcome incompleteness of depth-first search? Iterative deepening depth-first search Depth-first search is memory efficient but is incomplete in infinite state space Iterative deepening is complete and enjoys the linear memory requirement of depth-first search 57 / 76 Iterative deepening: idea Introduce a cutoff depth θ to depth-first search — backtrack immediately if the node depth exceeds θ Successively run depth-first searches with increasing cutoff depth θ = 1, 2, . . . 58 / 76 Iterative deepening: a sample run initial state goal state unbounded path 59 / 76 Iterative deepening: a sample run initial state Cutoff depth = 1 No goal states with depth less than or equal to 1 goal state Ô Depth-first search fails. unbounded path 60 / 76 Iterative deepening: a sample run initial state Cutoff depth = 2 No goal states with depth less than or equal to 2 goal state Ô Depth-first search fails. unbounded path 61 / 76 Iterative deepening: a sample run initial state Cutoff depth = 3 A goal state exists at depth 3. goal state Ô Depth-first search succeeds. unbounded path 62 / 76 Space complexity Since it performs a series of depth-first searches (with different depth-limits) up to the solution depth d, O(bd) 63 / 76 Time complexity ▶ ▶ ▶ Depth 1 nodes are generated d times. Depth 2 nodes are generated d − 1 times. .. . Depth d nodes are generated once. (# of nodes at depth d) = bd (b: branching factor) 1bd + 2bd−1 + 3bd−2 + · · · + (d − 1)b2 + db = O(bd ) 64 / 76 Summary: Tree search algorithms Iterative Search algorithm Breadth-first Depth-first Deepening Completeness yes no yes Admissibility no/yes* no no/yes* d+1 m Time complexity O(b ) O(b ) O(bd ) Space complexity O(bd+1 ) O(bm) O(bd) * admissible if action costs are all identical. b : branching factor d : depth of the shallowest goal state m : maximum depth of the state space Admissibility of breadth-first and iterative deepening searches are shown as “no/yes*,” to make it clear that in general these algorithms are not admissible (if action costs vary). 65 / 76 Uniform-cost tree search How do we take edge (action) costs into account? Uniform-cost tree search Cost-aware extension of the breadth-first search 67 / 76 Breadth-first search 1 2 3 4 5 6 7 8 OPEN ← new Queue Enqueue(OPEN, s) loop do if OPEN = ∅ then return “failure” v ← Dequeue(OPEN) if IsGoal(v) then return Solution(v, s) foreach u ∈ Expand(v) do Enqueue(OPEN, u) 68 / 76 Uniform-cost tree search expands nodes with the cheapest cost first To make this possible, ▶ For each node n, maintain the path cost from the initial node. g[v] = path cost from the initial state to v ▶ Use a priority queue for the OPEN list — to select the node with the minimum g-value in OPEN. 69 / 76 Function Expand(v) now maintains the cost g from the initial state Input : state v to expand Output : set of successors of n 1 2 3 4 5 6 7 S ← ∅; foreach node u ∈ Succ(v) do Reserve memory for node u Parent[u] ← v g[u] ← g[v] + c(u, v) S ← S ∪ {u} return S 70 / 76 Function Expand(v) v c(v, u1 ) u1 c(v, u2 ) u2 c(v, u3 ) u3 71 / 76 Function Expand(v) v c(v, u1 ) u1 c(v, u2 ) u2 c(v, u3 ) u3 Succ(v) 72 / 76 Function Expand(v) v c(v, u1 ) u1 Parent[u1 ] = v g[u1 ] ← g[v] + c(v, u1 ) c(v, u2 ) u2 Parent[u2 ] = v g[u2 ] ← g[v] + c(v, u2 ) c(v, u3 ) u3 Parent[u3 ] = v g[u3 ] ← g[v] + c(v, u3 ) 73 / 76 Priority queue Two functions for manipulating priority queue Pg : Insert(Pg , v) Put item v in Pg . DeleteMin(Pg ) Remove and return an item with the minimum g-value from Pg . Thus, the returned item v is the one with v = argmin g[u] u∈Pg before its removal 74 / 76 Uniform-cost tree search 1 2 3 4 5 6 7 8 OPEN ← new PriorityQueueg Insert(OPEN, s) loop do if OPEN is empty then return failure n ← DeleteMin(OPEN) if IsGoal(n) then return Solution(n) foreach m ∈ Expand(n) do Insert(OPEN, m) 75 / 76 What about search algorithms for general graphs? (not just trees?) We’ll find them out in the next class. 76 / 76