Skip to content

Commit c189bd6

Browse files
authored
Merge pull request #115 from JuliaOpt/smip-sddp
Stochastic Mixed-Integer SDDP
2 parents 2eafb18 + 7ddc8e4 commit c189bd6

14 files changed

+223
-148
lines changed

examples/benchmark.jl

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,20 +167,23 @@ function init_problem()
167167
x_bounds = [(VOLUME_MIN, VOLUME_MAX), (VOLUME_MIN, VOLUME_MAX)]
168168
u_bounds = [(CONTROL_MIN, CONTROL_MAX), (CONTROL_MIN, CONTROL_MAX), (0, Inf), (0, Inf)]
169169

170-
model = LinearDynamicLinearCostSPmodel(N_STAGES,
171-
u_bounds,
172-
x0,
173-
cost_t,
174-
dynamic,
175-
aleas)
170+
model = LinearSPModel(N_STAGES,
171+
u_bounds,
172+
x0,
173+
cost_t,
174+
dynamic,
175+
aleas)
176176

177177
set_state_bounds(model, x_bounds)
178178

179179

180180
EPSILON = .05
181181
MAX_ITER = 20
182182
solver = SOLVER
183-
params = SDDPparameters(solver, N_SCENARIOS, EPSILON, MAX_ITER)
183+
params = SDDPparameters(solver,
184+
passnumber=N_SCENARIOS,
185+
gap=EPSILON,
186+
max_iterations=MAX_ITER)
184187

185188
return model, params
186189
end

examples/dam.jl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,16 +142,19 @@ function init_problem()
142142

143143
x_bounds = [(0, 100)]
144144
u_bounds = [(0, 7), (0, 7)]
145-
model = StochDynamicProgramming.LinearDynamicLinearCostSPmodel(N_STAGES,
146-
u_bounds,
147-
x0,
148-
cost_t,
149-
dynamic, aleas)
145+
model = StochDynamicProgramming.LinearSPModel(N_STAGES,
146+
u_bounds,
147+
x0,
148+
cost_t,
149+
dynamic, aleas)
150150

151151
set_state_bounds(model, x_bounds)
152152

153153
solver = SOLVER
154-
params = StochDynamicProgramming.SDDPparameters(solver, N_SCENARIOS, EPSILON, MAX_ITER)
154+
params = StochDynamicProgramming.SDDPparameters(solver,
155+
passnumber=N_SCENARIOS,
156+
gap=EPSILON,
157+
max_iterations=MAX_ITER)
155158

156159
return model, params
157160
end

examples/damsvalley.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,21 +107,21 @@ function init_problem()
107107

108108
x_bounds = [(VOLUME_MIN, VOLUME_MAX) for i in 1:N_DAMS]
109109
u_bounds = vcat([(CONTROL_MIN, CONTROL_MAX) for i in 1:N_DAMS], [(0., 200) for i in 1:N_DAMS]);
110-
model = LinearDynamicLinearCostSPmodel(N_STAGES,
111-
u_bounds,
112-
X0,
113-
cost_t,
114-
dynamic,
115-
aleas,
116-
final_cost_dams)
110+
model = LinearSPModel(N_STAGES, u_bounds,
111+
X0, cost_t,
112+
dynamic, aleas,
113+
final_cost_dams)
117114

118115
# Add bounds for stocks:
119116
set_state_bounds(model, x_bounds)
120117

121118
# We need to use CPLEX to solve QP at final stages:
122119
solver = CPLEX.CplexSolver(CPX_PARAM_SIMDISPLAY=0, CPX_PARAM_BARDISPLAY=0)
123-
params = SDDPparameters(solver, FORWARD_PASS, EPSILON, MAX_ITER)
124120

121+
params = SDDPparameters(solver,
122+
passnumber=FORWARD_PASS,
123+
gap=EPSILON,
124+
max_iterations=MAX_ITER)
125125
return model, params
126126
end
127127

examples/stock-example.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,18 @@ end
5858
######## Setting up the SPmodel
5959
s_bounds = [(0, 1)] # bounds on the state
6060
u_bounds = [(CONTROL_MIN, CONTROL_MAX)] # bounds on controls
61-
spmodel = LinearDynamicLinearCostSPmodel(N_STAGES,u_bounds,[S0],cost_t,dynamic,xi_laws)
61+
spmodel = LinearSPModel(N_STAGES,u_bounds,[S0],cost_t,dynamic,xi_laws)
6262
set_state_bounds(spmodel, s_bounds) # adding the bounds to the model
6363
println("Model set up")
6464

