Skip to content

Commit 5a1f860

Browse files
authored
Merge pull request #113 from JuliaOpt/doc-refactoring
[DOC] update documentation for core functions
2 parents 06382c6 + 9e3a66b commit 5a1f860

File tree

8 files changed

+130
-110
lines changed

8 files changed

+130
-110
lines changed

src/SDDPoptimize.jl

Lines changed: 67 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ fulfilled.
2121
the stochastic problem we want to optimize
2222
* `param::SDDPparameters`:
2323
the parameters of the SDDP algorithm
24-
* `display::Int64`:
24+
* `verbose::Int64`:
2525
Default is `0`
2626
If non null, display progression in terminal every
27-
`n` iterations, where `n` is number specified by display.
27+
`n` iterations, where `n` is the number specified by display.
2828
2929
# Returns
3030
* `V::Array{PolyhedralFunction}`:
@@ -36,20 +36,20 @@ fulfilled.
3636
number of times the solver has been called
3737
3838
"""
39-
function solve_SDDP(model::SPModel, param::SDDPparameters, display=0::Int64)
39+
function solve_SDDP(model::SPModel, param::SDDPparameters, verbose=0::Int64)
4040
# initialize value functions:
4141
V, problems = initialize_value_functions(model, param)
42-
(display > 0) && println("Initial value function loaded into memory.")
43-
# Run SDDP upon example:
44-
sddp_stats = run_SDDP!(model, param, V, problems, display)
42+
(verbose > 0) && println("Initial value function loaded into memory.")
43+
# Run SDDP:
44+
sddp_stats = run_SDDP!(model, param, V, problems, verbose)
4545
return V, problems, sddp_stats
4646
end
4747

4848

49-
function solve_SDDP(model::SPModel, param::SDDPparameters, V::Vector{PolyhedralFunction}, display=0::Int64)
49+
function solve_SDDP(model::SPModel, param::SDDPparameters, V::Vector{PolyhedralFunction}, verbose=0::Int64)
5050
# First step: process value functions if hotstart is called
5151
problems = hotstart_SDDP(model, param, V)
52-
sddp_stats = run_SDDP!(model, param, V, problems, display)
52+
sddp_stats = run_SDDP!(model, param, V, problems, verbose)
5353
return V, problems, sddp_stats
5454
end
5555

@@ -59,15 +59,15 @@ function run_SDDP!(model::SPModel,
5959
param::SDDPparameters,
6060
V::Vector{PolyhedralFunction},
6161
problems::Vector{JuMP.Model},
62-
display=0::Int64)
62+
verbose=0::Int64)
6363

6464
#Initialization of the counter
6565
stats = SDDPStat(0, [], [], [], 0)
6666

67-
if display > 0
68-
println("Initialize cuts")
69-
end
67+
(verbose > 0) && println("Initialize cuts")
7068

69+
# If computation of upper-bound is needed, a set of scenarios is built
70+
# to keep always the same realization for upper bound estimation:
7171
if param.compute_upper_bound > 0
7272
upperbound_scenarios = simulate_scenarios(model.noises, param.monteCarloSize)
7373
end
@@ -77,22 +77,23 @@ function run_SDDP!(model::SPModel,
7777
stopping_test::Bool = false
7878
iteration_count::Int64 = 0
7979

80+
# Launch execution of forward and backward passes:
8081
while (iteration_count < param.maxItNumber) & (~stopping_test)
81-
8282
# Time execution of current pass:
8383
tic()
8484

85-
# Build given number of scenarios according to distribution
85+
# Draw a set of scenarios according to the probability
8686
# law specified in model.noises:
8787
noise_scenarios = simulate_scenarios(model.noises, param.forwardPassNumber)
8888

89+
####################
8990
# Forward pass
9091
_, stockTrajectories,_,callsolver_forward = forward_simulations(model,
9192
param,
9293
problems,
9394
noise_scenarios)
9495

95-
96+
####################
9697
# Backward pass
9798
callsolver_backward = backward_pass!(model,
9899
param,
@@ -101,24 +102,30 @@ function run_SDDP!(model::SPModel,
101102
stockTrajectories,
102103
model.noises)
103104

104-
#Update the number of call
105+
# Update the number of solver call
105106
stats.ncallsolver += callsolver_forward + callsolver_backward
106-
107107
iteration_count += 1
108108
stats.niterations += 1
109109

110+
####################
111+
# If specified, prune cuts:
110112
if (param.compute_cuts_pruning > 0) && (iteration_count%param.compute_cuts_pruning==0)
111-
(display > 0) && println("Prune cuts ...")
113+
(verbose > 0) && println("Prune cuts ...")
112114
remove_redundant_cuts!(V)
113115
prune_cuts!(model, param, V)
114116
problems = hotstart_SDDP(model, param, V)
115117
end
118+
119+
# Update estimation of lower bound:
116120
lwb = get_bellman_value(model, param, 1, V[1], model.initialState)
117121
push!(stats.lower_bounds, lwb)
118122

123+
####################
124+
# If specified, compute upper-bound:
119125
if (param.compute_upper_bound > 0) && (iteration_count%param.compute_upper_bound==0)
120-
(display > 0) && println("Compute upper-bound with ",
126+
(verbose > 0) && println("Compute upper-bound with ",
121127
param.monteCarloSize, " scenarios...")
128+
# estimate upper-bound with Monte-Carlo estimation:
122129
upb, costs = estimate_upper_bound(model, param, upperbound_scenarios, problems)
123130
if param.gap > 0.
124131
stopping_test = test_stopping_criterion(lwb, upb, param.gap)
@@ -128,26 +135,21 @@ function run_SDDP!(model::SPModel,
128135
push!(stats.exectime, toq())
129136
push!(stats.upper_bounds, upb)
130137

131-
if (display > 0) && (iteration_count%display==0)
132-
if upb < Inf
133-
println("Pass number ", iteration_count,
134-
"\tUpper-bound: ", upb,
135-
"\tLower-bound: ", round(stats.lower_bounds[end], 4),
136-
"\tTime: ", round(stats.exectime[end], 2),"s")
137-
else
138-
println("Pass number ", iteration_count,
139-
"\tLower-bound: ", round(stats.lower_bounds[end], 4),
140-
"\tTime: ", round(stats.exectime[end], 2),"s")
141-
end
138+
if (verbose > 0) && (iteration_count%verbose==0)
139+
print("Pass number ", iteration_count)
140+
(upb < Inf) && print("\tUpper-bound: ", upb)
141+
println("\tLower-bound: ", round(stats.lower_bounds[end], 4),
142+
"\tTime: ", round(stats.exectime[end], 2),"s")
142143
end
143144

144145
end
145146

146-
# Estimate upper bound with a great number of simulations:
147-
if (display>0) && (param.compute_upper_bound != 0)
147+
##########
148+
# Estimate final upper bound with param.monteCarloSize simulations:
149+
if (verbose>0) && (param.compute_upper_bound != 0)
148150
V0 = get_bellman_value(model, param, 1, V[1], model.initialState)
149151

150-
if param.compute_upper_bound == -1
152+
if param.compute_upper_bound == 0
151153
println("Estimate upper-bound with Monte-Carlo ...")
152154
upb, costs = estimate_upper_bound(model, param, V, problems)
153155
end
@@ -188,7 +190,6 @@ function estimate_upper_bound(model::SPModel, param::SDDPparameters,
188190
V::Vector{PolyhedralFunction},
189191
problem::Vector{JuMP.Model},
190192
n_simulation=1000::Int)
191-
192193
aleas = simulate_scenarios(model.noises, n_simulation)
193194
costs, stockTrajectories, _ = forward_simulations(model, param, problem, aleas)
194195
return upper_bound(costs), costs
@@ -252,49 +253,46 @@ linear problem.
252253
* `Array::JuMP.Model`:
253254
"""
254255
function build_models(model::SPModel, param::SDDPparameters)
256+
return JuMP.Model[build_model(model, param, t) for t=1:model.stageNumber-1]
257+
end
255258

