search.ipynb 577 ko
Newer Older
    "* `formulate_goal(self, state)`: Given a `state` of the agent, this method formulates the `goal` for it.\n",
    "\n",
    "* `formulate_problem(self, state, goal)`: It is used in problem formulation given a `state` and a `goal` for the `agent`.\n",
    "\n",
    "* `search(self, problem)`: This method is used to search a sequence of `actions` to solve a `problem`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let us now define a Simple Problem Solving Agent Program. We will create a simple `vacuumAgent` class which will inherit from the abstract class `SimpleProblemSolvingAgentProgram` and overrides its methods. We will create a simple intelligent vacuum agent which can be in any one of the following states. It will move to any other state depending upon the current state as shown in the picture by arrows:\n",
    "\n",
    "![simple problem solving agent](images/simple_problem_solving_agent.jpg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class vacuumAgent(SimpleProblemSolvingAgentProgram):\n",
    "        def update_state(self, state, percept):\n",
    "            return percept\n",
    "\n",
    "        def formulate_goal(self, state):\n",
    "            goal = [state7, state8]\n",
    "            return goal  \n",
    "\n",
    "        def formulate_problem(self, state, goal):\n",
    "            problem = state\n",
    "            return problem   \n",
    "    \n",
    "        def search(self, problem):\n",
    "            if problem == state1:\n",
    "                seq = [\"Suck\", \"Right\", \"Suck\"]\n",
    "            elif problem == state2:\n",
    "                seq = [\"Suck\", \"Left\", \"Suck\"]\n",
    "            elif problem == state3:\n",
    "                seq = [\"Right\", \"Suck\"]\n",
    "            elif problem == state4:\n",
    "                seq = [\"Suck\"]\n",
    "            elif problem == state5:\n",
    "                seq = [\"Suck\"]\n",
    "            elif problem == state6:\n",
    "                seq = [\"Left\", \"Suck\"]\n",
    "            return seq"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we will define all the 8 states and create an object of the above class. Then, we will pass it different states and check the output:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Left\n",
      "Suck\n",
      "Right\n"
     ]
    }
   ],
    "state1 = [(0, 0), [(0, 0), \"Dirty\"], [(1, 0), [\"Dirty\"]]]\n",
    "state2 = [(1, 0), [(0, 0), \"Dirty\"], [(1, 0), [\"Dirty\"]]]\n",
    "state3 = [(0, 0), [(0, 0), \"Clean\"], [(1, 0), [\"Dirty\"]]]\n",
    "state4 = [(1, 0), [(0, 0), \"Clean\"], [(1, 0), [\"Dirty\"]]]\n",
    "state5 = [(0, 0), [(0, 0), \"Dirty\"], [(1, 0), [\"Clean\"]]]\n",
    "state6 = [(1, 0), [(0, 0), \"Dirty\"], [(1, 0), [\"Clean\"]]]\n",
    "state7 = [(0, 0), [(0, 0), \"Clean\"], [(1, 0), [\"Clean\"]]]\n",
    "state8 = [(1, 0), [(0, 0), \"Clean\"], [(1, 0), [\"Clean\"]]]\n",
    "a = vacuumAgent(state1)\n",
    "print(a(state6)) \n",
    "print(a(state1))\n",
    "print(a(state3))"
  {
   "cell_type": "markdown",
    "## SEARCHING ALGORITHMS VISUALIZATION\n",
    "In this section, we have visualizations of the following searching algorithms:\n",
    "1. Breadth First Tree Search\n",
    "2. Depth First Tree Search\n",
    "3. Breadth First Search\n",
    "4. Depth First Graph Search\n",
    "5. Best First Graph Search\n",
    "6. Uniform Cost Search\n",
    "7. Depth Limited Search\n",
    "8. Iterative Deepening Search\n",
    "9. A\\*-Search\n",
    "10. Recursive Best First Search\n",
    "\n",
    "We add the colors to the nodes to have a nice visualisation when displaying. So, these are the different colors we are using in these visuals:\n",
    "* Un-explored nodes - <font color='black'>white</font>\n",
    "* Frontier nodes - <font color='orange'>orange</font>\n",
    "* Currently exploring node - <font color='red'>red</font>\n",
    "* Already explored nodes - <font color='gray'>gray</font>"
   ]
  },
  {
   "cell_type": "markdown",
    "## 1. BREADTH-FIRST TREE SEARCH\n",
    "We have a working implementation in search module. But as we want to interact with the graph while it is searching, we need to modify the implementation. Here's the modified breadth first tree search."
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
        "def tree_breadth_search_for_vis(problem):\n",
    "    \"\"\"Search through the successors of a problem to find a goal.\n",
    "    The argument frontier should be an empty queue.\n",
    "    Don't worry about repeated paths to a state. [Figure 3.7]\"\"\"\n",
    "    \n",
    "    # we use these two variables at the time of visualisations\n",
    "    iterations = 0\n",
    "    all_node_colors = []\n",
    "    node_colors = {k : 'white' for k in problem.graph.nodes()}\n",
    "    #Adding first node to the queue\n",
    "    frontier = deque([Node(problem.initial)])\n",
    "    node_colors[Node(problem.initial).state] = \"orange\"\n",
    "    iterations += 1\n",
    "    all_node_colors.append(dict(node_colors))\n",
    "        #Popping first node of queue\n",
    "        node = frontier.popleft()\n",
    "        # modify the currently searching node to red\n",
    "        node_colors[node.state] = \"red\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        \n",
    "        if problem.goal_test(node.state):\n",
    "            # modify goal node to green after reaching the goal\n",
    "            node_colors[node.state] = \"green\"\n",
    "            iterations += 1\n",
    "            all_node_colors.append(dict(node_colors))\n",
    "            return(iterations, all_node_colors, node)\n",
    "        frontier.extend(node.expand(problem))\n",
    "           \n",
    "        for n in node.expand(problem):\n",
    "            node_colors[n.state] = \"orange\"\n",
    "            iterations += 1\n",
    "            all_node_colors.append(dict(node_colors))\n",
    "        # modify the color of explored nodes to gray\n",
    "        node_colors[node.state] = \"gray\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "def breadth_first_tree_search(problem):\n",
    "    \"Search the shallowest nodes in the search tree first.\"\n",
    "    iterations, all_node_colors, node = tree_breadth_search_for_vis(problem)\n",
    "    return(iterations, all_node_colors, node)"
Anthony Marakis's avatar
Anthony Marakis a validé
    "Now, we use `ipywidgets` to display a slider, a button and our romania map. By sliding the slider we can have a look at all the intermediate steps of a particular search algorithm. By pressing the button **Visualize**, you can see all the steps without interacting with the slider. These two helper functions are the callback functions which are called when we interact with the slider and the button."
   "execution_count": 15,
   "source": [
    "all_node_colors = []\n",
    "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n",
    "a, b, c = breadth_first_tree_search(romania_problem)\n",
    "display_visual(romania_graph_data, user_input=False, \n",
    "               algorithm=breadth_first_tree_search, \n",
    "               problem=romania_problem)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Depth-First Tree Search:\n",
    "Now let's discuss another searching algorithm, Depth-First Tree Search."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": true
   },
        "def tree_depth_search_for_vis(problem):\n",
    "    \"\"\"Search through the successors of a problem to find a goal.\n",
    "    The argument frontier should be an empty queue.\n",
    "    Don't worry about repeated paths to a state. [Figure 3.7]\"\"\"\n",
    "    \n",
    "    # we use these two variables at the time of visualisations\n",
    "    iterations = 0\n",
    "    all_node_colors = []\n",
    "    node_colors = {k : 'white' for k in problem.graph.nodes()}\n",
    "    \n",
    "    #Adding first node to the stack\n",
    "    frontier = [Node(problem.initial)]\n",
    "    \n",
    "    node_colors[Node(problem.initial).state] = \"orange\"\n",
    "    iterations += 1\n",
    "    all_node_colors.append(dict(node_colors))\n",
    "    \n",
    "    while frontier:\n",
    "        #Popping first node of stack\n",
    "        node = frontier.pop()\n",
    "        \n",
    "        # modify the currently searching node to red\n",
    "        node_colors[node.state] = \"red\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        \n",
    "        if problem.goal_test(node.state):\n",
    "            # modify goal node to green after reaching the goal\n",
    "            node_colors[node.state] = \"green\"\n",
    "            iterations += 1\n",
    "            all_node_colors.append(dict(node_colors))\n",
    "            return(iterations, all_node_colors, node)\n",
    "        \n",
    "        frontier.extend(node.expand(problem))\n",
    "           \n",
    "        for n in node.expand(problem):\n",
    "            node_colors[n.state] = \"orange\"\n",
    "            iterations += 1\n",
    "            all_node_colors.append(dict(node_colors))\n",
    "\n",
    "        # modify the color of explored nodes to gray\n",
    "        node_colors[node.state] = \"gray\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        \n",
    "    return None\n",
    "\n",
    "def depth_first_tree_search(problem):\n",
    "    \"Search the deepest nodes in the search tree first.\"\n",
    "    iterations, all_node_colors, node = tree_depth_search_for_vis(problem)\n",
    "    return(iterations, all_node_colors, node)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
    "all_node_colors = []\n",
    "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n",
    "display_visual(romania_graph_data, user_input=False, \n",
    "               algorithm=depth_first_tree_search, \n",
    "               problem=romania_problem)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
    "## 3. BREADTH-FIRST GRAPH SEARCH\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "Let's change all the `node_colors` to starting position and define a different problem statement."
   "execution_count": 18,
    "collapsed": true
   },
   "outputs": [],
   "source": [
"def breadth_first_search_graph(problem):\n",
    "    \"[Figure 3.11]\"\n",
    "    \n",
    "    # we use these two variables at the time of visualisations\n",
    "    iterations = 0\n",
    "    all_node_colors = []\n",
    "    node_colors = {k : 'white' for k in problem.graph.nodes()}\n",
    "    \n",
    "    node = Node(problem.initial)\n",
    "    \n",
    "    node_colors[node.state] = \"red\"\n",
    "    iterations += 1\n",
    "    all_node_colors.append(dict(node_colors))\n",
    "      \n",
    "    if problem.goal_test(node.state):\n",
    "        node_colors[node.state] = \"green\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        return(iterations, all_node_colors, node)\n",
    "    frontier = deque([node])\n",
    "    \n",
    "    # modify the color of frontier nodes to blue\n",
    "    node_colors[node.state] = \"orange\"\n",
    "    iterations += 1\n",
    "    all_node_colors.append(dict(node_colors))\n",
    "        \n",
    "    explored = set()\n",
    "    while frontier:\n",
    "        node = frontier.popleft()\n",
    "        node_colors[node.state] = \"red\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        \n",
    "        explored.add(node.state)     \n",
    "        \n",
    "        for child in node.expand(problem):\n",
    "            if child.state not in explored and child not in frontier:\n",
    "                if problem.goal_test(child.state):\n",
    "                    node_colors[child.state] = \"green\"\n",
    "                    iterations += 1\n",
    "                    all_node_colors.append(dict(node_colors))\n",
    "                    return(iterations, all_node_colors, child)\n",
    "                frontier.append(child)\n",
    "\n",
    "                node_colors[child.state] = \"orange\"\n",
    "                iterations += 1\n",
    "                all_node_colors.append(dict(node_colors))\n",
    "                    \n",
    "        node_colors[node.state] = \"gray\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
   "execution_count": 19,
   "source": [
    "all_node_colors = []\n",
    "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n",
    "display_visual(romania_graph_data, user_input=False, \n",
    "               algorithm=breadth_first_search_graph, \n",
    "               problem=romania_problem)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Depth-First Graph Search:  \n",
    "Although we have a working implementation in search module, we have to make a few changes in the algorithm to make it suitable for visualization."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "collapsed": true
   },
   "source": [    "def graph_search_for_vis(problem):\n",
    "    \"\"\"Search through the successors of a problem to find a goal.\n",
    "    The argument frontier should be an empty queue.\n",
    "    If two paths reach a state, only use the first one. [Figure 3.7]\"\"\"\n",
    "    # we use these two variables at the time of visualisations\n",
    "    iterations = 0\n",
    "    all_node_colors = []\n",
    "    node_colors = {k : 'white' for k in problem.graph.nodes()}\n",
    "    frontier = [(Node(problem.initial))]\n",
    "    explored = set()\n",
    "    \n",
    "    # modify the color of frontier nodes to orange\n",
    "    node_colors[Node(problem.initial).state] = \"orange\"\n",
    "    iterations += 1\n",
    "    all_node_colors.append(dict(node_colors))\n",
    "      \n",
    "    while frontier:\n",
    "        # Popping first node of stack\n",
    "        node = frontier.pop()\n",
    "        \n",
    "        # modify the currently searching node to red\n",
    "        node_colors[node.state] = \"red\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        \n",
    "        if problem.goal_test(node.state):\n",
    "            # modify goal node to green after reaching the goal\n",
    "            node_colors[node.state] = \"green\"\n",
    "            iterations += 1\n",
    "            all_node_colors.append(dict(node_colors))\n",
    "            return(iterations, all_node_colors, node)\n",
    "        \n",
    "        explored.add(node.state)\n",
    "        frontier.extend(child for child in node.expand(problem)\n",
    "                        if child.state not in explored and\n",
    "                        child not in frontier)\n",
    "        \n",
    "        for n in frontier:\n",
    "            # modify the color of frontier nodes to orange\n",
    "            node_colors[n.state] = \"orange\"\n",
    "            iterations += 1\n",
    "            all_node_colors.append(dict(node_colors))\n",
    "\n",
    "        # modify the color of explored nodes to gray\n",
    "        node_colors[node.state] = \"gray\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        \n",
    "    return None\n",
    "\n",
    "\n",
    "def depth_first_graph_search(problem):\n",
    "    \"\"\"Search the deepest nodes in the search tree first.\"\"\"\n",
    "    iterations, all_node_colors, node = graph_search_for_vis(problem)\n",
    "    return(iterations, all_node_colors, node)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
    "all_node_colors = []\n",
    "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n",
    "display_visual(romania_graph_data, user_input=False, \n",
    "               algorithm=depth_first_graph_search, \n",
    "               problem=romania_problem)"
   "cell_type": "markdown",
    "## 5. BEST FIRST SEARCH\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "\n",
    "Let's change all the `node_colors` to starting position and define a different problem statement."
   "execution_count": 22,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def best_first_graph_search_for_vis(problem, f):\n",
    "    \"\"\"Search the nodes with the lowest f scores first.\n",
    "    You specify the function f(node) that you want to minimize; for example,\n",
    "    if f is a heuristic estimate to the goal, then we have greedy best\n",
    "    first search; if f is node.depth then we have breadth-first search.\n",
    "    There is a subtlety: the line \"f = memoize(f, 'f')\" means that the f\n",
    "    values will be cached on the nodes as they are computed. So after doing\n",
    "    a best first search you can examine the f values of the path returned.\"\"\"\n",
    "    \n",
    "    # we use these two variables at the time of visualisations\n",
    "    iterations = 0\n",
    "    all_node_colors = []\n",
    "    node_colors = {k : 'white' for k in problem.graph.nodes()}\n",
    "    \n",
    "    f = memoize(f, 'f')\n",
    "    node = Node(problem.initial)\n",
    "    \n",
    "    node_colors[node.state] = \"red\"\n",
    "    iterations += 1\n",
    "    all_node_colors.append(dict(node_colors))\n",
    "    \n",
    "    if problem.goal_test(node.state):\n",
    "        node_colors[node.state] = \"green\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        return(iterations, all_node_colors, node)\n",
    "    frontier = PriorityQueue('min', f)\n",
    "    frontier.append(node)\n",
    "    \n",
    "    node_colors[node.state] = \"orange\"\n",
    "    iterations += 1\n",
    "    all_node_colors.append(dict(node_colors))\n",
    "    \n",
    "    explored = set()\n",
    "    while frontier:\n",
    "        node = frontier.pop()\n",
    "        \n",
    "        node_colors[node.state] = \"red\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        \n",
    "        if problem.goal_test(node.state):\n",
    "            node_colors[node.state] = \"green\"\n",
    "            iterations += 1\n",
    "            all_node_colors.append(dict(node_colors))\n",
    "            return(iterations, all_node_colors, node)\n",
    "        \n",
    "        explored.add(node.state)\n",
    "        for child in node.expand(problem):\n",
    "            if child.state not in explored and child not in frontier:\n",
    "                frontier.append(child)\n",
    "                node_colors[child.state] = \"orange\"\n",
    "                iterations += 1\n",
    "                all_node_colors.append(dict(node_colors))\n",
    "            elif child in frontier:\n",
    "                incumbent = frontier[child]\n",
    "                if f(child) < f(incumbent):\n",
    "                    del frontier[incumbent]\n",
    "                    frontier.append(child)\n",
    "                    node_colors[child.state] = \"orange\"\n",
    "                    iterations += 1\n",
    "                    all_node_colors.append(dict(node_colors))\n",
    "\n",
    "        node_colors[node.state] = \"gray\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
Vinay Varma's avatar
Vinay Varma a validé
    "    return None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. UNIFORM COST SEARCH\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "Let's change all the `node_colors` to starting position and define a different problem statement."
Vinay Varma's avatar
Vinay Varma a validé
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "collapsed": true
   },
Vinay Varma's avatar
Vinay Varma a validé
   "outputs": [],
   "source": [
    "def uniform_cost_search_graph(problem):\n",
    "    \"[Figure 3.14]\"\n",
Vinay Varma's avatar
Vinay Varma a validé
    "    #Uniform Cost Search uses Best First Search algorithm with f(n) = g(n)\n",
    "    iterations, all_node_colors, node = best_first_graph_search_for_vis(problem, lambda node: node.path_cost)\n",
    "    return(iterations, all_node_colors, node)\n"
   "execution_count": 24,
    "all_node_colors = []\n",
    "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n",
    "display_visual(romania_graph_data, user_input=False, \n",
    "               algorithm=uniform_cost_search_graph, \n",
    "               problem=romania_problem)"
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. Depth Limited Search\n",
    "\n",
    "Let's change all the 'node_colors' to starting position and define a different problem statement.  \n",
    "Although we have a working implementation, but we need to make changes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "def depth_limited_search(problem, limit = -1):\n",
    "    '''\n",
    "    Perform depth first search of graph g.\n",
    "    if limit >= 0, that is the maximum depth of the search.\n",
    "    '''\n",
    "    # we use these two variables at the time of visualisations\n",
    "    iterations = 0\n",
    "    all_node_colors = []\n",
    "    node_colors = {k : 'white' for k in problem.graph.nodes()}\n",
    "    \n",
    "    frontier = [Node(problem.initial)]\n",
    "    explored = set()\n",
    "    \n",
    "    cutoff_occurred = False\n",
    "    node_colors[Node(problem.initial).state] = \"orange\"\n",
    "    iterations += 1\n",
    "    all_node_colors.append(dict(node_colors))\n",
    "      \n",
    "    while frontier:\n",
    "        # Popping first node of queue\n",
    "        node = frontier.pop()\n",
    "        \n",
    "        # modify the currently searching node to red\n",
    "        node_colors[node.state] = \"red\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        \n",
    "        if problem.goal_test(node.state):\n",
    "            # modify goal node to green after reaching the goal\n",
    "            node_colors[node.state] = \"green\"\n",
    "            iterations += 1\n",
    "            all_node_colors.append(dict(node_colors))\n",
    "            return(iterations, all_node_colors, node)\n",
    "\n",
    "        elif limit >= 0:\n",
    "            cutoff_occurred = True\n",
    "            limit += 1\n",
    "            all_node_color.pop()\n",
    "            iterations -= 1\n",
    "            node_colors[node.state] = \"gray\"\n",
    "\n",
    "        \n",
    "        explored.add(node.state)\n",
    "        frontier.extend(child for child in node.expand(problem)\n",
    "                        if child.state not in explored and\n",
    "                        child not in frontier)\n",
    "        \n",
    "        for n in frontier:\n",
    "            limit -= 1\n",
    "            # modify the color of frontier nodes to orange\n",
    "            node_colors[n.state] = \"orange\"\n",
    "            iterations += 1\n",
    "            all_node_colors.append(dict(node_colors))\n",
    "\n",
    "        # modify the color of explored nodes to gray\n",
    "        node_colors[node.state] = \"gray\"\n",
    "        iterations += 1\n",
    "        all_node_colors.append(dict(node_colors))\n",
    "        \n",
    "    return 'cutoff' if cutoff_occurred else None\n",
    "\n",
    "\n",
    "def depth_limited_search_for_vis(problem):\n",
    "    \"\"\"Search the deepest nodes in the search tree first.\"\"\"\n",
    "    iterations, all_node_colors, node = depth_limited_search(problem)\n",
    "    return(iterations, all_node_colors, node)     "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "all_node_colors = []\n",
    "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n",
    "display_visual(romania_graph_data, user_input=False, \n",
    "               algorithm=depth_limited_search_for_vis, \n",
    "               problem=romania_problem)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8. Iterative deepening search\n",
    "\n",
    "Let's change all the 'node_colors' to starting position and define a different problem statement.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def iterative_deepening_search_for_vis(problem):\n",
    "    for depth in range(sys.maxsize):\n",
    "        iterations, all_node_colors, node=depth_limited_search_for_vis(problem)\n",
    "        if iterations:\n",
    "            return (iterations, all_node_colors, node)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "all_node_colors = []\n",
    "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n",
    "display_visual(romania_graph_data, user_input=False, \n",
    "               algorithm=iterative_deepening_search_for_vis, \n",
    "               problem=romania_problem)"
   ]
  },
   "cell_type": "markdown",
   "metadata": {},
