def test_shuffle(self): r = Route() p = Point("a", 0, 0) p2 = Point("b", 0, 2) p3 = Point("c", 2, 2) r.add(p, p2, p3) r2 = r.shuffle() assert r is not r2 assert len(r) == len(r2)
def test_repr(self): """Asserts that the id's of the points all appear""" p = Point("aaa", 1, 2) p2 = Point("bbb", 3, 4) p3 = Point("ccc", 5, 6) r = Route() r.add(p, p2, p3) rep = r.__repr__() assert p.id in rep assert p2.id in rep assert p3.id in rep
def get_route(self, route: Route) -> Route: """ This works by coping Points over from the input route to the output route ordered by hopping to the next nearest Point over each iteration until all nodes have been visited. Args: route (Route) - The starting route Returns: Route - The result of repeatedly hopping to the nearest neighbor """ nn_route = Route() # initialize by picking a starting point current_point = route.pop(0) while len(route): nn_route.add(current_point) # 1. create a list of tuples with the point and the distance # 2. pick the closest # 3. remove that from the source route and add to the new route distances = [(other, current_point.distance(other)) for other in route] next_point, distance = min(distances, key=lambda x: x[1]) route.remove(next_point) current_point = next_point return nn_route
def test_valid_map(self): """ Asserts that the test file `input_file` decodes to what we expect """ input_file = "index,x_coord,y_coord\n1,0,0\n2,0,1\n" p = Point("1", 0, 0) p2 = Point("2", 0, 1) expected_route = Route() expected_route.add(p, p2) with patch("solver.map_utils.open", self.get_f_open(input_file), create=True) as file: route = load_map("mock_file") # Note - this does rely on hashing/eq working on points assert route == expected_route
def combine(self, parent_a: Route, parent_b: Route) -> list: """Uses Cyclic Crossover to combine 2 parents""" cycles = self.combine_get_cycles(parent_a, parent_b) child_a = [False] * len(parent_a) child_b = [False] * len(parent_a) # Use alternate loops for i, cycle in enumerate(cycles): for index in cycle: if i % 2: child_a[index] = parent_a[index] child_b[index] = parent_b[index] else: child_a[index] = parent_b[index] child_b[index] = parent_a[index] child_a = Route(child_a) child_b = Route(child_b) return [child_a, child_b]
def combine_get_cycles(self, parent_a: Route, parent_b: Route) -> list: """Gets the loops from 2 parents. This is done using the method described here: http://www.rubicite.com/Tutorials/GeneticAlgorithms/CrossoverOperators/CycleCrossoverOperator.aspx """ seen = [False] * len(parent_a) # Tracks which parts have been seen cycles = [] for cycle_start in range(len(parent_a)): if not seen[cycle_start]: cycle = [cycle_start] seen[cycle_start] = True index = parent_a.index(parent_b[cycle_start]) while index != cycle_start: cycle.append(index) seen[index] = True index = parent_a.index(parent_b[index]) cycles.append(cycle) return cycles
def test_total_distance(self): r = Route() p = Point("a", 0, 0) p2 = Point("b", 0, 2) r.add(p, p2) assert r.total_distance == pytest.approx(2) p3 = Point("c", 2, 2) r.add(p3) assert r.total_distance == pytest.approx(4)
def test_pop_index(self): p = Point("a", 1, 2) p2 = Point("b", 1, 2) r = Route() r.add(p, p2) assert len(r) == 2 r.pop(0) assert len(r) == 1 assert r[0].id == "b"
def get_route(self, route: Route) -> Route: return route.shuffle()
def test_distance_one_point(self): r = Route() p = Point("a", 1, 2) r.add(p) with pytest.raises(ValueError): r.total_distance
def test_distance_no_points(self): r = Route() with pytest.raises(ValueError): r.total_distance
def test_add_duplicate_point(self): p = Point("a", 1, 2) r = Route() r.add(p) r.add(p) assert len(r) == 1
def test_add_valid_point(self): p = Point("a", 1, 2) r = Route() r.add(p) assert len(r) == 1
def test_add_invalid(self): p = "Bart Simpson" r = Route() with pytest.raises(TypeError): r.add(p)