256-
models = Vector{JuMP.Model}(model.stageNumber-1)
257-
258-
for t = 1:model.stageNumber-1
259-
m = Model(solver=param.solver)
259+
function build_model(model, param, t)
260+
m = Model(solver=param.solver)
260261

261-
nx = model.dimStates
262-
nu = model.dimControls
263-
nw = model.dimNoises
262+
nx = model.dimStates
263+
nu = model.dimControls
264+
nw = model.dimNoises
264265

265-
@variable(m, model.xlim[i][1] <= x[i=1:nx] <= model.xlim[i][2])
266-
@variable(m, model.ulim[i][1] <= u[i=1:nu] <= model.ulim[i][2])
267-
@variable(m, model.xlim[i][1] <= xf[i=1:nx]<= model.xlim[i][2])
268-
@variable(m, alpha)
266+
@variable(m, model.xlim[i][1] <= x[i=1:nx] <= model.xlim[i][2])
267+
@variable(m, model.ulim[i][1] <= u[i=1:nu] <= model.ulim[i][2])
268+
@variable(m, model.xlim[i][1] <= xf[i=1:nx]<= model.xlim[i][2])
269+
@variable(m, alpha)
269270

270-
@variable(m, w[1:nw] == 0)
271-
m.ext[:cons] = @constraint(m, state_constraint, x .== 0)
271+
@variable(m, w[1:nw] == 0)
272+
m.ext[:cons] = @constraint(m, state_constraint, x .== 0)
272273