Vinay Varma's avatar
Vinay Varma a validé
    "## GREEDY BEST FIRST SEARCH\n",
    "Let's change all the node_colors to starting position and define a different problem statement."
   "execution_count": 25,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
Vinay Varma's avatar
Vinay Varma a validé
    "def greedy_best_first_search(problem, h=None):\n",
    "    \"\"\"Greedy Best-first graph search is an informative searching algorithm with f(n) = h(n).\n",
    "    You need to specify the h function when you call best_first_search, or\n",
    "    else in your Problem subclass.\"\"\"\n",
    "    h = memoize(h or problem.h, 'h')\n",
    "    iterations, all_node_colors, node = best_first_graph_search_for_vis(problem, lambda n: h(n))\n",
    "    return(iterations, all_node_colors, node)\n"
   "execution_count": 26,
    "all_node_colors = []\n",
Vinay Varma's avatar
Vinay Varma a validé
    "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n",
    "display_visual(romania_graph_data, user_input=False, \n",
    "               algorithm=greedy_best_first_search, \n",
    "               problem=romania_problem)"
Vinay Varma's avatar
Vinay Varma a validé
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9. A\\* SEARCH\n",
Vinay Varma's avatar
Vinay Varma a validé
    "\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "Let's change all the `node_colors` to starting position and define a different problem statement."
   "execution_count": 27,
   "metadata": {
    "collapsed": true
   },
