Esempio n. 1
0
    def _crossover_ox(self, parent1, parent2):
        """Performs order crossover to create a child path from two given
        parent paths.

        :param Path parent1: First parent path.
        :param Path parent2: Second parent path.
        :return: Child path.
        :rtype: Path
        """

        # Initial child path
        child = Path(len(parent1))

        # Copy random subpath from parent 1 to child
        start, end = self._rand_subpath()
        subpath = parent1.path[start:end + 1]
        tmp = parent2.path

        # Rotate tmp with pivot in the end + 1
        tmp = tmp[end + 1:] + tmp[:end + 1]
        # Remove cities found in subpath from parent 2
        tmp = list(filter(lambda x: x not in subpath, tmp))

        # Join subpath and tmp to form a child
        child.path = subpath + tmp

        # Rotate the path so it always starts at 0
        last_zero_idx = len(child) - child[::-1].index(0) - 1
        child.path = child[last_zero_idx:] + child[:last_zero_idx]

        child.distance = self.tsp.path_dist(child)
        return child
Esempio n. 2
0
    def test_set_path_exception(self):
        data = [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5, 6, 7], [1], []]

        path = Path(6)

        for p in data:
            with self.subTest(path=p):
                with self.assertRaises(ValueError):
                    path.path = p
Esempio n. 3
0
    def test_set_path(self):
        data = [[1, 2, 3, 4, 5, 6], [9, 20, 1, 5, 55, 7], [0, 0, 0, 0, 0, 0]]

        path = Path(6)

        for p in data:
            with self.subTest(path=p):
                path.path = p
                self.assertListEqual(path._path, p)
Esempio n. 4
0
    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)
Esempio n. 5
0
    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

        # Create starting path: 0, 1, 2, ..., 0, this path will be permuted
        path = Path(self.tsp.dimension + 1)
        path.path = list(range(len(path) - 1)) + [0]
        path.distance = self.tsp.path_dist(path)
        # Best path
        min_path = deepcopy(path)
        # Create permutations skipping the last stop (return to 0)
        perms = permutations(path.path[1:-1])

        if steps:
            total = factorial(self.tsp.dimension - 1)

        # Start the timer
        self._start_timer()

        # Loop through all permutations to find the shortest path
        for i, perm in enumerate(perms):
            path.path = [0] + list(perm) + [0]
            path.distance = self.tsp.path_dist(path)

            if path.distance < min_path.distance:
                min_path = deepcopy(path)

            if steps:
                # Need to use deepcopies because object could change before the
                # reference will be used
                yield SolverState(self._time(), i / total, deepcopy(path),
                                  deepcopy(min_path))

        yield SolverState(self._time(), 1, None, min_path, True)
Esempio n. 6
0
    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)