Skip to content

Commit f11f986

Browse files
authored
Merge pull request #122 from JuliaOpt/dev-nightly
v0.1.4
2 parents e7e5303 + 73a5d90 commit f11f986

26 files changed

+1515
-554
lines changed

doc/quickstart.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.. _quickstart:
22

33
====================
4-
Step-by-step example
4+
SDDP: Step-by-step example
55
====================
66

77
This page gives a short introduction to the interface of this package. It explains the resolution with SDDP of a classical example: the management of a dam over one year with random inflow.
@@ -52,7 +52,7 @@ Dynamic
5252
We write the dynamic (which return a vector)::
5353

5454
function dynamic(t, x, u, xi)
55-
return [x[1] + u[1] - xi[1]]
55+
return [x[1] + u[1] - xi[1]]
5656
end
5757

5858

@@ -105,6 +105,10 @@ As our problem is purely linear, we instantiate::
105105

106106
spmodel = LinearDynamicLinearCostSPmodel(N_STAGES,u_bounds,X0,cost_t,dynamic,xi_laws)
107107

108+
We add the state bounds to the model afterward::
109+
110+
set_state_bounds(spmodel, s_bounds)
111+
108112

109113
Solver
110114
^^^^^^

doc/quickstart_sdp.rst

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
.. _quickstart_sdp:
2+
3+
====================
4+
SDP: Step-by-step example
5+
====================
6+
7+
This page gives a short introduction to the interface of this package. It explains the resolution with Stochastic Dynamic Programming of a classical example: the management of a dam over one year with random inflow.
8+
9+
Use case
10+
========
11+
In the following, :math:`x_t` will denote the state and :math:`u_t` the control at time :math:`t`.
12+
We will consider a dam, whose dynamic is:
13+
14+
.. math::
15+
x_{t+1} = x_t - u_t + w_t
16+
17+
At time :math:`t`, we have a random inflow :math:`w_t` and we choose to turbine a quantity :math:`u_t` of water.
18+
19+
The turbined water is used to produce electricity, which is being sold at a price :math:`c_t`. At time :math:`t` we gain:
20+
21+
.. math::
22+
C(x_t, u_t, w_t) = c_t \times u_t
23+
24+
We want to minimize the following criterion:
25+
26+
.. math::
27+
J = \underset{x, u}{\min} \sum_{t=0}^{T-1} C(x_t, u_t, w_t)
28+
29+
We will assume that both states and controls are bounded:
30+
31+
.. math::
32+
x_t \in [0, 100], \qquad u_t \in [0, 7]
33+
34+
35+
Problem definition in Julia
36+
===========================
37+
38+
We will consider 52 time steps as we want to find optimal value functions for one year::
39+
40+
N_STAGES = 52
41+
42+
43+
and we consider the following initial position::
44+
45+
X0 = [50]
46+
47+
Note that X0 is a vector.
48+
49+
Dynamic
50+
^^^^^^^
51+
52+
We write the dynamic (which return a vector)::
53+
54+
function dynamic(t, x, u, xi)
55+
return [x[1] + u[1] - xi[1]]
56+
end
57+
58+
59+
Cost
60+
^^^^
61+
62+
we store evolution of costs :math:`c_t` in an array `COSTS`, and we define the cost function (which return a float)::
63+
64+
function cost_t(t, x, u, w)
65+
return COSTS[t] * u[1]
66+
end
67+
68+
Noises
69+
^^^^^^
70+
71+
Noises are defined in an array of Noiselaw. This type defines a discrete probability.
72+
73+
74+
For instance, if we want to define a uniform probability with size :math:`N= 10`, such that:
75+
76+
.. math::
77+
\mathbb{P} \left(X_i = i \right) = \dfrac{1}{N} \qquad \forall i \in 1 .. N
78+
79+
we write::
80+
81+
N = 10
82+
proba = 1/N*ones(N) # uniform probabilities
83+
xi_support = collect(linspace(1,N,N))
84+
xi_law = NoiseLaw(xi_support, proba)
85+
86+
87+
Thus, we could define a different probability laws for each time :math:`t`. Here, we suppose that the probability is constant over time, so we could build the following vector::
88+
89+
xi_laws = NoiseLaw[xi_law for t in 1:N_STAGES-1]
90+
91+
92+
Bounds
93+
^^^^^^
94+
95+
We add bounds over the state and the control::
96+
97+
s_bounds = [(0, 100)]
98+
u_bounds = [(0, 7)]
99+
100+
101+
Problem definition
102+
^^^^^^^^^^^^^^^^^^
103+
104+
We have two options to contruct a model that can be solved by the SDP algorithm.
105+
We can instantiate a model that can be solved by SDDP as well::
106+
107+
spmodel = LinearDynamicLinearCostSPmodel(N_STAGES,u_bounds,X0,cost_t,dynamic,xi_laws)
108+
109+
set_state_bounds(spmodel, s_bounds)
110+
111+
Or we can instantiate a StochDynProgModel that can be solved only by SDP but we
112+
need to define the constraint function and the final cost function::
113+
114+
function constraints(t, x, u, xi) # return true when there is no constraints ecept state and control bounds
115+
return true
116+
end
117+
118+
function final_cost_function(x)
119+
return 0
120+
end
121+
122+
spmodel = StochDynProgModel(N_STAGES, s_bounds, u_bounds, X0, cost_t,
123+
final_cost_function, dynamic, constraints,
124+
xi_laws)
125+
126+
127+
Solver
128+
^^^^^^
129+
130+
It remains to define SDP algorithm parameters::
131+
132+
stateSteps = [1] # discretization steps of the state space
133+
controlSteps = [0.1] # discretization steps of the control space
134+
infoStruct = "HD" # noise at time t is known before taking the decision at time t
135+
paramSDP = SDPparameters(spmodel, stateSteps, controlSteps, infoStruct)
136+
137+
138+
Now, we solve the problem by computing Bellman values::
139+
140+
Vs = solve_DP(spmodel,paramSDP, 1)
141+
142+
:code:`V` is an array storing the value functions
143+
144+
We have an exact lower bound given by :code:`V` with the function::
145+
146+
value_sdp = StochDynamicProgramming.get_bellman_value(spmodel,paramSDP,Vs)
147+
148+
149+
Find optimal controls
150+
=====================
151+
152+
Once Bellman functions are computed, we can control our system over assessments scenarios, without assuming knowledge of the future.
153+
154+
We build 1000 scenarios according to the laws stored in :code:`xi_laws`::
155+
156+
scenarios = StochDynamicProgramming.simulate_scenarios(xi_laws,1000)
157+
158+
We compute 1000 simulations of the system over these scenarios::
159+
160+
costsdp, states, controls =sdp_forward_simulation(spmodel,paramSDP,scenarios,Vs)
161+
162+
:code:`costsdp` returns the costs for each scenario, :code:`states` the simulation of each state variable along time, for each scenario, and
163+
:code:`controls` returns the optimal controls for each scenario
164+

