Skip to content

Commit 9a305bf

Browse files
committed
Implement anticipative solvers for both stochastic benchmarks
1 parent b6ec7c7 commit 9a305bf

File tree

5 files changed

+115
-21
lines changed

5 files changed

+115
-21
lines changed

src/ContextualStochasticArgmax/ContextualStochasticArgmax.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,26 @@ end
118118

119119
include("policies.jl")
120120

121+
"""
122+
$TYPEDSIGNATURES
123+
124+
Generates the anticipative solver for the benchmark.
125+
"""
126+
function Utils.generate_anticipative_solver(::ContextualStochasticArgmaxBenchmark)
127+
return AnticipativeSolver()
128+
end
129+
130+
"""
131+
$TYPEDSIGNATURES
132+
133+
Generates the parametric anticipative solver for the benchmark.
134+
"""
135+
function Utils.generate_parametric_anticipative_solver(
136+
::ContextualStochasticArgmaxBenchmark
137+
)
138+
return AnticipativeSolver()
139+
end
140+
121141
export ContextualStochasticArgmaxBenchmark
122142

123143
end

src/ContextualStochasticArgmax/policies.jl

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using Statistics: mean
2-
31
"""
42
$TYPEDSIGNATURES
53
@@ -28,3 +26,35 @@ Each policy has signature `(ctx_sample, scenarios) -> Vector{DataSample}`.
2826
function Utils.generate_baseline_policies(::ContextualStochasticArgmaxBenchmark)
2927
return (; saa=Policy("SAA", "argmax of mean scenarios", csa_saa_policy))
3028
end
29+
30+
"""
31+
$TYPEDEF
32+
33+
A policy that acts with perfect information about the future scenario.
34+
"""
35+
struct AnticipativeSolver end
36+
37+
function Base.show(io::IO, ::AnticipativeSolver)
38+
return print(io, "Anticipative solver for ContextualStochasticArgmaxBenchmark")
39+
end
40+
41+
"""
42+
$TYPEDSIGNATURES
43+
44+
Evaluate the anticipative policy for a given `scenario`.
45+
Returns the optimal action `one_hot_argmax(scenario)`.
46+
"""
47+
function (::AnticipativeSolver)(scenario; context...)
48+
return one_hot_argmax(scenario)
49+
end
50+
51+
"""
52+
$TYPEDSIGNATURES
53+
54+
Evaluate the anticipative policy with a parametric prediction `θ` and a `scenario`.
55+
Returns the optimal action for the combined signal `one_hot_argmax(scenario + θ)`.
56+
"""
57+
function (::AnticipativeSolver)(θ, scenario; context...)
58+
ξ = scenario + θ
59+
return one_hot_argmax(ξ)
60+
end

src/StochasticVehicleScheduling/StochasticVehicleScheduling.jl

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ include("solution/algorithms/mip.jl")
4747
include("solution/algorithms/column_generation.jl")
4848
include("solution/algorithms/local_search.jl")
4949
include("solution/algorithms/deterministic_mip.jl")
50+
include("solution/algorithms/anticipative_solver.jl")
5051

5152
include("maximizer.jl")
5253

@@ -113,13 +114,21 @@ end
113114
$TYPEDSIGNATURES
114115
115116
Return the anticipative solver: a callable `(scenario::VSPScenario; instance, kwargs...) -> y`
116-
that solves the 1-scenario stochastic VSP via column generation.
117+
that solves the 1-scenario stochastic VSP.
117118
"""
118119
function Utils.generate_anticipative_solver(::StochasticVehicleSchedulingBenchmark)
119-
return (scenario::VSPScenario; instance::Instance, kwargs...) -> begin
120-
stochastic_inst = build_stochastic_instance(instance, [scenario])
121-
return column_generation_algorithm(stochastic_inst)
122-
end
120+
return AnticipativeSolver()
121+
end
122+
123+
"""
124+
$TYPEDSIGNATURES
125+
126+
Return the parametric anticipative solver: a callable `(θ, scenario::VSPScenario; instance, kwargs...) -> y`.
127+
"""
128+
function Utils.generate_parametric_anticipative_solver(
129+
::StochasticVehicleSchedulingBenchmark
130+
)
131+
return AnticipativeSolver()
123132
end
124133

