Skip to content

Commit

Permalink
Add root_node_risk_measure kwarg to SDDP.train (#804)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Nov 14, 2024
1 parent f3447e9 commit cc604a4
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 4 deletions.
16 changes: 15 additions & 1 deletion src/algorithm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ struct Options{T}
last_log_iteration::Ref{Int}
# For threading
lock::ReentrantLock
root_node_risk_measure::AbstractRiskMeasure
# Internal function: users should never construct this themselves.
function Options(
model::PolicyGraph{T},
Expand All @@ -136,6 +137,7 @@ struct Options{T}
duality_handler::AbstractDualityHandler = ContinuousConicDuality(),
forward_pass_callback = x -> nothing,
post_iteration_callback = result -> nothing,
root_node_risk_measure::AbstractRiskMeasure = Expectation(),
) where {T}
return new{T}(
initial_state,
Expand All @@ -160,6 +162,7 @@ struct Options{T}
post_iteration_callback,
Ref{Int}(0), # last_log_iteration
ReentrantLock(),
root_node_risk_measure,
)
end
end
Expand Down Expand Up @@ -946,7 +949,10 @@ function iteration(model::PolicyGraph{T}, options::Options) where {T}
)
end
@_timeit_threadsafe model.timer_output "calculate_bound" begin
bound = calculate_bound(model)
bound = calculate_bound(
model;
risk_measure = options.root_node_risk_measure,
)
end
lock(options.lock)
try
Expand Down Expand Up @@ -1036,6 +1042,12 @@ Train the policy for `model`.
- `risk_measure`: the risk measure to use at each node. Defaults to
[`Expectation`](@ref).
- `root_node_risk_measure::AbstractRiskMeasure`: the risk measure to use at
the root node when computing the `Bound` column. Note that the choice of
this option does not change the primal policy, and it applies only if the
transition from the root node to the first stage is stochastic. Defaults to
[`Expectation`](@ref).
- `sampling_scheme`: a sampling scheme to use on the forward pass of the
algorithm. Defaults to [`InSampleMonteCarlo`](@ref).
Expand Down Expand Up @@ -1086,6 +1098,7 @@ function train(
run_numerical_stability_report::Bool = true,
stopping_rules = AbstractStoppingRule[],
risk_measure = SDDP.Expectation(),
root_node_risk_measure::AbstractRiskMeasure = Expectation(),
sampling_scheme = SDDP.InSampleMonteCarlo(),
cut_type = SDDP.SINGLE_CUT,
cycle_discretization_delta::Float64 = 0.0,
Expand Down Expand Up @@ -1248,6 +1261,7 @@ function train(
duality_handler,
forward_pass_callback,
post_iteration_callback,
root_node_risk_measure,
)
status = :not_solved
try
Expand Down
5 changes: 4 additions & 1 deletion src/plugins/parallel_schemes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,10 @@ function master_loop(
end
end
slave_update(model, result)
bound = calculate_bound(model)
bound = calculate_bound(
model;
risk_measure = options.root_node_risk_measure,
)
push!(
options.log,
Log(
Expand Down
31 changes: 29 additions & 2 deletions test/algorithm.jl
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ Unable to retrieve solution from node 1.
Termination status : INFEASIBLE
Primal status : NO_SOLUTION
Dual status : NO_SOLUTION.
Dual status : INFEASIBILITY_CERTIFICATE.
The current subproblem was written to `subproblem_1.mof.json`.
Expand Down Expand Up @@ -205,7 +205,7 @@ Unable to retrieve solution from node 1.
Termination status : INFEASIBLE
Primal status : NO_SOLUTION
Dual status : NO_SOLUTION.
Dual status : INFEASIBILITY_CERTIFICATE.
The current subproblem was written to `subproblem_1.mof.json`.
Expand Down Expand Up @@ -387,6 +387,33 @@ function test_numerical_difficulty_callback()
return
end

function test_root_node_risk_measure()
model = SDDP.LinearPolicyGraph(;
stages = 3,
lower_bound = 0.0,
optimizer = HiGHS.Optimizer,
) do sp, stage
@variable(sp, 0 <= x <= 100, SDDP.State, initial_value = 0)
@variable(sp, 0 <= u_p <= 200)
@variable(sp, u_o >= 0)
@variable(sp, w)
SDDP.parameterize-> JuMP.fix(w, ω), sp, [100, 300])
@constraint(sp, x.out == x.in + u_p + u_o - w)
@stageobjective(sp, 100 * u_p + 300 * u_o + 50 * x.out)
end
SDDP.train(model; root_node_risk_measure = SDDP.WorstCase())
@test isapprox(model.most_recent_training_results.log[end].bound, 107500.0)
@test isapprox(
SDDP.calculate_bound(model; risk_measure = SDDP.WorstCase()),
107500.0,
)
@test isapprox(
SDDP.calculate_bound(model; risk_measure = SDDP.Expectation()),
85000.0,
)
return
end

end # module

TestAlgorithm.runtests()

0 comments on commit cc604a4

Please sign in to comment.