Vinay Varma's avatar
Vinay Varma a validé
   "outputs": [],
   "source": [
    "def astar_search_graph(problem, h=None):\n",
Vinay Varma's avatar
Vinay Varma a validé
    "    \"\"\"A* search is best-first graph search with f(n) = g(n)+h(n).\n",
    "    You need to specify the h function when you call astar_search, or\n",
    "    else in your Problem subclass.\"\"\"\n",
    "    h = memoize(h or problem.h, 'h')\n",
    "    iterations, all_node_colors, node = best_first_graph_search_for_vis(problem, \n",
    "                                                                lambda n: n.path_cost + h(n))\n",
    "    return(iterations, all_node_colors, node)\n"
Vinay Varma's avatar
Vinay Varma a validé
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
Vinay Varma's avatar
Vinay Varma a validé
   "metadata": {},
Vinay Varma's avatar
Vinay Varma a validé
   "source": [
    "all_node_colors = []\n",
    "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)\n",
    "display_visual(romania_graph_data, user_input=False, \n",
    "               algorithm=astar_search_graph, \n",
    "               problem=romania_problem)"
Vinay Varma's avatar
Vinay Varma a validé
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
Vinay Varma's avatar
Vinay Varma a validé
   "metadata": {
    "scrolled": false
   },
   "source": [
    "all_node_colors = []\n",
    "# display_visual(romania_graph_data, user_input=True, algorithm=breadth_first_tree_search)\n",
    "algorithms = {  \"Breadth First Tree Search\": breadth_first_tree_search,\n",
    "                \"Depth First Tree Search\": depth_first_tree_search,\n",
    "                \"Breadth First Search\": breadth_first_search,\n",
    "                \"Depth First Graph Search\": depth_first_graph_search,\n",
    "                \"Uniform Cost Search\": uniform_cost_search,\n",
    "                \"A-star Search\": astar_search}\n",
    "display_visual(romania_graph_data, algorithm=algorithms, user_input=True)"
surya saini's avatar
surya saini a validé
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## A* Heuristics\n",
surya saini's avatar
surya saini a validé
    "\n",
    "Different heuristics provide different efficiency in solving A* problems which are generally defined by the number of explored nodes as well as the branching factor. With the classic 8 puzzle we can show the efficiency of different heuristics through the number of explored nodes.\n",
surya saini's avatar
surya saini a validé
    "\n",
    "### 8 Puzzle Problem\n",
surya saini's avatar
surya saini a validé
    "\n",
    "The *8 Puzzle Problem* consists of a 3x3 tray in which the goal is to get the initial configuration to the goal state by shifting the numbered tiles into the blank space.\n",
surya saini's avatar
surya saini a validé
    "\n",
    "example:- \n",
surya saini's avatar
surya saini a validé
    "\n",
    "              Initial State                        Goal State\n",
    "              | 7 | 2 | 4 |                       | 1 | 2 | 3 |\n",
    "              | 5 | 0 | 6 |                       | 4 | 5 | 6 |\n",
    "              | 8 | 3 | 1 |                       | 7 | 8 | 0 |\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "We have a total of 9 blank tiles giving us a total of 9! initial configuration but not all of these are solvable. The solvability of a configuration can be checked by calculating the Inversion Permutation. If the total Inversion Permutation is even then the initial configuration is solvable else the initial configuration is not solvable which means that only 9!/2 initial states lead to a solution.\n",
    "<br>\n",
    "Let's define our goal state."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "goal = [1, 2, 3, 4, 5, 6, 7, 8, 0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Heuristics :-\n",
surya saini's avatar
surya saini a validé
    "\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "1) Manhattan Distance:- For the 8 puzzle problem Manhattan distance is defined as the distance of a tile from its goal state( for the tile numbered '1' in the initial configuration Manhattan distance is 4 \"2 for left and 2 for upward displacement\").\n",
surya saini's avatar
surya saini a validé
    "\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "2) No. of Misplaced Tiles:- The heuristic calculates the number of misplaced tiles between the current state and goal state.\n",
surya saini's avatar
surya saini a validé
    "\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "3) Sqrt of Manhattan Distance:- It calculates the square root of Manhattan distance.\n",
surya saini's avatar
surya saini a validé
    "\n",
    "4) Max Heuristic:- It assign the score as the maximum between \"Manhattan Distance\" and \"No. of Misplaced Tiles\"."
surya saini's avatar
surya saini a validé
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": true
   },
