class: center, middle # Artificial Intelligence ### Informed Search --- class: center, middle # Search --- # Reminder: The Pathfinding problem Given a graph G = (V,E), with edge weights w, a start node `\( s \in V \)`, a destination node `\( d \in V \)`, find a sequence of vertices `\( v_1, v_2, \ldots, v_n \)`, such that `\(v_1 = s, v_n = d \)` and `\( \forall i: (v_i, v_{i+1}) \in E \)` We call the sequence `\( v_1, v_2, \ldots, v_n \)` a *path*, and the *cost* of the path is `\( \sum_i w((v_i,v_{i+1})) \)` This means what you would expect: To find a path from a start node to a destination node means to find vertices to walk through that lead from the start to the destination by being connected with edges. The cost is the sum of the costs of edges that need to be traversed. --- # Reminder: Romania
--- # How could we find a path?
--- class: medium # Heuristic Search * What if we can give the path finding algorithm some more information? * For example, we may not know how to drive everywhere, but we can measure the straight line distance * This "extra" information is called a "heuristic" * Search algorithms can use it to "guide" the search process --- # Heuristic Search: General algorithm * We use the same algorithm as before: - Keep track of which nodes are candidates for expansion (starting with the start node) - Take one of these nodes and expand it - If you reach the target, you have found a path * Instead of using a stack or queue, we use a *priority queue*, where the nodes are ordered according to some value derived from the heuristic * So how do we determine this value? --- # Greedy Search * Let's just use our heuristic! * We order the nodes in the priority queue by heuristic value * Heuristic: straight line distance to Bucharest --- class: tiny # Greedy Search
Heuristic:
Arad
366
Bucharest
0
Craiova
160
Drobeta
242
Eforie
161
Fagaras
176
Giurgiu
77
Hirsova
151
Iasi
226
Lugoj
244
Mehadia
241
Neamt
234
Oradea
380
Pitesti
100
Rimnicu Vilcea
193
Sibiu
253
Timisoara
329
Urziceni
80
Vaslui
199
Zerind
374
Frontier: Arad (366) --- class: tiny # Greedy Search
Heuristic:
Arad
366
Bucharest
0
Craiova
160
Drobeta
242
Eforie
161
Fagaras
176
Giurgiu
77
Hirsova
151
Iasi
226
Lugoj
244
Mehadia
241
Neamt
234
Oradea
380
Pitesti
100
Rimnicu Vilcea
193
Sibiu
253
Timisoara
329
Urziceni
80
Vaslui
199
Zerind
374
Frontier: Sibiu (253), Timisoara (329), Zerind (374) --- class: tiny # Greedy Search
Heuristic:
Arad
366
Bucharest
0
Craiova
160
Drobeta
242
Eforie
161
Fagaras
176
Giurgiu
77
Hirsova
151
Iasi
226
Lugoj
244
Mehadia
241
Neamt
234
Oradea
380
Pitesti
100
Rimnicu Vilcea
193
Sibiu
253
Timisoara
329
Urziceni
80
Vaslui
199
Zerind
374
Frontier: Fagaras (176), Rimnicu Vilcea (193), Timisoara (329), Zerind (374), Oradea (380) --- class: tiny # Greedy Search
Heuristic:
Arad
366
Bucharest
0
Craiova
160
Drobeta
242
Eforie
161
Fagaras
176
Giurgiu
77
Hirsova
151
Iasi
226
Lugoj
244
Mehadia
241
Neamt
234
Oradea
380
Pitesti
100
Rimnicu Vilcea
193
Sibiu
253
Timisoara
329
Urziceni
80
Vaslui
199
Zerind
374
Frontier: Bucharest (0), Rimnicu Vilcea (193), Timisoara (329), Zerind (374), Oradea (380) --- class: center, middle # A* Search --- # A* Search * Greedy search sometimes does not give us the optimal result * It tries to get to the goal as fast as possible, but ignores the cost of actually getting to each node * Idea: Instead of using the node with the lowest heuristic value, use the node with the lowest sum of heuristic value and cost to get to * This is called A* search --- class: tiny # A* Search
Heuristic:
Arad
366
Bucharest
0
Craiova
160
Drobeta
242
Eforie
161
Fagaras
176
Giurgiu
77
Hirsova
151
Iasi
226
Lugoj
244
Mehadia
241
Neamt
234
Oradea
380
Pitesti
100
Rimnicu Vilcea
193
Sibiu
253
Timisoara
329
Urziceni
80
Vaslui
199
Zerind
374
Frontier: Arad (0 + 366) --- class: tiny # A* Search
Heuristic:
Arad
366
Bucharest
0
Craiova
160
Drobeta
242
Eforie
161
Fagaras
176
Giurgiu
77
Hirsova
151
Iasi
226
Lugoj
244
Mehadia
241
Neamt
234
Oradea
380
Pitesti
100
Rimnicu Vilcea
193
Sibiu
253
Timisoara
329
Urziceni
80
Vaslui
199
Zerind
374
Frontier: Sibiu (140 + 253), Timisoara (118 + 329), Zerind (75 + 374) --- class: tiny # A* Search
Heuristic:
Arad
366
Bucharest
0
Craiova
160
Drobeta
242
Eforie
161
Fagaras
176
Giurgiu
77
Hirsova
151
Iasi
226
Lugoj
244
Mehadia
241
Neamt
234
Oradea
380
Pitesti
100
Rimnicu Vilcea
193
Sibiu
253
Timisoara
329
Urziceni
80
Vaslui
199
Zerind
374
Frontier: Rimnicu Vilcea (220 + 193), Fagaras (239 + 176), Timisoara (118 + 329), Zerind (75 + 374), Orodea (291 + 380) --- class: tiny # A* Search
Heuristic:
Arad
366
Bucharest
0
Craiova
160
Drobeta
242
Eforie
161
Fagaras
176
Giurgiu
77
Hirsova
151
Iasi
226
Lugoj
244
Mehadia
241
Neamt
234
Oradea
380
Pitesti
100
Rimnicu Vilcea
193
Sibiu
253
Timisoara
329
Urziceni
80
Vaslui
199
Zerind
374
Frontier: Fagaras (239 + 176), Pitesti (317 + 100), Timisoara (118 + 329), Zerind (75 + 374), Craiova (366 + 160), Orodea (291 + 380) --- class: tiny # A* Search
Heuristic:
Arad
366
Bucharest
0
Craiova
160
Drobeta
242
Eforie
161
Fagaras
176
Giurgiu
77
Hirsova
151
Iasi
226
Lugoj
244
Mehadia
241
Neamt
234
Oradea
380
Pitesti
100
Rimnicu Vilcea
193
Sibiu
253
Timisoara
329
Urziceni
80
Vaslui
199
Zerind
374
Frontier: Pitesti (317 + 100), Timisoara (118 + 329), Zerind (75 + 374), Bucharest (450 + 0), Craiova (366 + 160), Orodea (291 + 380) --- class: tiny # A* Search
Heuristic:
Arad
366
Bucharest
0
Craiova
160
Drobeta
242
Eforie
161
Fagaras
176
Giurgiu
77
Hirsova
151
Iasi
226
Lugoj
244
Mehadia
241
Neamt
234
Oradea
380
Pitesti
100
Rimnicu Vilcea
193
Sibiu
253
Timisoara
329
Urziceni
80
Vaslui
199
Zerind
374
Frontier: Bucharest (418 + 0), Timisoara (118 + 329), Zerind (75 + 374), Craiova (366 + 160), Orodea (291 + 380) --- class: mmedium # A* Search * To find *optimal* solution, **keep expanding nodes** until the goal node is the best node in the frontier * A* is guaranteed to find the optimal solution if the heuristic is: - Admissible: Never overestimate the cost - Consistent: For a node x and its neighbor y, the heuristic value for x has to be less than or equal to that of y plus the cost of getting from x to y * You can also reduce the memory requirements of A* by using Iterative Deepening: - Limit search to a particular depth - If no path is found, increase the limit --- class: mmedium # A* Search Not only does A* produce the optimal path (with an admissible and consistent heuristic), it is also optimally efficient: * Consider another (optimal) algorithm * It gets the same heuristic * It starts from the same start node with the same goal * A\* will expand **at most** as many nodes as that other algorithm (minus some tie-breaking cases) --- # Dijkstra's algorithm * Maybe you have heard of Dijkstra's algorithm (and its variants) before? * Dijkstra's algorithm is basically A* without using the heuristic * In some formulations you also let the algorithm compute a path for *every* possible destination * This will give you a shortest path tree, which may be useful if you have to repeatedly find a path to different destinations --- # Comparison .medium[ * BFS: Queue * DFS: Stack * Greedy: Priority Queue * A\*: Priority Queue * Dijkstra: Priority Queue ]
Greedy
A*
Dijkstra
Heuristic
Yes
Yes
No
Cost
No
Yes
Yes
Priority
h
c+h
c
Note: If your heuristic always returns 0, you get Dijkstra. --- class: center, middle # More Graphs --- # Example: NavMeshes * How could we do pathfinding in a "real" 2D/3D environment? (Robot driving around an apartment, NPC walking around a video game, etc.) * Convert our environment into a graph! * We can "collect" all space where there is no obstacle into **convex polygons** * Then we connect adjacent polygons --- # Example: World of Warcraft
--- # Example: World of Warcraft
--- # Example: World of Warcraft
--- # Example: World of Warcraft
--- # Example: World of Warcraft
class: small # Another Application * Graphs are a very flexible way to represent problems! * Take, for example, Super Mario * Each action Mario performs will generate a "new" game state, and we can search for a way to beat a level this way!
--- # Search Graphs * What if we have infinite (or at least very large) graphs? * We can't really store the entire graph in memory * But do we need to? --- # Lazy Evaluation * Some programming languages (like Haskell) use something called *lazy evaluation* * This means a value is only evaluated when it is needed * For example `[1..]` is an infinite list of integers in Haskell * But as long as you don't try to access *all* elements, it does not need to generate them * We can "simulate" this behavior in other languages using functions --- # Lazy Evaluation for Graphs * Instead of representing an entire graph in memory, let's say we just store one node * Each node can, when requested, give us its neighbors * We can then even "forget" the original node, if we want to * The only requirement is that each node only has finitely many neighbors (and preferably not too many) --- # An Infinite Graph * Let's say we have a graph with one node for each integer * Each node has 2 to 4 neighbors: The predecessor and successor, twice its value, and half its value (if it is even). All distances are 1 * "1" has the neighbors "0" and "2" * "3" has the neighbors "2", "4", and "6" * "4" has the neighbors "3", "5", "8", and "2" * "16" has the neighbors "15", "17", "32", and "8" --- # Goal Conditions * We often also don't want to look for one specific (of our infinitely many) nodes, but rather a node that satisfies some condition * For example, what if we want a node that is greater than 1000 and a power of 2? * Each of the nodes "1024", "2048", "4096", etc. would be a valid solution * Let's use A* starting from "127" to find such a goal node * We need a heuristic! --- # Integer graph heuristic Let's use the following heuristic: $$ h(n) = \lvert \lfloor \log_2(n) \rfloor - \lfloor \log_2(1024)\rfloor \rvert $$ What does it do? Look for the next lowest power of two of the current node and the lowest valued goal node and calculate the difference. Now we could do A* on this graph! --- # References * [A* implementations in multiple languages](https://rosettacode.org/wiki/A*_search_algorithm) * [Sliding puzzle using A*](https://blog.goodaudience.com/solving-8-puzzle-using-a-algorithm-7b509c331288) * [JavaScript visualization of several search algorithms on a grid](https://qiao.github.io/PathFinding.js/visual/) * [A* is optimally efficient](https://www.cs.ubc.ca/~kevinlb/teaching/cs322%20-%202008-9/Lectures/Search5.pdf)