Skip to content

Commit 2d092fe

Browse files
committed
cleanup warcraft benchmark
1 parent ab7f421 commit 2d092fe

File tree

3 files changed

+84
-128
lines changed

3 files changed

+84
-128
lines changed

docs/src/tutorials/warcraft.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,22 @@ b = WarcraftBenchmark()
1515
# These benchmark objects behave as generators that can generate various needed elements in order to build an algorithm to tackle the problem.
1616
# First of all, all benchmarks are capable of generating datasets as needed, using the [`generate_dataset`](@ref) method.
1717
# This method takes as input the benchmark object for which the dataset is to be generated, and a second argument specifying the number of samples to generate:
18-
dataset = generate_dataset(b, 50)
18+
dataset = generate_dataset(b, 50);
1919

20-
# We obtain a vector of [`DataSample`](@ref) object, which contains all needed data for the problem.
20+
# We obtain a vector of [`DataSample`](@ref) objects, containing all needed data for the problem.
2121
# Subdatasets can be created through regular slicing:
2222
train_dataset, test_dataset = dataset[1:45], dataset[46:50]
2323

24-
# And getting an individual sample will return a NamedTuple with four keys: `features`, `instance`, `costs`, and `solution`:
24+
# And getting an individual sample will return a [`DataSample`](@ref) with four fields: `x`, `instance`, `θ`, and `y`:
2525
sample = test_dataset[1]
26-
# In the case the the Warcraft benchmark, `features` correspond to the input image:
26+
# `x` correspond to the input features, i.e. the input image (3D array) in the Warcraft benchmark case:
2727
x = sample.x
28-
# `costs` correspond to the true unknown terrain weights:
28+
# `θ` correspond to the true unknown terrain weights. They are negative because optimization is formulated as a maximization problem by convention:
2929
θ_true = sample.θ
30-
# `solution` correspond to the true shortest path:
30+
# `y` correspond to the optimal shortest path:
3131
y_true = sample.y
3232
# `instance` is not used in this benchmark, therefore set to nothing:
33-
sample.instance
33+
isnothing(sample.instance)
3434

3535
# For some benchmarks, we provide the following plotting method [`plot_data`](@ref) to visualize the data:
3636
plot_data(b, sample)
@@ -85,4 +85,4 @@ final_gap = compute_gap(b, test_dataset, model, maximizer)
8585
#
8686
θ = model(x)
8787
y = maximizer(θ)
88-
plot_data(b, DataSample(; x, θ, y); θ_true=θ_true)
88+
plot_data(b, DataSample(; x, θ, y))

src/Warcraft/Warcraft.jl

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,55 +15,23 @@ using Random
1515
using SimpleWeightedGraphs
1616
using SparseArrays
1717

18-
include("dataset.jl")
18+
include("utils.jl")
1919

2020
"""
2121
$TYPEDEF
22+
23+
Benchmark for the Warcraft shortest path problem.
24+
Does not have any field.
2225
"""
2326
struct WarcraftBenchmark <: AbstractBenchmark end
2427

2528
"""
2629
$TYPEDSIGNATURES
2730
28-
Plot the image `im`, the weights `weights`, and the path `path` on the same Figure.
29-
"""
30-
function Utils.plot_data(
31-
::WarcraftBenchmark,
32-
sample::DataSample;
33-
θ_title="Weights",
34-
y_title="Path",
35-
θ_true=sample.θ,
36-
kwargs...,
37-
)
38-
(; x, y, θ) = sample
39-
im = dropdims(x; dims=4)
40-
img = convert_image_for_plot(im)
41-
p1 = Plots.plot(
42-
img; aspect_ratio=:equal, framestyle=:none, size=(300, 300), title="Terrain image"
43-
)
44-
p2 = Plots.heatmap(
45-
abs.(θ);
46-
yflip=true,
47-
aspect_ratio=:equal,
48-
framestyle=:none,
49-
padding=(0.0, 0.0),
50-
size=(300, 300),
51-
legend=false,
52-
title=θ_title,
53-
clim=(minimum(θ_true), maximum(θ_true)),
54-
)
55-
p3 = Plots.plot(
56-
Gray.(y .* 0.7);
57-
aspect_ratio=:equal,
58-
framestyle=:none,
59-
size=(300, 300),
60-
title=y_title,
61-
)
62-
return plot(p1, p2, p3; layout=(1, 3), size=(900, 300))
63-
end
31+
Downloads and decompresses the Warcraft dataset the first time it is called.
6432
65-
"""
66-
$TYPEDSIGNATURES
33+
!!! warning
34+
`dataset_size` is capped at 10000, i.e. the number of available samples in the dataset files.
6735
"""
6836
function Utils.generate_dataset(::WarcraftBenchmark, dataset_size::Int=10)
6937
decompressed_path = datadep"warcraft/data"
@@ -72,6 +40,9 @@ end
7240

