def get_waypoint_before_or_at_step(self, agent_id: int, step: int) -> Waypoint:
        """
        Get the way point point from which the current position can be extracted.

        Parameters
        ----------
        agent_id
        step

        Returns
        -------
        WalkingElement

        """
        trainrun = self.trainrun_dict[agent_id]
        entry_time_step = trainrun[0].scheduled_at
        # the agent has no position before and at choosing to enter the grid (one tick elapses before the agent enters the grid)
        if step <= entry_time_step:
            return Waypoint(position=None, direction=self.env.agents[agent_id].initial_direction)

        # the agent has no position as soon as the target is reached
        exit_time_step = trainrun[-1].scheduled_at
        if step >= exit_time_step:
            # agent loses position as soon as target cell is reached
            return Waypoint(position=None, direction=trainrun[-1].waypoint.direction)

        waypoint = None
        for trainrun_waypoint in trainrun:
            if step < trainrun_waypoint.scheduled_at:
                return waypoint
            if step >= trainrun_waypoint.scheduled_at:
                waypoint = trainrun_waypoint.waypoint
        assert waypoint is not None
        return waypoint
Exemple #2
0
def test_get_shortest_paths_max_depth():
    env = load_flatland_environment_from_file('test_002.pkl', 'env_data.tests')
    env.reset()
    actual = get_shortest_paths(env.distance_map, max_depth=2)

    expected = {
        0: [
            Waypoint(position=(1, 1), direction=1),
            Waypoint(position=(1, 2), direction=1)
        ],
        1: [
            Waypoint(position=(3, 18), direction=3),
            Waypoint(position=(3, 17), direction=3),
        ]
    }

    for agent_handle in expected:
        assert np.array_equal(actual[agent_handle], expected[agent_handle]), \
            "[{}] actual={},expected={}".format(agent_handle, actual[agent_handle], expected[agent_handle])
    def _shortest_path_for_agent(agent,agent_pos,agent_dir):
        if agent_pos is None :
            if agent.status == RailAgentStatus.READY_TO_DEPART:
                position = agent.initial_position
            elif agent.status == RailAgentStatus.ACTIVE:
                position = agent.position
            elif agent.status == RailAgentStatus.DONE:
                position = agent.target
            else:
                shortest_paths[agent.handle] = None
                return
            direction = agent.direction
        else :
            position = agent_pos
            direction = agent_dir     
        shortest_paths[agent.handle] = []
        distance = math.inf
        depth = 0
        while (position != agent.target and (max_depth is None or depth < max_depth)):
            next_actions = get_valid_move_actions_(direction, position, distance_map.rail)
            best_next_action = None
            for next_action in next_actions:
                next_action_distance = distance_map.get()[
                    agent.handle, next_action.next_position[0], next_action.next_position[
                        1], next_action.next_direction]
                if next_action_distance < distance:
                    best_next_action = next_action
                    distance = next_action_distance

            shortest_paths[agent.handle].append(Waypoint(position, direction))
            depth += 1

            # if there is no way to continue, the rail must be disconnected!
            # (or distance map is incorrect)
            if best_next_action is None:
                shortest_paths[agent.handle] = None
                return

            position = best_next_action.next_position
            direction = best_next_action.next_direction
        if max_depth is None or depth < max_depth:
            shortest_paths[agent.handle].append(Waypoint(position, direction))
