File size: 10,671 Bytes
ad7641a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
from algs.alg_functions_LNS2 import *
from algs.alg_sipps import run_sipps
from algs.alg_temporal_a_star import run_temporal_a_star
from run_single_MAPF_func import run_mapf_alg


def run_lns2(
        start_nodes: List[Node],
        goal_nodes: List[Node],
        nodes: List[Node],
        nodes_dict: Dict[str, Node],
        h_dict: Dict[str, np.ndarray],
        map_dim: Tuple[int, int],
        params: Dict,
) -> Tuple[Dict[str, List[Node]] | None, dict]:
    """
    To begin with,
    - MAPF-LNS2 calls a MAPF algorithm to solve the instance and obtains a (partial or complete) plan
    from the MAPF algorithm.
    - For each agent that does not yet have a path, MAPF-LNS2 plans a path for it that
    minimizes the number of collisions with the existing paths (Section 4).
    - MAPF-LNS2 then repeats a repairing procedure until the plan P becomes
    feasible.
    - At each iteration, MAPF-LNS2 selects a subset of agents As ⊆ A by a neighborhood selection method (see
    Section 5). We denote the paths of the agents in As as P−.
    - It then calls a modified MAPF algorithm to replan the paths of the agents in As to minimize
    the number of collisions with each other and with the paths in P \\ P−.
    Specifically, MAPF-LNS2 uses a modification of Prioritized Planning (PP) as the modified MAPF algorithm.
    PP assigns a random priority ordering to the agents in As and replans their paths one at a time according
    to the ordering. Each time, it calls a single-agent pathfinding algorithm (see Section 4) to find a path for
    an agent that minimizes the number of collisions with the new paths of the higher-priority agents in As and
    the paths in P \\ P−. We denote the new paths of the agents in As as P+.
    - Finally, MAPF-LNS2 replaces the old plan P with the new plan (P \\ P−) ∪ P+ iff the number of colliding pairs
    (CP) of the paths in the new plan is no larger than that of the old plan.
    """
    alg_name: str = params['alg_name']
    constr_type: str = params['constr_type']
    n_neighbourhood: bool = params['n_neighbourhood']
    to_render: bool = params['to_render']
    max_time: bool = params['max_time']

    start_time = time.time()
    # create agents
    agents, agents_dict = create_lns_agents(start_nodes, goal_nodes)

    # init solution
    create_init_solution(
        agents, nodes, nodes_dict, h_dict, map_dim, constr_type, start_time, params
    )
    cp_graph, cp_graph_names = get_cp_graph(agents)
    cp_len = len(cp_graph)
    occupied_from: Dict[str, AgentAlg] = {a.start_node.xy_name: a for a in agents}

    # repairing procedure
    while cp_len > 0:
        if time.time() - start_time >= max_time:
            return None, {'agents': agents}
        print(f'\n[{alg_name}] {cp_len=}')
        agents_subset: List[AgentAlg] = get_agents_subset(cp_graph, cp_graph_names, n_neighbourhood, agents, occupied_from, h_dict)
        old_paths: Dict[str, List[Node]] = {a.name: a.path[:] for a in agents_subset}
        agents_outer: List[AgentAlg] = [a for a in agents if a not in agents_subset]

        # assert len(set(agents_outer)) == len(agents_outer)
        # assert len(set(agents_subset)) == len(agents_subset)
        # assert len(set(agents)) == len(agents)
        # assert len(agents_subset) + len(agents_outer) == len(agents)

        solve_subset_with_prp(
            agents_subset, agents_outer, nodes, nodes_dict, h_dict, map_dim, start_time,
            constr_type, agents,
        )

        old_cp_graph, old_cp_graph_names = cp_graph, cp_graph_names
        # cp_graph, cp_graph_names = get_cp_graph(agents)
        cp_graph, cp_graph_names = get_cp_graph(agents_subset, agents_outer, cp_graph)
        if len(cp_graph) > cp_len:
            for agent in agents_subset:
                agent.path = old_paths[agent.name]
            cp_graph, cp_graph_names = old_cp_graph, old_cp_graph_names
            continue
        cp_len = len(cp_graph)
    # align_all_paths(agents)
    # for i in range(len(agents[0].path)):
    #     check_vc_ec_neic_iter(agents, i)
    runtime = time.time() - start_time
    makespan: int = max([len(a.path) for a in agents])
    return {a.name: a.path for a in agents}, {'agents': agents, 'time': runtime, 'makespan': makespan}


