Skip to content

Commit 7105116

Browse files
author
Vincent Leclere
committed
Merge branch 'sdp_Interpolations_dot_jl'
2 parents 78491df + 95cd3d5 commit 7105116

File tree

5 files changed

+701
-406
lines changed

5 files changed

+701
-406
lines changed

REQUIRE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ Distributions
55
JuMP
66
FactCheck
77
ProgressMeter
8+
Interpolations
89
Iterators
9-

examples/benchmark.jl

Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
# Copyright 2015, Vincent Leclere, Francois Pacaud and Henri Gerard
2+
# This Source Code Form is subject to the terms of the Mozilla Public
3+
# License, v. 2.0. If a copy of the MPL was not distributed with this
4+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
5+
#############################################################################
6+
# Benchmark SDDP and DP algorithms upon damsvalley example
7+
# This benchmark includes:
8+
# - comparison of execution time
9+
# - gap between the stochastic solution and the anticipative solution
10+
#
11+
# Usage:
12+
# ```
13+
# $ julia benchmark.jl {DP|SDDP}
14+
#
15+
# ```
16+
#############################################################################
17+
18+
srand(2713)
19+
push!(LOAD_PATH, "../src")
20+
21+
using StochDynamicProgramming, JuMP
22+
using Clp
23+
24+
SOLVER = ClpSolver()
25+
#= const SOLVER = CplexSolver(CPX_PARAM_SIMDISPLAY=0, CPX_PARAM_THREADS=4) =#
26+
27+
const N_STAGES = 10
28+
29+
# COST:
30+
const COST = -66*2.7*(1 + .5*(rand(N_STAGES) - .5))
31+
32+
# Define dynamic of the dam:
33+
function dynamic(t, x, u, w)
34+
return [x[1] - u[1] - u[3] + w[1], x[2] - u[2] - u[4] + u[1] + u[3]]
35+
end
36+
37+
# Define cost corresponding to each timestep:
38+
function cost_t(t, x, u, w)
39+
return COST[t] * (u[1] + u[2])
40+
end
41+
42+
function final_cost(x)
43+
return 0.
44+
end
45+
46+
47+
48+
"""Solve the problem with a solver, supposing the aleas are known
49+
in advance."""
50+
function solve_anticipative_problem(model, scenario)
51+
m = Model(solver=SOLVER)
52+
53+
54+
@defVar(m, model.xlim[1][1] <= x1[1:(N_STAGES)] <= model.xlim[1][2])
55+
@defVar(m, model.xlim[2][1] <= x2[1:(N_STAGES)] <= model.xlim[2][2])
56+
@defVar(m, model.ulim[1][1] <= u1[1:N_STAGES-1] <= model.ulim[1][2])
57+
@defVar(m, model.ulim[2][1] <= u2[1:N_STAGES-1] <= model.ulim[2][2])
58+
59+
@setObjective(m, Min, sum{COST[i]*(u1[i] + u2[i]), i = 1:N_STAGES-1})
60+
61+
for i in 1:N_STAGES-1
62+
@addConstraint(m, x1[i+1] - x1[i] + u1[i] - scenario[i] == 0)
63+
@addConstraint(m, x2[i+1] - x2[i] + u2[i] - u1[i] == 0)
64+
end
65+
66+
@addConstraint(m, x1[1] == model.initialState[1])
67+
@addConstraint(m, x2[1] == model.initialState[2])
68+
69+
status = solve(m)
70+
return getObjectiveValue(m)
71+
end
72+
73+
74+
"""Build aleas probabilities for each week.
75+
76+
Return an array with size (N_ALEAS, N_STAGES)
77+
78+
"""
79+
function build_aleas()
80+
W_MAX = round(Int, .5/7. * 100)
81+
W_MIN = 0
82+
DW = 1
83+
# Define aleas' space:
84+
N_ALEAS = Int(round(Int, (W_MAX - W_MIN) / DW + 1))
85+
ALEAS = linspace(W_MIN, W_MAX, N_ALEAS)
86+
87+
aleas = zeros(N_ALEAS, N_STAGES)
88+
89+
# take into account seasonality effects:
90+
unorm_prob = linspace(1, N_ALEAS, N_ALEAS)
91+
proba1 = unorm_prob / sum(unorm_prob)
92+
proba2 = proba1[N_ALEAS:-1:1]
93+
94+
for t in 1:N_STAGES
95+
aleas[:, t] = (1 - sin(pi*t/N_STAGES)) * proba1 + sin(pi*t/N_STAGES) * proba2
96+
end
97+
return aleas
98+
end
99+
100+
101+
"""Build an admissible scenario for water inflow.
102+
103+
Parameters:
104+
- n_scenarios (Int64)
105+
Number of scenarios to generate
106+
107+
- probabilities (Array{Float64, 2})
108+
Probabilities of occurence of water inflow for each week
109+
110+
Return an array with size (n_scenarios, N_STAGES)
111+
112+
"""
113+
function build_scenarios(n_scenarios::Int64, probabilities::Array{Float64})
114+
scenarios = zeros(n_scenarios, N_STAGES)
115+
116+
for scen in 1:n_scenarios
117+
for t in 1:N_STAGES
118+
Pcum = cumsum(probabilities[:, t])
119+
120+
n_random = rand()
121+
prob = findfirst(x -> x > n_random, Pcum)
122+
scenarios[scen, t] = prob
123+
end
124+
end
125+
return scenarios
126+
end
127+
128+
129+
"""Build n_scenarios according to a given distribution.
130+
131+
Return a Vector{NoiseLaw}"""
132+
function generate_probability_laws(n_scenarios)
133+
aleas = build_scenarios(n_scenarios, build_aleas())
134+
135+
laws = Vector{NoiseLaw}(N_STAGES)
136+
137+
# uniform probabilities:
138+
proba = 1/n_scenarios*ones(n_scenarios)
139+
140+
for t=1:N_STAGES
141+
laws[t] = NoiseLaw(aleas[:, t], proba)
142+
end
143+
144+
return laws
145+
end
146+
147+
148+
"""Instantiate the problem.
149+
150+
Return a tuple (SPModel, SDDPparameters)
151+
152+
"""
153+
function init_problem()
154+
155+
N_SCENARIOS = 10
156+
157+
x0 = [50, 50]
158+
aleas = generate_probability_laws(N_SCENARIOS)
159+
160+
# Constants:
161+
VOLUME_MAX = 100
162+
VOLUME_MIN = 0
163+
CONTROL_MAX = round(Int, .4/7. * VOLUME_MAX) + 1
164+
CONTROL_MIN = 0
165+
166+
167+
168+
x_bounds = [(VOLUME_MIN, VOLUME_MAX), (VOLUME_MIN, VOLUME_MAX)]
169+
u_bounds = [(CONTROL_MIN, CONTROL_MAX), (CONTROL_MIN, CONTROL_MAX), (0, Inf), (0, Inf)]
170+
171+
model = LinearDynamicLinearCostSPmodel(N_STAGES,
172+
u_bounds,
173+
x0,
174+
cost_t,
175+
dynamic,
176+
aleas)
177+
178+
set_state_bounds(model, x_bounds)
179+
180+
181+
EPSILON = .05
182+
MAX_ITER = 20
183+
solver = SOLVER
184+
params = SDDPparameters(solver, N_SCENARIOS, EPSILON, MAX_ITER)
185+
186+
return model, params
187+
end
188+
189+
190+
"""Benchmark SDDP."""
191+
function benchmark_sddp()
192+
model, params = init_problem()
193+
194+
# Launch a first start to compile solve_SDDP
195+
params.maxItNumber = 2
196+
V, pbs = solve_SDDP(model, params, 0)
197+
params.maxItNumber = 20
198+
# Launch benchmark
199+
println("Launch SDDP ...")
200+
tic()
201+
V, pbs = solve_SDDP(model, params, 0)
202+
texec = toq()
203+
println("Time to solve SDDP: ", texec, "s")
204+
205+
# Test results upon 100 assessment scenarios:
206+
n_assessments = 100
207+
aleas = simulate_scenarios(model.noises,
208+
(model.stageNumber,
209+
n_assessments,
210+
model.dimNoises))
211+
212+
tic()
213+
costs_sddp, stocks = forward_simulations(model, params, V, pbs, aleas)
214+
texec = toq()
215+
println("Time to perform simulation: ", texec, "s")
216+
217+
# Get costs with deterministic solution:
218+
println("Compute anticipative solution ...")
219+
costs_det = zeros(n_assessments)
220+
for n in 1:n_assessments
221+
costs_det[n] = solve_determinist_problem(model, aleas[:, n, :])
222+
end
223+
224+
println("SDDP cost: \t", mean(costs_sddp))
225+
println("Deterministic cost: \t", mean(costs_det))
226+
println("Gap: \t", mean(costs_det)/mean(costs_sddp))
227+
return stocks, V
228+
end
229+
230+
231+
"""Benchmark SDP."""
232+
function benchmark_sdp()
233+
234+
N_STAGES::Int64 = 5
235+
TF = N_STAGES
236+
# Capacity of dams:
237+
VOLUME_MAX::Float64 = 20.
238+
VOLUME_MIN::Float64 = 0.
239+
240+
# Specify the maximum flow of turbines:
241+
CONTROL_MAX::Float64 = 5.
242+
CONTROL_MIN::Float64 = 0.
243+
244+
# Some statistics about aleas (water inflow):
245+
W_MAX::Float64 = 5.
246+
W_MIN::Float64 = 0.
247+
DW::Float64 = 1.
248+
249+
T0 = 1
250+
251+
# Define aleas' space:
252+
N_ALEAS = Int(round(Int, (W_MAX - W_MIN) / DW + 1))
253+
ALEAS = linspace(W_MIN, W_MAX, N_ALEAS);
254+
255+
N_CONTROLS = 2;
256+
N_STATES = 2;
257+
N_NOISES = 1;
258+
259+
infoStruct = "HD"
260+
261+
COST = 66*2.7*(1 + .5*(rand(TF) - .5));
262+
263+
# Define dynamic of the dam:
264+
function dynamic(t, x, u, w)
265+
return [x[1] + u[1] + w[1] - u[2], x[2] - u[1]]
266+
end
267+
268+
# Define cost corresponding to each timestep:
269+
function cost_t(t, x, u, w)
270+
return COST[t] * u[1]
271+
end
272+
273+
function constraints(t, x, u, w)
274+
return (VOLUME_MIN<=x[1]<=VOLUME_MAX)&(VOLUME_MIN<=x[2]<=VOLUME_MAX)
275+
end
276+
277+
function finalCostFunction(x)
278+
return 0.
279+
end
280+
281+
"""Build admissible scenarios for water inflow over the time horizon."""
282+
function build_scenarios(n_scenarios::Int64)
283+
scenarios = zeros(n_scenarios, TF)
284+
285+
for scen in 1:n_scenarios
286+
scenarios[scen, :] = (W_MAX-W_MIN)*rand(TF)+W_MIN
287+
end
288+
return scenarios
289+
end
290+
291+
"""Build probability distribution at each timestep based on N scenarios.
292+
Return a Vector{NoiseLaw}"""
293+
function generate_probability_laws(N_STAGES, N_SCENARIOS)
294+
aleas = zeros(N_SCENARIOS, TF, 1)
295+
aleas[:, :, 1] = build_scenarios(N_SCENARIOS)
296+
297+
laws = Vector{NoiseLaw}(N_STAGES)
298+
299+
# uniform probabilities:
300+
proba = 1/N_SCENARIOS*ones(N_SCENARIOS)
301+
302+
for t=1:N_STAGES
303+
aleas_t = reshape(aleas[:, t, :], N_SCENARIOS, 1)'
304+
laws[t] = NoiseLaw(aleas_t, proba)
305+
end
306+
307+
return laws
308+
end
309+
310+
N_SCENARIO = 5
311+
aleas = generate_probability_laws(TF, N_SCENARIO)
312+
313+
x_bounds = [(VOLUME_MIN, VOLUME_MAX), (VOLUME_MIN, VOLUME_MAX)];
314+
u_bounds = [(CONTROL_MIN, CONTROL_MAX), (VOLUME_MIN, 10)];
315+
316+
x0 = [20., 22.]
317+
318+
modelSDP = StochDynProgModel(N_STAGES-1, N_CONTROLS,
319+
N_STATES, N_NOISES,
320+
x_bounds, u_bounds,
321+
x0, cost_t,
322+
finalCostFunction, dynamic,
323+
constraints, aleas);
324+
325+
stateSteps = [1.,1.];
326+
controlSteps = [1.,1.];
327+
monteCarloSize = 10.;
328+
329+
paramsSDP = StochDynamicProgramming.SDPparameters(modelSDP, stateSteps,
330+
controlSteps,
331+
monteCarloSize,
332+
infoStruct);
333+
334+
print("Problem size (T*X*U*W): ")
335+
println(paramsSDP.totalStateSpaceSize*paramsSDP.totalControlSpaceSize)
336+
337+
n_benchmark = 10
338+
timing = zeros(n_benchmark)
339+
for n in 1:n_benchmark
340+
tic()
341+
V_sdp = sdp_optimize(modelSDP, paramsSDP,false);
342+
timing[n] = toq()
343+
end
344+
@show timing
345+
println("SDP execution time: ", mean(timing[2:end]), " s +/-", 3.std(timing[2:end]))
346+
347+
end
348+
349+
# SDDP benchmark:
350+
if ARGS[1] == "SDDP"
351+
benchmark_sddp()
352+
elseif ARGS[1] == "DP"
353+
benchmark_sdp()
354+
end

0 commit comments

Comments
 (0)