Skip to content

Commit 0dd0403

Browse files
committed
Merge pull request #62 from leclere/dev-cuts-pruning
Add cuts pruning to SDDP
2 parents 7665569 + dc19424 commit 0dd0403

File tree

3 files changed

+111
-4
lines changed

3 files changed

+111
-4
lines changed

src/SDDPoptimize.jl

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,81 @@ function add_cuts_to_model!(model::SPModel, t::Int64, problem::JuMP.Model, V::Po
504504
end
505505
end
506506

507+
508+
"""
509+
Prune all polyhedral functions in input array.
510+
511+
Parameters:
512+
- model (SPModel)
513+
- params (SDDPparameters)
514+
- V Vector{PolyhedralFunction}
515+
Polyhedral functions where cuts will be removed
516+
517+
"""
518+
function prune_cuts!(model::SPModel, params::SDDPparameters, V::Vector{PolyhedralFunction})
519+
for i in 1:length(V)
520+
V[i] = prune_cuts(model, params, V[i])
521+
end
522+
end
523+
524+
525+
"""
526+
Remove useless cuts in PolyhedralFunction.
527+
528+
Parameters:
529+
- model (SPModel)
530+
- params (SDDPparameters)
531+
- V (PolyhedralFunction)
532+
Polyhedral function where cuts will be removed
533+
534+
Return:
535+
- PolyhedralFunction: pruned polyhedral function
536+
537+
"""
538+
function exact_prune_cuts(model::SPModel, params::SDDPparameters, V::PolyhedralFunction)
539+
ncuts = V.numCuts
540+
# Find all active cuts:
541+
if ncuts > 1
542+
active_cuts = Bool[is_cut_active(model, i, V, params.solver) for i=1:ncuts]
543+
return PolyhedralFunction(V.betas[active_cuts], V.lambdas[active_cuts, :], sum(active_cuts))
544+
else
545+
return V
546+
end
547+
end
548+
549+
550+
"""
551+
Test whether the cut number k is active in polyhedral function Vt.
552+
553+
Parameters:
554+
- model (SPModel)
555+
- k (Int)
556+
Position of cut to test in PolyhedralFunction object
557+
- Vt (PolyhedralFunction)
558+
Object storing all cuts
559+
- solver
560+
Solver to use to solve linear problem
561+
562+
Return:
563+
- Bool: true if the cut is active, false otherwise
564+
565+
"""
566+
function is_cut_relevant(model::SPModel, k::Int, Vt::PolyhedralFunction, solver)
567+
568+
m = Model(solver=solver)
569+
@defVar(m, alpha)
570+
@defVar(m, model.xlim[i][1] <= x[i=1:model.dimStates] <= model.xlim[i][2])
571+
572+
for i in 1:Vt.numCuts
573+
if i!=k
574+
lambda = vec(Vt.lambdas[i, :])
575+
@addConstraint(m, Vt.betas[i] + dot(lambda, x) <= alpha)
576+
end
577+
end
578+
579+
λ_k = vec(Vt.lambdas[k, :])
580+
@setObjective(m, Min, alpha - dot(λ_k, x) - Vt.betas[k])
581+
solve(m)
582+
return getObjectiveValue(m) < 0.
583+
end
584+

src/utils.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,29 @@ function read_polyhedral_functions(dump::AbstractString)
7373
end
7474

7575

76+
"""
77+
Remove redundant cuts in Polyhedral Value functions
78+
79+
"""
80+
function remove_cuts(V::PolyhedralFunction)
81+
Vf = hcat(V.lambdas, V.betas)
82+
Vf = unique(Vf, 1)
83+
return PolyhedralFunction(Vf[:, end], Vf[:, 1:end-1], size(Vf)[1])
84+
end
85+
86+
87+
"""
88+
Remove redundant cuts in a vector of Polyhedral Functions.
89+
90+
"""
91+
function remove_redundant_cuts!(Vts::Vector{PolyhedralFunction})
92+
n_functions = length(Vts)
93+
for i in 1:n_functions
94+
Vts[i] = remove_cuts(Vts[i])
95+
end
96+
end
97+
98+
7699
"""
77100
Extract a vector stored in a 3D Array
78101

test/runtests.jl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,12 @@ facts("SDDP algorithm: 1D case") do
105105
u_bounds, x0,
106106
cost,
107107
dynamic, laws)
108+
set_state_bounds(model, x_bounds)
108109
# Generate scenarios for forward simulations:
109110
noise_scenarios = simulate_scenarios(model.noises,params.forwardPassNumber)
110111

111112
sddp_costs = 0
112113
context("Linear cost") do
113-
# Instantiate a SDDP linear model:
114-
set_state_bounds(model, x_bounds)
115-
116-
117114
# Compute bellman functions with SDDP:
118115
V, pbs = solve_SDDP(model, params, 0)
119116
@fact typeof(V) --> Vector{StochDynamicProgramming.PolyhedralFunction}
@@ -152,6 +149,15 @@ facts("SDDP algorithm: 1D case") do
152149
@fact mean(sddp_costs) --> roughly(mean(sddp_costs2))
153150
end
154151

152+
context("Cuts pruning") do
153+
v = V[1]
154+
vt = PolyhedralFunction([v.betas[1]; v.betas[1] - 1.], v.lambdas[[1,1],:], 2)
155+
StochDynamicProgramming.prune_cuts!(model, params, V)
156+
isactive1 = StochDynamicProgramming.is_cut_active(model, 1, vt, params.solver)
157+
isactive2 = StochDynamicProgramming.is_cut_active(model, 2, vt, params.solver)
158+
@fact isactive1 --> true
159+
@fact isactive2 --> false
160+
end
155161

156162
context("Piecewise linear cost") do
157163
# Test Piecewise linear costs:

0 commit comments

Comments
 (0)