diff --git a/notebook.py b/notebook.py index d60ced855..29501e9d2 100644 --- a/notebook.py +++ b/notebook.py @@ -1113,3 +1113,42 @@ def plot_pomdp_utility(utility): plt.text((right + left)/2 - 0.02, 10, 'Ask') plt.text((right + 1)/2 - 0.07, 10, 'Delete') plt.show() + + +# Function to Visualize Water Pouring Problem +def visual_pour(searcher, problem): + "Show what happens when searcvher solves problem." + problem = Instrumented(problem) + print('\n{}:'.format(searcher.__name__)) + result = searcher(problem) + if result: + node = result + "The sequence of actions to get to this node." + action = [] + while node.parent: + action.append(node.action) + node = node.parent + actions = action[::-1] + state = problem.initial + path_cost = 0 + for steps, action in enumerate(actions, 1): + path_cost += problem.step_cost(state, action, 0) + result = problem.result(state, action) + print(' {} =={}==> {}; cost {} after {} steps' + .format(state, action, result, path_cost, steps, + '; GOAL!' if problem.goal_test(result) else '')) + state = result + msg = 'GOAL FOUND' if result else 'no solution' + print('{} after {} results and {} goal checks' + .format(msg, problem._counter['result'], problem._counter['goal_test'])) + + +class Instrumented: + "Instrument an object to count all the attribute accesses in _counter." + def __init__(self, obj): + self._object = obj + self._counter = Counter() + + def __getattr__(self, attr): + self._counter[attr] += 1 + return getattr(self._object, attr) diff --git a/search.ipynb b/search.ipynb index aeb035902..182476fc5 100644 --- a/search.ipynb +++ b/search.ipynb @@ -20,7 +20,7 @@ "outputs": [], "source": [ "from search import *\n", - "from notebook import psource, heatmap, gaussian_kernel, show_map, final_path_colors, display_visual, plot_NQueens\n", + "from notebook import psource, heatmap, gaussian_kernel, show_map, final_path_colors, display_visual, plot_NQueens , visual_pour\n", "\n", "# Needed to hide warnings in the matplotlib sections\n", "import warnings\n", @@ -47,6 +47,7 @@ "* Hill Climbing\n", "* Simulated Annealing\n", "* Genetic Algorithm\n", + "* Water Pouring Problem\n", "* AND-OR Graph Search\n", "* Online DFS Agent\n", "* LRTA* Agent" @@ -1459,7 +1460,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -1544,7 +1545,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -4951,6 +4952,428 @@ "*NOTE: Because the algorithm is non-deterministic, there is a chance a different solution is given. It might even be wrong, if we are very unlucky!*" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Water Pouring Problem\n", + "\n", + "Here is another problem domain, to show you how to define one. The idea is that we have a number of water jugs and a water tap and the goal is to measure out a specific amount of water (in, say, ounces or liters). You can completely fill or empty a jug, but because the jugs don't have markings on them, you can't partially fill them with a specific amount. You can, however, pour one jug into another, stopping when the second is full or the first is empty." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "
\n", + "class PourProblem(Problem):\n",
+ " """Problem about pouring water between jugs to achieve some water level.\n",
+ " Each state is a tuples of levels. In the initialization, provide a tuple of \n",
+ " capacities, e.g. PourProblem(capacities=(8, 16, 32), initial=(2, 4, 3), \n",
+ " goals={7}), which means three jugs of capacity 8, 16, 32, currently filled \n",
+ " with 2, 4, 3 units of water, respectively, and the goal is to get a level \n",
+ " of 7 in any one of the jugs."""\n",
+ " def __init__(self, initial = None, goals = (), capacities = None):\n",
+ " self.initial = initial\n",
+ " self.goals = goals\n",
+ " self.capacities = capacities\n",
+ "\n",
+ " def actions(self, state):\n",
+ " """The actions executable in this state."""\n",
+ " jugs = range(len(state))\n",
+ " return ([('Fill', i) for i in jugs if state[i] != self.capacities[i]] +\n",
+ " [('Dump', i) for i in jugs if state[i] != 0] +\n",
+ " [('Pour', i, j) for i in jugs for j in jugs if i != j])\n",
+ "\n",
+ " def result(self, state, action):\n",
+ " """The state that results from executing this action in this state."""\n",
+ " result = list(state)\n",
+ " act, i, j = action[0], action[1], action[-1]\n",
+ " if act == 'Fill': # Fill i to capacity\n",
+ " result[i] = self.capacities[i]\n",
+ " elif act == 'Dump': # Empty i\n",
+ " result[i] = 0\n",
+ " elif act == 'Pour':\n",
+ " a, b = state[i], state[j]\n",
+ " result[i], result[j] = ((0, a + b) \n",
+ " if (a + b <= self.capacities[j]) else\n",
+ " (a + b - self.capacities[j], self.capacities[j]))\n",
+ " else:\n",
+ " raise ValueError('unknown action', action)\n",
+ " return tuple(result)\n",
+ "\n",
+ " def goal_test(self, state):\n",
+ " """True if any of the jugs has a level equal to one of the goal levels."""\n",
+ " return any(level in self.goals for level in state)\n",
+ " \n",
+ " \n",
+ " def step_cost(self, state, action, result=None):\n",
+ " "The cost of taking this action from this state."\n",
+ " return 1 # Override this if actions have different costs\n",
+ "def visual_pour(searcher, problem):\n",
+ " "Show what happens when searcvher solves problem."\n",
+ " problem = Instrumented(problem)\n",
+ " print('\\n{}:'.format(searcher.__name__))\n",
+ " result = searcher(problem)\n",
+ " if result:\n",
+ " node = result\n",
+ " "The sequence of actions to get to this node."\n",
+ " action = []\n",
+ " while node.parent:\n",
+ " action.append(node.action)\n",
+ " node = node.parent\n",
+ " actions = action[::-1]\n",
+ " state = problem.initial\n",
+ " path_cost = 0\n",
+ " for steps, action in enumerate(actions, 1):\n",
+ " path_cost += problem.step_cost(state, action, 0)\n",
+ " result = problem.result(state, action)\n",
+ " print(' {} =={}==> {}; cost {} after {} steps'\n",
+ " .format(state, action, result, path_cost, steps,\n",
+ " '; GOAL!' if problem.goal_test(result) else ''))\n",
+ " state = result\n",
+ " msg = 'GOAL FOUND' if result else 'no solution'\n",
+ " print('{} after {} results and {} goal checks'\n",
+ " .format(msg, problem._counter['result'], problem._counter['goal_test']))\n",
+ "