Skip to content

Search notebook update #933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@ Here is a table of algorithms, the figure, name of the algorithm in the book and
| 3.18 | Iterative-Deepening-Search | `iterative_deepening_search` | [`search.py`][search] | Done | Included |
| 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 | |
| 3.26 | Recursive-Best-First-Search | `recursive_best_first_search` | [`search.py`][search] | Done | Included |
| 4.2 | Hill-Climbing | `hill_climbing` | [`search.py`][search] | Done | Included |
| 4.5 | Simulated-Annealing | `simulated_annealing` | [`search.py`][search] | Done | Included |
| 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 | |
| 4.21 | Online-DFS-Agent | `online_dfs_agent` | [`search.py`][search] | | |
| 4.24 | LRTA\*-Agent | `LRTAStarAgent` | [`search.py`][search] | Done | |
| 4.11 | And-Or-Graph-Search | `and_or_graph_search` | [`search.py`][search] | Done | Included |
| 4.21 | Online-DFS-Agent | `online_dfs_agent` | [`search.py`][search] | Done | Included |
| 4.24 | LRTA\*-Agent | `LRTAStarAgent` | [`search.py`][search] | Done | Included |
| 5.3 | Minimax-Decision | `minimax_decision` | [`games.py`][games] | Done | Included |
| 5.7 | Alpha-Beta-Search | `alphabeta_search` | [`games.py`][games] | Done | Included |
| 6 | CSP | `CSP` | [`csp.py`][csp] | Done | Included |
Expand Down
7 changes: 6 additions & 1 deletion notebook.py
Original file line number Diff line number Diff line change
Expand Up @@ -991,8 +991,13 @@ def visualize_callback(Visualize):
"Depth First Tree Search",
"Breadth First Search",
"Depth First Graph Search",
"Best First Graph Search",
"Uniform Cost Search",
"A-star Search"})
"Depth Limited Search",
"Iterative Deepening Search",
"Greedy Best First Search",
"A-star Search",
"Recursive Best First Search"})

