#!/usr/bin/env python # coding: utf-8 # # Problem-Solving Agents (Section 3.1) # # Goal-based agents for which states of the environment are considered # as atomic representations, ones with no visible internal structure. # # Formulating the search problem. We assume # * environment's state is **observable** # * environment's state is **discrete** # * environment is **deterministic** # * environment is **sequential** # * environment is **static** # * **single** agent # # Problem definition: # * set of possible states # * initial state # * possible actions available at each state # * transition model, from state, action pair to next state # * goal test # * path cost, in this chapter assumed to be sum of step costs # # Definition of state set is critical. Want enough detail to enable # discovery of useful solution, but want minimal detail to ensure a # practical search. # # Consider the [sliding tile 8-puzzle](http://www.tilepuzzles.com/default.asp?p=12). Example of # * too much detail # * a state includes positions of every tile, orientation in three dimensions of puzzle, time of day, what you had for breakfast, the age of your older sister # * too little detail # * state is either "initial" or "solved" # # How would you write the problem definition for our simple graph? # * set of possible states? # * initial state? # * possible actions available at each state? # * goal test? # * path cost? # # How would you write the problem definition for the [8 or 15 puzzle](http://en.wikipedia.org/wiki/Fifteen_puzzle)? # * set of possible states? # * initial state? # * possible actions available at each state? # * goal test? # * path cost? # # How would you write the problem definition for the [Towers of Hanoi puzzle?](http://en.wikipedia.org/wiki/Tower_of_Hanoi) # * set of possible states? # * initial state? # * possible actions available at each state? # * goal test? # * path cost? # # How would you write the problem definition for the [Peg Board Puzzle](http://pegboardgame.blogspot.com/)? # * set of possible states? # * initial state? # * possible actions available at each state? # * goal test? # * path cost? # # How would you write the problem definition for scheduling 10 different observations using the [Hubble Space Telescope](https://arxiv.org/pdf/1810.04815.pdf)? # * set of possible states? # * initial state? # * possible actions available at each state? # * goal test? # * path cost? # # Ways of searching a graph to find path from start to goal node? # # Uninformed search means that the choice of action is not "informed" by any knowledge of the goal. # # Breadth-first search completely explores each level of the search space before proceeding to the next. # # Depth-first search completely explores a path until it ends, then backs up a level and tries again. # In[2]: from IPython.display import IFrame IFrame("https://www.cs.colostate.edu/~anderson/cs440/notebooks/simplegraphsteps.pdf", width=800, height=600) # This example does not show the reduced space requirements of # depth-first search. If the solution path was via the second child of 'a', # then we would see that the nodes through the first child of 'a' do not # need to be saved. # # Here is an algorithm definition for both breadth-first and depth-first # search, tailored to fit the python implementation you must complete # for # [Assignment 1](http://nbviewer.ipython.org/url/www.cs.colostate.edu/~anderson/cs440/notebooks/A1%20Uninformed%20Search.ipynb). The # algorithm maintains a local variable named `un_expanded` to be a list # of nodes whose children have not yet been generated (like the authors' # `frontier` variable), and a dictionary named `expanded` to keep # the nodes for which we have generated the children (like the authors' # `explored` variable). In each a node is stored with its parent, # allowing a solution path to be generated be stepping backwards from # the goal node once it is found. # # Given the `start_state`, `goal_state`, `successors_f`, and # `breadth_first` (a boolean variable): # # * Initialize `expanded` to be an empty dictionary # * Initialize `un_expanded` to be a list containing the pair `(start_state, None)` # * If `start_state` is the `goal_state`, return the list containing just `start_state` # * Repeat the following steps while `un_expanded` is not empty: # * Pop from the end of `un_expanded` a (`state`, `parent`) pair. # * Generate the `children` of `state` using the `successors_f` function. # * Add `tuple(state): parent` to the `expanded` dictionary # * For efficiency, remove from `children` any states that are already in `expanded` or `un_expanded`. When looking for states in `expanded`, remember to apply `tuple` to them first. # * If the goal has been found (in python, `goal_state` is in `children`): # * Initialize the solution path with the list `[state, goal_state]`. # * While `parent` exists: # * Insert `parent` to the front of the solution path. # * Set `parent` to the parent of `parent`. # * Return the solution path. # * Sort and reverse the list of states in `children`, so that we all find the same solution paths. # * Create a modified `children` list by changing each entry to be a pair (`child`, `parent`), where `parent` is the parent of the child. # * Insert the modified `children` list into the `un_expanded` list at the front if doing breadth-first search, or at the back if doing depth-first search. Use the boolean variable `breadth_first` provided as the last argument in the call to this function to control inserting at the front the back. Do this insertion with one statement, not a for loop, to preserve the order of the children. # The line that starts with "For efficiency, remove from children any states ..." can be tricky, especially if you try to implement this with a for loop that steps through the elements of `children` and remove them. # # Here is an example of a result that might surprise you. # In[4]: nums = [0, 6, 4, 5, 2, 3, 9, 7, 8] nums # In[5]: for num in nums: print(num) # In[6]: for num in nums: if num < 5: nums.remove(num) nums # Why is 3 still in this list? # # This is because you are modifying the list `nums` which is controlling the for loop. After 2 is removed, the for loop moves on to the value at 9. (Try printing the value of `nums` at the end of each repetition to see this.) # # How can we fix this? There are two ways. The first is to use a copy of the `nums` list to control the for loop, so you are free to modify the original `nums` list. # In[7]: import copy nums = [0, 6, 4, 5, 2, 3, 9, 7, 8] for num in copy.copy(nums): if num < 5: nums.remove(num) nums # A second way is to use a list comprehension. This ends up constructing a new list, keeping just the elements you want. # In[10]: nums = [0, 6, 4, 5, 2, 3, 9, 7, 8] nums = [num for num in nums if num >= 5] nums