From c5dce791c0285e7d5a5781eb4ecff6a4810ffb87 Mon Sep 17 00:00:00 2001 From: AdityaDaflapurkar Date: Thu, 11 Jan 2018 01:07:24 +0530 Subject: [PATCH 1/4] Update PeakFindingProblem code to allow diagonal motion --- search.py | 49 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/search.py b/search.py index 19481ea31..f1e632d96 100644 --- a/search.py +++ b/search.py @@ -512,34 +512,65 @@ def and_search(states, problem, path): class PeakFindingProblem(Problem): """Problem of finding the highest peak in a limited grid""" - def __init__(self, initial, grid): + def __init__(self, initial, grid, allow_diagonal_motion): """The grid is a 2 dimensional array/list whose state is specified by tuple of indices""" Problem.__init__(self, initial) self.grid = grid + self.allow_diagonal_motion = allow_diagonal_motion self.n = len(grid) assert self.n > 0 self.m = len(grid[0]) assert self.m > 0 def actions(self, state): - """Allows movement in only 4 directions""" - # TODO: Add flag to allow diagonal motion + """Allows motion only in the 4 cardinal directions if diagonal motion is not allowed. Otherwise allows motion in all 8 directions""" allowed_actions = [] if state[0] > 0: + allowed_actions.append('W') + if state[1] > 0: allowed_actions.append('N') if state[0] < self.n - 1: - allowed_actions.append('S') - if state[1] > 0: - allowed_actions.append('W') - if state[1] < self.m - 1: allowed_actions.append('E') + if state[1] < self.m - 1: + allowed_actions.append('S') + + if allow_diagonal_motion: + if state[0] > 0 and state[1] > 0: + allowed_actions.append('NW') + if state[0] < self.n - 1 and state[1] > 0: + allowed_actions.append('NE') + if state[0] < self.n - 1 and state[1] < self.m - 1: + allowed_actions.append('SE') + if state[0] > 0 and state[1] < self.m - 1: + allowed_actions.append('SW') + return allowed_actions def result(self, state, action): """Moves in the direction specified by action""" x, y = state - x = x + (1 if action == 'S' else (-1 if action == 'N' else 0)) - y = y + (1 if action == 'E' else (-1 if action == 'W' else 0)) + + if action == 'W': + x = x - 1 + elif action == 'N': + y = y - 1 + elif action == 'E': + x = x + 1 + elif action == 'S': + y = y + 1 + elif action == 'NW': + x = x - 1 + y = y - 1 + elif action == 'NE': + x = x + 1 + y = y - 1 + elif action == 'SW': + x = x - 1 + y = y + 1 + elif action == 'SE': + x = x + 1 + y = y + 1 + return (x, y) def value(self, state): From aff7680294b8c9f5fd9abcf5b721ef85f899220b Mon Sep 17 00:00:00 2001 From: AdityaDaflapurkar Date: Thu, 11 Jan 2018 01:26:49 +0530 Subject: [PATCH 2/4] Fix unit test issues --- search.py | 2 +- tests/test_search.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/search.py b/search.py index f1e632d96..95c85c607 100644 --- a/search.py +++ b/search.py @@ -534,7 +534,7 @@ def actions(self, state): if state[1] < self.m - 1: allowed_actions.append('S') - if allow_diagonal_motion: + if self.allow_diagonal_motion: if state[0] > 0 and state[1] > 0: allowed_actions.append('NW') if state[0] < self.n - 1 and state[1] > 0: diff --git a/tests/test_search.py b/tests/test_search.py index f22ca6f89..524e60e9a 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -73,27 +73,27 @@ def test_recursive_best_first_search(): def test_hill_climbing(): prob = PeakFindingProblem((0, 0), [[0, 5, 10, 20], - [-3, 7, 11, 5]]) + [-3, 7, 11, 5]], False) assert hill_climbing(prob) == (0, 3) prob = PeakFindingProblem((0, 0), [[0, 5, 10, 8], [-3, 7, 9, 999], - [1, 2, 5, 11]]) + [1, 2, 5, 11]], False) assert hill_climbing(prob) == (0, 2) prob = PeakFindingProblem((2, 0), [[0, 5, 10, 8], [-3, 7, 9, 999], - [1, 2, 5, 11]]) + [1, 2, 5, 11]], False) assert hill_climbing(prob) == (1, 3) def test_simulated_annealing(): random.seed("aima-python") prob = PeakFindingProblem((0, 0), [[0, 5, 10, 20], - [-3, 7, 11, 5]]) + [-3, 7, 11, 5]], False) sols = {prob.value(simulated_annealing(prob)) for i in range(100)} assert max(sols) == 20 prob = PeakFindingProblem((0, 0), [[0, 5, 10, 8], [-3, 7, 9, 999], - [1, 2, 5, 11]]) + [1, 2, 5, 11]], False) sols = {prob.value(simulated_annealing(prob)) for i in range(100)} assert max(sols) == 999 From a5fbb7a49ace05bbb953bd0c8a4b86d5af58a46d Mon Sep 17 00:00:00 2001 From: AdityaDaflapurkar Date: Sun, 14 Jan 2018 02:01:22 +0530 Subject: [PATCH 3/4] update PeakFindingProblem to take actions as input param --- search.py | 54 +++++++++++--------------------------------- tests/test_search.py | 10 ++++---- 2 files changed, 18 insertions(+), 46 deletions(-) diff --git a/search.py b/search.py index 95c85c607..a2ba81677 100644 --- a/search.py +++ b/search.py @@ -508,41 +508,30 @@ def and_search(states, problem, path): # body of and or search return or_search(problem.initial, problem, []) +# Pre-defined actions for PeakFindingProblem +primary_actions = { 'W':(-1,0), 'N':(0,1), 'E':(1,0), 'S':(0,-1) } +aggregate_actions = { 'W':(-1,0), 'N':(0,1), 'E':(1,0), 'S':(0,-1), 'NW':(-1,1), 'NE':(1,1), 'SE':(1,-1), 'SW':(-1,-1) } class PeakFindingProblem(Problem): """Problem of finding the highest peak in a limited grid""" - def __init__(self, initial, grid, allow_diagonal_motion): + def __init__(self, initial, grid, defined_actions = primary_actions): """The grid is a 2 dimensional array/list whose state is specified by tuple of indices""" Problem.__init__(self, initial) self.grid = grid - self.allow_diagonal_motion = allow_diagonal_motion + self.defined_actions = defined_actions self.n = len(grid) assert self.n > 0 self.m = len(grid[0]) assert self.m > 0 def actions(self, state): - """Allows motion only in the 4 cardinal directions if diagonal motion is not allowed. Otherwise allows motion in all 8 directions""" + """Returns the list of actions which are allowed to be taken from the given state""" allowed_actions = [] - if state[0] > 0: - allowed_actions.append('W') - if state[1] > 0: - allowed_actions.append('N') - if state[0] < self.n - 1: - allowed_actions.append('E') - if state[1] < self.m - 1: - allowed_actions.append('S') - - if self.allow_diagonal_motion: - if state[0] > 0 and state[1] > 0: - allowed_actions.append('NW') - if state[0] < self.n - 1 and state[1] > 0: - allowed_actions.append('NE') - if state[0] < self.n - 1 and state[1] < self.m - 1: - allowed_actions.append('SE') - if state[0] > 0 and state[1] < self.m - 1: - allowed_actions.append('SW') + for action in self.defined_actions: + next_state = (state[0] + self.defined_actions[action][0], state[1] + self.defined_actions[action][1]) + if next_state[0] >= 0 and next_state[1] >= 0 and next_state[0] <= self.n - 1 and next_state[1] <= self.m - 1: + allowed_actions.append(action) return allowed_actions @@ -550,26 +539,8 @@ def result(self, state, action): """Moves in the direction specified by action""" x, y = state - if action == 'W': - x = x - 1 - elif action == 'N': - y = y - 1 - elif action == 'E': - x = x + 1 - elif action == 'S': - y = y + 1 - elif action == 'NW': - x = x - 1 - y = y - 1 - elif action == 'NE': - x = x + 1 - y = y - 1 - elif action == 'SW': - x = x - 1 - y = y + 1 - elif action == 'SE': - x = x + 1 - y = y + 1 + x = x + self.defined_actions[action][0] + y = y + self.defined_actions[action][1] return (x, y) @@ -1360,3 +1331,4 @@ def compare_graph_searchers(): GraphProblem('Q', 'WA', australia_map)], header=['Searcher', 'romania_map(Arad, Bucharest)', 'romania_map(Oradea, Neamt)', 'australia_map']) + diff --git a/tests/test_search.py b/tests/test_search.py index 524e60e9a..7c343d752 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -73,27 +73,27 @@ def test_recursive_best_first_search(): def test_hill_climbing(): prob = PeakFindingProblem((0, 0), [[0, 5, 10, 20], - [-3, 7, 11, 5]], False) + [-3, 7, 11, 5]]) assert hill_climbing(prob) == (0, 3) prob = PeakFindingProblem((0, 0), [[0, 5, 10, 8], [-3, 7, 9, 999], - [1, 2, 5, 11]], False) + [1, 2, 5, 11]]) assert hill_climbing(prob) == (0, 2) prob = PeakFindingProblem((2, 0), [[0, 5, 10, 8], [-3, 7, 9, 999], - [1, 2, 5, 11]], False) + [1, 2, 5, 11]]) assert hill_climbing(prob) == (1, 3) def test_simulated_annealing(): random.seed("aima-python") prob = PeakFindingProblem((0, 0), [[0, 5, 10, 20], - [-3, 7, 11, 5]], False) + [-3, 7, 11, 5]], primary_actions) sols = {prob.value(simulated_annealing(prob)) for i in range(100)} assert max(sols) == 20 prob = PeakFindingProblem((0, 0), [[0, 5, 10, 8], [-3, 7, 9, 999], - [1, 2, 5, 11]], False) + [1, 2, 5, 11]], aggregate_actions) sols = {prob.value(simulated_annealing(prob)) for i in range(100)} assert max(sols) == 999 From a317307d5ec7310e7e5d52afc493bd2b94decf65 Mon Sep 17 00:00:00 2001 From: AdityaDaflapurkar Date: Fri, 26 Jan 2018 11:02:01 +0530 Subject: [PATCH 4/4] Refactor code in search.py --- search.py | 18 +++++++----------- tests/test_search.py | 4 ++-- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/search.py b/search.py index 8ce46a3d9..8bf742489 100644 --- a/search.py +++ b/search.py @@ -7,7 +7,7 @@ from utils import ( is_in, argmin, argmax, argmax_random_tie, probability, weighted_sampler, memoize, print_table, open_data, Stack, FIFOQueue, PriorityQueue, name, - distance + distance, vector_add ) from collections import defaultdict @@ -527,13 +527,14 @@ def and_search(states, problem, path): return or_search(problem.initial, problem, []) # Pre-defined actions for PeakFindingProblem -primary_actions = { 'W':(-1,0), 'N':(0,1), 'E':(1,0), 'S':(0,-1) } -aggregate_actions = { 'W':(-1,0), 'N':(0,1), 'E':(1,0), 'S':(0,-1), 'NW':(-1,1), 'NE':(1,1), 'SE':(1,-1), 'SW':(-1,-1) } +directions4 = { 'W':(-1, 0), 'N':(0, 1), 'E':(1, 0), 'S':(0, -1) } +directions8 = dict(directions4) +directions8.update({'NW':(-1, 1), 'NE':(1, 1), 'SE':(1, -1), 'SW':(-1, -1) }) class PeakFindingProblem(Problem): """Problem of finding the highest peak in a limited grid""" - def __init__(self, initial, grid, defined_actions = primary_actions): + def __init__(self, initial, grid, defined_actions=directions4): """The grid is a 2 dimensional array/list whose state is specified by tuple of indices""" Problem.__init__(self, initial) self.grid = grid @@ -547,7 +548,7 @@ def actions(self, state): """Returns the list of actions which are allowed to be taken from the given state""" allowed_actions = [] for action in self.defined_actions: - next_state = (state[0] + self.defined_actions[action][0], state[1] + self.defined_actions[action][1]) + next_state = vector_add(state, self.defined_actions[action]) if next_state[0] >= 0 and next_state[1] >= 0 and next_state[0] <= self.n - 1 and next_state[1] <= self.m - 1: allowed_actions.append(action) @@ -555,12 +556,7 @@ def actions(self, state): def result(self, state, action): """Moves in the direction specified by action""" - x, y = state - - x = x + self.defined_actions[action][0] - y = y + self.defined_actions[action][1] - - return (x, y) + return vector_add(state, self.defined_actions[action]) def value(self, state): """Value of a state is the value it is the index to""" diff --git a/tests/test_search.py b/tests/test_search.py index 7c343d752..04cb2db35 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -88,12 +88,12 @@ def test_hill_climbing(): def test_simulated_annealing(): random.seed("aima-python") prob = PeakFindingProblem((0, 0), [[0, 5, 10, 20], - [-3, 7, 11, 5]], primary_actions) + [-3, 7, 11, 5]], directions4) sols = {prob.value(simulated_annealing(prob)) for i in range(100)} assert max(sols) == 20 prob = PeakFindingProblem((0, 0), [[0, 5, 10, 8], [-3, 7, 9, 999], - [1, 2, 5, 11]], aggregate_actions) + [1, 2, 5, 11]], directions8) sols = {prob.value(simulated_annealing(prob)) for i in range(100)} assert max(sols) == 999