def run_k_lns2(
        start_nodes: List[Node],
        goal_nodes: List[Node],
        nodes: List[Node],
        nodes_dict: Dict[str, Node],
        h_dict: Dict[str, np.ndarray],
        map_dim: Tuple[int, int],
        params: Dict,
) -> Tuple[Dict[str, List[Node]] | None, dict]:
    """
    -> MAPF:
    - stop condition: all agents at their locations or time is up
    - behaviour, when agent is at its goal: the goal remains the same
    - output: success, time, makespan, soc
    LMAPF:
    - stop condition: the end of n iterations where every iteration has a time limit
    - behaviour, when agent is at its goal: agent receives a new goal
    - output: throughput
    """
    alg_name: str = params['alg_name']
    pf_alg_name: str = params['pf_alg_name']
    pf_alg: str = params['pf_alg']
    k_limit: bool = params['k_limit']
    n_neighbourhood: bool = params['n_neighbourhood']
    to_render: bool = params['to_render']
    img_np: np.ndarray = params['img_np']
    max_time: bool = params['max_time']

    if to_render:
        fig, ax = plt.subplots(1, 2, figsize=(14, 7))

    start_time = time.time()
    # create agents
    agents, agents_dict = create_lns_agents(start_nodes, goal_nodes)
    vc_empty_np, ec_empty_np, pc_empty_np = init_constraints(map_dim, k_limit + 1)

    k_iter: int = 0
    while True:
        k_iter += 1

        if time.time() - start_time >= max_time:
            return None, {'agents': agents}

        # ------------------------------ #
        # Solve k steps
        # ------------------------------ #

        # init solution
        agents = get_shuffled_agents(agents)
        create_k_limit_init_solution(
            agents, nodes, nodes_dict, h_dict, map_dim, pf_alg_name, pf_alg, k_limit, start_time,
            vc_empty_np, ec_empty_np, pc_empty_np, params
        )
        if time.time() - start_time >= max_time:
            return None, {'agents': agents}
        cp_graph, cp_graph_names = get_k_limit_cp_graph(agents, k_limit=k_limit)
        cp_len = len(cp_graph)
        occupied_from: Dict[str, AgentAlg] = {a.curr_node.xy_name: a for a in agents}

        # repairing procedure
        lns_iter = 0
        while cp_len > 0:
            lns_iter += 1
            if time.time() - start_time >= max_time:
                return None, {'agents': agents}

            agents_subset: List[AgentAlg] = get_k_limit_agents_subset(
                cp_graph, cp_graph_names, n_neighbourhood, agents, occupied_from, h_dict
            )
            old_paths: Dict[str, List[Node]] = {a.name: a.k_path[:] for a in agents_subset}
            agents_outer: List[AgentAlg] = [a for a in agents if a not in agents_subset]
            print(f'\r[{alg_name}] {lns_iter=}, {cp_len=}', end='')

            solve_k_limit_subset_with_prp(
                agents_subset, agents_outer, nodes, nodes_dict, h_dict, map_dim, start_time,
                pf_alg_name, pf_alg, vc_empty_np, ec_empty_np, pc_empty_np, k_limit, agents
            )

            old_cp_graph, old_cp_graph_names = cp_graph, cp_graph_names
            cp_graph, cp_graph_names = get_k_limit_cp_graph(agents_subset, agents_outer, cp_graph, k_limit=k_limit)
            if len(cp_graph) > cp_len:
                for agent in agents_subset:
                    agent.k_path = old_paths[agent.name]
                cp_graph, cp_graph_names = old_cp_graph, old_cp_graph_names
                continue
            cp_len = len(cp_graph)

        # ------------------------------ #
        # ------------------------------ #
        # ------------------------------ #
        if to_render:
            for i in range(k_limit):
                for a in agents:
                    a.curr_node = a.k_path[i]
                # plot the iteration
                i_agent = agents[0]
                plot_info = {
                    'img_np': img_np,
                    'agents': agents,
                    'i_agent': i_agent,
                }
                plot_step_in_env(ax[0], plot_info)
                plt.pause(0.001)
                # plt.pause(1)

        # align_all_paths(agents)
        # for i in range(len(agents[0].path)):
        #     check_vc_ec_neic_iter(agents, i)

        # append paths
        for agent in agents:
            if len(agent.path) == 0:
                agent.path.append(agent.start_node)
            agent.path.extend(agent.k_path[1:])
            if len(agent.path) > 0:
                agent.curr_node = agent.path[-1]

        # print
        runtime = time.time() - start_time
        finished: List[AgentAlg] = [a for a in agents if len(a.path) > 0 and a.path[-1] == a.goal_node]
        print(f'\r[{alg_name}] {k_iter=: <3} | agents: {len(finished): <3} / {len(agents)} | {runtime=: .2f} s.')  # , end=''

        # return check
        if solution_is_found(agents):
            runtime = time.time() - start_time
            makespan: int = max([len(a.path) for a in agents])
            return {a.name: a.path for a in agents}, {'agents': agents, 'time': runtime, 'makespan': makespan}

        # reshuffle
        # agents = get_shuffled_agents(agents)
        for agent in agents:
            agent.k_path = []


@use_profiler(save_dir='../stats/alg_lns2.pstat')
def main():
    to_render = True
    # final_render = False

    n_neighbourhood: int = 10

    k_limit: int = 10
    # k_limit: int = 20

    # params_lns2 = {
    #     'max_time': 1000,
    #     'alg_name': 'LNS2',
    #     'constr_type': 'soft',
    #     'n_neighbourhood': n_neighbourhood,
    #     'final_render': final_render,
    # }
    # run_mapf_alg(alg=run_lns2, params=params_lns2)

    # params_k_lns2_sipps = {
    #     'max_time': 1000,
    #     'alg_name': 'k-LNS2-SIPPS',
    #     'pf_alg_name': 'sipps',
    #     'pf_alg': run_sipps,
    #     'k_limit': k_limit,
    #     'n_neighbourhood': n_neighbourhood,
    #     'final_render': final_render,
    # }
    # run_mapf_alg(alg=run_k_lns2, params=params_k_lns2_sipps)

    params_k_lns2_a_star = {
        'max_time': 1000,
        'alg_name': 'k-LNS2-A*',
        'pf_alg_name': 'a_star',
        'pf_alg': run_temporal_a_star,
        'k_limit': k_limit,
        'n_neighbourhood': n_neighbourhood,
        'final_render': to_render,
        'to_render': to_render,
    }
    run_mapf_alg(alg=run_k_lns2, params=params_k_lns2_a_star)


if __name__ == '__main__':
    main()