diff --git a/Project.toml b/Project.toml index 7156dc1..03f30c7 100644 --- a/Project.toml +++ b/Project.toml @@ -43,8 +43,9 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6" JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" +ProgressMeter = "92933f4c-e287-5a05-a399-4b506db050ca" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Aqua", "Documenter", "Graphs", "JET", "JuliaFormatter", "Random", "Test"] +test = ["Aqua", "Documenter", "Graphs", "JET", "JuliaFormatter", "ProgressMeter", "Random", "Test"] diff --git a/docs/src/api.md b/docs/src/api.md index fda1a0d..0f8d48e 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -25,6 +25,7 @@ select_agents ## Feasibility and cost ```@docs +path_weight flowtime is_feasible find_conflict diff --git a/src/MultiAgentPathFinding.jl b/src/MultiAgentPathFinding.jl index 0d830fe..328a150 100644 --- a/src/MultiAgentPathFinding.jl +++ b/src/MultiAgentPathFinding.jl @@ -32,7 +32,8 @@ using Graphs: has_edge, is_directed, add_edge!, - weights + weights, + dijkstra_shortest_paths using ProgressMeter: Progress, ProgressUnknown, next! using Random: randperm, shuffle using SimpleWeightedGraphs: SimpleWeightedGraph @@ -64,7 +65,7 @@ include("benchmarks/combine.jl") export MAPF, Conflict, Solution, TimedPath export nb_agents, select_agents -export flowtime, find_conflict, is_feasible +export flowtime, path_weight, find_conflict, is_feasible export independent_dijkstra, cooperative_astar, repeated_cooperative_astar export feasibility_search, optimality_search, double_search export read_benchmark, list_map_names, list_scenario_names diff --git a/src/benchmarks/combine.jl b/src/benchmarks/combine.jl index c3db7b3..b8a4f4d 100644 --- a/src/benchmarks/combine.jl +++ b/src/benchmarks/combine.jl @@ -8,16 +8,8 @@ See possible names at (data wi function read_benchmark(map_name::AbstractString, scenario_name::AbstractString) map_matrix = read_benchmark_map(map_name) g, coord_to_vertex = parse_benchmark_map(map_matrix) - scenario = read_benchmark_scenario(scenario_name, map_name) departures, arrivals = parse_benchmark_scenario(scenario, coord_to_vertex) - - mapf = MAPF( - g; - departures=departures, - arrivals=arrivals, - vertex_conflicts=LazyVertexConflicts(), - edge_conflicts=LazySwappingConflicts(), - ) + mapf = MAPF(g; departures=departures, arrivals=arrivals) return mapf end diff --git a/src/benchmarks/map.jl b/src/benchmarks/map.jl index 8772087..45e4fd4 100644 --- a/src/benchmarks/map.jl +++ b/src/benchmarks/map.jl @@ -59,10 +59,8 @@ function parse_benchmark_map(map_matrix::Matrix{Char};) for j in 1:w, i in 1:h passable[i, j] || continue for Δi in (-1, 0, 1), Δj in (-1, 0, 1) - no_loop = (Δi, Δj) != (0, 0) - still_inside = (1 <= i + Δi <= h) && (1 <= j + Δj <= h) + still_inside = (1 <= i + Δi <= h) && (1 <= j + Δj <= w) if ( - no_loop && still_inside && passable[i + Δi, j + Δj] && passable[i + Δi, j] && @@ -71,15 +69,18 @@ function parse_benchmark_map(map_matrix::Matrix{Char};) s = coord_to_vertex[i, j] d = coord_to_vertex[i + Δi, j + Δj] diag = Δi != 0 && Δj != 0 - w = diag ? sqrt(2.0) : 1.0 - push!(sources, s) - push!(destinations, d) - push!(weights, w) + weight = diag ? sqrt(2.0) : 1.0 + if s <= d + push!(sources, s) + push!(destinations, d) + push!(weights, weight) + end end end end g = SimpleWeightedGraph(sources, destinations, weights) + return g, coord_to_vertex end diff --git a/src/benchmarks/scenario.jl b/src/benchmarks/scenario.jl index 98d2205..fad8822 100644 --- a/src/benchmarks/scenario.jl +++ b/src/benchmarks/scenario.jl @@ -10,6 +10,28 @@ function list_scenario_names() ) end +""" + map_from_scenario(scenario_name) + +Return the map associated with a benchmark scenario. +""" +function map_from_scenario(scenario_name::AbstractString) + name = split(scenario_name, '-')[1] + return "$name.map" +end + +""" + scenarios_from_map(map_name) + +List the scenarios associated with a benchmark map. +""" +function scenarios_from_map(map_name::AbstractString) + name = split(map_name, '.')[1] + return filter(list_scenario_names()) do scenario_name + startswith(scenario_name, name) && scenario_name[length(name) + 1] == '-' + end +end + """ MAPFBenchmarkProblem @@ -41,9 +63,10 @@ Returns a `Vector{MAPFBenchmarkProblem}`. """ function read_benchmark_scenario(scenario_name::AbstractString, map_name::AbstractString) scenario_path = "" - if occursin("random", scenario_name) + scenario_type = split(scenario_name, '-')[end - 1] + if scenario_type == "random" scenario_path = joinpath(datadep"mapf-scen-random", "scen-random", scenario_name) - elseif occursin("even", scenario_name) + elseif scenario_type == "even" scenario_path = joinpath(datadep"mapf-scen-even", "scen-even", scenario_name) else error("Invalid scenario") @@ -100,12 +123,24 @@ function parse_benchmark_scenario( problem = scenario[a] is, js = problem.start_i, problem.start_j id, jd = problem.goal_i, problem.goal_j - s = coord_to_vertex[is, js] - d = coord_to_vertex[id, jd] - departures[a] = s - arrivals[a] = d + departures[a] = coord_to_vertex[is, js] + arrivals[a] = coord_to_vertex[id, jd] end @assert length(unique(departures)) == length(departures) @assert length(unique(arrivals)) == length(arrivals) return departures, arrivals end + +function check_benchmark_scenario( + scenario::Vector{MAPFBenchmarkProblem}, g::AbstractGraph, coord_to_vertex::Dict +) + departures, arrivals = parse_benchmark_scenario(scenario, coord_to_vertex) + for a in 1:length(scenario) + dists = dijkstra_shortest_paths(g, departures[a]).dists + if !isapprox(dists[arrivals[a]], scenario[a].optimal_length) + return false + end + break # TODO: check all agents + end + return true +end diff --git a/src/structs/path.jl b/src/structs/path.jl index c9b08a0..a289fff 100644 --- a/src/structs/path.jl +++ b/src/structs/path.jl @@ -107,7 +107,7 @@ end """ path_weight(timed_path, mapf[; tmin, tmax]) -Compute the weight of a timed path through a `MAPF` graph by summing edge weights between times `tmin` and `tmax` (which default to the departure and arrival time). +Compute the weight of a timed path by summing edge weights between times `tmin` and `tmax` (which default to the departure and arrival time). """ function path_weight( timed_path::TimedPath, diff --git a/test/benchmarks.jl b/test/benchmarks.jl index 035f990..c56e978 100644 --- a/test/benchmarks.jl +++ b/test/benchmarks.jl @@ -1,7 +1,15 @@ using MultiAgentPathFinding using MultiAgentPathFinding: - read_benchmark_map, read_benchmark_scenario, parse_benchmark_map, benchmark_cell_color + read_benchmark_map, + read_benchmark_scenario, + parse_benchmark_map, + parse_benchmark_scenario, + benchmark_cell_color, + map_from_scenario, + scenarios_from_map, + check_benchmark_scenario using Graphs +using ProgressMeter using Test map_name = "Berlin_1_256.map" @@ -9,14 +17,29 @@ scenario_name = "Berlin_1_256-even-1.scen" map_matrix = read_benchmark_map(map_name) # benchmark_cell_color.(map_matrix) -@time g, coord_to_vertex = parse_benchmark_map(map_matrix) - -scenario = read_benchmark_scenario(scenario_name, map_name) - +g, coord_to_vertex = parse_benchmark_map(map_matrix) +scenario = read_benchmark_scenario(scenario_name, map_name); +departures, arrivals = parse_benchmark_scenario(scenario, coord_to_vertex); mapf = read_benchmark(map_name, scenario_name) + +@test size(map_matrix) == (256, 256) @test nb_agents(mapf) == 950 @test nv(mapf.g) == 47540 small_mapf = select_agents(mapf, 1:100) sol = cooperative_astar(small_mapf; show_progress=false) @test is_feasible(sol, small_mapf) + +all_scenarios_coherent = true +@showprogress for map_name in list_map_names() + map_matrix = read_benchmark_map(map_name) + g, coord_to_vertex = parse_benchmark_map(map_matrix) + for scenario_name in scenarios_from_map(map_name) + scenario = read_benchmark_scenario(scenario_name, map_name) + departures, arrivals = parse_benchmark_scenario(scenario, coord_to_vertex) + if !check_benchmark_scenario(scenario, g, coord_to_vertex) + global all_scenarios_coherent = false + end + end +end +@test all_scenarios_coherent