Exemple #4
0
def get_k_shortest_paths(env: RailEnv,
                         source_position: Tuple[int, int],
                         source_direction: int,
                         target_position=Tuple[int, int],
                         k: int = 1,
                         debug=False) -> List[Tuple[Waypoint]]:
    """
    Computes the k shortest paths using modified Dijkstra
    following pseudo-code https://en.wikipedia.org/wiki/K_shortest_path_routing
    In contrast to the pseudo-code in wikipedia, we do not a allow for loopy paths.

    Parameters
    ----------
    env :             RailEnv
    source_position:  Tuple[int,int]
    source_direction: int
    target_position:  Tuple[int,int]
    k :               int
        max number of shortest paths
    debug:            bool
        print debug statements

    Returns
    -------
    List[Tuple[WalkingElement]]
        We use tuples since we need the path elements to be hashable.
        We use a list of paths in order to keep the order of length.
    """

    # P: set of shortest paths from s to t
    # P =empty,
    shortest_paths: List[Tuple[Waypoint]] = []

    # countu: number of shortest paths found to node u
    # countu = 0, for all u in V
    count = {(r, c, d): 0
             for r in range(env.height) for c in range(env.width)
             for d in range(4)}

    # B is a heap data structure containing paths
    # N.B. use OrderedSet to make result deterministic!
    heap: OrderedSet[Tuple[Waypoint]] = OrderedSet()

    # insert path Ps = {s} into B with cost 0
    heap.add((Waypoint(source_position, source_direction), ))

    # while B is not empty and countt < K:
    while len(heap) > 0 and len(shortest_paths) < k:
        if debug:
            print("iteration heap={}, shortest_paths={}".format(
                heap, shortest_paths))
        # – let Pu be the shortest cost path in B with cost C
        cost = np.inf
        pu = None
        for path in heap:
            if len(path) < cost:
                pu = path
                cost = len(path)
        u: Waypoint = pu[-1]
        if debug:
            print("  looking at pu={}".format(pu))

        #     – B = B − {Pu }
        heap.remove(pu)
        #     – countu = countu + 1

        urcd = (*u.position, u.direction)
        count[urcd] += 1

        # – if u = t then P = P U {Pu}
        if u.position == target_position:
            if debug:
                print(" found of length {} {}".format(len(pu), pu))
            shortest_paths.append(pu)

        # – if countu ≤ K then
        # CAVEAT: do not allow for loopy paths
        elif count[urcd] <= k:
            possible_transitions = env.rail.get_transitions(*urcd)
            if debug:
                print("  looking at neighbors of u={}, transitions are {}".
                      format(u, possible_transitions))
            #     for each vertex v adjacent to u:
            for new_direction in range(4):
                if debug:
                    print("        looking at new_direction={}".format(
                        new_direction))
                if possible_transitions[new_direction]:
                    new_position = get_new_position(u.position, new_direction)
                    if debug:
                        print("        looking at neighbor v={}".format(
                            (*new_position, new_direction)))

                    v = Waypoint(position=new_position,
                                 direction=new_direction)
                    # CAVEAT: do not allow for loopy paths
                    if v in pu:
                        continue

                    # – let Pv be a new path with cost C + w(u, v) formed by concatenating edge (u, v) to path Pu
                    pv = pu + (v, )
                    #     – insert Pv into B
                    heap.add(pv)

    # return P
    return shortest_paths
def test_shortest_path_predictor(rendering=False):
    rail, rail_map = make_simple_rail()
    env = RailEnv(
        width=rail_map.shape[1],
        height=rail_map.shape[0],
        rail_generator=rail_from_grid_transition_map(rail),
        schedule_generator=random_schedule_generator(),
        number_of_agents=1,
        obs_builder_object=TreeObsForRailEnv(
            max_depth=2, predictor=ShortestPathPredictorForRailEnv()),
    )
    env.reset()

    # set the initial position
    agent = env.agents[0]
    agent.initial_position = (5, 6)  # south dead-end
    agent.position = (5, 6)  # south dead-end
    agent.direction = 0  # north
    agent.initial_direction = 0  # north
    agent.target = (3, 9)  # east dead-end
    agent.moving = True
    agent.status = RailAgentStatus.ACTIVE

    env.reset(False, False)

    if rendering:
        renderer = RenderTool(env, gl="PILSVG")
        renderer.render_env(show=True, show_observations=False)
        input("Continue?")

    # compute the observations and predictions
    distance_map = env.distance_map.get()
    assert distance_map[0, agent.initial_position[0], agent.initial_position[1], agent.direction] == 5.0, \
        "found {} instead of {}".format(
            distance_map[agent.handle, agent.initial_position[0], agent.position[1], agent.direction], 5.0)

    paths = get_shortest_paths(env.distance_map)[0]
    assert paths == [
        Waypoint((5, 6), 0),
        Waypoint((4, 6), 0),
        Waypoint((3, 6), 0),
        Waypoint((3, 7), 1),
        Waypoint((3, 8), 1),
        Waypoint((3, 9), 1)
    ]

    # extract the data
    predictions = env.obs_builder.predictions
    positions = np.array(
        list(map(lambda prediction: [*prediction[1:3]], predictions[0])))
    directions = np.array(
        list(map(lambda prediction: [prediction[3]], predictions[0])))
    time_offsets = np.array(
        list(map(lambda prediction: [prediction[0]], predictions[0])))

    # test if data meets expectations
    expected_positions = [
        [5, 6],
        [4, 6],
        [3, 6],
        [3, 7],
        [3, 8],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
        [3, 9],
    ]
    expected_directions = [
        [Grid4TransitionsEnum.NORTH],  # next is [5,6] heading north
        [Grid4TransitionsEnum.NORTH],  # next is [4,6] heading north
        [Grid4TransitionsEnum.NORTH],  # next is [3,6] heading north
        [Grid4TransitionsEnum.EAST],  # next is [3,7] heading east
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
        [Grid4TransitionsEnum.EAST],
    ]

    expected_time_offsets = np.array([
        [0.],
        [1.],
        [2.],
        [3.],
        [4.],
        [5.],
        [6.],
        [7.],
        [8.],
        [9.],
        [10.],
        [11.],
        [12.],
        [13.],
        [14.],
        [15.],
        [16.],
        [17.],
        [18.],
        [19.],
        [20.],
    ])

    assert np.array_equal(time_offsets, expected_time_offsets), \
        "time_offsets {}, expected {}".format(time_offsets, expected_time_offsets)

    assert np.array_equal(positions, expected_positions), \
        "positions {}, expected {}".format(positions, expected_positions)
    assert np.array_equal(directions, expected_directions), \
        "directions {}, expected {}".format(directions, expected_directions)
