{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Depth-Limited Search"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Depth-first search will not find a goal if it searches down a path\n",
"that has infinite length. So, in general, depth-first search is not\n",
"guaranteed to find a solution, so it is not complete. \n",
"\n",
"This problem is eliminated by limiting the depth of the search to some\n",
"value $l$. However, this introduces another way of preventing depth-first\n",
"search from finding the goal. If the goal is deeper than $l$ it will\n",
"not be found.\n",
"\n",
"How would you make an intelligent guess for $l$ for a given search\n",
"problem?\n",
"\n",
"Its time complexity is $O(b^l)$ and its space complexity is $O(bl)$.\n",
"What would the space complexity be of the backtracking version of\n",
"this search?\n",
"\n",
"Regular depth-first search is a special case, for which $l=\\infty$."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Iterative-Deepening Search"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If a depth-limited depth-first search limited to depth $l$ does not find the goal,\n",
"try it again with limit at $l+1$. Continue this until goal is found.\n",
"\n",
"Make depth-limited depth-first search complete by repeatedly applying\n",
"it with greater values for the depth limit $l$.\n",
"\n",
"Feels like breadth-first search, in that a level is fully explored\n",
"before extending the search to the next level. But, unlike\n",
"breadth-first search, after one level is fully explored, all nodes\n",
"already expanded are thrown away and the search starts with a clear\n",
"memory. \n",
"\n",
"Seems very wasteful! Is it really? How many nodes are generated\n",
"at the final level $d$?\n",
"\n",
"$$b^d$$\n",
"\n",
"How many nodes are expanded in\n",
"the tree on your way to the final level, down to depth $d-1$? \n",
"\n",
"$$b + b^2 + \\cdots + b^{d-1} = O(b^{d-1})$$\n",
"\n",
"How much of a waste is it to throw away those $O(b^{d-1})$ nodes?\n",
"Say $b=10$ and $d=5$. We are throwing away on the order of $10^4 =\n",
"1,000$ nodes, regenerating them and then generating $b^d = 10^5 =\n",
"10,000$ new nodes. Regenerating those 1,000 nodes seems trivial\n",
"compared to making the 10,000 new ones.\n",
"\n",
"Our textbook authors say:\n",
"\n",
"> \"In general, iterative deepening is the preferred uninformed search method when the search space is large and the depth of the solution is not known.\"\n",
"\n",
"Watch this [short video by Richard Korf](http://www.youtube.com/watch?v=EnX8cQPiB1M), one of the developers of iterative deepening."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Recursive definition"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's discuss the recursive implementation in Figures 3.17 (with\n",
"Figure 3.18). Rather than the explicit storage of expanded nodes in a\n",
"python dictionary named `expanded`, we can rely on the local\n",
"variables implicitly stored in the function call\n",
"stack. \n",
"\n",
"First, define the recursive depth-limited search function that\n",
"generates the children of a state and calls itself recursively on each\n",
"of the child states. Let's define it as a mix of python and English.\n",
"Let `take_action_f` be a function that generates one new state given a\n",
"current state and a valid action from that state. Also let\n",
"`actions_f` be a function that returns a list of valid actions from a\n",
"given state. We will see examples of these in the next lecture notes."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def depth_limited_search(state, goal_state, actions_f, take_action_f, depth_limit):\n",
" \n",
" # If we have reached the goal, exit, returning an empty solution path.\n",
" If state == goal_state, then\n",
" return []\n",
" \n",
" # If we have reached the depth limit, return the string 'cutoff'.\n",
" If depth_limit is 0, then\n",
" Return the string 'cutoff' to signal that the depth limit was reached\n",
" \n",
" cutoff_occurred = False\n",
" \n",
" # For each possible action from state ...\n",
" For each action in actions_f(state):\n",
" \n",
" # Apply the action to the current state to get a next state, named child_state\n",
" child_state = take_action_f(state, action)\n",
" \n",
" # Recursively call this function to continue the search starting from the child_state.\n",
" # Decrease by one the depth_limit for this search.\n",
" result = depth_limited_search(child_state, goal_state, actions_f, take_action_f, depth_limit - 1)\n",
" \n",
" # If result was 'cufoff', just note that this happened.\n",
" If result is 'cutoff', then\n",
" cutoff_occurred = True\n",
" \n",
" # If result was not 'failure', search succeeded so add childState to front of solution path and\n",
" # return that path.\n",
" else if result is not 'failure' then\n",
" Add child_state to front of partial solution path, in result, returned by depth_limited_search\n",
" return result\n",
" \n",
" # We reach here only if cutoff or failure occurred. Return whichever occurred.\n",
" If cutoff_occurred, then\n",
" return 'cutoff'\n",
" else\n",
" return 'failure'"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"def iterative_deepening_search(start_state, goal_state, actions_f, take_action_f, max_depth):\n",
" \n",
" # Conduct multiple searches, starting with smallest depth, then increasing it by 1 each time.\n",
" for depth in range(max_depth):\n",
" \n",
" # Conduct search from startState\n",
" result = depth_limited_search(start_state, goal_state, actions_f, take_action_f, depth)\n",
" \n",
" # If result was failure, return 'failure'.\n",
" if result is 'failure':\n",
" return 'failure'\n",
" \n",
" # Otherwise, if result was not cutoff, it succeeded, so add start_state to solution path and return it.\n",
" if result is not 'cutoff', then\n",
" Add start_state to front of solution path, in result, returned by depth_limited_search \n",
" return result\n",
" \n",
" # If we reach here, no solution found within the max_depth limit.\n",
" return 'cutoff'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Bidirectional Search"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If, and this is a big if, every action in a search problem has a known\n",
"inverse action allowing search to go backwards, then an $O(b^d)$\n",
"search can be reduced to two $O(b^{d/2})$ searches by iteratively, or\n",
"simultaneously in parallel, searching forward from the start state and\n",
"searching backwards from the goal state. This also assumes there is\n",
"one goal state, or a finite number of goal states."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Uninformed Search Summary"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This table is from page 91 of our texbook. Here $b$ is the branching factor, $d$ is the depth of the shallowest solution, $m$ is the maximum depth of the search tree, and $l$ is the depth limit."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"| Criterion | Breadth-First | Depth-First | Depth-Limited | Iterative-Deepening | Bidirectional \n",
"| :-: | :-: | :-: | :-: | :-: | :-:\n",
"| Complete? | Yes | No | No | Yes | Yes |\n",
"| Optimal? | Yes | No | No | Yes | Yes |\n",
"| Time | $O(b^d)$ | $O(b^m)$ | $O(b^l)$ | $O(b^d)$ | $O(b^{d/2})$ |\n",
"| Space | $O(b^d)$ | $O(bm)$ | $O(bl)$ | $O(bd)$ | $O(b^{d/2})$ |\n"
]
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.3"
},
"toc": {
"base_numbering": 1,
"nav_menu": {},
"number_sections": true,
"sideBar": true,
"skip_h1_title": false,
"title_cell": "Table of Contents",
"title_sidebar": "Contents",
"toc_cell": false,
"toc_position": {},
"toc_section_display": true,
"toc_window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 1
}