class TestRace(unittest.TestCase): def setUp(self): romania = pickle.load(open('romania_graph.pickle', 'rb')) self.romania = ExplorableGraph(romania) self.romania.reset_search() # you can also load atlanta graph like in unit tests. def test_call_load_data(self): max_time = 600 * 1000 # time in milliseconds start_time_ms = get_time_milliseconds() def time_left(): return max_time - (get_time_milliseconds() - start_time_ms) data = load_data(self.romania, time_left) if time_left() < 0: self.fail(msg="You went over the maximum time for load_data.") def test_run_race(self): max_time = 600 * 1000 # time in milliseconds start_time_ms = get_time_milliseconds() def time_left(): return max_time - (get_time_milliseconds() - start_time_ms) data = load_data(self.romania, time_left) start = 'a' goal = 'u' path = custom_search(self.romania, start, goal, data=data)
class TestBidirectionalSearch(unittest.TestCase): """Test the bidirectional search algorithms: UCS, A*""" def setUp(self): """Load Atlanta map data""" with open('atlanta_osm.pickle', 'rb') as atl: atlanta = pickle.load(atl) self.atlanta = ExplorableGraph(atlanta) self.atlanta.reset_search() def test_bidirectional_ucs(self): """Test and generate GeoJSON for bidirectional UCS search""" path = bidirectional_ucs(self.atlanta, '69581003', '69581000') all_explored = self.atlanta.explored_nodes plot_search(self.atlanta, 'atlanta_search_bidir_ucs.json', path, all_explored) print(path)
class TestBidirectionalSearch(unittest.TestCase): """Test the bidirectional search algorithms: UCS, A*""" def setUp(self): """Load Atlanta map data""" atlanta = pickle.load(open('atlanta_osm.pickle', 'rb')) self.atlanta = ExplorableGraph(atlanta) self.atlanta.reset_search() romania = pickle.load(open('romania_graph.pickle', 'rb')) self.romania = ExplorableGraph(romania) self.romania.reset_search() def test_bidirectional_ucs(self): """Test and generate GeoJSON for bidirectional UCS search""" #path = bidirectional_ucs(self.atlanta, '69581003', '69581000') #all_explored = self.atlanta.explored_nodes #plot_search(self.atlanta, 'atlanta_search_bidir_ucs.json', path, # all_explored) start = 'a' goal = 'u' node_positions = { n: self.romania.node[n]['pos'] for n in self.romania.node.keys() } self.romania.reset_search() path = bidirectional_ucs(self.romania, start, goal) def test_bidirectional_a_star(self): """Test and generate GeoJSON for bidirectional A* search""" path = bidirectional_a_star(self.atlanta, '69581003', '69581000') all_explored = self.atlanta.explored_nodes plot_search(self.atlanta, 'atlanta_search_bidir_a_star.json', path, all_explored)
class TestBidirectionalSearchRomania(unittest.TestCase): """Test the bidirectional search algorithms: UCS, A*""" def setUp(self): """Load romania map data""" romania = pickle.load(open('romania_graph.pickle', 'rb')) self.romania = ExplorableGraph(romania) self.romania.reset_search() def test_bidirectional_ucs(self): """Test and generate GeoJSON for bidirectional UCS search""" start = 'v' goal = 'z' node_positions = { n: self.romania.node[n]['pos'] for n in self.romania.node.keys() } self.romania.reset_search() path = bidirectional_ucs(self.romania, start, goal) all_explored = self.romania.explored_nodes self.draw_graph(self.romania, node_positions=node_positions, start=start, goal=goal, path=path) def test_bidirectional_a_star(self): """Test and generate GeoJSON for bidirectional A* search""" path = bidirectional_a_star(self.romania, '69581003', '69581000') all_explored = self.romania.explored_nodes @staticmethod def draw_graph(graph, node_positions={}, start=None, goal=None, path=[]): """Visualize results of graph search""" explored = list(graph.explored_nodes) labels = {} for node in graph: labels[node] = node if not node_positions: node_positions = networkx.spring_layout(graph) networkx.draw_networkx_nodes(graph, node_positions) networkx.draw_networkx_edges(graph, node_positions, style='dashed') networkx.draw_networkx_labels(graph, node_positions, labels) networkx.draw_networkx_nodes(graph, node_positions, nodelist=explored, node_color='g') if path: edges = [(path[i], path[i + 1]) for i in range(0, len(path) - 1)] networkx.draw_networkx_edges(graph, node_positions, edgelist=edges, edge_color='b') if start: networkx.draw_networkx_nodes(graph, node_positions, nodelist=[start], node_color='b') if goal: networkx.draw_networkx_nodes(graph, node_positions, nodelist=[goal], node_color='y') plt.plot() plt.show()
class TestBidirectionalSearch(unittest.TestCase): """Test the bidirectional search algorithms: UCS, A*""" def setUp(self): """Load Atlanta map data""" atlanta = pickle.load(open('atlanta_osm.pickle', 'rb')) self.atlanta = ExplorableGraph(atlanta) self.atlanta.reset_search() romania = pickle.load(open('romania_graph.pickle', 'rb')) self.romania = ExplorableGraph(romania) self.romania.reset_search() def test_bidirectional_ucs(self): """Test and generate GeoJSON for bidirectional UCS search""" # path = bidirectional_ucs(self.atlanta, '69581003', '69581000') # all_explored = self.atlanta.explored_nodes # plot_search(self.atlanta, 'atlanta_search_bidir_ucs.json', path, # all_explored) # start = 'c' # goal = 'r' # # node_positions = {n: self.romania.node[n]['pos'] for n in # self.romania.node.keys()} # # self.romania.reset_search() # path = bidirectional_a_star(self.romania, start, goal) # print path goals = ['a', 'n', 'e'] node_positions = { n: self.romania.node[n]['pos'] for n in self.romania.node.keys() } self.romania.reset_search() path = tridirectional_search(self.romania, goals) print path self.draw_graph(self.romania, node_positions=node_positions, start=goals[0], goal=goals[2], path=path) # def test_bidirectional_a_star(self): # """Test and generate GeoJSON for bidirectional A* search""" # path = bidirectional_a_star(self.atlanta, '69581003', '69581000') # all_explored = self.atlanta.explored_nodes # plot_search(self.atlanta, 'atlanta_search_bidir_a_star.json', path, # all_explored) @staticmethod def draw_graph(graph, node_positions=None, start=None, goal=None, path=None): """Visualize results of graph search""" explored = list(graph.explored_nodes) labels = {} for node in graph: labels[node] = node if node_positions is None: node_positions = networkx.spring_layout(graph) networkx.draw_networkx_nodes(graph, node_positions) networkx.draw_networkx_edges(graph, node_positions, style='dashed') networkx.draw_networkx_labels(graph, node_positions, labels) networkx.draw_networkx_nodes(graph, node_positions, nodelist=explored, node_color='g') if path is not None: edges = [(path[i], path[i + 1]) for i in range(0, len(path) - 1)] networkx.draw_networkx_edges(graph, node_positions, edgelist=edges, edge_color='b') if start: networkx.draw_networkx_nodes(graph, node_positions, nodelist=[start], node_color='b') if goal: networkx.draw_networkx_nodes(graph, node_positions, nodelist=[goal], node_color='y') plt.plot() plt.show()
class TestBasicSearch(unittest.TestCase): """Test the simple search algorithms: BFS, UCS, A*""" def setUp(self): """Romania map data from Russell and Norvig, Chapter 3.""" romania = pickle.load(open('romania_graph.pickle', 'rb')) self.romania = ExplorableGraph(romania) self.romania.reset_search() def test_bfs(self): """Test and visualize breadth-first search""" start = 'a' goal = 'u' node_positions = { n: self.romania.node[n]['pos'] for n in self.romania.node.keys() } self.romania.reset_search() path = breadth_first_search(self.romania, start, goal) self.draw_graph(self.romania, node_positions=node_positions, start=start, goal=goal, path=path) def test_ucs(self): """Test and visualize uniform-cost search""" start = 'a' goal = 'u' node_positions = { n: self.romania.node[n]['pos'] for n in self.romania.node.keys() } self.romania.reset_search() path = uniform_cost_search(self.romania, start, goal) self.draw_graph(self.romania, node_positions=node_positions, start=start, goal=goal, path=path) def test_a_star(self): """Test and visualize A* search""" start = 'a' goal = 'u' node_positions = { n: self.romania.node[n]['pos'] for n in self.romania.node.keys() } self.romania.reset_search() path = a_star(self.romania, start, goal) self.draw_graph(self.romania, node_positions=node_positions, start=start, goal=goal, path=path) def test_bidirectional_ucs(self): """Test and visualize bidirectional UCS search""" start = 'a' goal = 'u' node_positions = { n: self.romania.node[n]['pos'] for n in self.romania.node.keys() } self.romania.reset_search() path = bidirectional_ucs(self.romania, start, goal) self.draw_graph(self.romania, node_positions=node_positions, start=start, goal=goal, path=path) def test_bidirectional_a_star(self): """Test and visualize bidirectional A* search""" start = 'a' goal = 'u' node_positions = { n: self.romania.node[n]['pos'] for n in self.romania.node.keys() } self.romania.reset_search() path = bidirectional_a_star(self.romania, start, goal) self.draw_graph(self.romania, node_positions=node_positions, start=start, goal=goal, path=path) @staticmethod def draw_graph(graph, node_positions=None, start=None, goal=None, path=None): """Visualize results of graph search""" explored = list(graph.explored_nodes) labels = {} for node in graph: labels[node] = node if node_positions is None: node_positions = networkx.spring_layout(graph) networkx.draw_networkx_nodes(graph, node_positions) networkx.draw_networkx_edges(graph, node_positions, style='dashed') networkx.draw_networkx_labels(graph, node_positions, labels) networkx.draw_networkx_nodes(graph, node_positions, nodelist=explored, node_color='g') if path is not None: edges = [(path[i], path[i + 1]) for i in range(0, len(path) - 1)] networkx.draw_networkx_edges(graph, node_positions, edgelist=edges, edge_color='b') if start: networkx.draw_networkx_nodes(graph, node_positions, nodelist=[start], node_color='b') if goal: networkx.draw_networkx_nodes(graph, node_positions, nodelist=[goal], node_color='y') plt.plot() plt.show()
class TestBasicSearch(unittest.TestCase): """Test the simple search algorithms: BFS, UCS, A*""" def setUp(self): """Romania map data from Russell and Norvig, Chapter 3.""" with open('romania_graph.pickle', 'rb') as rom: romania = pickle.load(rom) self.romania = ExplorableGraph(romania) self.romania.reset_search() def test_bfs(self): """Test and visualize breadth-first search""" start = 'a' goal = 'u' node_positions = { n: self.romania.node[n]['pos'] for n in self.romania.node.keys() } self.romania.reset_search() path = breadth_first_search(self.romania, start, goal) print(path) print(self.romania.explored_nodes) self.draw_graph(self.romania, node_positions=node_positions, start=start, goal=goal, path=path) def test_bfs_empty_path(self): start = "a" goal = "a" path = breadth_first_search(self.romania, start, goal) self.assertEqual([], path) @staticmethod def draw_graph(graph, node_positions=None, start=None, goal=None, path=None): """Visualize results of graph search""" explored = [ key for key in graph.explored_nodes if graph.explored_nodes[key] > 0 ] labels = {} for node in graph: labels[node] = node if node_positions is None: node_positions = networkx.spring_layout(graph) networkx.draw_networkx_nodes(graph, node_positions) networkx.draw_networkx_edges(graph, node_positions, style='dashed') networkx.draw_networkx_labels(graph, node_positions, labels) networkx.draw_networkx_nodes(graph, node_positions, nodelist=explored, node_color='g') edge_labels = networkx.get_edge_attributes(graph, 'weight') networkx.draw_networkx_edge_labels(graph, node_positions, edge_labels=edge_labels) if path is not None: edges = [(path[i], path[i + 1]) for i in range(0, len(path) - 1)] networkx.draw_networkx_edges(graph, node_positions, edgelist=edges, edge_color='b') if start: networkx.draw_networkx_nodes(graph, node_positions, nodelist=[start], node_color='b') if goal: networkx.draw_networkx_nodes(graph, node_positions, nodelist=[goal], node_color='y') plt.plot() plt.show()
class SearchUnitTests(unittest.TestCase): """ Error Diagnostic code courtesy one of our former students - Mac Chan The following unit tests will check for all pairs on romania and random points on atlanta. Comment out any tests that you haven't implemented yet. If you failed on bonnie because of non-optimal path, make sure you pass all the local tests. Change test_count=-1 if you failed the path test on bonnie, it will run tests on atlanta until it finds a set of points that fail. If you failed on bonnie because of your explored set is too large, there is no easy way to test without a reference implementation. But you can read the pdf slides for the optimized terminal condition. To run, nosetests --nocapture -v search_unit_tests.py:SearchUnitTests nosetests --nocapture -v search_unit_tests.py:SearchUnitTests.test_bfs_romania """ def setUp(self): """Setup both atlanta and romania graph data.""" romania = pickle.load(open('romania_graph.pickle', 'rb')) self.romania = ExplorableGraph(romania) self.romania.reset_search() atlanta = pickle.load(open('atlanta_osm.pickle', 'rb')) self.atlanta = ExplorableGraph(atlanta) self.atlanta.reset_search() self.margin_of_error = 1.0e-6 def reference_path(self, graph, src_node, dst_node, weight='weight'): """ Path as generated by networkx shortest path. Args: graph (ExplorableGraph): Undirected graph to search. src_node (node): Key for the start node. dst_node (node): Key for the end node. weight (:obj:`str`): If None, every edge has weight/distance/cost 1. If a string, use this edge attribute as the edge weight. Any edge attribute not present defaults to 1. Returns: Tuple with (cost of path, path as list). """ graph.reset_search() path = networkx.shortest_path(graph, src_node, dst_node, weight=weight) cost = self.sum_weight(graph, path) return cost, path def reference_bfs_path(self, graph, src_node, dst_node): """ Breadth First Search as generated by networkx shortest path. Args: graph (ExplorableGraph): Undirected graph to search. src_node (node): Key for the start node. dst_node (node): Key for the end node. Returns: """ return self.reference_path(graph, src_node, dst_node, weight=None) @staticmethod def sum_weight(graph, path): """ Calculate the total cost of a path by summing edge weights. Args: graph (ExplorableGraph): Graph that contains path. path (list(nodes)): List of nodes from src to dst. Returns: Sum of edge weights in path. """ pairs = zip(path, path[1:]) return sum([graph.get_edge_data(a, b)['weight'] for a, b in pairs]) def run_romania_data(self, ref_method, method, **kwargs): """ Run the test search against the Romania data. Args: ref_method (func): Reference search function to compare test search method (func): Test search function. kwargs: Keyword arguments. Asserts: True if the path from the test search is equivalent to the reference search. """ keys = self.romania.node.keys() pairs = zip(keys, keys[1:]) for src, dst in pairs: self.romania.reset_search() path = method(self.romania, src, dst, **kwargs) print path ref_len, ref_path = ref_method(self.romania, src, dst) if path != ref_path: print src, dst self.assertEqual(path, ref_path) def run_romania_tri(self, method, **kwargs): """ Run the tridirectional test search against the Romania data. Args: method (func): Test search function. kwargs: Keyword arguments. Asserts: True if the path from the test search is equivalent to the reference search. """ keys = self.romania.node.keys() triplets = zip(keys, keys[1:], keys[2:]) for goals in triplets: for all_combo in itertools.permutations(goals): #all_combo=('d', 'f', 'g') self.romania.reset_search() path = method(self.romania, all_combo, **kwargs) # print all_combo,"des" # print path,"path" path_len = self.sum_weight(self.romania, path) s1len, _ = self.reference_path(self.romania, all_combo[0], all_combo[1]) s2len, _ = self.reference_path(self.romania, all_combo[2], all_combo[1]) s3len, _ = self.reference_path(self.romania, all_combo[0], all_combo[2]) min_len = min(s1len + s2len, s1len + s3len, s3len + s2len) if path_len != min_len: print all_combo self.assertEqual(path_len, min_len) def run_atlanta_data(self, method, test_count=10, **kwargs): """ Run the bidirectional test search against the Atlanta data. Args: method (func): Test search function. test_count (int): Number of tests to run. Default is 10. kwargs: Keyword arguments. Asserts: True if the path from the test search is equivalent to the reference search. """ keys = list(networkx.connected_components(self.atlanta).next()) random.shuffle(keys) for src, dst in zip(keys, keys[1:])[::2]: self.atlanta.reset_search() path = method(self.atlanta, src, dst, **kwargs) path_len = self.sum_weight(self.atlanta, path) ref_len, ref_path = self.reference_path(self.atlanta, src, dst) if abs(path_len - ref_len) > self.margin_of_error: print src, dst self.assertAlmostEqual(path_len, ref_len, delta=self.margin_of_error) test_count -= 1 if test_count == 0: break def run_atlanta_tri(self, method, test_count=10, **kwargs): """ Run the tridirectional test search against the Atlanta data. Args: method (func): Test search function. test_count (int): Number of tests to run. Default is 10. kwargs: Keyword arguments. Asserts: True if the path from the test search is equivalent to the reference search. """ keys = list(next(networkx.connected_components(self.atlanta))) random.shuffle(keys) for goals in zip(keys, keys[1:], keys[2:])[::3]: self.atlanta.reset_search() path = method(self.atlanta, goals, **kwargs) path_len = self.sum_weight(self.atlanta, path) s1len, _ = self.reference_path(self.atlanta, goals[0], goals[1]) s2len, _ = self.reference_path(self.atlanta, goals[2], goals[1]) s3len, _ = self.reference_path(self.atlanta, goals[0], goals[2]) min_len = min(s1len + s2len, s1len + s3len, s3len + s2len) if abs(path_len - min_len) > self.margin_of_error: print goals self.assertAlmostEqual(path_len, min_len, delta=self.margin_of_error) test_count -= 1 if test_count == 0: break def same_node_bi(self, graph, method, test_count=10, **kwargs): """ Run the a bidirectional test search using same start and end node. Args: graph (ExplorableGraph): Graph that contains path. method (func): Test search function. test_count (int): Number of tests to run. Default is 10. kwargs: Keyword arguments. Asserts: True if the path between the same start and end node is empty. """ keys = list(networkx.connected_components(graph).next()) random.shuffle(keys) for i in range(test_count): path = method(graph, keys[i], keys[i], **kwargs) self.assertFalse(path) # def test_same_node_bi(self): # """ # Test bidirectional search using the same start and end nodes. # # Searches Tested: # breadth_first_search # uniform_cost_search # a_star, null_heuristic # a_star, euclidean_dist_heuristic # bidirectional_ucs # bidirectional_a_star, null_heuristic # bidirectional_a_star, euclidean_dist_heuristic # """ # self.same_node_bi(self.romania, breadth_first_search) # self.same_node_bi(self.romania, uniform_cost_search) # self.same_node_bi(self.romania, a_star, heuristic=null_heuristic) # self.same_node_bi(self.romania, a_star, # heuristic=euclidean_dist_heuristic) # self.same_node_bi(self.romania, bidirectional_ucs) # self.same_node_bi(self.romania, bidirectional_a_star, # heuristic=null_heuristic) # self.same_node_bi(self.romania, bidirectional_a_star, # heuristic=euclidean_dist_heuristic) # def same_node_tri_test(self, graph, method, test_count=10, **kwargs): # """ # Run the tridirectional test search using same start and end nodes # # Args: # graph (ExplorableGraph): Graph that contains path. # method (func): Test search function. # test_count (int): Number of tests to run. Default is 10. # kwargs: Keyword arguments. # # Asserts: # True if the path between the same start and end node is empty. # """ # # keys = list(next(networkx.connected_components(graph))) # random.shuffle(keys) # for i in range(test_count): # path = method(graph, [keys[i], keys[i], keys[i]], **kwargs) # self.assertFalse(path) # # def test_same_node_tri(self): # """ # Test bidirectional search using the same start and end nodes. # # Searches Tested: # tridirectional_search # tridirectional_upgraded, null_heuristic # tridirectional_upgraded, euclidean_dist_heuristic # """ # # self.same_node_tri_test(self.romania, tridirectional_search) # self.same_node_tri_test(self.romania, tridirectional_upgraded, # heuristic=null_heuristic) # self.same_node_tri_test(self.romania, tridirectional_upgraded, # heuristic=euclidean_dist_heuristic) # def test_bfs_romania(self): # """Test breadth first search with Romania data.""" # # self.run_romania_data(self.reference_bfs_path, breadth_first_search) # # def test_ucs_romania(self): # """Test uniform cost search with Romania data.""" # # self.run_romania_data(self.reference_path, uniform_cost_search) # # def test_a_star_null_romania(self): # """Test A* search with Romania data and the Null heuristic.""" # # self.run_romania_data(self.reference_path, a_star, # heuristic=null_heuristic) # # def test_a_star_euclidean_romania(self): # """Test A* search with Romania data and the Euclidean heuristic.""" # # self.run_romania_data(self.reference_path, a_star, # heuristic=euclidean_dist_heuristic) # def test_bi_ucs_romania(self): # """Test Bi-uniform cost search with Romania data.""" # # self.run_romania_data(self.reference_path, bidirectional_ucs) # def test_bi_ucs_atlanta(self): # """ # Test Bi-uniform cost search with Atlanta data. # # To loop test forever, set test_count to -1 # """ # # self.run_atlanta_data(bidirectional_ucs, test_count=1) # def test_bi_a_star_null_romania(self): # """Test Bi-A* search with Romania data and the Null heuristic.""" # # self.run_romania_data(self.reference_path, bidirectional_a_star, # heuristic=null_heuristic) # # def test_bi_a_star_null_atlanta(self): # """ # Test Bi-A* search with Atlanta data and the Null heuristic. # # To loop test forever, set test_count to -1 # """ # # self.run_atlanta_data(bidirectional_a_star, heuristic=null_heuristic, # test_count=10) # # def test_bi_a_star_euclidean_romania(self): # """Test Bi-A* search with Romania data and the Euclidean heuristic.""" # # self.run_romania_data(self.reference_path, bidirectional_a_star, # heuristic=euclidean_dist_heuristic) # # def test_bi_a_star_euclidean_atlanta(self): # """ # Test Bi-A* search with Atlanta data and the Euclidean heuristic. # # To loop test forever, set test_count to -1 # """ # # self.run_atlanta_data(bidirectional_a_star, # heuristic=euclidean_dist_heuristic, # test_count=10) # # def test_tri_ucs_romania(self): # """Test Tri-UC search with Romania data.""" # # self.run_romania_tri(tridirectional_search) # # def test_tri_ucs_atlanta(self): # """ # Test Tri-UC search with Atlanta data. # # To loop test forever, set test_count to -1 # """ # # self.run_atlanta_tri(tridirectional_search, test_count=10) # # def test_tri_upgraded_null_romania(self): # """ # Test upgraded tri search with Romania data and the Null heuristic. # """ # # self.run_romania_tri(tridirectional_upgraded, heuristic=null_heuristic) # # def test_tri_upgraded_null_atlanta(self): # """ # Test upgraded tri search with Atlanta data and the Null heuristic. # # To loop test forever, set test_count to -1 # """ # # self.run_atlanta_tri(tridirectional_upgraded, test_count=10, # heuristic=null_heuristic) # def test_tri_upgraded_euclidean_romania(self): """ Test upgraded tri search with Romania data and the Euclidean heuristic. """ self.run_romania_tri(tridirectional_upgraded, heuristic=euclidean_dist_heuristic)
class SearchUnitTests(unittest.TestCase): """ Error Diagnostic code courtesy one of our former students - Mac Chan The following unit tests will check for all pairs on romania and random points on atlanta. Comment out any tests that you haven't implemented yet. If you failed on bonnie because of non-optimal path, make sure you pass all the local tests. Change test_count=-1 if you failed the path test on bonnie, it will run tests on atlanta until it finds a set of points that fail. If you failed on bonnie because of your explored set is too large, there is no easy way to test without a reference implementation. But you can read the pdf slides for the optimized terminal condition. To run, nosetests --nocapture -v search_unit_tests.py:SearchUnitTests nosetests --nocapture -v search_unit_tests.py:SearchUnitTests.test_bfs_romania """ def setUp(self): """Setup both atlanta and romania graph data.""" with (open("romania_graph.pickle", "rb")) as romFile: romania = pickle.load(romFile) self.romania = ExplorableGraph(romania) self.romania.reset_search() with (open("atlanta_osm.pickle", "rb")) as atlFile: atlanta = pickle.load(atlFile) self.atlanta = ExplorableGraph(atlanta) self.atlanta.reset_search() self.margin_of_error = 1.0e-6 def reference_path(self, graph, src_node, dst_node, weight='weight'): """ Path as generated by networkx shortest path. Args: graph (ExplorableGraph): Undirected graph to search. src_node (node): Key for the start node. dst_node (node): Key for the end node. weight (:obj:`str`): If None, every edge has weight/distance/cost 1. If a string, use this edge attribute as the edge weight. Any edge attribute not present defaults to 1. Returns: Tuple with (cost of path, path as list). """ graph.reset_search() path = networkx.shortest_path(graph, src_node, dst_node, weight=weight) cost = self.sum_weight(graph, path) return cost, path def reference_bfs_path(self, graph, src_node, dst_node): """ Breadth First Search as generated by networkx shortest path. Args: graph (ExplorableGraph): Undirected graph to search. src_node (node): Key for the start node. dst_node (node): Key for the end node. Returns: """ return self.reference_path(graph, src_node, dst_node, weight=None) @staticmethod def sum_weight(graph, path): """ Calculate the total cost of a path by summing edge weights. Args: graph (ExplorableGraph): Graph that contains path. path (list(nodes)): List of nodes from src to dst. Returns: Sum of edge weights in path. """ pairs = zip(path, path[1:]) return sum([graph.get_edge_data(a, b)['weight'] for a, b in pairs]) def run_romania_data(self, ref_method, method, **kwargs): """ Run the test search against the Romania data. Args: ref_method (func): Reference search function to compare test search method (func): Test search function. kwargs: Keyword arguments. Asserts: True if the path from the test search is equivalent to the reference search. """ keys = self.romania.nodes.keys() pairs = itertools.permutations(keys, 2) for src, dst in pairs: self.romania.reset_search() path = method(self.romania, src, dst, **kwargs) ref_len, ref_path = ref_method(self.romania, src, dst) if path != ref_path: print(src, dst) self.assertEqual(ref_path, path) def run_romania_tri(self, method, **kwargs): """ Run the tridirectional test search against the Romania data. Args: method (func): Test search function. kwargs: Keyword arguments. Asserts: True if the path from the test search is equivalent to the reference search. """ keys = self.romania.nodes.keys() triplets = itertools.permutations(keys, 3) for goals in triplets: self.romania.reset_search() path = method(self.romania, goals, **kwargs) path_len = self.sum_weight(self.romania, path) s1len, _ = self.reference_path(self.romania, goals[0], goals[1]) s2len, _ = self.reference_path(self.romania, goals[2], goals[1]) s3len, _ = self.reference_path(self.romania, goals[0], goals[2]) min_len = min(s1len + s2len, s1len + s3len, s3len + s2len) if path_len != min_len: print(goals) self.assertEqual(min_len, path_len) def run_atlanta_data(self, method, test_count=10, **kwargs): """ Run the bidirectional test search against the Atlanta data. In the interest of time and memory, this is not an exhaustive search of all possible pairs in the graph. Args: method (func): Test search function. test_count (int): Number of tests to run. Default is 10. kwargs: Keyword arguments. Asserts: True if the path from the test search is equivalent to the reference search. """ keys = list(networkx.connected_components(self.atlanta).__next__()) random.shuffle(keys) for src, dst in list(zip(keys, keys[1:]))[::2]: self.atlanta.reset_search() path = method(self.atlanta, src, dst, **kwargs) path_len = self.sum_weight(self.atlanta, path) ref_len, ref_path = self.reference_path(self.atlanta, src, dst) if abs(path_len - ref_len) > self.margin_of_error: print(src, dst) self.assertAlmostEqual(path_len, ref_len, delta=self.margin_of_error) test_count -= 1 if test_count == 0: break def run_atlanta_tri(self, method, test_count=10, **kwargs): """ Run the tridirectional test search against the Atlanta data. In the interest of time and memory, this is not an exhaustive search of all possible triplets in the graph. Args: method (func): Test search function. test_count (int): Number of tests to run. Default is 10. kwargs: Keyword arguments. Asserts: True if the path from the test search is equivalent to the reference search. """ keys = list(next(networkx.connected_components(self.atlanta))) random.shuffle(keys) for goals in list(zip(keys, keys[1:], keys[2:]))[::3]: self.atlanta.reset_search() path = method(self.atlanta, goals, **kwargs) path_len = self.sum_weight(self.atlanta, path) s1len, _ = self.reference_path(self.atlanta, goals[0], goals[1]) s2len, _ = self.reference_path(self.atlanta, goals[2], goals[1]) s3len, _ = self.reference_path(self.atlanta, goals[0], goals[2]) min_len = min(s1len + s2len, s1len + s3len, s3len + s2len) if abs(path_len - min_len) > self.margin_of_error: print(goals) self.assertAlmostEqual(path_len, min_len, delta=self.margin_of_error) test_count -= 1 if test_count == 0: break def same_node_bi(self, graph, method, test_count=10, **kwargs): """ Run the a bidirectional test search using same start and end node. Args: graph (ExplorableGraph): Graph that contains path. method (func): Test search function. test_count (int): Number of tests to run. Default is 10. kwargs: Keyword arguments. Asserts: True if the path between the same start and end node is empty. """ keys = list(networkx.connected_components(graph).__next__()) random.shuffle(keys) for i in range(test_count): path = method(graph, keys[i], keys[i], **kwargs) self.assertFalse(path) def test_same_node_bi(self): """ Test bidirectional search using the same start and end nodes. Searches Tested: breadth_first_search uniform_cost_search a_star, null_heuristic a_star, euclidean_dist_heuristic bidirectional_ucs bidirectional_a_star, null_heuristic bidirectional_a_star, euclidean_dist_heuristic """ self.same_node_bi(self.romania, breadth_first_search) self.same_node_bi(self.romania, uniform_cost_search) self.same_node_bi(self.romania, a_star, heuristic=null_heuristic) self.same_node_bi(self.romania, a_star, heuristic=euclidean_dist_heuristic) self.same_node_bi(self.romania, bidirectional_ucs) self.same_node_bi(self.romania, bidirectional_a_star, heuristic=null_heuristic) self.same_node_bi(self.romania, bidirectional_a_star, heuristic=euclidean_dist_heuristic) def same_node_tri_test(self, graph, method, test_count=10, **kwargs): """ Run the tridirectional test search using same start and end nodes Args: graph (ExplorableGraph): Graph that contains path. method (func): Test search function. test_count (int): Number of tests to run. Default is 10. kwargs: Keyword arguments. Asserts: True if the path between the same start and end node is empty. """ keys = list(next(networkx.connected_components(graph))) random.shuffle(keys) for i in range(test_count): path = method(graph, [keys[i], keys[i], keys[i]], **kwargs) self.assertFalse(path) ''' def test_same_node_tri(self): """ Test bidirectional search using the same start and end nodes. Searches Tested: tridirectional_search tridirectional_upgraded, null_heuristic tridirectional_upgraded, euclidean_dist_heuristic """ self.same_node_tri_test(self.romania, tridirectional_search) self.same_node_tri_test(self.romania, tridirectional_upgraded, heuristic=null_heuristic) self.same_node_tri_test(self.romania, tridirectional_upgraded, heuristic=euclidean_dist_heuristic) def test_bfs_romania(self): """Test breadth first search with Romania data.""" keys = self.romania.nodes.keys() pairs = itertools.permutations(keys, 2) for src in keys: for dst in keys: self.romania.reset_search() path = breadth_first_search(self.romania, src, dst) ref_len, ref_path = self.reference_bfs_path(self.romania, src, dst) self.assertTrue(is_valid(self.romania, path, src, dst), msg="path %s for start '%s' and goal '%s' is not valid" % (path, src, dst)) if src != dst: # we want path == [] if src == dst self.assertTrue(len(path) == len(ref_path), msg="Path is too long. Real path: %s, your path: %s" % (ref_path, path)) def test_ucs_romania(self): """Test uniform cost search with Romania data.""" self.run_romania_data(self.reference_path, uniform_cost_search) ''' def test_a_star_null_romania(self): #Test A* search with Romania data and the Null heuristic.""" self.run_romania_data(self.reference_path, a_star, heuristic=null_heuristic) ''' def test_a_star_euclidean_romania(self): """Test A* search with Romania data and the Euclidean heuristic.""" self.run_romania_data(self.reference_path, a_star, heuristic=euclidean_dist_heuristic) def test_bi_ucs_romania(self): """Test Bi-uniform cost search with Romania data.""" self.run_romania_data(self.reference_path, bidirectional_ucs) def test_bi_ucs_atlanta(self): """ Test Bi-uniform cost search with Atlanta data. To loop test forever, set test_count to -1 """ self.run_atlanta_data(bidirectional_ucs, test_count=10) def test_bi_a_star_null_romania(self): """Test Bi-A* search with Romania data and the Null heuristic.""" self.run_romania_data(self.reference_path, bidirectional_a_star, heuristic=null_heuristic) def test_bi_a_star_null_atlanta(self): """ Test Bi-A* search with Atlanta data and the Null heuristic. To loop test forever, set test_count to -1 """ self.run_atlanta_data(bidirectional_a_star, heuristic=null_heuristic, test_count=10) def test_bi_a_star_euclidean_romania(self): """Test Bi-A* search with Romania data and the Euclidean heuristic.""" self.run_romania_data(self.reference_path, bidirectional_a_star, heuristic=euclidean_dist_heuristic) def test_bi_a_star_euclidean_atlanta(self): """ Test Bi-A* search with Atlanta data and the Euclidean heuristic. To loop test forever, set test_count to -1 """ self.run_atlanta_data(bidirectional_a_star, heuristic=euclidean_dist_heuristic, test_count=10) def test_bi_a_star_haversine_atlanta(self): """ Test Bi-A* search with Atlanta data and the Haversine heuristic. To loop test forever, set test_count to -1 """ self.run_atlanta_data(bidirectional_a_star, heuristic=haversine_dist_heuristic, test_count=10) def test_tri_ucs_romania(self): """Test Tri-UC search with Romania data.""" self.run_romania_tri(tridirectional_search) def test_tri_ucs_atlanta(self): """ Test Tri-UC search with Atlanta data. To loop test forever, set test_count to -1 """ self.run_atlanta_tri(tridirectional_search, test_count=10) ''' def test_tri_upgraded_null_romania(self): """ Test upgraded tri search with Romania data and the Null heuristic. """ self.run_romania_tri(tridirectional_upgraded, heuristic=null_heuristic) '''
class TestSearchExperimental(unittest.TestCase): """Test your search algorithms with a nxn grid based graph and visualize your results""" def setUp(self): """Load grid map data""" file_name = 'grid.gpickle' # Use this function to create any custom grid #create_grid(20, file_name=file_name) with open(file_name, "rb") as file: self.original_grid = pickle.load(file) self.grid = ExplorableGraph(self.original_grid) self.grid.reset_search() def test_grid_viz_bidirectional_search(self): """ Use this function to test out your code on a grid to visualize the paths and explored nodes for Bidirectional Search. This function will save the image files grid_expansion_bidirectional_search.png and grid_paths_bidirectional_search.png. """ coordinates = [(0, 0), (6, 7)] path = bidirectional_ucs(self.grid, coordinates[0], coordinates[1]) # path = bidirectional_a_star(self.grid, coordinates[0], coordinates[1], heuristic=custom_heuristic) explored = list(self.grid.explored_nodes.keys()) """ Color Map Code: * Nodes never explored : White * Nodes explored but not in path : Red * Nodes in path : Green """ val_map = { 0: { 0: { 0: 'w', 1: 'w' }, 1: { 0: 'w', 1: 'w' }, }, 1: { 0: { 0: 'r', 1: 'r' }, 1: { 0: 'g', 1: 'b' } } } color_values = [ val_map[node in explored][node in path][node in coordinates] for node in self.grid.nodes() ] save_graph(self.original_grid, "grid_paths_bidirectional_search.png", show_node_labels=True, show_edge_labels=False, color_values=color_values) expanded_nodes_dict = dict(self.grid.explored_nodes) # Color of nodes gets lighter as it gets explored more expansion_color_values = list(expanded_nodes_dict.values()) save_graph(self.original_grid, "grid_expansion_bidirectional_search.png", show_node_labels=True, show_edge_labels=False, color_values=expansion_color_values) def test_grid_viz_tridirectional_search(self): """ Use this function to test out your code on a grid to visualize the paths and explored nodes for Tridirectional Search. This function will save the image files grid_paths_tridirectional_search.png and grid_expansion_tridirectional_search.png """ coordinates = [(0, 0), (5, 1), (19, 10)] path = tridirectional_search(self.grid, coordinates) # path = tridirectional_upgraded(self.grid, coordinates, heuristic=custom_heuristic) # path = three_bidirectional_search(self.grid, coordinates, heuristic=custom_heuristic) explored = list(self.grid.explored_nodes.keys()) """ Color Map Code: * Source/destination coordinates : Blue * Nodes never explored : White * Nodes explored but not in path : Red * Nodes in path : Green """ val_map = { 0: { 0: { 0: 'w', 1: 'w' }, 1: { 0: 'w', 1: 'w' }, }, 1: { 0: { 0: 'r', 1: 'r' }, 1: { 0: 'g', 1: 'b' } } } color_values = [ val_map[node in explored][node in path][node in coordinates] for node in self.grid.nodes() ] save_graph(self.original_grid, "grid_paths_tridirectional_search.png", show_node_labels=True, show_edge_labels=False, color_values=color_values) expanded_nodes_dict = dict(self.grid.explored_nodes) # Color of nodes gets lighter as it gets explored more expansion_color_values = list(expanded_nodes_dict.values()) save_graph(self.original_grid, "grid_expansion_tridirectional_search.png", show_node_labels=True, show_edge_labels=False, color_values=expansion_color_values)
class TestBasicSearch(unittest.TestCase): """Test the simple search algorithms: BFS, UCS, A*""" def setUp(self): """Romania map data from Russell and Norvig, Chapter 3.""" with open('romania_graph.pickle', 'rb') as rom: romania = pickle.load(rom) print("***") print(type(romania)) print("***") """ create graph """ """ currency_map = [('STNR', 'ISBL', 0.99), ('STNR', 'GBP', 0.0645), ('STNR', 'EUR', 0.0465), ('STNR', 'BTC', 0.0610), ('BTC', 'AUD', 0.005), ('EUR', 'AUD', 0.0650), ('EUR', 'CNY', 0.0120), ('GBP', 'AUD', 0.0493), ('GBP', 'CNY', 0.0571), ('AUD', 'TRY', 0.0621), ('AUD', 'UGX', 0.0023), ('AUD', 'INR', 0.0260), ('CNY', 'TRY', 0.0170), ('CNY', 'UGX', 0.0892), ('CNY', 'INR', 0.0400), ('INR', 'ISBL', 0.0847), ('UGX', 'ISBL', 0.0100), ('TRY', 'ISBL', 0.0124) ] """ land_map = [('St', 'J', 238), ('St', 'E', 106), ('E', 'Bo', 113), ('Bo', 'N', 145), ('Bo', 'L', 123), ('N', 'Se', 115), ('L', 'H', 123), ('H', 'P', 134), ('Se', 'T', 212), #211 on map ('Se', 'P', 244), ('I', 'P', 124), ('I', 'T', 153), ('J', 'Ba', 155), #140 on map ('Ba', 'T', 168)] pos_ = {'I' : (220.2, 382.1), 'P' : (154.5, 350.4), 'T' : (360.4, 323.2), 'J' : (248.3 , 287.1), 'N' : (148.7, 240.5), 'H' : (87.5, 300.5), 'L' : (63.2, 211.7), 'Bo' : (111.3, 132.6), 'E' : (182.9, 92.0), 'Se' : (206.0, 178.6), 'Ba': (365.3, 185.6), 'St' : (246.4, 49.3)} G = networkx.Graph() """ for i in range(len(currency_map)): G.add_weighted_edges_from([currency_map[i]]) """ for i in range(len(land_map)): G.add_weighted_edges_from([land_map[i]]) networkx.set_node_attributes(G, pos_, 'pos') #edge_labels = networkx.get_edge_attributes(G, 'weight') #weight = G.get_edge_data('Washington', 'Boise')['weight'] #print("edge_labels = ", weight) # start=start, goal=goal, path=path) self.romania = ExplorableGraph(G) #print("Graph values") #self.draw_graph(self.romania, node_positions=pos_) node_list = list(G.nodes(data = True)) print("node_list = ", node_list[0][0]) for i in range(len(node_list)): d = self.euclidean_dist(self.romania, node_list[i][0], 'St' ) print("euclidean distnace [", node_list[i][0], "] = ", d) self.romania.reset_search() @staticmethod def euclidean_dist(graph, v, goal): x1,y1 = graph.nodes[v]['pos'] x2,y2 = graph.nodes[goal]['pos'] #print(x1, ", ", y1, ", ", x2, ", ", y2) distance = math.sqrt(math.pow((x2 - x1),2) + math.pow((y2 - y1),2)) return int(distance) @staticmethod def get_path_cost(graph, path): cost = 0 for i in range(0, len(path)): if(i+1 >= len(path)): break cost = cost + graph.get_edge_weight(path[i], path[i+1]) return cost #Test for 2a and 2b """ def test_ucs(self): #Test and visualize uniform-cost search start = 'ISBL' goal = 'STNR' #node_positions = {n: self.romania.nodes[n]['pos'] for n in # self.romania.nodes.keys()} self.romania.reset_search() path = uniform_cost_search(self.romania, start, goal) print("path = " , path) print("cost = ", get_path_cost(self.romania, path)) #self.draw_graph(self.romania, node_positions=node_positions, # start=start, goal=goal, path=path) """ def test_astar_path(self): start = "I" goal = "St" path = uniform_cost_search(self.romania, start, goal) print("Path using ucs = ", path) print("*** starting a-star***") path = a_star(self.romania, start, goal) print("Path using a-star = ", path) print("Path cost =", self.get_path_cost(self.romania, path)) """ def test_best_path(self): start_list = ['Washington', 'Oregon', 'Stanford', 'UCLA'] goal_list = ['Brown', 'MIT', 'Georgetown', 'Duke'] for i in start_list: for j in goal_list: self.bi_ucs(i, j) def test_astar_path(self): start = "Gatech" goal = "OSU" path = uniform_cost_search(self.romania, start, goal) print("Path using ucs = ", path) path = a_star(self.romania, start, goal) print("Path using a-star = ", path) """ @staticmethod def draw_graph(graph, node_positions=None, start=None, goal=None, path=None): """Visualize results of graph search""" explored = [key for key in graph.explored_nodes if graph.explored_nodes[key] > 0] labels = {} for node in graph: labels[node] = node if node_positions is None: node_positions = networkx.spring_layout(graph) networkx.draw_networkx_nodes(graph, node_positions) networkx.draw_networkx_edges(graph, node_positions, style='dashed') networkx.draw_networkx_labels(graph, node_positions, labels) networkx.draw_networkx_nodes(graph, node_positions, nodelist=explored, node_color='g') edge_labels = networkx.get_edge_attributes(graph, 'weight') networkx.draw_networkx_edge_labels(graph, node_positions, edge_labels=edge_labels) if path is not None: edges = [(path[i], path[i + 1]) for i in range(0, len(path) - 1)] networkx.draw_networkx_edges(graph, node_positions, edgelist=edges, edge_color='b') if start: networkx.draw_networkx_nodes(graph, node_positions, nodelist=[start], node_color='b') if goal: networkx.draw_networkx_nodes(graph, node_positions, nodelist=[goal], node_color='y') plt.plot() plt.show()
class SearchUnitTests(unittest.TestCase): def setUp(self): """Setup both atlanta and romania graph data.""" with (open("romania_graph.pickle", "rb")) as romFile: romania = pickle.load(romFile) self.romania = ExplorableGraph(romania) self.romania.reset_search() with (open("atlanta_osm.pickle", "rb")) as atlFile: atlanta = pickle.load(atlFile) self.atlanta = ExplorableGraph(atlanta) self.atlanta.reset_search() self.margin_of_error = 1.0e-6 def reference_path(self, graph, src_node, dst_node, weight='weight'): """ Path as generated by networkx shortest path. Args: graph (ExplorableGraph): Undirected graph to search. src_node (node): Key for the start node. dst_node (node): Key for the end node. weight (:obj:`str`): If None, every edge has weight/distance/cost 1. If a string, use this edge attribute as the edge weight. Any edge attribute not present defaults to 1. Returns: Tuple with (cost of path, path as list). """ graph.reset_search() path = networkx.shortest_path(graph, src_node, dst_node, weight=weight) cost = self.sum_weight(graph, path) return cost, path def reference_bfs_path(self, graph, src_node, dst_node): """ Breadth First Search as generated by networkx shortest path. Args: graph (ExplorableGraph): Undirected graph to search. src_node (node): Key for the start node. dst_node (node): Key for the end node. Returns: """ return self.reference_path(graph, src_node, dst_node, weight=None) @staticmethod def sum_weight(graph, path): """ Calculate the total cost of a path by summing edge weights. Args: graph (ExplorableGraph): Graph that contains path. path (list(nodes)): List of nodes from src to dst. Returns: Sum of edge weights in path. """ pairs = zip(path, path[1:]) return sum([graph.get_edge_data(a, b)['weight'] for a, b in pairs]) def run_romania_data(self, ref_method, method, **kwargs): """ Run the test search against the Romania data. Args: ref_method (func): Reference search function to compare test search method (func): Test search function. kwargs: Keyword arguments. Asserts: True if the path from the test search is equivalent to the reference search. """ keys = self.romania.node.keys() pairs = itertools.permutations(keys, 2) for src, dst in pairs: self.romania.reset_search() path = method(self.romania, src, dst, **kwargs) ref_len, ref_path = ref_method(self.romania, src, dst) if path != ref_path: print (src, dst) self.assertEqual(ref_path, path) def run_romania_tri(self, method, **kwargs): """ Run the tridirectional test search against the Romania data. Args: method (func): Test search function. kwargs: Keyword arguments. Asserts: True if the path from the test search is equivalent to the reference search. """ keys = self.romania.node.keys() triplets = itertools.permutations(keys, 3) for goals in triplets: self.romania.reset_search() path = method(self.romania, goals, **kwargs) path_len = self.sum_weight(self.romania, path) s1len, _ = self.reference_path(self.romania, goals[0], goals[1]) s2len, _ = self.reference_path(self.romania, goals[2], goals[1]) s3len, _ = self.reference_path(self.romania, goals[0], goals[2]) min_len = min(s1len + s2len, s1len + s3len, s3len + s2len) if path_len != min_len: print (goals) self.assertEqual(min_len, path_len) def test_tri_ucs_romania(self): """Test Tri-UC search with Romania data.""" self.run_romania_tri(tridirectional_search)