def test_get_shortest_paths():
    #env = load_flatland_environment_from_file('test_002.mpk', 'env_data.tests')
    env, env_dict = RailEnvPersister.load_new("test_002.mpk", "env_data.tests")

    #print("env len(agents): ", len(env.agents))
    #print(env.distance_map)
    #print("env number_of_agents:", env.number_of_agents)

    #print("env agents:", env.agents)

    #env.distance_map.reset(env.agents, env.rail)

    #actual = get_shortest_paths(env.distance_map)
    #print("shortest paths:", actual)

    #print(env.distance_map)
    #print("Dist map agents:", env.distance_map.agents)

    #print("\nenv reset()")
    env.reset()
    actual = get_shortest_paths(env.distance_map)
    #print("env agents: ", len(env.agents))
    #print("env number_of_agents: ", env.number_of_agents)

    assert len(
        actual) == 2, "get_shortest_paths should return a dict of length 2"

    expected = {
        0: [
            Waypoint(position=(1, 1), direction=1),
            Waypoint(position=(1, 2), direction=1),
            Waypoint(position=(1, 3), direction=1),
            Waypoint(position=(2, 3), direction=2),
            Waypoint(position=(2, 4), direction=1),
            Waypoint(position=(2, 5), direction=1),
            Waypoint(position=(2, 6), direction=1),
            Waypoint(position=(2, 7), direction=1),
            Waypoint(position=(2, 8), direction=1),
            Waypoint(position=(2, 9), direction=1),
            Waypoint(position=(2, 10), direction=1),
            Waypoint(position=(2, 11), direction=1),
            Waypoint(position=(2, 12), direction=1),
            Waypoint(position=(2, 13), direction=1),
            Waypoint(position=(2, 14), direction=1),
            Waypoint(position=(2, 15), direction=1),
            Waypoint(position=(2, 16), direction=1),
            Waypoint(position=(2, 17), direction=1),
            Waypoint(position=(2, 18), direction=1)
        ],
        1: [
            Waypoint(position=(3, 18), direction=3),
            Waypoint(position=(3, 17), direction=3),
            Waypoint(position=(3, 16), direction=3),
            Waypoint(position=(2, 16), direction=0),
            Waypoint(position=(2, 15), direction=3),
            Waypoint(position=(2, 14), direction=3),
            Waypoint(position=(2, 13), direction=3),
            Waypoint(position=(2, 12), direction=3),
            Waypoint(position=(2, 11), direction=3),
            Waypoint(position=(2, 10), direction=3),
            Waypoint(position=(2, 9), direction=3),
            Waypoint(position=(2, 8), direction=3),
            Waypoint(position=(2, 7), direction=3),
            Waypoint(position=(2, 6), direction=3),
            Waypoint(position=(2, 5), direction=3),
            Waypoint(position=(2, 4), direction=3),
            Waypoint(position=(2, 3), direction=3),
            Waypoint(position=(2, 2), direction=3),
            Waypoint(position=(2, 1), direction=3)
        ]
    }

    for agent_handle in expected:
        assert np.array_equal(actual[agent_handle], expected[agent_handle]), \
            "[{}] actual={},expected={}".format(agent_handle, actual[agent_handle], expected[agent_handle])