7341
"""
7442
$TYPEDSIGNATURES
43+
44+
Returns an optimization algorithm that computes a longest path on the grid graph with given weights.
45+
Uses a shortest path algorithm on opposite weights to get the longest path.
7546
"""
7647
function Utils.generate_maximizer(::WarcraftBenchmark; dijkstra=true)
7748
return dijkstra ? dijkstra_maximizer : bellman_maximizer
@@ -105,8 +76,47 @@ function Utils.generate_statistical_model(::WarcraftBenchmark)
10576
return model_embedding
10677
end
10778

108-
function Utils.objective_value(::WarcraftBenchmark, θ̄::AbstractArray, y::AbstractArray)
109-
return dot(-θ̄, y)
79+
"""
80+
$TYPEDSIGNATURES
81+
82+
Plot the content of input `DataSample` as images.
83+
`x` as the initial image, `θ` as the weights, and `y` as the path.
84+
85+
The keyword argument `θ_true` is used to set the color range of the weights plot.
86+
"""
87+
function Utils.plot_data(
88+
::WarcraftBenchmark,
89+
sample::DataSample;
90+
θ_true=sample.θ,
91+
θ_title="Weights",
92+
y_title="Path",
93+
kwargs...,
94+
)
95+
(; x, y, θ) = sample
96+
im = dropdims(x; dims=4)
97+
img = convert_image_for_plot(im)
98+
p1 = Plots.plot(
99+
img; aspect_ratio=:equal, framestyle=:none, size=(300, 300), title="Terrain image"
100+
)
101+
p2 = Plots.heatmap(
102+
-θ;
103+
yflip=true,
104+
aspect_ratio=:equal,
105+
framestyle=:none,
106+
padding=(0.0, 0.0),
107+
size=(300, 300),
108+
legend=false,
109+
title=θ_title,
110+
clim=(minimum(-θ_true), maximum(-θ_true)),
111+
)
112+
p3 = Plots.plot(
113+
Gray.(y .* 0.7);
114+
aspect_ratio=:equal,
115+
framestyle=:none,
116+
size=(300, 300),
117+
title=y_title,
118+
)
119+
return plot(p1, p2, p3; layout=(1, 3), size=(900, 300))
110120
end
111121

112122
export WarcraftBenchmark

src/Warcraft/dataset.jl renamed to src/Warcraft/utils.jl