273-
@constraint(m, xf .== model.dynamics(t, x, u, w))
274+
@constraint(m, xf .== model.dynamics(t, x, u, w))
274275

275-
if model.equalityConstraints != nothing
276-
@constraint(m, model.equalityConstraints(t, x, u, w) .== 0)
277-
end
278-
if model.inequalityConstraints != nothing
279-
@constraint(m, model.inequalityConstraints(t, x, u, w) .<= 0)
280-
end
276+
if model.equalityConstraints != nothing
277+
@constraint(m, model.equalityConstraints(t, x, u, w) .== 0)
278+
end
279+
if model.inequalityConstraints != nothing
280+
@constraint(m, model.inequalityConstraints(t, x, u, w) .<= 0)
281+
end
281282

282-
if typeof(model) == LinearDynamicLinearCostSPmodel
283-
@objective(m, Min, model.costFunctions(t, x, u, w) + alpha)
283+
if typeof(model) == LinearDynamicLinearCostSPmodel
284+
@objective(m, Min, model.costFunctions(t, x, u, w) + alpha)
284285

285-
elseif typeof(model) == PiecewiseLinearCostSPmodel
286-
@variable(m, cost)
286+
elseif typeof(model) == PiecewiseLinearCostSPmodel
287+
@variable(m, cost)
287288

288-
for i in 1:length(model.costFunctions)
289-
@constraint(m, cost >= model.costFunctions[i](t, x, u, w))
290-
end
291-
@objective(m, Min, cost + alpha)
289+
for i in 1:length(model.costFunctions)
290+
@constraint(m, cost >= model.costFunctions[i](t, x, u, w))
292291
end
293-
294-
models[t] = m
295-
292+
@objective(m, Min, cost + alpha)
296293
end
297-
return models
294+
295+
return m
298296
end
299297

300298

