def _init_population(self): """Initializes population by creating specified number of random paths. """ self._population.clear() for _ in range(self.population_size): path = Path(self.tsp.dimension + 1) path.path = list(range(self.tsp.dimension)) + [0] path.shuffle(1, -1) path.distance = self.tsp.path_dist(path) self._population.append(path) self._population.sort(key=lambda p: p.distance)
def test_shuffle(self): data = [([0, 1, 2, 3, 4, 5, 6, 7], 2, 5), ([0, 1, 2, 3, 3, 5, 6, 0], 1, -1), ([5, 4, 3, 2, 1], 0, 4), ([1, 2, 3], 0, 2), ([], 0, 0)] for p, i, j in data: with self.subTest(path=p, i=i, j=j): path = Path(path=deepcopy(p)) path.shuffle(i, j) # Make sure no elements are lost or added for n in p: self.assertEqual(path.path.count(n), p.count(n)) # Compare slices that shouldn't be shuffled self.assertListEqual(path[0:i], p[0:i]) self.assertListEqual(path[j:-1], p[j:-1])
def solve(self, tsp, steps=True): # Make sure given argument is of correct type if not isinstance(tsp, TSP): raise TypeError('solve() argument has to be of type \'TSP\'') self.tsp = tsp # Total number of iterations or time for calculating progress if steps: current = 0 iters = log(self.end_temp / self.init_temp, 1 - self.cooling_rate) total = self.run_time if self.run_time else iters # Start with random path cur_path = Path(self.tsp.dimension + 1) cur_path.path = list(range(len(cur_path) - 1)) + [0] cur_path.shuffle(1, -1) cur_path.distance = self.tsp.path_dist(cur_path) # And set it as current minimum min_path = deepcopy(cur_path) # Start the timer self._start_timer() # Init temperature temp = self.init_temp # Repeat as long as system temperature is higher than minimum while True: # Update iteration counter ro time counterif running in step mode if steps: current = self._time_ms() if self.run_time else current + 1 # Get random neighbour of current path new_path = self._rand_neigh(cur_path) # Difference between current and new path delta_dist = new_path.distance - cur_path.distance # If it's shorter or equal if delta_dist <= 0: # If it's shorter set it as current minimum if new_path.distance < min_path.distance: min_path = deepcopy(new_path) # Set new path as current path cur_path = deepcopy(new_path) elif exp(-delta_dist / temp) > random(): # If path is longer accept it with random probability cur_path = deepcopy(new_path) # Cooling down temp *= 1 - self.cooling_rate # Terminate search after reaching end temperature if not self.run_time and temp < self.end_temp: break # Terminate search after exceeding specified runtime # We use `total` to not have to convert to nanoseconds every time if self.run_time and self._time_ms() >= self.run_time: break # Report current solver state if steps: yield SolverState(self._time(), current / total, deepcopy(new_path), deepcopy(min_path)) yield SolverState(self._time(), 1, None, deepcopy(min_path), True)