@@ -9,7 +9,158 @@ using LinearAlgebra
99using Random
1010using SparseArrays
1111
12- include (" shortest_paths.jl" )
12+ """
13+ $TYPEDEF
14+
15+ Benchmark problem for the shortest path problem.
16+ In this benchmark, all graphs are acyclic directed grids, all of the same size `grid_size`.
17+ Features are given at instance level (one dimensional vector of length `p` for each graph).
18+
19+ Data is generated using the process described in: <https://arxiv.org/abs/2307.13565>.
20+
21+ # Fields
22+ $TYPEDFIELDS
23+ """
24+ struct FixedSizeShortestPathBenchmark <: AbstractBenchmark
25+ " grid graph instance"
26+ graph:: SimpleDiGraph{Int64}
27+ " grid size of graphs"
28+ grid_size:: Tuple{Int,Int}
29+ " size of feature vectors"
30+ p:: Int
31+ " degree of formula between features and true weights"
32+ deg:: Int
33+ " multiplicative noise for true weights sampled between [1-ν, 1+ν], should be between 0 and 1"
34+ ν:: Float32
35+ end
36+
37+ function Base. show (io:: IO , bench:: FixedSizeShortestPathBenchmark )
38+ (; grid_size, p, deg, ν) = bench
39+ return print (
40+ io, " FixedSizeShortestPathBenchmark(grid_size=$grid_size , p=$p , deg=$deg , ν=$ν )"
41+ )
42+ end
43+
44+ """
45+ $TYPEDSIGNATURES
46+
47+ Constructor for [`FixedSizeShortestPathBenchmark`](@ref).
48+ """
49+ function FixedSizeShortestPathBenchmark (;
50+ grid_size:: Tuple{Int,Int} = (5 , 5 ), p:: Int = 5 , deg:: Int = 1 , ν= 0.0f0
51+ )
52+ @assert ν >= 0.0 && ν <= 1.0
53+ g = DiGraph (collect (edges (Graphs. grid (grid_size))))
54+ return FixedSizeShortestPathBenchmark (g, grid_size, p, deg, ν)
55+ end
56+
57+ """
58+ $TYPEDSIGNATURES
59+
60+ Outputs a function that computes the longest path on the grid graph, given edge weights θ as input.
61+
62+ ```julia
63+ maximizer = generate_maximizer(bench)
64+ maximizer(θ)
65+ ```
66+ """
67+ function Utils. generate_maximizer (bench:: FixedSizeShortestPathBenchmark ; use_dijkstra= true )
68+ g = bench. graph
69+ V = Graphs. nv (g)
70+ E = Graphs. ne (g)
71+
72+ I = [src (e) for e in edges (g)]
73+ J = [dst (e) for e in edges (g)]
74+ algo =
75+ use_dijkstra ? Graphs. dijkstra_shortest_paths : Graphs. bellman_ford_shortest_paths
76+
77+ function shortest_path_maximizer (θ; kwargs... )
78+ weights = sparse (I, J, - θ, V, V)
79+ parents = algo (g, 1 , weights). parents
80+ y = falses (V, V)
81+ u = V
82+ while u != 1
83+ prev = parents[u]
84+ y[prev, u] = true
85+ u = prev
86+ end
87+
88+ solution = falses (E)
89+ for (i, edge) in enumerate (edges (g))
90+ if y[src (edge), dst (edge)]
91+ solution[i] = true
92+ end
93+ end
94+ return solution
95+ end
96+
97+ return shortest_path_maximizer
98+ end
99+
100+ """
101+ $TYPEDSIGNATURES
102+
103+ Generate dataset for the shortest path problem.
104+ """
105+ function Utils. generate_dataset (
106+ bench:: FixedSizeShortestPathBenchmark ,
107+ dataset_size:: Int = 10 ;
108+ seed:: Int = 0 ,
109+ type:: Type = Float32,
110+ )
111+ # Set seed
112+ rng = MersenneTwister (seed)
113+ (; graph, p, deg, ν) = bench
114+
115+ E = Graphs. ne (graph)
116+
117+ # Features
118+ features = [randn (rng, type, p) for _ in 1 : dataset_size]
119+
120+ # True weights
121+ B = rand (rng, Bernoulli (0.5 ), E, p)
122+ ξ = if ν == 0.0
123+ [ones (type, E) for _ in 1 : dataset_size]
124+ else
125+ [rand (rng, Uniform {type} (1 - ν, 1 + ν), E) for _ in 1 : dataset_size]
126+ end
127+ costs = [
128+ (1 .+ (3 .+ B * zᵢ ./ type (sqrt (p))) .^ deg) .* ξᵢ for (ξᵢ, zᵢ) in zip (ξ, features)
129+ ]
130+
131+ shortest_path_maximizer = Utils. generate_maximizer (bench)
132+
133+ # Label solutions
134+ solutions = shortest_path_maximizer .(.- costs)
135+ return [DataSample (; x= x, θ= θ, y= y) for (x, θ, y) in zip (features, costs, solutions)]
136+ end
137+
138+ """
139+ $TYPEDSIGNATURES
140+
141+ Initialize a linear model for `bench` using `Flux`.
142+ """
143+ function Utils. generate_statistical_model (bench:: FixedSizeShortestPathBenchmark )
144+ (; p, graph) = bench
145+ return Chain (Dense (p, ne (graph)))
146+ end
147+
148+ function objective_value (:: FixedSizeShortestPathBenchmark , θ, y)
149+ return dot (θ, y)
150+ end
151+
152+ function Utils. compute_gap (
153+ bench:: FixedSizeShortestPathBenchmark , model, features, costs, solutions, maximizer
154+ )
155+ res = 0.0
156+ for (x, ȳ, θ̄) in zip (features, solutions, costs)
157+ θ = model (x)
158+ y = maximizer (θ)
159+ val = objective_value (bench, θ̄, ȳ)
160+ res += (objective_value (bench, θ̄, y) - val) / val
161+ end
162+ return res / length (features)
163+ end
13164
14165export FixedSizeShortestPathBenchmark
15166
0 commit comments