6565
######### Solving the problem via SDDP
6666
if run_sddp
6767
println("Starting resolution by SDDP")
68-
paramSDDP = SDDPparameters(SOLVER, 10, 0, MAX_ITER) # 10 forward pass, stop at MAX_ITER
68+
# 10 forward pass, stop at MAX_ITER
69+
paramSDDP = StochDynamicProgramming.SDDPparameters(SOLVER,
70+
passnumber=10,
71+
gap=0,
72+
max_iterations=MAX_ITER)
6973
V, pbs = solve_SDDP(spmodel, paramSDDP, 2) # display information every 2 iterations
7074
lb_sddp = StochDynamicProgramming.get_lower_bound(spmodel, paramSDDP, V)
7175
println("Lower bound obtained by SDDP: "*string(round(lb_sddp,4)))

src/SDDPoptimize.jl

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ fulfilled.
3737
3838
"""
3939
function solve_SDDP(model::SPModel, param::SDDPparameters, verbose=0::Int64)
40+
if model.IS_SMIP && isa(param.MIPSOLVER, Void)
41+
error("MIP solver is not defined. Please set `param.MIPSOLVER`")
42+
end
4043
# initialize value functions:
4144
V, problems = initialize_value_functions(model, param)
4245
(verbose > 0) && println("Initial value function loaded into memory.")
@@ -47,6 +50,9 @@ end
4750

4851

4952
function solve_SDDP(model::SPModel, param::SDDPparameters, V::Vector{PolyhedralFunction}, verbose=0::Int64)
53+
if model.IS_SMIP && isa(param.MIPSOLVER, Void)
54+
error("MIP solver is not defined. Please set `param.MIPSOLVER`")
55+
end
5056
# First step: process value functions if hotstart is called
5157
problems = hotstart_SDDP(model, param, V)
5258
sddp_stats = run_SDDP!(model, param, V, problems, verbose)
@@ -191,11 +197,8 @@ function estimate_upper_bound(model::SPModel, param::SDDPparameters,
191197
problem::Vector{JuMP.Model},
192198
n_simulation=1000::Int)
193199
aleas = simulate_scenarios(model.noises, n_simulation)
194-
costs, stockTrajectories, _ = forward_simulations(model, param, problem, aleas)
195-
return upper_bound(costs), costs
200+
return estimate_upper_bound(model, param, aleas, problem)
196201
end
197-
198-
199202
function estimate_upper_bound(model::SPModel, param::SDDPparameters,
200203
aleas::Array{Float64, 3},
201204
problem::Vector{JuMP.Model})
@@ -204,12 +207,6 @@ function estimate_upper_bound(model::SPModel, param::SDDPparameters,
204207
end
205208

206209

207-
"""Build a collection of cuts initialized at 0"""
208-
function get_null_value_functions_array(model::SPModel)
209-
return [PolyhedralFunction(zeros(1), zeros(1, model.dimStates), 1) for i in 1:model.stageNumber]
210-
end
211-
212-
213210
"""
214211
Build a cut approximating terminal cost with null function
215212
@@ -257,33 +254,35 @@ function build_models(model::SPModel, param::SDDPparameters)
257254
end
258255

259256
function build_model(model, param, t)
260-
m = Model(solver=param.solver)
257+
m = Model(solver=param.SOLVER)
261258

262259
nx = model.dimStates
263260
nu = model.dimControls
264261
nw = model.dimNoises
265262

263+
# define variables in JuMP:
266264
@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])
268265
@variable(m, model.xlim[i][1] <= xf[i=1:nx]<= model.xlim[i][2])
266+
@variable(m, model.ulim[i][1] <= u[i=1:nu] <= model.ulim[i][2])
269267
@variable(m, alpha)
270268

271269
@variable(m, w[1:nw] == 0)
272270
m.ext[:cons] = @constraint(m, state_constraint, x .== 0)
273271

274272
@constraint(m, xf .== model.dynamics(t, x, u, w))
275273

274+
# Add equality and inequality constraints:
276275
if model.equalityConstraints != nothing
277276
@constraint(m, model.equalityConstraints(t, x, u, w) .== 0)
278277
end
279278
if model.inequalityConstraints != nothing
280279
@constraint(m, model.inequalityConstraints(t, x, u, w) .<= 0)
281280
end
282281