125134
"""
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
@kwdef struct AnticipativeSolver{A}
2+
single_scenario_algorithm::A = compact_mip
3+
end
4+
5+
function Base.show(io::IO, ::AnticipativeSolver)
6+
return print(io, "Anticipative solver for StochasticVehicleSchedulingBenchmark")
7+
end
8+
9+
function (solver::AnticipativeSolver)(scenario; instance::Instance, kwargs...)
10+
stochastic_inst = build_stochastic_instance(instance, [scenario])
11+
return solver.single_scenario_algorithm(stochastic_inst)
12+
end
13+
14+
function (solver::AnticipativeSolver)(θ, scenario; instance::Instance, kwargs...)
15+
stochastic_inst = build_stochastic_instance(instance, [scenario])
16+
return solver.single_scenario_algorithm(stochastic_inst, θ)
17+
end

src/StochasticVehicleScheduling/solution/algorithms/mip.jl

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ Quadratic constraints are linearized using Mc Cormick linearization.
66
Note: If you have Gurobi, use `grb_model` as `model_builder` instead of `highs_model`.
77
"""
88
function compact_linearized_mip(
9-
instance::Instance; scenario_range=nothing, model_builder=scip_model, silent=true
9+
instance::Instance,
10+
θ=nothing;
11+
scenario_range=nothing,
12+
model_builder=scip_model,
13+
silent=true,
1014
)
1115
(; graph, slacks, intrinsic_delays, vehicle_cost, delay_cost) = instance
1216
nb_nodes = nv(graph)
@@ -28,13 +32,17 @@ function compact_linearized_mip(
2832
@variable(model, R[v in nodes, ω in Ω] >= 0) # propagated delay of job v
2933
@variable(model, yR[u in nodes, v in nodes, ω in Ω; has_edge(graph, u, v)] >= 0) # yR[u, v] = y[u, v] * R[u, ω]
3034

31-
@objective(
32-
model,
33-
Min,
35+
obj = (
3436
delay_cost * sum(sum(R[v, ω] for v in job_indices) for ω in Ω) / nb_scenarios # average total delay
35-
+
36-
vehicle_cost * sum(y[1, v] for v in job_indices) # nb_vehicles
37+
+
38+
vehicle_cost * sum(y[1, v] for v in job_indices) # nb_vehicles
3739
)
40+
if !isnothing(θ)
41+
@assert length(θ) == ne(graph)
42+
obj += sum(θ[a] * y[src(edge), dst(edge)] for (a, edge) in enumerate(edges(graph)))
43+
end
44+
45+
@objective(model, Min, obj)
3846

3947
# Flow contraints
4048
@constraint(
@@ -103,7 +111,11 @@ Note: If you have Gurobi, use `grb_model` as `model_builder` instead of `highs_m
103111
You need to use a solver that supports quadratic constraints to use this method.
104112
"""
105113
function compact_mip(
106-
instance::Instance; scenario_range=nothing, model_builder=scip_model, silent=true
114+
instance::Instance,
115+
θ=nothing;
116+
scenario_range=nothing,
117+
model_builder=scip_model,
118+
silent=true,
107119
)
108120
(; graph, slacks, intrinsic_delays, vehicle_cost, delay_cost) = instance
109121
nb_nodes = nv(graph)
@@ -124,13 +136,17 @@ function compact_mip(
124136
@variable(model, R[v in nodes, ω in Ω] >= 0) # propagated delay of job v
125137
@variable(model, yR[u in nodes, v in nodes, ω in Ω; has_edge(graph, u, v)] >= 0) # yR[u, v] = y[u, v] * R[u, ω]
126138

127-
@objective(
128-
model,
129-
Min,
139+
obj = (
130140
delay_cost * sum(sum(R[v, ω] for v in job_indices) for ω in Ω) / nb_scenarios # average total delay
131-
+
132-
vehicle_cost * sum(y[1, v] for v in job_indices) # nb_vehicles
141+
+
142+
vehicle_cost * sum(y[1, v] for v in job_indices) # nb_vehicles
133143
)
144+
if !isnothing(θ)
145+
@assert length(θ) == ne(graph)
146+
obj += sum(θ[a] * y[src(edge), dst(edge)] for (a, edge) in enumerate(edges(graph)))
147+
end
148+
149+
@objective(model, Min, obj)
134150

135151
# Flow contraints
136152
@constraint(
@@ -170,6 +186,8 @@ $TYPEDSIGNATURES
170186
SAA variant: build stochastic instance from `scenarios` then solve via
171187
[`compact_mip`](@ref).
172188
"""
173-
function compact_mip(instance::Instance, scenarios::Vector{VSPScenario}; kwargs...)
174-
return compact_mip(build_stochastic_instance(instance, scenarios); kwargs...)
189+
function compact_mip(
190+
instance::Instance, scenarios::Vector{VSPScenario}, θ=nothing; kwargs...
191+
)
192+
return compact_mip(build_stochastic_instance(instance, scenarios), θ; kwargs...)
175193
end

0 commit comments

Comments
 (0)