Lines changed: 25 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -29,34 +29,20 @@ Create the dataset corresponding to the data located at `decompressed_path`, pos
2929
The dataset is made of images of Warcraft terrains, cell cost labels and shortest path labels.
3030
It is a `Vector` of tuples, each `Tuple` being a dataset point.
3131
"""
32-
function create_dataset(decompressed_path::String, nb_samples::Int=10000)
32+
function create_dataset(decompressed_path::String, nb_samples::Int)
33+
N = min(nb_samples, 10000)
3334
terrain_images, terrain_labels, terrain_weights = read_dataset(
3435
decompressed_path, "train"
3536
)
3637
X = [
3738
reshape(terrain_images[:, :, :, i], (size(terrain_images[:, :, :, i])..., 1)) for
38-
i in 1:nb_samples
39+
i in 1:N
3940
]
40-
Y = [terrain_labels[:, :, i] for i in 1:nb_samples]
41-
WG = [terrain_weights[:, :, i] for i in 1:nb_samples]
41+
Y = [terrain_labels[:, :, i] for i in 1:N]
42+
WG = [-terrain_weights[:, :, i] for i in 1:N]
4243
return [DataSample(; x, y, θ) for (x, y, θ) in zip(X, Y, WG)]
4344
end
4445

45-
# """
46-
# $TYPEDSIGNATURES
47-
48-
# Split a dataset contained in `X` into train and test datasets.
49-
# The proportion of the initial dataset kept in the train set is `train_percentage`.
50-
# """
51-
# function train_test_split(X::AbstractVector, train_percentage::Real=0.5)
52-
# N = length(X)
53-
# N_train = floor(Int, N * train_percentage)
54-
# N_test = N - N_train
55-
# train_ind, test_ind = 1:N_train, (N_train + 1):(N_train + N_test)
56-
# X_train, X_test = X[train_ind], X[test_ind]
57-
# return X_train, X_test
58-
# end
59-
6046
"""
6147
$TYPEDSIGNATURES
6248
@@ -72,66 +58,6 @@ function convert_image_for_plot(image::Array{Float32,3})::Array{RGB{N0f8},2}
7258
return new_img
7359
end
7460

75-
# """
76-
# $TYPEDSIGNATURES
77-
78-
# Plot the image `im`, the weights `weights`, and the path `path` on the same Figure.
79-
# """
80-
# function plot_image_path(x, y; y_title="Path")
81-
# im = dropdims(x; dims=4)
82-
# img = convert_image_for_plot(im)
83-
# p1 = Plots.plot(
84-
# img; aspect_ratio=:equal, framestyle=:none, size=(300, 300), title="Terrain image"
85-
# )
86-
# p3 = Plots.plot(
87-
# Gray.(y .* 0.7);
88-
# aspect_ratio=:equal,
89-
# framestyle=:none,
90-
# size=(300, 300),
91-
# title=y_title,
92-
# )
93-
# return plot(p1, p3; layout=(1, 2), size=(600, 300))
94-
# end
95-
96-
# """
97-
# $TYPEDSIGNATURES
98-
99-
# Plot the train and test losses, as well as the train and test gaps computed over epochs.
100-
# """
101-
# function plot_loss_and_gap(losses::Matrix{Float64}, gaps::Matrix{Float64}; filepath=nothing)
102-
# nb_epochs = length(losses)
103-
# p1 = plot(
104-
# collect(1:nb_epochs),
105-
# losses;
106-
# title="Loss",
107-
# xlabel="epochs",
108-
# ylabel="loss",
109-
# label=["train" "test"],
110-
# )
111-
# p2 = plot(
112-
# collect(0:nb_epochs),
113-
# gaps;
114-
# title="Gap",
115-
# xlabel="epochs",
116-
# ylabel="ratio",
117-
# label=["train" "test"],
118-
# )
119-
# pl = plot(p1, p2; layout=(1, 2))
120-
# isnothing(filepath) || Plots.savefig(pl, filepath)
121-
# return pl
122-
# end
123-
124-
"""
125-
$TYPEDSIGNATURES
126-
"""
127-
function dijkstra_maximizer::AbstractMatrix; kwargs...)
128-
g = grid_graph(-θ)
129-
p = dijkstra_shortest_paths(g, 1)
130-
path = get_path(p.parents, 1, nv(g))
131-
y = path_to_matrix(path, 12, 12)
132-
return y
133-
end
134-
13561
"""
13662
grid_bellman_ford_warcraft(g, s, d, length_max)
13763
@@ -184,10 +110,30 @@ end
184110

185111
"""
186112
$TYPEDSIGNATURES
113+
114+
Computes the longest path in given grid graph weights by computing the shortest path in the graph with opposite weights.
115+
Using the Ford-Bellman dynamic programming algorithm.
187116
"""
188117
function bellman_maximizer::AbstractMatrix; kwargs...)
189118
g = grid_graph(-θ)
190119
path = grid_bellman_ford_warcraft(g, 1, nv(g))
191120
y = path_to_matrix(path, 12, 12)
192121
return y
193122
end
123+
124+
"""
125+
$TYPEDSIGNATURES
126+
127+
Computes the longest path in given grid graph weights by computing the shortest path in the graph with opposite weights.
128+
Using the Dijkstra algorithm.
129+
130+
!!! warning
131+
Only works on graph with positive weights, i.e. if `θ` only contains negative values.
132+
"""
133+
function dijkstra_maximizer::AbstractMatrix; kwargs...)
134+
g = grid_graph(-θ)
135+
p = dijkstra_shortest_paths(g, 1)
136+
path = get_path(p.parents, 1, nv(g))
137+
y = path_to_matrix(path, 12, 12)
138+
return y
139+
end

0 commit comments

Comments
 (0)