From c988f3073bc75d9d76815f09dd2f755339fb7984 Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Fri, 2 Mar 2018 06:02:32 +0530 Subject: [PATCH 1/3] Added section on Hill Climbing --- search.ipynb | 1006 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 983 insertions(+), 23 deletions(-) diff --git a/search.ipynb b/search.ipynb index 332ba11b9..2ac393ea0 100644 --- a/search.ipynb +++ b/search.ipynb @@ -85,12 +85,160 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": true - }, - "outputs": [], + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " Codestin Search App\n", + " \n", + " \n", + "\n", + "\n", + "

\n", + "\n", + "
class Problem(object):\n",
+       "\n",
+       "    """The abstract class for a formal problem. You should subclass\n",
+       "    this and implement the methods actions and result, and possibly\n",
+       "    __init__, goal_test, and path_cost. Then you will create instances\n",
+       "    of your subclass and solve them with the various search functions."""\n",
+       "\n",
+       "    def __init__(self, initial, goal=None):\n",
+       "        """The constructor specifies the initial state, and possibly a goal\n",
+       "        state, if there is a unique goal. Your subclass's constructor can add\n",
+       "        other arguments."""\n",
+       "        self.initial = initial\n",
+       "        self.goal = goal\n",
+       "\n",
+       "    def actions(self, state):\n",
+       "        """Return the actions that can be executed in the given\n",
+       "        state. The result would typically be a list, but if there are\n",
+       "        many actions, consider yielding them one at a time in an\n",
+       "        iterator, rather than building them all at once."""\n",
+       "        raise NotImplementedError\n",
+       "\n",
+       "    def result(self, state, action):\n",
+       "        """Return the state that results from executing the given\n",
+       "        action in the given state. The action must be one of\n",
+       "        self.actions(state)."""\n",
+       "        raise NotImplementedError\n",
+       "\n",
+       "    def goal_test(self, state):\n",
+       "        """Return True if the state is a goal. The default method compares the\n",
+       "        state to self.goal or checks for state in self.goal if it is a\n",
+       "        list, as specified in the constructor. Override this method if\n",
+       "        checking against a single self.goal is not enough."""\n",
+       "        if isinstance(self.goal, list):\n",
+       "            return is_in(state, self.goal)\n",
+       "        else:\n",
+       "            return state == self.goal\n",
+       "\n",
+       "    def path_cost(self, c, state1, action, state2):\n",
+       "        """Return the cost of a solution path that arrives at state2 from\n",
+       "        state1 via action, assuming cost c to get up to state1. If the problem\n",
+       "        is such that the path doesn't matter, this function will only look at\n",
+       "        state2.  If the path does matter, it will consider c and maybe state1\n",
+       "        and action. The default method costs 1 for every step in the path."""\n",
+       "        return c + 1\n",
+       "\n",
+       "    def value(self, state):\n",
+       "        """For optimization problems, each state has a value.  Hill-climbing\n",
+       "        and related algorithms try to maximize this value."""\n",
+       "        raise NotImplementedError\n",
+       "
\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "%psource Problem" + "psource(Problem)" ] }, { @@ -128,13 +276,173 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " Codestin Search App\n", + " \n", + " \n", + "\n", + "\n", + "

\n", + "\n", + "
class Node:\n",
+       "\n",
+       "    """A node in a search tree. Contains a pointer to the parent (the node\n",
+       "    that this is a successor of) and to the actual state for this node. Note\n",
+       "    that if a state is arrived at by two paths, then there are two nodes with\n",
+       "    the same state.  Also includes the action that got us to this state, and\n",
+       "    the total path_cost (also known as g) to reach the node.  Other functions\n",
+       "    may add an f and h value; see best_first_graph_search and astar_search for\n",
+       "    an explanation of how the f and h values are handled. You will not need to\n",
+       "    subclass this class."""\n",
+       "\n",
+       "    def __init__(self, state, parent=None, action=None, path_cost=0):\n",
+       "        """Create a search tree Node, derived from a parent by an action."""\n",
+       "        self.state = state\n",
+       "        self.parent = parent\n",
+       "        self.action = action\n",
+       "        self.path_cost = path_cost\n",
+       "        self.depth = 0\n",
+       "        if parent:\n",
+       "            self.depth = parent.depth + 1\n",
+       "\n",
+       "    def __repr__(self):\n",
+       "        return "<Node {}>".format(self.state)\n",
+       "\n",
+       "    def __lt__(self, node):\n",
+       "        return self.state < node.state\n",
+       "\n",
+       "    def expand(self, problem):\n",
+       "        """List the nodes reachable in one step from this node."""\n",
+       "        return [self.child_node(problem, action)\n",
+       "                for action in problem.actions(self.state)]\n",
+       "\n",
+       "    def child_node(self, problem, action):\n",
+       "        """[Figure 3.10]"""\n",
+       "        next = problem.result(self.state, action)\n",
+       "        return Node(next, self, action,\n",
+       "                    problem.path_cost(self.path_cost, self.state,\n",
+       "                                      action, next))\n",
+       "\n",
+       "    def solution(self):\n",
+       "        """Return the sequence of actions to go from the root to this node."""\n",
+       "        return [node.action for node in self.path()[1:]]\n",
+       "\n",
+       "    def path(self):\n",
+       "        """Return a list of nodes forming the path from the root to this node."""\n",
+       "        node, path_back = self, []\n",
+       "        while node:\n",
+       "            path_back.append(node)\n",
+       "            node = node.parent\n",
+       "        return list(reversed(path_back))\n",
+       "\n",
+       "    # We want for a queue of nodes in breadth_first_search or\n",
+       "    # astar_search to have no duplicated states, so we treat nodes\n",
+       "    # with the same state as equal. [Problem: this may not be what you\n",
+       "    # want in other contexts.]\n",
+       "\n",
+       "    def __eq__(self, other):\n",
+       "        return isinstance(other, Node) and self.state == other.state\n",
+       "\n",
+       "    def __hash__(self):\n",
+       "        return hash(self.state)\n",
+       "
\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "%psource Node" + "psource(Node)" ] }, { @@ -171,13 +479,150 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " Codestin Search App\n", + " \n", + " \n", + "\n", + "\n", + "

\n", + "\n", + "
class GraphProblem(Problem):\n",
+       "\n",
+       "    """The problem of searching a graph from one node to another."""\n",
+       "\n",
+       "    def __init__(self, initial, goal, graph):\n",
+       "        Problem.__init__(self, initial, goal)\n",
+       "        self.graph = graph\n",
+       "\n",
+       "    def actions(self, A):\n",
+       "        """The actions at a graph node are just its neighbors."""\n",
+       "        return list(self.graph.get(A).keys())\n",
+       "\n",
+       "    def result(self, state, action):\n",
+       "        """The result of going to a neighbor is just that neighbor."""\n",
+       "        return action\n",
+       "\n",
+       "    def path_cost(self, cost_so_far, A, action, B):\n",
+       "        return cost_so_far + (self.graph.get(A, B) or infinity)\n",
+       "\n",
+       "    def find_min_edge(self):\n",
+       "        """Find minimum value of edges."""\n",
+       "        m = infinity\n",
+       "        for d in self.graph.dict.values():\n",
+       "            local_min = min(d.values())\n",
+       "            m = min(m, local_min)\n",
+       "\n",
+       "        return m\n",
+       "\n",
+       "    def h(self, node):\n",
+       "        """h function is straight-line distance from a node's state to goal."""\n",
+       "        locs = getattr(self.graph, 'locations', None)\n",
+       "        if locs:\n",
+       "            if type(node) is str:\n",
+       "                return int(distance(locs[node], locs[self.goal]))\n",
+       "\n",
+       "            return int(distance(locs[node.state], locs[self.goal]))\n",
+       "        else:\n",
+       "            return infinity\n",
+       "
\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "%psource GraphProblem" + "psource(GraphProblem)" ] }, { @@ -484,13 +929,146 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " Codestin Search App\n", + " \n", + " \n", + "\n", + "\n", + "

\n", + "\n", + "
class SimpleProblemSolvingAgentProgram:\n",
+       "\n",
+       "    """Abstract framework for a problem-solving agent. [Figure 3.1]"""\n",
+       "\n",
+       "    def __init__(self, initial_state=None):\n",
+       "        """State is an abstract representation of the state\n",
+       "        of the world, and seq is the list of actions required\n",
+       "        to get to a particular state from the initial state(root)."""\n",
+       "        self.state = initial_state\n",
+       "        self.seq = []\n",
+       "\n",
+       "    def __call__(self, percept):\n",
+       "        """[Figure 3.1] Formulate a goal and problem, then\n",
+       "        search for a sequence of actions to solve it."""\n",
+       "        self.state = self.update_state(self.state, percept)\n",
+       "        if not self.seq:\n",
+       "            goal = self.formulate_goal(self.state)\n",
+       "            problem = self.formulate_problem(self.state, goal)\n",
+       "            self.seq = self.search(problem)\n",
+       "            if not self.seq:\n",
+       "                return None\n",
+       "        return self.seq.pop(0)\n",
+       "\n",
+       "    def update_state(self, percept):\n",
+       "        raise NotImplementedError\n",
+       "\n",
+       "    def formulate_goal(self, state):\n",
+       "        raise NotImplementedError\n",
+       "\n",
+       "    def formulate_problem(self, state, goal):\n",
+       "        raise NotImplementedError\n",
+       "\n",
+       "    def search(self, problem):\n",
+       "        raise NotImplementedError\n",
+       "
\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "%psource SimpleProblemSolvingAgentProgram" + "psource(SimpleProblemSolvingAgentProgram)" ] }, { @@ -1482,6 +2060,388 @@ "puzzle.solve([2,4,3,1,5,6,7,8,0], [1,2,3,4,5,6,7,8,0],sqrt_manhanttan) # Sqrt_manhattan" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## HILL CLIMBING\n", + "\n", + "Hill Climbing is a heuristic search used for optimization problems.\n", + "Given a large set of inputs and a good heuristic function, it tries to find a sufficiently good solution to the problem. \n", + "This solution may or may not be the global optimum.\n", + "The algorithm is a variant of generate and test algorithm. \n", + "
\n", + "As a whole, the algorithm works as follows:\n", + "- Evaluate the initial state.\n", + "- If it is equal to the goal state, return.\n", + "- Find a neighboring state (one which is heuristically similar to the current state)\n", + "- Evaluate this state. If it is closer to the goal state than before, replace the initial state with this state and repeat these steps.\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " Codestin Search App\n", + " \n", + " \n", + "\n", + "\n", + "

\n", + "\n", + "
def hill_climbing(problem):\n",
+       "    """From the initial node, keep choosing the neighbor with highest value,\n",
+       "    stopping when no neighbor is better. [Figure 4.2]"""\n",
+       "    current = Node(problem.initial)\n",
+       "    while True:\n",
+       "        neighbors = current.expand(problem)\n",
+       "        if not neighbors:\n",
+       "            break\n",
+       "        neighbor = argmax_random_tie(neighbors,\n",
+       "                                     key=lambda node: problem.value(node.state))\n",
+       "        if problem.value(neighbor.state) <= problem.value(current.state):\n",
+       "            break\n",
+       "        current = neighbor\n",
+       "    return current.state\n",
+       "
\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "psource(hill_climbing)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will find an approximate solution to the traveling salespersons problem using this algorithm.\n", + "
\n", + "We need to define a class for this problem.\n", + "
\n", + "`Problem` will be used as a base class." + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "class TSP_problem(Problem):\n", + "\n", + " \"\"\" subclass of Problem to define various functions \"\"\"\n", + "\n", + " def two_opt(self, state):\n", + " \"\"\" Neighbour generating function for Traveling Salesman Problem \"\"\"\n", + " neighbour_state = state[:]\n", + " left = random.randint(0, len(neighbour_state) - 1)\n", + " right = random.randint(0, len(neighbour_state) - 1)\n", + " if left > right:\n", + " left, right = right, left\n", + " neighbour_state[left: right + 1] = reversed(neighbour_state[left: right + 1])\n", + " return neighbour_state\n", + "\n", + " def actions(self, state):\n", + " \"\"\" action that can be excuted in given state \"\"\"\n", + " return [self.two_opt]\n", + "\n", + " def result(self, state, action):\n", + " \"\"\" result after applying the given action on the given state \"\"\"\n", + " return action(state)\n", + "\n", + " def path_cost(self, c, state1, action, state2):\n", + " \"\"\" total distance for the Traveling Salesman to be covered if in state2 \"\"\"\n", + " cost = 0\n", + " for i in range(len(state2) - 1):\n", + " cost += distances[state2[i]][state2[i + 1]]\n", + " cost += distances[state2[0]][state2[-1]]\n", + " return cost\n", + "\n", + " def value(self, state):\n", + " \"\"\" value of path cost given negative for the given state \"\"\"\n", + " return -1 * self.path_cost(None, None, None, state)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We will use cities from the Romania map as our cities for this problem.\n", + "
\n", + "A list of all cities and a dictionary storing distances between them will be populated." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Arad', 'Bucharest', 'Craiova', 'Drobeta', 'Eforie', 'Fagaras', 'Giurgiu', 'Hirsova', 'Iasi', 'Lugoj', 'Mehadia', 'Neamt', 'Oradea', 'Pitesti', 'Rimnicu', 'Sibiu', 'Timisoara', 'Urziceni', 'Vaslui', 'Zerind']\n" + ] + } + ], + "source": [ + "distances = {}\n", + "all_cities = []\n", + "\n", + "for city in romania_map.locations.keys():\n", + " distances[city] = {}\n", + " all_cities.append(city)\n", + " \n", + "all_cities.sort()\n", + "print(all_cities)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we need to populate the individual lists inside the dictionary with the manhattan distance between the cities." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "for name_1, coordinates_1 in romania_map.locations.items():\n", + " for name_2, coordinates_2 in romania_map.locations.items():\n", + " distances[name_1][name_2] = np.linalg.norm(\n", + " [coordinates_1[0] - coordinates_2[0], coordinates_1[1] - coordinates_2[1]])\n", + " distances[name_2][name_1] = np.linalg.norm(\n", + " [coordinates_1[0] - coordinates_2[0], coordinates_1[1] - coordinates_2[1]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The way neighbours are chosen currently isn't suitable for the travelling salespersons problem.\n", + "We need a neighboring state that is similar in total path distance to the current state.\n", + "
\n", + "We need to change the function that finds neighbors." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def hill_climbing(problem):\n", + " \n", + " \"\"\"From the initial node, keep choosing the neighbor with highest value,\n", + " stopping when no neighbor is better. [Figure 4.2]\"\"\"\n", + " \n", + " def find_neighbors(state, number_of_neighbors=100):\n", + " \"\"\" finds neighbors using two_opt method \"\"\"\n", + " \n", + " neighbors = []\n", + " \n", + " for i in range(number_of_neighbors):\n", + " new_state = problem.two_opt(state)\n", + " neighbors.append(Node(new_state))\n", + " state = new_state\n", + " \n", + " return neighbors\n", + "\n", + " # as this is a stochastic algorithm, we will set a cap on the number of iterations\n", + " iterations = 10000\n", + " \n", + " current = Node(problem.initial)\n", + " while iterations:\n", + " neighbors = find_neighbors(current.state)\n", + " if not neighbors:\n", + " break\n", + " neighbor = argmax_random_tie(neighbors,\n", + " key=lambda node: problem.value(node.state))\n", + " if problem.value(neighbor.state) <= problem.value(current.state):\n", + " current.state = neighbor.state\n", + " iterations -= 1\n", + " \n", + " return current.state" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "An instance of the TSP_problem class will be created." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "tsp = TSP_problem(all_cities)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now generate an approximate solution to the problem by calling `hill_climbing`.\n", + "The results will vary a bit each time you run it." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Fagaras',\n", + " 'Neamt',\n", + " 'Iasi',\n", + " 'Vaslui',\n", + " 'Hirsova',\n", + " 'Eforie',\n", + " 'Urziceni',\n", + " 'Bucharest',\n", + " 'Giurgiu',\n", + " 'Pitesti',\n", + " 'Craiova',\n", + " 'Drobeta',\n", + " 'Mehadia',\n", + " 'Lugoj',\n", + " 'Timisoara',\n", + " 'Arad',\n", + " 'Zerind',\n", + " 'Oradea',\n", + " 'Sibiu',\n", + " 'Rimnicu']" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "hill_climbing(tsp)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The solution looks like this.\n", + "It is not difficult to see why this might be a good solution.\n", + "
\n", + "![title](images/hillclimb-tsp.png)" + ] + }, { "cell_type": "markdown", "metadata": {}, From 87f5fad28d262ef2b21a22e61a46870f5fa9cd7e Mon Sep 17 00:00:00 2001 From: AngryCracker Date: Fri, 2 Mar 2018 06:02:50 +0530 Subject: [PATCH 2/3] Added images --- images/hillclimb-tsp.png | Bin 0 -> 32028 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/hillclimb-tsp.png diff --git a/images/hillclimb-tsp.png b/images/hillclimb-tsp.png new file mode 100644 index 0000000000000000000000000000000000000000..8446bbafc45203f5a29feb1218a793c9583e8866 GIT binary patch literal 32028 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 Date: Fri, 2 Mar 2018 06:06:07 +0530 Subject: [PATCH 3/3] Updated README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2dcf7d368..fc5f38bb5 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and | 3.22 | Best-First-Search | `best_first_graph_search` | [`search.py`][search] | Done | Included | | 3.24 | A\*-Search | `astar_search` | [`search.py`][search] | Done | Included | | 3.26 | Recursive-Best-First-Search | `recursive_best_first_search` | [`search.py`][search] | Done | | -| 4.2 | Hill-Climbing | `hill_climbing` | [`search.py`][search] | Done | | +| 4.2 | Hill-Climbing | `hill_climbing` | [`search.py`][search] | Done | Included | | 4.5 | Simulated-Annealing | `simulated_annealing` | [`search.py`][search] | Done | | | 4.8 | Genetic-Algorithm | `genetic_algorithm` | [`search.py`][search] | Done | Included | | 4.11 | And-Or-Graph-Search | `and_or_graph_search` | [`search.py`][search] | Done | |