examples/battery_storage_parallel.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ println("library loaded")
7777
end
7878

7979
function constraint(t, x, u, xi)
80-
return( (x[1] <= s_bounds[1][2] )&(x[1] >= s_bounds[1][1]))
80+
return true
8181
end
8282

8383
function finalCostFunction(x)

examples/benchmark.jl

Lines changed: 11 additions & 8 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
@@ -259,7 +262,7 @@ function benchmark_sdp()
259262
end
260263

261264
function constraints(t, x, u, w)
262-
return (VOLUME_MIN<=x[1]<=VOLUME_MAX)&(VOLUME_MIN<=x[2]<=VOLUME_MAX)
265+
return true
263266
end
264267

265268
function finalCostFunction(x)

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: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ end
8484
const FORWARD_PASS = 10.
8585
const EPSILON = .05
8686
# Maximum number of iterations
87-
const MAX_ITER = 50
87+
const MAX_ITER = 10
8888
##################################################
8989

9090
"""Build probability distribution at each timestep.
@@ -107,25 +107,25 @@ 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+
Vfinal=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

128128
# Solve the problem:
129129
model, params = init_problem()
130-
V, pbs = solve_SDDP(model, params, 1)
130+
V, pbs = @time solve_SDDP(model, params, 1)
131131

examples/parallel_sddp.jl

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
2+
import StochDynamicProgramming
3+
4+
5+
"""
6+
Solve SDDP in parallel, dispatching both forward and backward passes to process,
7+
which is not the most standard parallelization of SDDP.
8+
9+
# Arguments
10+
* `model::SPmodel`:
11+
the stochastic problem we want to optimize
12+
* `param::SDDPparameters`:
13+
the parameters of the SDDP algorithm
14+
* `V::Array{PolyhedralFunction}`:
15+
the current estimation of Bellman's functions
16+
* `n_parallel_pass::Int`: default is 4
17+
Number of parallel pass to compute
18+
* `synchronize::Int`: default is 5
19+
Synchronize the cuts between the different processes every "synchronise" iterations
20+
* `display::Int`: default is 0
21+
Says whether to display results or not
22+
23+
# Return
24+
* `V::Array{PolyhedralFunction}`:
25+
the collection of approximation of the bellman functions
26+
"""
27+
function psolve_sddp(model, params, V; n_parallel_pass=4,
28+
synchronize=5, display=0)
29+
# Redefine seeds in every processes to maximize randomness:
30+
@everywhere srand()
31+
32+
mitn = params.maxItNumber
33+
params.maxItNumber = synchronize
34+
35+
# Count number of available CPU:
36+
ncpu = nprocs() - 1
37+
(display > 0) && println("\nLaunch simulation on ", ncpu, " processes")
38+
workers = procs()[2:end]
39+
40+
fpn = params.forwardPassNumber
41+
# As we distribute computation in n process, we perform forward pass in parallel:
42+
params.forwardPassNumber = max(1, round(Int, params.forwardPassNumber/ncpu))
43+
44+
# Start parallel computation:
45+
for i in 1:n_parallel_pass
46+
# Distribute computation of SDDP in each process:
47+
refs = [@spawnat w StochDynamicProgramming.solve_SDDP(model, params, V, display)[1] for w in workers]
48+
# Catch the result in the main process:
49+
V = StochDynamicProgramming.catcutsarray([fetch(r) for r in refs]...)
50+
# We clean the resultant cuts:
51+
StochDynamicProgramming.remove_redundant_cuts!(V)
52+
(display > 0) && println("Lower bound at pass ", i, ": ", StochDynamicProgramming.get_lower_bound(model, params, V))
53+
end
54+
55+
params.forwardPassNumber = fpn
56+
params.maxItNumber = mitn
57+
return V
58+
end

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)))

0 commit comments

Comments
 (0)