283-
if typeof(model) == LinearDynamicLinearCostSPmodel
282+
# Define objective function (could be linear or piecewise linear)
283+
if isa(model.costFunctions, Function)
284284
@objective(m, Min, model.costFunctions(t, x, u, w) + alpha)
285-
286-
elseif typeof(model) == PiecewiseLinearCostSPmodel
285+
elseif isa(model.costFunctions, Vector{Function})
287286
@variable(m, cost)
288287

289288
for i in 1:length(model.costFunctions)
@@ -292,6 +291,11 @@ function build_model(model, param, t)
292291
@objective(m, Min, cost + alpha)
293292
end
294293

294+
# Add binary variable if problem is a SMIP:
295+
if model.IS_SMIP
296+
m.colCat[2*nx+1:2*nx+nu] = model.controlCat
297+
end
298+
295299
return m
296300
end
297301

@@ -319,7 +323,7 @@ function initialize_value_functions(model::SPModel,
319323

320324
solverProblems = build_models(model, param)
321325
V = PolyhedralFunction[
322-
PolyhedralFunction([], Array{Float64}(0, model.dimStates), 0) for i in 1:model.stageNumber]
326+
PolyhedralFunction(model.dimStates) for i in 1:model.stageNumber]
323327

324328
# Build scenarios according to distribution laws:
325329
aleas = simulate_scenarios(model.noises, param.forwardPassNumber)
@@ -399,7 +403,7 @@ Bellman value (Float64)
399403
function get_bellman_value(model::SPModel, param::SDDPparameters,
400404
t::Int64, Vt::PolyhedralFunction, xt::Vector{Float64})
401405

402-
m = Model(solver=param.solver)
406+
m = Model(solver=param.SOLVER)
403407
@variable(m, alpha)
404408

405409
for i in 1:Vt.numCuts
@@ -515,7 +519,7 @@ function exact_prune_cuts(model::SPModel, params::SDDPparameters, V::PolyhedralF
515519
ncuts = V.numCuts
516520
# Find all active cuts:
517521
if ncuts > 1
518-
active_cuts = Bool[is_cut_relevant(model, i, V, params.solver) for i=1:ncuts]
522+
active_cuts = Bool[is_cut_relevant(model, i, V, params.SOLVER) for i=1:ncuts]
519523
return PolyhedralFunction(V.betas[active_cuts], V.lambdas[active_cuts, :], sum(active_cuts))
520524
else
521525
return V

src/SDPoptimize.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function build_sdpmodel_from_spmodel(model::SPModel)
7676
return 0
7777
end
7878

79-
if isa(model,PiecewiseLinearCostSPmodel)||isa(model,LinearDynamicLinearCostSPmodel)
79+
if isa(model,LinearSPModel)
8080
function cons_fun(t,x,u,w)
8181
return true
8282
end

src/StochDynamicProgramming.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
module StochDynamicProgramming
1111
include("SDPutils.jl")
1212

13-
using JuMP, Distributions
13+
using MathProgBase, JuMP, Distributions
1414

1515
export solve_SDDP,
1616
NoiseLaw, simulate_scenarios,
17-
SDDPparameters, LinearDynamicLinearCostSPmodel, set_state_bounds,
18-
PiecewiseLinearCostSPmodel, extensive_formulation,
17+
SDDPparameters, LinearSPModel, set_state_bounds,
18+
extensive_formulation,
1919
PolyhedralFunction, NextStep, forward_simulations,
2020
StochDynProgModel, SDPparameters, solve_DP,
2121
sdp_forward_simulation, sampling, get_control, get_bellman_value,

src/extensiveFormulation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function extensive_formulation(model, param; verbose=0)
2929

3030
X_init = model.initialState
3131
T = model.stageNumber-1
32-
mod = Model(solver=param.solver)
32+
mod = Model(solver=param.SOLVER)
3333

3434
#Calculate the number of nodes n at each step on the scenario tree
3535
N = Array{Int64,2}(zeros(T+1,1))

src/forwardBackwardIterations.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,13 @@ function backward_pass!(model::SPModel,
213213
alea_t = collect(law[t].support[:, w])
214214

215215
callsolver += 1
216+
216217
# We solve LP problem with current noise and position:
217218
solved, nextstep = solve_one_step_one_alea(model, param,
218219
solverProblems[t],
219-
t, state_t, alea_t)
220+
t, state_t, alea_t,
221+
relaxation=model.IS_SMIP)
222+
220223
if solved
221224
# We catch the subgradient λ:
222225
subgradient_array[:, w] = nextstep.sub_gradient

0 commit comments

Comments
 (0)