예제 #1
0
    def _insert_path(self,
                     agent: LearningAgent,
                     path: Path,
                     new_node: PathNode,
                     max_insert_idx: int = -1) -> Tuple[Path, int]:
        # Deal with deafult values
        if (max_insert_idx == -1):
            max_insert_idx = len(path.request_order)

        # Get info about new_node
        location, deadline = path.get_info(new_node)
        is_dropoff = new_node.is_dropoff

        # Find insertion point with minimum dropoff delay
        num_dropoffs_after = 0
        min_future_delay = float('inf')
        max_total_delay = Path.NOT_APPLICABLE
        min_delay_idx = Path.NOT_APPLICABLE

        # Check all insertion points by iterating backward through the request order
        for insert_idx in range(len(path.request_order), -1, -1):
            delay, delay_for_new_node, min_future_delay = self._can_insert_node(
                agent, path, new_node, insert_idx, min_future_delay)

            # If pickup node, only insert if it's before dropoff node
            if (insert_idx <= max_insert_idx):
                # If it can be inserted, check if it has less delay than previous feasible paths
                if (delay != Path.NOT_APPLICABLE):
                    total_delay = path.total_delay - (
                        delay * num_dropoffs_after) + delay_for_new_node

                    # Save if it has less delay
                    if (total_delay >= max_total_delay):
                        max_total_delay = total_delay
                        min_individual_delay = delay
                        min_delay_idx = insert_idx

                # Special check for capacity constratints
                if (not is_dropoff):
                    _, _, _, current_capacity = self._get_node_info(
                        agent, path, insert_idx - 1)
                    if (current_capacity >= self.envt.MAX_CAPACITY):
                        break

            # Update num_dropoffs_after
            if insert_idx > 0 and path.request_order[insert_idx -
                                                     1].is_dropoff:
                num_dropoffs_after += 1

        # If an insertion location is found, insert into path
        if (min_delay_idx != Path.NOT_APPLICABLE):
            # Fill in new_node's info
            self._insert_pathnode(agent, path, new_node, min_delay_idx,
                                  min_individual_delay, max_insert_idx)
            # Update total_delay
            path.total_delay = max_total_delay

        # If no insertion point was found, abort
        return path, min_delay_idx
예제 #2
0
    def _can_insert_node(
            self, agent: LearningAgent, path: Path, new_node: PathNode,
            insert_idx: int,
            min_future_delay: float) -> Tuple[float, float, float]:

        # Get info about new_node
        location, deadline = path.get_info(new_node)
        delay_for_new_node = 0.0
        node_delay = float(Path.NOT_APPLICABLE)

        # Get info about prev_node
        prev_location, prev_deadline, visit_time, current_capacity = self._get_node_info(
            agent, path, insert_idx - 1)

        # Check if it violates the capacity constraint
        if (new_node.is_dropoff or current_capacity < self.envt.MAX_CAPACITY):
            # Check if it meets later nodes' deadlines
            travel_time_prev = self.envt.get_travel_time(
                prev_location, location)
            delay = 0.0
            if (insert_idx < len(path.request_order)):
                next_location, _ = path.get_info(
                    path.request_order[insert_idx])
                travel_time_next = self.envt.get_travel_time(
                    location, next_location)
                travel_time_default = self.envt.get_travel_time(
                    prev_location, next_location)
                delay = travel_time_prev + travel_time_next - travel_time_default

            if (delay <= min_future_delay):
                # Check if inserted node meets its own deadline
                if (deadline >= visit_time + travel_time_prev):
                    node_delay = delay
                    # Find out what delay for the new_node is
                    if (new_node.is_dropoff):
                        delay_for_new_node = deadline - (visit_time +
                                                         travel_time_prev)

        # Update min_future_delay
        prev_node_max_delay = prev_deadline - visit_time
        min_future_delay = min(min_future_delay, prev_node_max_delay)

        return node_delay, delay_for_new_node, min_future_delay
예제 #3
0
    def _insert_pathnode(self,
                         agent: LearningAgent,
                         path: Path,
                         new_node: PathNode,
                         min_delay_idx: int,
                         delay: float,
                         corresponding_dropoff_idx: int = Path.NOT_APPLICABLE):
        # Node information
        location, _ = path.get_info(new_node)
        is_dropoff = new_node.is_dropoff

        # Fill in details for new_node
        if (min_delay_idx == 0):
            new_node.current_capacity = path.current_capacity
            prev_time = self.envt.current_time + agent.position.time_to_next_location
            prev_loc = agent.position.next_location
        else:
            prev_idx = min_delay_idx - 1
            prev_node = path.request_order[prev_idx]
            new_node.current_capacity = prev_node.current_capacity

            prev_time = prev_node.expected_visit_time
            prev_loc, _ = path.get_info(prev_node)

        travel_time = self.envt.get_travel_time(prev_loc, location)
        new_node.expected_visit_time = prev_time + travel_time
        if not is_dropoff:
            new_node.current_capacity += 1

        # Insert new_node
        path.request_order.insert(min_delay_idx, new_node)

        # Update details of future nodes
        for idx in range(min_delay_idx + 1, len(path.request_order)):
            # Update visit time
            node = path.request_order[idx]
            node.expected_visit_time += delay

            # Increase capacity by one until dropoff
            if not is_dropoff and idx <= corresponding_dropoff_idx:
                node.current_capacity += 1
