1+ # ############################################################################
12# Copyright 2015, Vincent Leclere, Francois Pacaud and Henri Gerard
23# This Source Code Form is subject to the terms of the Mozilla Public
34# License, v. 2.0. If a copy of the MPL was not distributed with this
45# file, You can obtain one at http://mozilla.org/MPL/2.0/.
56# ############################################################################
6- # Define the Forward / Backward iterations of the SDDP algorithm
7- # ############################################################################
7+
8+ type Territories
9+ ncuts:: Int
10+ territories:: Array{Array}
11+ nstates:: Int
12+ states:: Array{Float64, 2}
13+ end
14+
15+
16+ """ Remove redundant cuts in Polyhedral Value function `V`"""
17+ function remove_cuts (V:: PolyhedralFunction )
18+ Vf = hcat (V. lambdas, V. betas)
19+ Vf = unique (Vf, 1 )
20+ return PolyhedralFunction (Vf[:, end ], Vf[:, 1 : end - 1 ], size (Vf)[1 ])
21+ end
22+
23+
24+ """ Remove redundant cuts in a vector of Polyhedral Functions `Vts`."""
25+ function remove_redundant_cuts! (Vts:: Vector{PolyhedralFunction} )
26+ n_functions = length (Vts)- 1
27+ for i in 1 : n_functions
28+ Vts[i] = remove_cuts (Vts[i])
29+ end
30+ end
831
932
1033"""
@@ -15,39 +38,53 @@ Exact pruning of all polyhedral functions in input array.
1538* `params::SDDPparameters`:
1639* `Vector{PolyhedralFunction}`:
1740 Polyhedral functions where cuts will be removed
18- * `iteration_count::Int64`:
41+ * `trajectories::Array{Float64, 3}`
42+ Previous trajectories
43+ * `territory::Array{Territories}`
44+ Container storing the territory for each cuts
45+ * `it::Int64`:
1946 current iteration number
2047* `verbose::Int64`
2148"""
2249function prune_cuts! (model:: SPModel ,
2350 param:: SDDPparameters ,
2451 V:: Vector{PolyhedralFunction} ,
25- iteration_count:: Int64 ,
52+ trajectories:: Array{Float64, 3} ,
53+ territory:: Union{Void, Array{Territories}} ,
54+ it:: Int64 ,
2655 verbose:: Int64 )
27- # If specified, prune cuts:
28- if (param. compute_cuts_pruning > 0 ) && (iteration_count% param. compute_cuts_pruning== 0 )
29- (verbose > 0 ) && println (" Prune cuts ..." )
30- remove_redundant_cuts! (V)
31- for i in 1 : length (V)- 1
32- V[i] = exact_prune_cuts (model, param, V[i])
56+ # Basic pruning: remove redundant cuts
57+ remove_redundant_cuts! (V)
58+
59+ # If pruning is performed with territory heuristic, update territory
60+ # at given iteration:
61+ if param. pruning[:type ] == " territory"
62+ for t in 1 : model. stageNumber- 1
63+ states = reshape (trajectories[t, :, :], param. forwardPassNumber, model. dimStates)
64+ find_territory! (territory[t], V[t], states)
3365 end
34- problems = hotstart_SDDP (model, param, V)
3566 end
36- end
3767
38- """ Remove redundant cuts in Polyhedral Value function `V`"""
39- function remove_cuts (V:: PolyhedralFunction )
40- Vf = hcat (V. lambdas, V. betas)
41- Vf = unique (Vf, 1 )
42- return PolyhedralFunction (Vf[:, end ], Vf[:, 1 : end - 1 ], size (Vf)[1 ])
43- end
68+ # If specified to prune cuts at this iteration, do it:
69+ if param. pruning[:pruning ] && (it% param. pruning[:period ]== 0 )
70+ # initial number of cuts:
71+ ncuts_initial = get_total_number_cuts (V)
72+ (verbose > 0 ) && print (" Prune cuts ..." )
4473
74+ for i in 1 : length (V)- 1
75+ if param. pruning[:type ] == " exact"
76+ # apply exact cuts pruning:
77+ V[i] = exact_prune_cuts (model, param, V[i])
78+ elseif param. pruning[:type ] == " territory"
79+ # apply heuristic to prune cuts:
80+ V[i] = remove_empty_cuts! (territory[i], V[i])
81+ end
82+ end
4583
46- """ Remove redundant cuts in a vector of Polyhedral Functions `Vts`."""
47- function remove_redundant_cuts! (Vts:: Vector{PolyhedralFunction} )
48- n_functions = length (Vts)- 1
49- for i in 1 : n_functions
50- Vts[i] = remove_cuts (Vts[i])
84+ # final number of cuts:
85+ ncuts_final = get_total_number_cuts (V)
86+
87+ (verbose > 0 ) && println (" Deflation: " , ncuts_final/ ncuts_initial)
5188 end
5289end
5390
@@ -111,3 +148,140 @@ function is_cut_relevant(model::SPModel, k::Int, Vt::PolyhedralFunction, solver;
111148 sol = getobjectivevalue (m)
112149 return sol < epsilon
113150end
151+
152+
153+ # #######################################
154+ # Territory algorithm
155+ # #######################################
156+
157+ Territories (ndim) = Territories (0 , [], 0 , Array {Float64} (0 , ndim))
158+
159+
160+ """ Update territories with cuts previously computed during backward pass. """
161+ function find_territory! (territory, V, states)
162+ nc = V. numCuts
163+ # get number of new positions to analyse:
164+ nx = size (states, 1 )
165+ nt = nc - territory. ncuts
166+
167+ for i in 1 : nt
168+ add_cut! (territory)
169+ update_territory! (territory, V, nc - nt + i)
170+ end
171+
172+ # ensure that territory has the same number of cuts as V!
173+ assert (territory. ncuts == V. numCuts)
174+
175+ for i in 1 : nx
176+ x = collect (states[i, :])
177+ add_state! (territory, V, x)
178+ end
179+
180+ end
181+
182+
183+ """ Update territories considering new cut with index `indcut`."""
184+ function update_territory! (territory, V, indcut)
185+ for k in 1 : territory. ncuts
186+ if k == indcut
187+ continue
188+ end
189+ todelete = []
190+ for (num, (ix, cost)) in enumerate (territory. territories[k])
191+ x = collect (territory. states[ix, :])
192+
193+ costnewcut = cutvalue (V, indcut, x)
194+
195+ if costnewcut > cost
196+ push! (todelete, num)
197+ push! (territory. territories[indcut], (ix, costnewcut))
198+ end
199+ end
200+ deleteat! (territory. territories[k], todelete)
201+ end
202+ end
203+
204+
205+ """ Add cut to `territory`."""
206+ function add_cut! (territory)
207+ push! (territory. territories, [])
208+ territory. ncuts += 1
209+ end
210+
211+
212+ """ Add a new state and update territories."""
213+ function add_state! (territory:: Territories , V:: PolyhedralFunction , x:: Array{Float64} )
214+ # Get cut which is the supremum at point `x`:
215+ bcost, bcuts = optimalcut (x, V)
216+
217+ # Add `x` to the territory of cut `bcuts`:
218+ territory. nstates += 1
219+ push! (territory. territories[bcuts], (territory. nstates, bcost))
220+
221+ # Add `x` to the list of visited state:
222+ territory. states = vcat (territory. states, x' )
223+ end
224+
225+
226+ """ Remove empty cuts in PolyhedralFunction"""
227+ function remove_empty_cuts! (territory:: Territories , V:: PolyhedralFunction )
228+ assert (territory. ncuts == V. numCuts)
229+
230+ nstates = [length (terr) for terr in territory. territories]
231+ active_cuts = nstates .> 0
232+
233+ territory. territories = territory. territories[active_cuts]
234+ territory. ncuts = sum (active_cuts)
235+ return PolyhedralFunction (V. betas[active_cuts],
236+ V. lambdas[active_cuts, :],
237+ sum (active_cuts))
238+ end
239+
240+
241+ """ Get cut which approximate the best value function at point `x`."""
242+ function optimalcut (xf:: Vector{Float64} , V:: PolyhedralFunction )
243+ bestcost = - Inf :: Float64
244+ bestcut = - 1
245+ nstates = size (V. lambdas, 2 )
246+ ncuts = size (V. lambdas, 1 )
247+
248+ @inbounds for i in 1 : ncuts
249+ cost = V. betas[i]
250+ for j in 1 : nstates
251+ cost += V. lambdas[i, j]* xf[j]
252+ end
253+ if cost > bestcost
254+ bestcost = cost
255+ bestcut = i
256+ end
257+ end
258+ return bestcost, bestcut
259+ end
260+
261+
262+ """
263+ Get approximation of value function at given point `x`.
264+
265+ # Arguments
266+ - `V::PolyhedralFunction`
267+ Approximation of the value function as linear cuts
268+ - `indc::Int64`
269+ Index of cut to consider
270+ - `x::Array{Float64}`
271+ Coordinates of state
272+
273+ # Return
274+ `cost::Float64`
275+ Value of cut `indc` at point `x`
276+ """
277+ function cutvalue (V:: PolyhedralFunction , indc:: Int , x:: Array{Float64} )
278+ cost = V. betas[indc]
279+ for j in 1 : size (V. lambdas, 2 )
280+ cost += V. lambdas[indc, j]* x[j]
281+ end
282+ return cost
283+ end
284+
285+ # Return total number of cuts in PolyhedralFunction array:
286+ get_total_number_cuts (V:: Array{PolyhedralFunction} ) = sum ([v. numCuts for v in V])
287+
0 commit comments