@@ -32,14 +32,11 @@ fulfilled.
3232* `problems::Array{JuMP.Model}`:
3333 the collection of linear problems used to approximate
3434 each value function
35- * `count_callsolver::Int64`:
36- number of times the solver has been called
35+ * `sddp_stats::SDDPStat`:
3736
3837"""
3938function 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
39+ check_SDDPparameters (model,param,verbose)
4340 # initialize value functions:
4441 V, problems = initialize_value_functions (model, param)
4542 (verbose > 0 ) && println (" Initial value function loaded into memory." )
@@ -48,164 +45,145 @@ function solve_SDDP(model::SPModel, param::SDDPparameters, verbose=0::Int64)
4845 return V, problems, sddp_stats
4946end
5047
48+ """
49+ Solve SDDP algorithm with hotstart and return estimation of bellman functions.
50+
51+ # Description
52+ Alternate forward and backward phase till the stopping criterion is
53+ fulfilled.
5154
55+ # Arguments
56+ * `model::SPmodel`:
57+ the stochastic problem we want to optimize
58+ * `param::SDDPparameters`:
59+ the parameters of the SDDP algorithm
60+ * `V::Vector{PolyhedralFunction}`:
61+ current lower approximation of Bellman functions
62+ * `verbose::Int64`:
63+ Default is `0`
64+ If non null, display progression in terminal every
65+ `n` iterations, where `n` is the number specified by display.
66+
67+ # Returns
68+ * `V::Array{PolyhedralFunction}`:
69+ the collection of approximation of the bellman functions
70+ * `problems::Array{JuMP.Model}`:
71+ the collection of linear problems used to approximate
72+ each value function
73+ * `sddp_stats::SDDPStat`:
74+ """
5275function 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
76+ check_SDDPparameters (model,param,verbose)
5677 # First step: process value functions if hotstart is called
5778 problems = hotstart_SDDP (model, param, V)
5879 sddp_stats = run_SDDP! (model, param, V, problems, verbose)
5980 return V, problems, sddp_stats
6081end
6182
6283
63- """ Run SDDP iterations."""
84+ """ Run SDDP iterations.
85+
86+ # Arguments
87+ * `model::SPmodel`:
88+ the stochastic problem we want to optimize
89+ * `param::SDDPparameters`:
90+ the parameters of the SDDP algorithm
91+ * `V::Vector{PolyhedralFunction}`:
92+ Polyhedral lower approximation of Bellman functions
93+ * `problems::Vector{JuMP.Model}`:
94+ * `verbose::Int64`:
95+ Default is `0`
96+ If non null, display progression in terminal every
97+ `n` iterations, where `n` is the number specified by display.
98+
99+ # Returns
100+ * `stats:SDDPStats`:
101+ contains statistics of the current algorithm
102+ """
64103function run_SDDP! (model:: SPModel ,
65104 param:: SDDPparameters ,
66105 V:: Vector{PolyhedralFunction} ,
67106 problems:: Vector{JuMP.Model} ,
68107 verbose= 0 :: Int64 )
69108
70109 # Initialization of the counter
71- stats = SDDPStat (0 , [], [], [], 0 )
110+ stats = SDDPStat ()
72111
73112 (verbose > 0 ) && println (" Initialize cuts" )
74113
75114 # If computation of upper-bound is needed, a set of scenarios is built
76115 # to keep always the same realization for upper bound estimation:
77- if param. compute_upper_bound > 0
78- upperbound_scenarios = simulate_scenarios (model. noises, param. monteCarloSize)
79- end
116+ # if param.compute_ub > 0 #TODO
117+ upperbound_scenarios = simulate_scenarios (model. noises, param. in_iter_mc)
80118
81119 upb = Inf
82120 costs = nothing
83121 stopping_test:: Bool = false
84- iteration_count :: Int64 = 0
122+
85123
86124 # Launch execution of forward and backward passes:
87- while (iteration_count < param . maxItNumber) & ( ~ stopping_test)
125+ while (~ stopping_test)
88126 # Time execution of current pass:
89127 tic ()
90128
91- # Draw a set of scenarios according to the probability
92- # law specified in model.noises:
93- noise_scenarios = simulate_scenarios (model. noises, param. forwardPassNumber)
94-
95129 # ###################
96- # Forward pass
97- _, stockTrajectories,_,callsolver_forward = forward_simulations (model,
98- param,
99- problems,
100- noise_scenarios)
130+ # Forward pass : compute stockTrajectories
131+ costs, stockTrajectories, callsolver_forward = forward_pass! (model, param, V, problems)
101132
102133 # ###################
103- # Backward pass
104- callsolver_backward = backward_pass! (model,
105- param,
106- V,
107- problems,
108- stockTrajectories,
109- model. noises)
110-
111- # Update the number of solver call
112- stats. ncallsolver += callsolver_forward + callsolver_backward
113- iteration_count += 1
114- stats. niterations += 1
134+ # Backward pass : update polyhedral approximation of Bellman functions
135+ callsolver_backward = backward_pass! (model, param, V, problems, stockTrajectories, model. noises)
115136
116137 # ###################
117- # If specified, prune cuts:
118- if (param. compute_cuts_pruning > 0 ) && (iteration_count% param. compute_cuts_pruning== 0 )
119- (verbose > 0 ) && println (" Prune cuts ..." )
120- remove_redundant_cuts! (V)
121- prune_cuts! (model, param, V)
122- problems = hotstart_SDDP (model, param, V)
123- end
124-
125- # Update estimation of lower bound:
126- lwb = get_bellman_value (model, param, 1 , V[1 ], model. initialState)
127- push! (stats. lower_bounds, lwb)
138+ # cut pruning
139+ prune_cuts! (model,param,V,stats. niterations,verbose)
128140
129141 # ###################
130- # If specified, compute upper-bound:
131- if (param. compute_upper_bound > 0 ) && (iteration_count% param. compute_upper_bound== 0 )
132- (verbose > 0 ) && println (" Compute upper-bound with " ,
133- param. monteCarloSize, " scenarios..." )
134- # estimate upper-bound with Monte-Carlo estimation:
135- upb, costs = estimate_upper_bound (model, param, upperbound_scenarios, problems)
136- if param. gap > 0.
137- stopping_test = test_stopping_criterion (lwb, upb, param. gap)
138- end
139- end
142+ # In iteration upper bound estimation
143+ upb = in_iteration_upb_estimation (model, param, stats. niterations, verbose,
144+ upperbound_scenarios, upb, problems)
140145
141- push! (stats. exectime, toq ())
142- push! (stats. upper_bounds, upb)
146+ # ###################
147+ # Update stats
148+ lwb = get_bellman_value (model, param, 1 , V[1 ], model. initialState)
149+ updateSDDPStat! (stats, callsolver_forward+ callsolver_backward, lwb, upb, toq ())
143150
144- if (verbose > 0 ) && (iteration_count% verbose== 0 )
145- print (" Pass number " , iteration_count)
146- (upb < Inf ) && print (" \t Upper-bound: " , upb)
147- println (" \t Lower-bound: " , round (stats. lower_bounds[end ], 4 ),
148- " \t Time: " , round (stats. exectime[end ], 2 )," s" )
149- end
151+ print_current_stats (stats,verbose)
150152
153+ # ###################
154+ # Stopping test
155+ stopping_test = test_stopping_criterion (param,stats)
156+ stats. niterations += 1
151157 end
152158
153159 # #########
154160 # Estimate final upper bound with param.monteCarloSize simulations:
155- if (verbose> 0 ) && (param. compute_upper_bound >= 0 )
156- V0 = get_bellman_value (model, param, 1 , V[1 ], model. initialState)
161+ display_final_solution (model, param,V,problems,stats,verbose)
162+ return stats
163+ end
164+
157165
158- if param. compute_upper_bound == 0
166+ """ Display final results once SDDP iterations are finished."""
167+ function display_final_solution (model:: SPModel , param:: SDDPparameters , V, problems, stats:: SDDPStat , verbose:: Int64 )
168+ if (verbose> 0 ) && (param. compute_ub >= 0 )
169+ lwb = get_bellman_value (model, param, 1 , V[1 ], model. initialState)
170+
171+ if param. compute_ub == 0
159172 println (" Estimate upper-bound with Monte-Carlo ..." )
160- upb, costs = estimate_upper_bound (model, param, V, problems)
173+ upb, costs = estimate_upper_bound (model, param, V, problems, param. monteCarloSize)
174+ else
175+ upb = stats. upperbounds[end ]
161176 end
162177
163178 println (" Estimation of upper-bound: " , round (upb,4 ),
164- " \t Exact lower bound: " , round (V0 ,4 ),
165- " \t Gap < " , round (100 * (upb- V0) / V0 , 2 ) , " \% with prob. > 97.5 \% " )
179+ " \t Exact lower bound: " , round (lwb ,4 ),
180+ " \t Gap < " , round (100 * (upb- lwb) / lwb , 2 ) , " \% with prob. > 97.5 \% " )
166181 println (" Estimation of cost of the solution (fiability 95\% ):" ,
167182 round (mean (costs),4 ), " +/- " , round (1.96 * std (costs)/ sqrt (length (costs)),4 ))
168183 end
169-
170- return stats
171184end
172185
173186
174- """
175- Estimate upper bound with Monte Carlo.
176-
177- # Arguments
178- * `model::SPmodel`:
179- the stochastic problem we want to optimize
180- * `param::SDDPparameters`:
181- the parameters of the SDDP algorithm
182- * `V::Array{PolyhedralFunction}`:
183- the current estimation of Bellman's functions
184- * `problems::Array{JuMP.Model}`:
185- Linear model used to approximate each value function
186- * `n_simulation::Float64`:
187- Number of scenarios to use to compute Monte-Carlo estimation
188-
189- # Return
190- * `upb::Float64`:
191- estimation of upper bound
192- * `costs::Vector{Float64}`:
193- Costs along different trajectories
194- """
195- function estimate_upper_bound (model:: SPModel , param:: SDDPparameters ,
196- V:: Vector{PolyhedralFunction} ,
197- problem:: Vector{JuMP.Model} ,
198- n_simulation= 1000 :: Int )
199- aleas = simulate_scenarios (model. noises, n_simulation)
200- return estimate_upper_bound (model, param, aleas, problem)
201- end
202- function estimate_upper_bound (model:: SPModel , param:: SDDPparameters ,
203- aleas:: Array{Float64, 3} ,
204- problem:: Vector{JuMP.Model} )
205- costs = forward_simulations (model, param, problem, aleas)[1 ]
206- return upper_bound (costs), costs
207- end
208-
209187
210188"""
211189Build a cut approximating terminal cost with null function
@@ -485,81 +463,3 @@ function add_cuts_to_model!(model::SPModel, t::Int64, problem::JuMP.Model, V::Po
485463 @constraint (problem, V. betas[i] + dot (lambda, xf) <= alpha)
486464 end
487465end
488-
489-
490- """
491- Exact pruning of all polyhedral functions in input array.
492-
493- # Arguments
494- * `model::SPModel`:
495- * `params::SDDPparameters`:
496- * `Vector{PolyhedralFunction}`:
497- Polyhedral functions where cuts will be removed
498- """
499- function prune_cuts! (model:: SPModel , params:: SDDPparameters , V:: Vector{PolyhedralFunction} )
500- for i in 1 : length (V)- 1
501- V[i] = exact_prune_cuts (model, params, V[i])
502- end
503- end
504-
505-
506- """
507- Remove useless cuts in PolyhedralFunction.
508-
509- # Arguments
510- * `model::SPModel`:
511- * `params::SDDPparameters`:
512- * `V::PolyhedralFunction`:
513- Polyhedral function where cuts will be removed
514-
515- # Return
516- * `PolyhedralFunction`: pruned polyhedral function
517- """
518- function exact_prune_cuts (model:: SPModel , params:: SDDPparameters , V:: PolyhedralFunction )
519- ncuts = V. numCuts
520- # Find all active cuts:
521- if ncuts > 1
522- active_cuts = Bool[is_cut_relevant (model, i, V, params. SOLVER) for i= 1 : ncuts]
523- return PolyhedralFunction (V. betas[active_cuts], V. lambdas[active_cuts, :], sum (active_cuts))
524- else
525- return V
526- end
527- end
528-
529-
530- """
531- Test whether the cut number k is relevant to define polyhedral function Vt.
532-
533- # Arguments
534- * `model::SPModel`:
535- * `k::Int`:
536- Position of cut to test in PolyhedralFunction object
537- * `Vt::PolyhedralFunction`:
538- Object storing all cuts
539- * `solver`:
540- Solver to use to solve linear problem
541- * `epsilon::Float64`: default is `1e-5`
542- Acceptable tolerance to test cuts relevantness
543-
544- # Return
545- * `Bool`: true if the cut is useful in the definition, false otherwise
546- """
547- function is_cut_relevant (model:: SPModel , k:: Int , Vt:: PolyhedralFunction , solver; epsilon= 1e-5 )
548- m = Model (solver= solver)
549- @variable (m, alpha)
550- @variable (m, model. xlim[i][1 ] <= x[i= 1 : model. dimStates] <= model. xlim[i][2 ])
551-
552- for i in 1 : Vt. numCuts
553- if i!= k
554- lambda = vec (Vt. lambdas[i, :])
555- @constraint (m, Vt. betas[i] + dot (lambda, x) <= alpha)
556- end
557- end
558-
559- λ_k = vec (Vt. lambdas[k, :])
560- @objective (m, Min, alpha - dot (λ_k, x) - Vt. betas[k])
561- solve (m)
562- sol = getobjectivevalue (m)
563- return sol < epsilon
564- end
565-
0 commit comments