diff --git a/gui/romania_problem.py b/gui/romania_problem.py index f13c7a1f5..67eced970 100644 --- a/gui/romania_problem.py +++ b/gui/romania_problem.py @@ -5,7 +5,8 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..')) from search import * from search import breadth_first_tree_search as bfts, depth_first_tree_search as dfts, \ - depth_first_graph_search as dfgs, breadth_first_search as bfs, uniform_cost_search as ucs + depth_first_graph_search as dfgs, breadth_first_search as bfs, uniform_cost_search as ucs, \ + astar_search as asts from utils import Stack, FIFOQueue, PriorityQueue from copy import deepcopy @@ -512,6 +513,14 @@ def uniform_cost_search(problem): return best_first_graph_search(problem, lambda node: node.path_cost) +def astar_search(problem, h=None): + """A* search is best-first graph search with f(n) = g(n)+h(n). + You need to specify the h function when you call astar_search, or + else in your Problem subclass.""" + h = memoize(h or problem.h, 'h') + return best_first_graph_search(problem, lambda n: n.path_cost + h(n)) + + # TODO: # Remove redundant code. # Make the interchangbility work between various algorithms at each step. @@ -561,6 +570,14 @@ def on_click(): display_final(final_path) next_button.config(state="disabled") counter += 1 + elif "A* - Search" == algo.get(): + node = astar_search(romania_problem) + if node is not None: + final_path = asts(romania_problem).solution() + final_path.append(start.get()) + display_final(final_path) + next_button.config(state="disabled") + counter += 1 def reset_map(): @@ -589,7 +606,7 @@ def main(): root, algo, "Breadth-First Tree Search", "Depth-First Tree Search", "Breadth-First Search", "Depth-First Graph Search", - "Uniform Cost Search") + "Uniform Cost Search", "A* - Search") Label(root, text="\n Search Algorithm").pack() algorithm_menu.pack() Label(root, text="\n Start City").pack() diff --git a/search.ipynb b/search.ipynb index b8edde1e9..019ea8eb4 100644 --- a/search.ipynb +++ b/search.ipynb @@ -15,7 +15,6 @@ "cell_type": "code", "execution_count": 1, "metadata": { - "collapsed": true, "scrolled": true }, "outputs": [], @@ -81,9 +80,7 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%psource Problem" @@ -123,9 +120,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%psource GraphProblem" @@ -141,9 +136,7 @@ { "cell_type": "code", "execution_count": 4, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "romania_map = UndirectedGraph(dict(\n", @@ -188,9 +181,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)" @@ -221,7 +212,11 @@ "name": "stdout", "output_type": "stream", "text": [ +<<<<<<< HEAD + "{'Rimnicu': (233, 410), 'Timisoara': (94, 410), 'Iasi': (473, 506), 'Neamt': (406, 537), 'Fagaras': (305, 449), 'Giurgiu': (375, 270), 'Urziceni': (456, 350), 'Mehadia': (168, 339), 'Lugoj': (165, 379), 'Sibiu': (207, 457), 'Oradea': (131, 571), 'Zerind': (108, 531), 'Craiova': (253, 288), 'Hirsova': (534, 350), 'Arad': (91, 492), 'Vaslui': (509, 444), 'Drobeta': (165, 299), 'Bucharest': (400, 327), 'Eforie': (562, 293), 'Pitesti': (320, 368)}\n" +======= "{'Oradea': (131, 571), 'Eforie': (562, 293), 'Timisoara': (94, 410), 'Hirsova': (534, 350), 'Bucharest': (400, 327), 'Rimnicu': (233, 410), 'Fagaras': (305, 449), 'Lugoj': (165, 379), 'Giurgiu': (375, 270), 'Mehadia': (168, 339), 'Pitesti': (320, 368), 'Drobeta': (165, 299), 'Craiova': (253, 288), 'Sibiu': (207, 457), 'Iasi': (473, 506), 'Urziceni': (456, 350), 'Vaslui': (509, 444), 'Neamt': (406, 537), 'Zerind': (108, 531), 'Arad': (91, 492)}\n" +>>>>>>> 8561c52d63fcaef4c0f99d997073aeb93e926e56 ] } ], @@ -240,10 +235,26 @@ { "cell_type": "code", "execution_count": 7, - "metadata": { - "collapsed": true - }, - "outputs": [], + "metadata": {}, + "outputs": [ + { + "ename": "ImportError", + "evalue": "No module named 'matplotlib'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mImportError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mget_ipython\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun_line_magic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'matplotlib'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'inline'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mnetworkx\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mnx\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mmatplotlib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpyplot\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mplt\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mmatplotlib\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mlines\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.5/site-packages/IPython/core/interactiveshell.py\u001b[0m in \u001b[0;36mrun_line_magic\u001b[0;34m(self, magic_name, line, _stack_depth)\u001b[0m\n\u001b[1;32m 2093\u001b[0m \u001b[0mkwargs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'local_ns'\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msys\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_getframe\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mstack_depth\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mf_locals\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2094\u001b[0m \u001b[0;32mwith\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbuiltin_trap\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2095\u001b[0;31m \u001b[0mresult\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2096\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2097\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mmatplotlib\u001b[0;34m(self, line)\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.5/site-packages/IPython/core/magic.py\u001b[0m in \u001b[0;36m\u001b[0;34m(f, *a, **k)\u001b[0m\n\u001b[1;32m 185\u001b[0m \u001b[0;31m# but it's overkill for just that one bit of state.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 186\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mmagic_deco\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 187\u001b[0;31m \u001b[0mcall\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mlambda\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mk\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 188\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 189\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mcallable\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0marg\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.5/site-packages/IPython/core/magics/pylab.py\u001b[0m in \u001b[0;36mmatplotlib\u001b[0;34m(self, line)\u001b[0m\n\u001b[1;32m 97\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Available matplotlib backends: %s\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mbackends_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 99\u001b[0;31m \u001b[0mgui\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbackend\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mshell\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0menable_matplotlib\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgui\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 100\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_show_matplotlib_backend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgui\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbackend\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 101\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.5/site-packages/IPython/core/interactiveshell.py\u001b[0m in \u001b[0;36menable_matplotlib\u001b[0;34m(self, gui)\u001b[0m\n\u001b[1;32m 2964\u001b[0m \"\"\"\n\u001b[1;32m 2965\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mIPython\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcore\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mpylabtools\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mpt\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2966\u001b[0;31m \u001b[0mgui\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbackend\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfind_gui_and_backend\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mgui\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpylab_gui_select\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 2967\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2968\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mgui\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;34m'inline'\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/.local/lib/python3.5/site-packages/IPython/core/pylabtools.py\u001b[0m in \u001b[0;36mfind_gui_and_backend\u001b[0;34m(gui, gui_select)\u001b[0m\n\u001b[1;32m 268\u001b[0m \"\"\"\n\u001b[1;32m 269\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 270\u001b[0;31m \u001b[0;32mimport\u001b[0m \u001b[0mmatplotlib\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 271\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 272\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mgui\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mgui\u001b[0m \u001b[0;34m!=\u001b[0m \u001b[0;34m'auto'\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mImportError\u001b[0m: No module named 'matplotlib'" + ] + } + ], "source": [ "%matplotlib inline\n", "import networkx as nx\n", @@ -266,10 +277,20 @@ { "cell_type": "code", "execution_count": 8, - "metadata": { - "collapsed": true - }, - "outputs": [], + "metadata": {}, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'nx' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# initialise a graph\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mG\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mGraph\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;31m# use this while labeling nodes in the map\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mnode_labels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdict\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mNameError\u001b[0m: name 'nx' is not defined" + ] + } + ], "source": [ "# initialise a graph\n", "G = nx.Graph()\n", @@ -1250,6 +1271,219 @@ "display_visual(user_input = True)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A* Search Heuristics Comparison\n", + "\n", + "Different Heuristics have different efficiency in solving a particular problem via A* search which is generally defined by the node of explored nodes as well as the branching factor. With the help of the Classic 8* Puzzle we can effectively visualize the difference in performance of these heuristics. \n", + "\n", + "### 8-Puzzle Problem\n", + "\n", + "*8-Puzzle Problem* is another problem that is classified as NP hard for which genetic algorithms provide a better solution than any pre-existing ones.\n", + "\n", + "The *8-Puzzle Problem* consists of a *3x3 tray* in which 8 tiles numbered 1-8 are placed and the 9th tile is uncovered. The aim of the game is that given a initial placement of the tiles, we have to reach the goal state on the constraint that a tile adjacent to be the blank space can be slid into that space.\n", + "\n", + "*example:*\n", + " Initial State Goal State\n", + "\n", + " | 7 | 2 | 4 | | | 1 | 2 |\n", + " | 5 | | 6 | ----> | 3 | 4 | 5 |\n", + " | 8 | 3 | 1 | | 6 | 7 | 8 |\n", + "\n", + "We have a total of 8+1(blank) tiles giving us total of 9! initial configurations but of all these configurations only 9!/2 can lead to a solution.The solvability can be checked by calculating the *Permutation Inversion* of each tile and then summing it up.\n", + "Inversion is defined as when a tile preceeds another tile with lower number.\n", + "Let's calculate the Permutation Inversion of the example shown above -\n", + " \n", + " Tile 7 -> 6 Inversions (for tile 2, 4, 5, 6, 3, 1)\n", + " Tile 2 -> 1 Inversions\n", + " Tile 4 -> 2 Inversions\n", + " Tile 5 -> 2 Inversions\n", + " Tile 6 -> 2 Inversions\n", + " Tile 8 -> 2 Inversions\n", + " Tile 3 -> 1 Inversions\n", + " Tile 1 -> 0 Inversions\n", + "Total Inversions = 16 Inversions, \n", + "Is total Inversions are even then the initial configuration is solvable else the configuration is impossible to solve.\n", + "\n", + "For example we can have a state \"724506831\" where 0 represents the empty tile.\n", + "\n", + "#### Heuristics:-\n", + "1.) Manhattan Distance:- For the 8 Puzzle problem \"Manhattan distance is defined as the distance of a tile from its \n", + " goal. In the example shown above the manhattan distance for the 'numbered tile 1' is 4\n", + " (2 unit left and 2 unit up).\n", + "\n", + "2.) No. of Misplaced Tiles:- This heuristics calculates the number of misplaced tile in the state from the goal \n", + " state.\n", + "\n", + "3.) Sqrt of Manhattan Distance:- Uses the sqaure root of the Manhattan distance\n", + "\n", + "4.) Max Heuristic :- Score on the basis of max of Manhattan Distance and No. of Misplced tiles." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# define heuristics\n", + "def linear(state,goal):\n", + " return sum([1 if state[i] != goal[i] else 0 for i in range(8)])\n", + "\n", + "def manhanttan(state,goal):\n", + " 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=0\n", + " y=0\n", + " for i in range(len(state)):\n", + " index_state[state[i]] = index[i]\n", + " mhd = 0\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", + " return mhd\n", + "\n", + "def sqrt_manhanttan(state,goal):\n", + " 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=0\n", + " y=0\n", + " for i in range(len(state)):\n", + " index_state[state[i]] = index[i]\n", + " mhd = 0\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", + " return math.sqrt(mhd)\n", + "\n", + "def max_heuristic(state,goal):\n", + " score1 = manhanttan(state, goal)\n", + " score2 = linear(state, goal)\n", + " return max(score1, score2)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Algorithm for 8 Puzzle problem\n", + "\n", + "def checkSolvability(state):\n", + " inversion = 0\n", + " for i in range(len(state)):\n", + " for j in range(i,len(state)):\n", + " if (state[i]>state[j] and state[j]!=0):\n", + " inversion += 1\n", + " check = True\n", + " if inversion%2 != 0:\n", + " check = False\n", + " print(check)\n", + " return check\n", + "\n", + "def getPossibleMoves(state,heuristic,goal,moves):\n", + " move = {0:[1,3], 1:[0,2,4], 2:[1,5], 3:[0,6,4], 4:[1,3,5,7], 5:[2,4,8], 6:[3,7], 7:[6,8], 8:[7,5]} # create a dictionary of moves\n", + " index = state[0].index(0)\n", + " possible_moves = []\n", + " for i in range(len(move[index])):\n", + " conf = list(state[0][:])\n", + " a = conf[index]\n", + " b = conf[move[index][i]]\n", + " conf[move[index][i]] = a\n", + " conf[index] = b\n", + " possible_moves.append(conf)\n", + " scores = []\n", + " for i in possible_moves:\n", + " scores.append(heuristic(i,goal))\n", + " scores = [x+moves for x in scores]\n", + " allowed_state = []\n", + " for i in range(len(possible_moves)):\n", + " node = []\n", + " node.append(possible_moves[i])\n", + " node.append(scores[i])\n", + " node.append(state[0])\n", + " allowed_state.append(node) \n", + " return allowed_state\n", + "\n", + "path = []\n", + "final = []\n", + "def create_path(goal,initial):\n", + " node = goal[0]\n", + " final.append(goal[0])\n", + " if goal[2] == initial:\n", + " return reversed(final)\n", + " else:\n", + " parent = goal[2]\n", + " for i in path:\n", + " if i[0] == parent:\n", + " parent = i\n", + " create_path(parent,initial)\t\n", + "\n", + "def show_path(initial):\n", + " move = []\n", + " for i in range(0,len(path)):\n", + " move.append(''.join(str(x) for x in path[i][0]))\n", + " print(\"Number of explored nodes by the following heuristic are: \", len(set(move)))\t\n", + " print(initial)\n", + " for i in reversed(final):\n", + " print(i)\n", + " return\n", + "\n", + "def solve(initial,goal,heuristic):\n", + " root = [initial,heuristic(initial,goal),'']\n", + " nodes = [] # nodes is a priority Queue based on the state score \n", + " nodes.append(root)\n", + " moves = 0\n", + " while len(nodes) != 0:\n", + " node = nodes[0]\n", + " del nodes[0]\n", + " path.append(node)\n", + " if node[0] == goal:\n", + " soln = create_path(path[-1],initial )\n", + " show_path(initial)\n", + " return \n", + " moves +=1\n", + " opened_nodes = getPossibleMoves(node,heuristic,goal,moves)\n", + " nodes = sorted(opened_nodes+nodes, key=itemgetter(1))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Heuristics is max_heuristic\n", + "True\n", + "Number of explored nodes by the following heuristic are: 126\n", + "[2, 4, 3, 1, 5, 6, 7, 8, 0]\n", + "[2, 4, 3, 1, 5, 0, 7, 8, 6]\n", + "[2, 4, 3, 1, 0, 5, 7, 8, 6]\n", + "[2, 0, 3, 1, 4, 5, 7, 8, 6]\n", + "[0, 2, 3, 1, 4, 5, 7, 8, 6]\n", + "[1, 2, 3, 0, 4, 5, 7, 8, 6]\n", + "[1, 2, 3, 4, 0, 5, 7, 8, 6]\n", + "[1, 2, 3, 4, 5, 0, 7, 8, 6]\n", + "[1, 2, 3, 4, 5, 6, 7, 8, 0]\n" + ] + } + ], + "source": [ + "goal_state = [1,2,3,4,5,6,7,8,0] # define the goal state\n", + "initial_state = [2,4,3,1,5,6,7,8,0] # define the initial state\n", + "print(\"Heuristics is max_heuristic\")\n", + "checkSolvability(initial_state)\n", + "solve(initial_state,goal_state,max_heuristic) # to check the different heuristics change the function name in solve" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1360,9 +1594,7 @@ { "cell_type": "code", "execution_count": 2, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%psource genetic_algorithm" @@ -1401,9 +1633,7 @@ { "cell_type": "code", "execution_count": 3, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%psource reproduce" @@ -1421,9 +1651,7 @@ { "cell_type": "code", "execution_count": 4, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%psource mutate" @@ -1441,9 +1669,7 @@ { "cell_type": "code", "execution_count": 5, - "metadata": { - "collapsed": true - }, + "metadata": {}, "outputs": [], "source": [ "%psource init_population" @@ -1484,10 +1710,8 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": { - "collapsed": true - }, + "execution_count": 12, + "metadata": {}, "outputs": [], "source": [ "edges = {\n", @@ -1509,14 +1733,14 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[['R', 'G', 'G', 'R'], ['R', 'G', 'R', 'R'], ['G', 'R', 'G', 'R'], ['R', 'G', 'R', 'G'], ['G', 'R', 'R', 'G'], ['G', 'R', 'G', 'R'], ['G', 'R', 'R', 'R'], ['R', 'G', 'G', 'G']]\n" + "[['R', 'G', 'G', 'R'], ['G', 'R', 'G', 'G'], ['G', 'G', 'G', 'G'], ['R', 'G', 'G', 'G'], ['R', 'G', 'G', 'R'], ['G', 'R', 'G', 'R'], ['G', 'G', 'G', 'R'], ['G', 'R', 'G', 'R']]\n" ] } ], @@ -1536,10 +1760,8 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": { - "collapsed": true - }, + "execution_count": 14, + "metadata": {}, "outputs": [], "source": [ "def fitness(c):\n", @@ -1555,14 +1777,14 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "['R', 'G', 'R', 'G']\n" + "['G', 'R', 'G', 'R']\n" ] } ], @@ -1580,7 +1802,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -1625,14 +1847,14 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[[0, 2, 7, 1, 7, 3, 2, 4], [2, 7, 5, 4, 4, 5, 2, 0], [7, 1, 6, 0, 1, 3, 0, 2], [0, 3, 6, 1, 3, 0, 5, 4], [0, 4, 6, 4, 7, 4, 1, 6]]\n" + "[[6, 7, 3, 6, 3, 0, 1, 4], [7, 1, 4, 1, 5, 2, 0, 0], [1, 4, 7, 0, 0, 2, 5, 2], [2, 0, 3, 7, 5, 7, 0, 0], [6, 3, 1, 7, 5, 6, 3, 0]]\n" ] } ], @@ -1656,10 +1878,8 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": { - "collapsed": true - }, + "execution_count": 7, + "metadata": {}, "outputs": [], "source": [ "def fitness(q):\n", @@ -1688,20 +1908,20 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "[5, 0, 6, 3, 7, 4, 1, 3]\n", - "26\n" + "[3, 5, 7, 2, 0, 6, 4, 1]\n", + "28\n" ] } ], "source": [ - "solution = genetic_algorithm(population, fitness, f_thres=25, gene_pool=range(8))\n", + "solution = genetic_algorithm(population, fitness, f_thres=28, gene_pool=range(8))\n", "print(solution)\n", "print(fitness(solution))" ] @@ -1719,6 +1939,13 @@ "source": [ "With that this tutorial on the genetic algorithm comes to an end. Hope you found this guide helpful!" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -1737,7 +1964,11 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", +<<<<<<< HEAD + "version": "3.5.4rc1" +======= "version": "3.5.2" +>>>>>>> 8561c52d63fcaef4c0f99d997073aeb93e926e56 }, "widgets": { "state": { diff --git a/search.py b/search.py index d136c0135..ea58d18f1 100644 --- a/search.py +++ b/search.py @@ -15,6 +15,7 @@ import random import sys import bisect +from operator import itemgetter infinity = float('inf') @@ -23,14 +24,14 @@ class Problem(object): - """The abstract class for a formal problem. You should subclass + """The abstract class for a formal problem. You should subclass this and implement the methods actions and result, and possibly __init__, goal_test, and path_cost. Then you will create instances of your subclass and solve them with the various search functions.""" def __init__(self, initial, goal=None): """The constructor specifies the initial state, and possibly a goal - state, if there is a unique goal. Your subclass's constructor can add + state, if there is a unique goal. Your subclass's constructor can add other arguments.""" self.initial = initial self.goal = goal @@ -637,7 +638,7 @@ class LRTAStarAgent: """ [Figure 4.24] Abstract class for LRTA*-Agent. A problem needs to be - provided which is an instanace of a subclass of Problem Class. + provided which is an instance of a subclass of Problem Class. Takes a OnlineSearchProblem [Figure 4.23] as a problem. """ @@ -708,14 +709,26 @@ def genetic_algorithm(population, fitness_fn, gene_pool=[0, 1], f_thres=None, ng population = [mutate(recombine(*select(2, population, fitness_fn)), gene_pool, pmut) for i in range(len(population))] - if f_thres: - fittest_individual = argmax(population, key=fitness_fn) - if fitness_fn(fittest_individual) >= f_thres: - return fittest_individual + fittest_individual = fitness_threshold(fitness_fn, f_thres, population) + if fittest_individual: + return fittest_individual + return argmax(population, key=fitness_fn) +def fitness_threshold(fitness_fn, f_thres, population): + if not f_thres: + return None + + fittest_individual = argmax(population, key=fitness_fn) + if fitness_fn(fittest_individual) >= f_thres: + return fittest_individual + + return None + + + def init_population(pop_number, gene_pool, state_length): """Initializes population for genetic algorithm pop_number : Number of individuals in population