@@ -339,13 +337,8 @@ function initialize_value_functions(model::SPModel,
339337
stockTrajectories[i, j, :] = get_random_state(model)
340338
end
341339

342-
343-
callsolver = backward_pass!(model,
344-
param,
345-
V,
346-
solverProblems,
347-
stockTrajectories,
348-
model.noises)
340+
callsolver = backward_pass!(model, param, V, solverProblems,
341+
stockTrajectories, model.noises)
349342

350343
return V, solverProblems
351344
end
@@ -370,6 +363,7 @@ function hotstart_SDDP(model::SPModel, param::SDDPparameters, V::Vector{Polyhedr
370363

371364
solverProblems = build_models(model, param)
372365

366+
# Add corresponding cuts to each problem:
373367
for t in 1:model.stageNumber-2
374368
add_cuts_to_model!(model, t, solverProblems[t], V[t+1])
375369
end

src/compare.jl

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,26 @@ of calls to the solver
2525
* `seeds::Int`
2626
The random number generator seeds
2727
28-
# Output
29-
* `Display in the terminal`
30-
Print information in the terminal
28+
# Returns
29+
Return for different benchmark the following parameters:
30+
solver_calls, solving_times, solving_mem, gap_sols
3131
"""
3232
function benchmark_parameters(model,
3333
SDDParametersCollection,
3434
scenarios::Array{Float64,3},
35-
seeds::Int)
35+
seeds::Int;
36+
verbose=0)
3637

3738
#Execute a first time each function to compile them
3839
(V, pbs, callsolver), t1, m1 = @timed solve_SDDP(model, SDDParametersCollection[1], 0)
3940
V0, t2, m2 = @timed get_bellman_value(model, SDDParametersCollection[1], 1, V[1], model.initialState)
4041
(upb, costs), t3, m3 = @timed estimate_upper_bound(model, SDDParametersCollection[1], scenarios, pbs)
4142

43+
# Define arrays to store results of benchmark
44+
solver_calls = []
45+
solving_times = []
46+
solving_mem = []
47+
gap_sols = []
4248

4349
for sddpparams in SDDParametersCollection
4450

@@ -52,13 +58,22 @@ function benchmark_parameters(model,
5258
simulationtime = t2+t3
5359
solvingmemory = m1
5460
simulationmemory = m2+m3
61+
g = round(100*(upb-V0)/V0)
5562

56-
print("Instance \t")
57-
print("Solving time = ",round(solvingtime,4),"\t")
58-
print("Solving memory = ", solvingmemory,"\t")
59-
print("Simulation time = ",round(simulationtime,4),"\t")
60-
print("Simulation memory = ", simulationmemory,"\t")
61-
print("Gap < ", round(100*(upb-V0)/V0),"% with prob 97.5%\t")
62-
println("number external solver call = ", sddpstats.ncallsolver)
63+
push!(solver_calls, sddpstats.ncallsolver)
64+
push!(solving_times, t1)
65+
push!(solving_mem, m1)
66+
push!(gap_sols, g)
67+
68+
if verbose > 0
69+
print("Instance \t")
70+
print("Solving time = ",round(solvingtime,4),"\t")
71+
print("Solving memory = ", solvingmemory,"\t")
72+
print("Simulation time = ",round(simulationtime,4),"\t")
73+
print("Simulation memory = ", simulationmemory,"\t")
74+
print("Gap < ", g,"% with prob 97.5%\t")
75+
println("number external solver call = ", sddpstats.ncallsolver)
76+
end
6377
end
78+
return solver_calls, solving_times, solving_mem, gap_sols
6479
end

src/extensiveFormulation.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ measurability constraints.
1313
# Arguments:
1414
* `model::SPModel`
1515
* `param::SDDPparameters`
16+
* `verbose`::Int`
17+
Optionnal, default is 0
1618
# Returns
1719
* `objective value`
18-
* `first control`
20+
* `first control`
1921
* `status of optimization problem`
2022
"""
21-
function extensive_formulation(model, param)
23+
function extensive_formulation(model, param; verbose=0)
2224

2325
#Recover all the constant in the model or in param
2426
laws = model.noises
@@ -91,18 +93,18 @@ function extensive_formulation(model, param)
9193
@constraint(mod, [x[1,k] for k = 1:DIM_STATE] .== X_init)
9294

9395
#Define the objective of the function
94-
@objective(mod, Min,
95-
sum{
96+
@objective(mod, Min,
97+
sum{
9698
sum{ proba[t][laws[t].supportSize*(n-1)+k]*c[t,laws[t].supportSize*(n-1)+k],
97-
k = 1:laws[t].supportSize},
99+
k = 1:laws[t].supportSize},
98100
t = 1:T, n=1:div(N[t+1],laws[t].supportSize)}
99101
)
100102

101103
status = solve(mod)
102104
solved = (status == :Optimal)
103105

104106
if solved
105-
println("EF value: "*string(getobjectivevalue(mod)))
107+
(verbose > 0) && println("EF value: "*string(getobjectivevalue(mod)))
106108
firstControl = collect(values(getvalue(u)))[1:DIM_CONTROL*laws[1].supportSize]
107109
return getobjectivevalue(mod), firstControl, status
108110
else

0 commit comments

Comments
 (0)