예제 #4
0
    def _get_node_info(self, agent: LearningAgent, path: Path, idx: int):
        if (idx != Path.NOT_APPLICABLE):
            node = path.request_order[idx]
            location, deadline = path.get_info(node)
            visit_time = node.expected_visit_time
            current_capacity = node.current_capacity
        else:
            location = agent.position.next_location
            deadline = float('inf')
            visit_time = self.envt.current_time + agent.position.time_to_next_location
            current_capacity = path.current_capacity

        return location, deadline, visit_time, current_capacity
예제 #5
0
    def get_new_path_complete_search(self, agent: LearningAgent,
                                     path: Path) -> Path:
        # TODO: Factor out remaining_delay
        path.total_delay = Path.NOT_APPLICABLE  # indicates that we haven't built a new path yet

        # Create list of nodes to visit
        possible_next_nodes: List[PathNode] = []
        for request_id, request in enumerate(path.requests):
            possible_next_nodes.append(
                PathNode(request.has_been_picked_up, request_id))
        current_index = 0
        current_remaining_delay = 0.0

        # Explore all combinations of requests
        stack: List[Tuple[List[PathNode], int, float]] = []
        current_request_order: List[PathNode] = []

        while True:
            # Check if you can go deeper into the search space
            stepBack = False
            if (current_index < len(possible_next_nodes)):
                # Expand the current tree
                next_node = possible_next_nodes[current_index]
                current_index += 1  # update current_index

                # If adding the next node is not feasible, continue
                next_location, deadline = path.get_info(next_node)
                if not current_request_order:
                    current_location = agent.position.next_location
                    current_time = self.envt.current_time + agent.position.time_to_next_location
                    current_capacity: int = path.current_capacity
                else:
                    current_location, _ = path.get_info(
                        current_request_order[-1])
                    current_time = current_request_order[
                        -1].expected_visit_time
                    current_capacity = current_request_order[
                        -1].current_capacity

                travel_time = self.envt.get_travel_time(
                    current_location, next_location)
                time_at_next_location = current_time + travel_time

                # If pick-up, check if it violates the current capacity
                if (not next_node.is_dropoff
                        and current_capacity + 1 > self.envt.MAX_CAPACITY):
                    stepBack = True

                # Check if it meets deadline
                if (time_at_next_location > deadline):
                    stepBack = True

            # Else, check if this path has been completed
            else:
                if (path.is_complete(current_request_order)):
                    # Check if this is the best path
                    previous_lowest_delay = path.total_delay
                    if (previous_lowest_delay == Path.NOT_APPLICABLE or
                            current_remaining_delay > previous_lowest_delay):
                        # Save, if it is
                        path.request_order = deepcopy(current_request_order)
                        path.total_delay = current_remaining_delay

                stepBack = True

            # If you can't go deeper, take a step back
            if (stepBack):
                if stack:
                    possible_next_nodes, current_index, current_remaining_delay = stack.pop(
                    )
                    current_request_order.pop()
                else:
                    break

            # Else, go one step deeper
            else:
                # Add to the current path, given it is feasible
                next_node.expected_visit_time = time_at_next_location
                if (next_node.is_dropoff):
                    next_node.current_capacity = current_capacity - 1
                else:
                    next_node.current_capacity = current_capacity + 1

                current_request_order.append(next_node)

                # Go one step deeper into the search space
                # Store state at current depth
                stack.append((deepcopy(possible_next_nodes), current_index,
                              current_remaining_delay))

                # Remove next_node from possible nodes at lower depth
                possible_next_nodes.pop(current_index - 1)

                # If it is a pickup location, add dropoff to possible_next_nodes
                if not next_node.is_dropoff:
                    corresponding_dropoff = PathNode(
                        True, next_node.relevant_request_id)
                    possible_next_nodes.append(corresponding_dropoff)

                # Update state for search at the next depth
                current_index = 0
                if (next_node.is_dropoff):
                    current_remaining_delay += (
                        deadline - time_at_next_location
                    )  # only dropoff delay is relevant

        return path