algo_dropdown = widgets.Dropdown(description="Search algorithm: ",
options=sorted(list(algorithm.keys())),
Expand Down
5,203 changes: 3,200 additions & 2,003 deletions search.ipynb

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions search.py
Original file line number Diff line number Diff line change
Expand Up @@ -767,8 +767,8 @@ def __init__(self, problem):
self.problem = problem
self.s = None
self.a = None
self.untried = defaultdict(list)
self.unbacktracked = defaultdict(list)
self.untried = dict()
self.unbacktracked = dict()
self.result = {}

def __call__(self, percept):
Expand All @@ -787,13 +787,13 @@ def __call__(self, percept):
self.a = None
else:
# else a <- an action b such that result[s', b] = POP(unbacktracked[s'])
unbacktracked_pop = self.unbacktracked[s1].pop(0)
unbacktracked_pop = self.unbacktracked.pop(s1)
for (s, b) in self.result.keys():
if self.result[(s, b)] == unbacktracked_pop:
self.a = b
break
else:
self.a = self.untried[s1].pop(0)
self.a = self.untried.pop(s1)
self.s = s1
return self.a

Expand Down Expand Up @@ -1120,7 +1120,7 @@ def distance_to_node(n):
7 - CCL Clean Clean Left
8 - CCR Clean Clean Right
"""
vacumm_world = Graph(dict(
vacuum_world = Graph(dict(
State_1=dict(Suck=['State_7', 'State_5'], Right=['State_2']),
State_2=dict(Suck=['State_8', 'State_4'], Left=['State_2']),
State_3=dict(Suck=['State_7'], Right=['State_4']),
Expand Down
65 changes: 50 additions & 15 deletions tests/test_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@


romania_problem = GraphProblem('Arad', 'Bucharest', romania_map)
vacumm_world = GraphProblemStochastic('State_1', ['State_7', 'State_8'], vacumm_world)
vacuum_world = GraphProblemStochastic('State_1', ['State_7', 'State_8'], vacuum_world)
LRTA_problem = OnlineSearchProblem('State_3', 'State_5', one_dim_state_space)
eight_puzzle = EightPuzzle((1, 2, 3, 4, 5, 7, 8, 6, 0))
eight_puzzle2 = EightPuzzle((1, 0, 6, 8, 7, 5, 4, 2), (0, 1, 2, 3, 4, 5, 6, 7, 8))
nqueens = NQueensProblem(8)


def test_find_min_edge():
assert romania_problem.find_min_edge() == 70

Expand Down Expand Up @@ -151,7 +152,33 @@ def test_conflict():
def test_recursive_best_first_search():
assert recursive_best_first_search(
romania_problem).solution() == ['Sibiu', 'Rimnicu', 'Pitesti', 'Bucharest']
assert recursive_best_first_search(
EightPuzzle((2, 4, 3, 1, 5, 6, 7, 8, 0))).solution() == [
'UP', 'LEFT', 'UP', 'LEFT', 'DOWN', 'RIGHT', 'RIGHT', 'DOWN'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably ok, but are you sure that there isn't another solution of equal length?

Copy link
Contributor Author

@ad71 ad71 Jul 6, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@norvig , there is in fact another solution of equal length

['LEFT', 'UP', 'UP', 'LEFT', 'DOWN', 'RIGHT', 'DOWN', 'RIGHT']

but this algorithm (or any search algorithm for that matter) gives one particular optimal solution every time, even if there exists another one of the same length. This happens due to an implementational detail in EightPuzzle.
In the EightPuzzle class, the actions method returns the possible actions, but the actions are stored in a list and every time node.expand(problem) is called from RBFS, the neighbors are traversed in the order [up, down, left, right], which is why the algorithm converges to the same solution every time. I also ran it for 1000 cycles just to make sure.
This can be fixed (though I don't think it requires fixing) by returning a set of possible actions instead of a list, but a lot of tests will be rendered useless.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To keep it simple, I'll accept this as is, but it would be a better test if it didn't rely on an arbitrary choice of ordering.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is, the test could be something like : achieves_goal(start, s) and len(s) == 8

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I'll take care of this in the next update

]

def manhattan(node):
state = node.state
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]}
index_state = {}
index = [[0,0], [0,1], [0,2], [1,0], [1,1], [1,2], [2,0], [2,1], [2,2]]
x, y = 0, 0

for i in range(len(state)):
index_state[state[i]] = index[i]

mhd = 0

for i in range(8):
for j in range(2):
mhd = abs(index_goal[i][j] - index_state[i][j]) + mhd

return mhd

assert recursive_best_first_search(
EightPuzzle((2, 4, 3, 1, 5, 6, 7, 8, 0)), h=manhattan).solution() == [
'LEFT', 'UP', 'UP', 'LEFT', 'DOWN', 'RIGHT', 'DOWN', 'UP', 'DOWN', 'RIGHT'
]

def test_hill_climbing():
prob = PeakFindingProblem((0, 0), [[0, 5, 10, 20],
Expand Down Expand Up @@ -200,23 +227,31 @@ def run_plan(state, problem, plan):
return False
predicate = lambda x: run_plan(x, problem, plan[1][x])
return all(predicate(r) for r in problem.result(state, plan[0]))
plan = and_or_graph_search(vacumm_world)
assert run_plan('State_1', vacumm_world, plan)
plan = and_or_graph_search(vacuum_world)
assert run_plan('State_1', vacuum_world, plan)


def test_online_dfs_agent():
odfs_agent = OnlineDFSAgent(LRTA_problem)
keys = [key for key in odfs_agent('State_3')]
assert keys[0] in ['Right', 'Left']
assert keys[1] in ['Right', 'Left']
assert odfs_agent('State_5') is None


def test_LRTAStarAgent():
my_agent = LRTAStarAgent(LRTA_problem)
assert my_agent('State_3') == 'Right'
assert my_agent('State_4') == 'Left'
assert my_agent('State_3') == 'Right'
assert my_agent('State_4') == 'Right'
assert my_agent('State_5') is None

my_agent = LRTAStarAgent(LRTA_problem)
assert my_agent('State_4') == 'Left'

my_agent = LRTAStarAgent(LRTA_problem)
assert my_agent('State_5') is None
lrta_agent = LRTAStarAgent(LRTA_problem)
assert lrta_agent('State_3') == 'Right'
assert lrta_agent('State_4') == 'Left'
assert lrta_agent('State_3') == 'Right'
assert lrta_agent('State_4') == 'Right'
assert lrta_agent('State_5') is None

lrta_agent = LRTAStarAgent(LRTA_problem)
assert lrta_agent('State_4') == 'Left'

lrta_agent = LRTAStarAgent(LRTA_problem)
assert lrta_agent('State_5') is None


def test_genetic_algorithm():
Expand Down