def test_get_k_shortest_paths(rendering=False):
    rail, rail_map = make_simple_rail_with_alternatives()

    env = RailEnv(
        width=rail_map.shape[1],
        height=rail_map.shape[0],
        rail_generator=rail_from_grid_transition_map(rail),
        schedule_generator=random_schedule_generator(),
        number_of_agents=1,
        obs_builder_object=GlobalObsForRailEnv(),
    )
    env.reset()

    initial_position = (3, 1)  # west dead-end
    initial_direction = Grid4TransitionsEnum.WEST  # west
    target_position = (3, 9)  # east

    # set the initial position
    agent = env.agents[0]
    agent.position = initial_position
    agent.initial_position = initial_position
    agent.direction = initial_direction
    agent.target = target_position  # east dead-end
    agent.moving = True

    env.reset(False, False)
    if rendering:
        renderer = RenderTool(env, gl="PILSVG")
        renderer.render_env(show=True, show_observations=False)
        input()

    actual = set(
        get_k_shortest_paths(
            env=env,
            source_position=initial_position,  # west dead-end
            source_direction=int(initial_direction),  # east
            target_position=target_position,
            k=10))

    expected = set([
        (Waypoint(position=(3, 1),
                  direction=3), Waypoint(position=(3, 0), direction=3),
         Waypoint(position=(3, 1),
                  direction=1), Waypoint(position=(3, 2), direction=1),
         Waypoint(position=(3, 3),
                  direction=1), Waypoint(position=(2, 3), direction=0),
         Waypoint(position=(1, 3),
                  direction=0), Waypoint(position=(0, 3), direction=0),
         Waypoint(position=(0, 4),
                  direction=1), Waypoint(position=(0, 5), direction=1),
         Waypoint(position=(0, 6),
                  direction=1), Waypoint(position=(0, 7), direction=1),
         Waypoint(position=(0, 8),
                  direction=1), Waypoint(position=(0, 9), direction=1),
         Waypoint(position=(1, 9),
                  direction=2), Waypoint(position=(2, 9), direction=2),
         Waypoint(position=(3, 9), direction=2)),
        (Waypoint(position=(3, 1),
                  direction=3), Waypoint(position=(3, 0), direction=3),
         Waypoint(position=(3, 1),
                  direction=1), Waypoint(position=(3, 2), direction=1),
         Waypoint(position=(3, 3),
                  direction=1), Waypoint(position=(3, 4), direction=1),
         Waypoint(position=(3, 5),
                  direction=1), Waypoint(position=(3, 6), direction=1),
         Waypoint(position=(4, 6),
                  direction=2), Waypoint(position=(5, 6), direction=2),
         Waypoint(position=(6, 6),
                  direction=2), Waypoint(position=(5, 6), direction=0),
         Waypoint(position=(4, 6),
                  direction=0), Waypoint(position=(4, 7), direction=1),
         Waypoint(position=(4, 8),
                  direction=1), Waypoint(position=(4, 9), direction=1),
         Waypoint(position=(3, 9), direction=0))
    ])

    assert actual == expected, "actual={},expected={}".format(actual, expected)
def test_get_shortest_paths_agent_handle():
    #env = load_flatland_environment_from_file('Level_distance_map_shortest_path.pkl', 'env_data.tests')
    env, _ = RailEnvPersister.load_new("Level_distance_map_shortest_path.mpk",
                                       "env_data.tests")
    env.reset()
    actual = get_shortest_paths(env.distance_map, agent_handle=6)

    print(actual, file=sys.stderr)

    expected = {
        6: [
            Waypoint(position=(5, 5), direction=0),
            Waypoint(position=(4, 5), direction=0),
            Waypoint(position=(3, 5), direction=0),
            Waypoint(position=(2, 5), direction=0),
            Waypoint(position=(1, 5), direction=0),
            Waypoint(position=(0, 5), direction=0),
            Waypoint(position=(0, 6), direction=1),
            Waypoint(position=(0, 7), direction=1),
            Waypoint(position=(0, 8), direction=1),
            Waypoint(position=(0, 9), direction=1),
            Waypoint(position=(0, 10), direction=1),
            Waypoint(position=(1, 10), direction=2),
            Waypoint(position=(2, 10), direction=2),
            Waypoint(position=(3, 10), direction=2),
            Waypoint(position=(4, 10), direction=2),
            Waypoint(position=(5, 10), direction=2),
            Waypoint(position=(6, 10), direction=2),
            Waypoint(position=(7, 10), direction=2),
            Waypoint(position=(8, 10), direction=2),
            Waypoint(position=(9, 10), direction=2),
            Waypoint(position=(10, 10), direction=2),
            Waypoint(position=(11, 10), direction=2),
            Waypoint(position=(12, 10), direction=2),
            Waypoint(position=(13, 10), direction=2),
            Waypoint(position=(14, 10), direction=2),
            Waypoint(position=(15, 10), direction=2),
            Waypoint(position=(16, 10), direction=2),
            Waypoint(position=(17, 10), direction=2),
            Waypoint(position=(18, 10), direction=2),
            Waypoint(position=(19, 10), direction=2),
            Waypoint(position=(20, 10), direction=2),
            Waypoint(position=(20, 9), direction=3),
            Waypoint(position=(20, 8), direction=3),
            Waypoint(position=(21, 8), direction=2),
            Waypoint(position=(21, 7), direction=3),
            Waypoint(position=(21, 6), direction=3),
            Waypoint(position=(21, 5), direction=3)
        ]
    }

    for agent_handle in expected:
        assert np.array_equal(actual[agent_handle], expected[agent_handle]), \
            "[{}] actual={},expected={}".format(agent_handle, actual[agent_handle], expected[agent_handle])