Example #1
0
    def test_in_path(self):
        data = [(4, None, True), (2, None, True), (7, None, True),
                (3, None, False), (7, 6, True), (4, 1, True), (8, 3, True),
                (4, 0, False), (7, 5, False), (10, 3, False)]

        path = Path(path=[4, 1, 8, 2, 9, 7])

        for city, limit, expected in data:
            with self.subTest(city=city, limit=limit):
                result = path.in_path(city, limit)
                self.assertEqual(result, expected)
Example #2
0
    def _crossover_nwox(self, parent1, parent2):
        """Performs non wrapping order crossover to create a child path from
        two given parents paths.

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

        # Initial child path
        child = Path(self.tsp.dimension + 1)

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

        # Fill in child's empty slots with cities from parent 2 in order
        parent_pos = child_pos = 0
        while parent_pos < self.tsp.dimension + 1:
            # Skip already filled subpath
            if start <= child_pos <= end:
                child_pos = end + 1
                continue

            # Get city from parent path
            city = parent2[parent_pos]

            if child.in_path(city):
                # If this city is already in child path then go to next one
                parent_pos += 1
                continue
            else:
                # Otherwise add it to child path and go to next
                child[child_pos] = city
                child_pos += 1
                parent_pos += 1

        # Add return to 0 if last stop is empty
        child[-1] = child[-1] if child[-1] != -1 else 0

        child.distance = self.tsp.path_dist(child)
        return child
Example #3
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

        # Path will always start and end in 0
        path = Path(self.tsp.dimension + 1)
        path[0] = path[-1] = 0

        # Start timer
        self._start_timer()

        # For each stop except the first and last one
        for i in range(1, len(path) - 1):
            prev = path[i - 1]
            min_dist = inf

            # Check all connections to different cities
            for j in range(self.tsp.dimension):
                # Skip cities that already are in path
                if path.in_path(j, i):
                    continue

                # Keep the new distance if it's lower than current minimum
                new_dist = self.tsp.dist(prev, j)
                if new_dist < min_dist:
                    min_dist = new_dist
                    path[i] = j

            if steps:
                progress = i / (len(path) - 1)
                yield SolverState(self._time(), progress, deepcopy(path), None)

        path.distance = self.tsp.path_dist(path)
        yield SolverState(self._time(), 1, None, deepcopy(path), True)
Example #4
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 and current number of steps for calculating progress
        if steps:
            total = factorial(self.tsp.dimension - 1) * 2
            current = 0

        # Working path
        path = Path(self.tsp.dimension + 1)
        path[-1] = 0
        # Minimal path and distance
        min_path = Path(self.tsp.dimension + 1)
        min_path.distance = inf
        # Nodes list (used as a stack)
        stack = []

        # Add starting city (0) to the stack
        stack.append((0, 0, 0))

        # Start the timer
        self._start_timer()

        while len(stack) > 0:
            # Increment step counter
            if steps:
                current += 1

            # Get node from the top of the stack
            cur_node = stack.pop()
            city, dist, level = cur_node
            # Update current path with this node
            path[level] = city
            # This is the level of all children of this node
            next_level = level + 1

            # If it's the last level of the tree
            if level == self.tsp.dimension - 1:
                path.distance = dist + self.tsp.dist(city, 0)
                # Yield the current state
                if steps:
                    yield SolverState(self._time(), current / total,
                                      deepcopy(path), deepcopy(min_path))
                # Distance of full path with return to 0
                # Keep it if it's better than the current minimum
                if path.distance < min_path.distance:
                    min_path = deepcopy(path)
                else:
                    continue

            # Iterate through all cities
            for i in range(self.tsp.dimension):
                # Skip current city itself, its predecessors and starting city
                if i == city or path.in_path(i, next_level) or i == 0:
                    continue

                # Skip this node if its distance is greater than min path
                next_dist = dist + self.tsp.dist(city, i)
                if next_dist >= min_path.distance:
                    continue

                # If it's valid node push it onto stack
                stack.append((i, next_dist, next_level))

        yield SolverState(self._time(), 1, None, deepcopy(min_path), True)