def greedy_repair(current, random_state): """ Greedily repairs a tour, stitching up nodes that are not departed with those not visited. """ visited = set(current.edges.values()) # This kind of randomness ensures we do not cycle between the same # destroy and repair steps every time. shuffled_idcs = random_state.permutation(len(current.nodes)) nodes = [current.nodes[idx] for idx in shuffled_idcs] while len(current.edges) != len(current.nodes): node = next(node for node in nodes if node not in current.edges) # Computes all nodes that have not currently been visited, # that is, those that this node might visit. This should # not result in a subcycle, as that would violate the TSP # constraints. unvisited = { other for other in current.nodes if other != node if other not in visited if not would_form_subcycle(node, other, current) } # Closest visitable node. nearest = min(unvisited, key=lambda other: distances.euclidean(node[1], other[1])) current.edges[node] = nearest visited.add(nearest) return current
def objective(self): """ The objective function is simply the sum of all individual edge lengths, using the rounded Euclidean norm. """ return sum( distances.euclidean(node[1], self.edges[node][1]) for node in self.nodes)
def load_instance(self, name: str) -> None: problem: Problem = load_problem(Instance.PATH.format(name)) coords: OrderedDict = problem.node_coords d = problem.dimension self.adjacency_matrix = np.zeros(shape=(d, d), dtype=np.int) for i in range(d): for j in range(d): self.adjacency_matrix[i, j] = euclidean(coords[i + 1], coords[j + 1]) self.city_coords: np.ndarray = np.array(list(coords.values()))
def create_distances_matrix(coords): """ :param coords: :return: """ n = len(coords) matrix = [] for coord1 in coords: row = [] for coord2 in coords: row.append(distances.euclidean(coord1, coord2)) matrix.append(row) return np.array(matrix)
def load_instance_tsplib(path): problem = load_problem(path) coords = problem.node_coords d = problem.dimension dimension_matrix = np.zeros(shape=(d, d), dtype=np.int) for i in range(d): for j in range(d): dimension_matrix[i, j] = int( round(euclidean(coords[i + 1], coords[j + 1]))) final_coords = np.array(list(coords.values())) return final_coords, dimension_matrix
def worst_removal(current, random_state): """ Worst removal iteratively removes the 'worst' edges, that is, those edges that have the largest distance. """ destroyed = current.copy() worst_edges = sorted(destroyed.nodes, key=lambda node: distances.euclidean( node[1], destroyed.edges[node][1])) for idx in range(edges_to_remove(current)): del destroyed.edges[worst_edges[-idx - 1]] return destroyed
def cost(subpath): path = fix_bounds(subpath, start_node, end_node) return sum( distances.euclidean(path[idx][1], path[idx + 1][1]) for idx in range(len(path) - 1))
def test_euclidean(start, end, dist, exc): if exc: with pytest.raises(exc): distances.euclidean(start, end) else: assert distances.euclidean(start, end) == dist
def test_euclidean(start, end, distance): assert distances.euclidean(start, end) == distance
def euclidean_jitter(a, b): dist = distances.euclidean(a, b) # works for n-dimensions return dist * random.random() * 2