surya saini's avatar
surya saini a validé
   "outputs": [],
   "source": [
Anthony Marakis's avatar
Anthony Marakis a validé
    "# Heuristics for 8 Puzzle Problem\n",
    "def linear(node):\n",
    "    return sum([1 if node.state[i] != goal[i] else 0 for i in range(8)])\n",
    "def manhattan(node):\n",
    "    state = node.state\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "    index_goal = {0:[2,2], 1:[0,0], 2:[0,1], 3:[0,2], 4:[1,0], 5:[1,1], 6:[1,2], 7:[2,0], 8:[2,1]}\n",
    "    index_state = {}\n",
    "    index = [[0,0], [0,1], [0,2], [1,0], [1,1], [1,2], [2,0], [2,1], [2,2]]\n",
    "    x, y = 0, 0\n",
    "    \n",
    "    for i in range(len(state)):\n",
    "        index_state[state[i]] = index[i]\n",
    "    \n",
    "    mhd = 0\n",
    "    \n",
    "    for i in range(8):\n",
    "        for j in range(2):\n",
    "            mhd = abs(index_goal[i][j] - index_state[i][j]) + mhd\n",
    "    \n",
    "    return mhd\n",
surya saini's avatar
surya saini a validé
    "\n",
    "def sqrt_manhattan(node):\n",
    "    state = node.state\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "    index_goal = {0:[2,2], 1:[0,0], 2:[0,1], 3:[0,2], 4:[1,0], 5:[1,1], 6:[1,2], 7:[2,0], 8:[2,1]}\n",
    "    index_state = {}\n",
    "    index = [[0,0], [0,1], [0,2], [1,0], [1,1], [1,2], [2,0], [2,1], [2,2]]\n",
    "    x, y = 0, 0\n",
    "    \n",
    "    for i in range(len(state)):\n",
    "        index_state[state[i]] = index[i]\n",
    "    \n",
    "    mhd = 0\n",
    "    \n",
    "    for i in range(8):\n",
    "        for j in range(2):\n",
    "            mhd = (index_goal[i][j] - index_state[i][j])**2 + mhd\n",
    "    \n",
    "    return math.sqrt(mhd)\n",
surya saini's avatar
surya saini a validé
    "\n",
    "def max_heuristic(node):\n",
    "    score1 = manhattan(node)\n",
    "    score2 = linear(node)\n",
Anthony Marakis's avatar
Anthony Marakis a validé
    "    return max(score1, score2)"
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can solve the puzzle using the `astar_search` method."
   ]
  },
surya saini's avatar
surya saini a validé
  {
   "cell_type": "code",
   "execution_count": 32,
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
surya saini's avatar
surya saini a validé
   "source": [
    "# Solving the puzzle \n",
    "puzzle = EightPuzzle((2, 4, 3, 1, 5, 6, 7, 8, 0))\n",
    "puzzle.check_solvability((2, 4, 3, 1, 5, 6, 7, 8, 0)) # checks whether the initialized configuration is solvable or not"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This case is solvable, let's proceed.\n",
    "<br>\n",
    "The default heuristic function returns the number of misplaced tiles."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['UP', 'LEFT', 'UP', 'LEFT', 'DOWN', 'RIGHT', 'RIGHT', 'DOWN']"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "astar_search(puzzle).solution()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In the following cells, we use different heuristic functions.\n",
    "<br>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['UP', 'LEFT', 'UP', 'LEFT', 'DOWN', 'RIGHT', 'RIGHT', 'DOWN']"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }