SDDP.add_node
— Functionadd_node(graph::Graph{T}, node::T) where {T}
Add a node to the graph graph
.
Examples
julia> graph = SDDP.Graph(:root);
+ {}
diff --git a/previews/PR811/.documenter-siteinfo.json b/previews/PR811/.documenter-siteinfo.json
index 4194f5c5f..4f5a36329 100644
--- a/previews/PR811/.documenter-siteinfo.json
+++ b/previews/PR811/.documenter-siteinfo.json
@@ -1 +1 @@
-{"documenter":{"julia_version":"1.11.2","generation_timestamp":"2024-12-03T02:40:32","documenter_version":"1.8.0"}}
\ No newline at end of file
+{"documenter":{"julia_version":"1.11.2","generation_timestamp":"2024-12-03T02:54:00","documenter_version":"1.8.0"}}
\ No newline at end of file
diff --git a/previews/PR811/apireference/index.html b/previews/PR811/apireference/index.html
index 7b11da475..888978ccf 100644
--- a/previews/PR811/apireference/index.html
+++ b/previews/PR811/apireference/index.html
@@ -25,7 +25,7 @@
Nodes
{}
Arcs
- {}source Add a node to the graph Examples Add a node to the graph Examples Add an edge to the graph Examples Add an edge to the graph Examples Add Examples Add Examples Create a linear graph with Examples Create a linear graph with Examples Construct a Markovian graph from the vector of transition matrices. The dimension of the first transition matrix should be Examples Construct a Markovian graph from the vector of transition matrices. The dimension of the first transition matrix should be Examples Construct a Markovian graph by fitting Markov chain to scenarios generated by Construct a graph composed of Examples Construct a Markovian graph by fitting Markov chain to scenarios generated by Construct a graph composed of Examples Create a linear policy graph with Keyword arguments Examples Create a linear policy graph with Keyword arguments ExamplesSDDP.add_node
— Functionadd_node(graph::Graph{T}, node::T) where {T}
graph
.julia> graph = SDDP.Graph(:root);
+ {}
SDDP.add_node
— Functionadd_node(graph::Graph{T}, node::T) where {T}
graph
.julia> graph = SDDP.Graph(:root);
julia> SDDP.add_node(graph, :A)
@@ -45,7 +45,7 @@
Nodes
2
Arcs
- {}
SDDP.add_edge
— Functionadd_edge(graph::Graph{T}, edge::Pair{T, T}, probability::Float64) where {T}
graph
.julia> graph = SDDP.Graph(0);
+ {}
SDDP.add_edge
— Functionadd_edge(graph::Graph{T}, edge::Pair{T, T}, probability::Float64) where {T}
graph
.julia> graph = SDDP.Graph(0);
julia> SDDP.add_node(graph, 1)
@@ -69,7 +69,7 @@
Nodes
A
Arcs
- root => A w.p. 1.0
SDDP.add_ambiguity_set
— Functionadd_ambiguity_set(
+ root => A w.p. 1.0
SDDP.add_ambiguity_set
— Functionadd_ambiguity_set(
graph::Graph{T},
set::Vector{T},
lipschitz::Vector{Float64},
@@ -102,7 +102,7 @@
2 => 3 w.p. 1.0
Partitions
{1, 2}
- {3}
add_ambiguity_set(graph::Graph{T}, set::Vector{T}, lipschitz::Float64)
set
to the belief partition of graph
.lipschitz
is a Lipschitz constant for each node in set
. The Lipschitz constant is the maximum slope of the cost-to-go function with respect to the belief state associated with each node at any point in the state-space.julia> graph = SDDP.LinearGraph(3);
+ {3}
add_ambiguity_set(graph::Graph{T}, set::Vector{T}, lipschitz::Float64)
set
to the belief partition of graph
.lipschitz
is a Lipschitz constant for each node in set
. The Lipschitz constant is the maximum slope of the cost-to-go function with respect to the belief state associated with each node at any point in the state-space.julia> graph = SDDP.LinearGraph(3);
julia> SDDP.add_ambiguity_set(graph, [1, 2], 1e3)
@@ -121,7 +121,7 @@
2 => 3 w.p. 1.0
Partitions
{1, 2}
- {3}
SDDP.LinearGraph
— FunctionLinearGraph(stages::Int)
stages
number of nodes.julia> graph = SDDP.LinearGraph(3)
+ {3}
SDDP.LinearGraph
— FunctionLinearGraph(stages::Int)
stages
number of nodes.julia> graph = SDDP.LinearGraph(3)
Root
0
Nodes
@@ -131,7 +131,7 @@
Arcs
0 => 1 w.p. 1.0
1 => 2 w.p. 1.0
- 2 => 3 w.p. 1.0
SDDP.MarkovianGraph
— FunctionMarkovianGraph(transition_matrices::Vector{Matrix{Float64}})
transition_matrices[t][i, j]
gives the probability of transitioning from Markov state i
in stage t - 1
to Markov state j
in stage t
.(1, N)
, and transition_matrics[1][1, i]
is the probability of transitioning from the root node to the Markov state i
.julia> graph = SDDP.MarkovianGraph([ones(1, 1), [0.5 0.5], [0.8 0.2; 0.2 0.8]])
+ 2 => 3 w.p. 1.0
SDDP.MarkovianGraph
— FunctionMarkovianGraph(transition_matrices::Vector{Matrix{Float64}})
transition_matrices[t][i, j]
gives the probability of transitioning from Markov state i
in stage t - 1
to Markov state j
in stage t
.(1, N)
, and transition_matrics[1][1, i]
is the probability of transitioning from the root node to the Markov state i
.julia> graph = SDDP.MarkovianGraph([ones(1, 1), [0.5 0.5], [0.8 0.2; 0.2 0.8]])
Root
(0, 1)
Nodes
@@ -147,7 +147,7 @@
(2, 1) => (3, 1) w.p. 0.8
(2, 1) => (3, 2) w.p. 0.2
(2, 2) => (3, 1) w.p. 0.2
- (2, 2) => (3, 2) w.p. 0.8
MarkovianGraph(;
+ (2, 2) => (3, 2) w.p. 0.8
MarkovianGraph(;
stages::Int,
transition_matrix::Matrix{Float64},
root_node_transition::Vector{Float64},
@@ -175,11 +175,11 @@
(2, 1) => (3, 1) w.p. 0.8
(2, 1) => (3, 2) w.p. 0.2
(2, 2) => (3, 1) w.p. 0.2
- (2, 2) => (3, 2) w.p. 0.8
MarkovianGraph(
+ (2, 2) => (3, 2) w.p. 0.8
MarkovianGraph(
simulator::Function;
budget::Union{Int,Vector{Int}},
scenarios::Int = 1000,
-)
simulator()
.budget
is the total number of nodes in the resulting Markov chain. This can either be specified as a single Int
, in which case we will attempt to intelligently distributed the nodes between stages. Alternatively, budget
can be a Vector{Int}
, which details the number of Markov state to have in each stage.SDDP.UnicyclicGraph
— FunctionUnicyclicGraph(discount_factor::Float64; num_nodes::Int = 1)
num_nodes
nodes that form a single cycle, with a probability of discount_factor
of continuing the cycle.julia> graph = SDDP.UnicyclicGraph(0.9; num_nodes = 2)
+)
simulator()
.budget
is the total number of nodes in the resulting Markov chain. This can either be specified as a single Int
, in which case we will attempt to intelligently distributed the nodes between stages. Alternatively, budget
can be a Vector{Int}
, which details the number of Markov state to have in each stage.SDDP.UnicyclicGraph
— FunctionUnicyclicGraph(discount_factor::Float64; num_nodes::Int = 1)
num_nodes
nodes that form a single cycle, with a probability of discount_factor
of continuing the cycle.julia> graph = SDDP.UnicyclicGraph(0.9; num_nodes = 2)
Root
0
Nodes
@@ -188,7 +188,7 @@
Arcs
0 => 1 w.p. 1.0
1 => 2 w.p. 1.0
- 2 => 1 w.p. 0.9
SDDP.LinearPolicyGraph
— FunctionLinearPolicyGraph(builder::Function; stages::Int, kwargs...)
stages
number of stages.stages
: the number of stages in the graphkwargs
: other keyword arguments are passed to SDDP.PolicyGraph
.julia> SDDP.LinearPolicyGraph(; stages = 2, lower_bound = 0.0) do sp, t
+ 2 => 1 w.p. 0.9
SDDP.LinearPolicyGraph
— FunctionLinearPolicyGraph(builder::Function; stages::Int, kwargs...)
stages
number of stages.stages
: the number of stages in the graphkwargs
: other keyword arguments are passed to SDDP.PolicyGraph
.julia> SDDP.LinearPolicyGraph(; stages = 2, lower_bound = 0.0) do sp, t
# ... build model ...
end
A policy graph with 2 nodes.
@@ -198,7 +198,7 @@
# ... build model ...
end
A policy graph with 2 nodes.
-Node indices: 1, 2
SDDP.MarkovianPolicyGraph
— FunctionMarkovianPolicyGraph(
+Node indices: 1, 2
SDDP.MarkovianPolicyGraph
— FunctionMarkovianPolicyGraph(
builder::Function;
transition_matrices::Vector{Array{Float64,2}},
kwargs...
@@ -215,7 +215,7 @@
# ... build model ...
end
A policy graph with 5 nodes.
- Node indices: (1, 1), (2, 1), (2, 2), (3, 1), (3, 2)
SDDP.PolicyGraph
— TypePolicyGraph(
+ Node indices: (1, 1), (2, 1), (2, 2), (3, 1), (3, 2)
SDDP.PolicyGraph
— TypePolicyGraph(
builder::Function,
graph::Graph{T};
sense::Symbol = :Min,
@@ -237,28 +237,28 @@
optimizer = HiGHS.Optimizer,
) do subproblem, index
# ... subproblem definitions ...
-end
SDDP.@stageobjective
— Macro@stageobjective(subproblem, expr)
Set the stage-objective of subproblem
to expr
.
Examples
@stageobjective(subproblem, 2x + y)
SDDP.parameterize
— Functionparameterize(
+end
SDDP.@stageobjective
— Macro@stageobjective(subproblem, expr)
Set the stage-objective of subproblem
to expr
.
Examples
@stageobjective(subproblem, 2x + y)
SDDP.parameterize
— Functionparameterize(
modify::Function,
subproblem::JuMP.Model,
realizations::Vector{T},
probability::Vector{Float64} = fill(1.0 / length(realizations))
) where {T}
Add a parameterization function modify
to subproblem
. The modify
function takes one argument and modifies subproblem
based on the realization of the noise sampled from realizations
with corresponding probabilities probability
.
In order to conduct an out-of-sample simulation, modify
should accept arguments that are not in realizations (but still of type T).
Examples
SDDP.parameterize(subproblem, [1, 2, 3], [0.4, 0.3, 0.3]) do ω
JuMP.set_upper_bound(x, ω)
-end
parameterize(node::Node, noise)
Parameterize node node
with the noise noise
.
SDDP.add_objective_state
— Functionadd_objective_state(update::Function, subproblem::JuMP.Model; kwargs...)
Add an objective state variable to subproblem
.
Required kwargs
are:
initial_value
: The initial value of the objective state variable at the root node.lipschitz
: The lipschitz constant of the objective state variable.Setting a tight value for the lipschitz constant can significantly improve the speed of convergence.
Optional kwargs
are:
lower_bound
: A valid lower bound for the objective state variable. Can be -Inf
.upper_bound
: A valid upper bound for the objective state variable. Can be +Inf
.Setting tight values for these optional variables can significantly improve the speed of convergence.
If the objective state is N
-dimensional, each keyword argument must be an NTuple{N,Float64}
. For example, initial_value = (0.0, 1.0)
.
SDDP.objective_state
— Functionobjective_state(subproblem::JuMP.Model)
Return the current objective state of the problem.
Can only be called from SDDP.parameterize
.
SDDP.Noise
— TypeNoise(support, probability)
An atom of a discrete random variable at the point of support support
and associated probability probability
.
SDDP.numerical_stability_report
— Functionnumerical_stability_report(
+end
parameterize(node::Node, noise)
Parameterize node node
with the noise noise
.
SDDP.add_objective_state
— Functionadd_objective_state(update::Function, subproblem::JuMP.Model; kwargs...)
Add an objective state variable to subproblem
.
Required kwargs
are:
initial_value
: The initial value of the objective state variable at the root node.lipschitz
: The lipschitz constant of the objective state variable.Setting a tight value for the lipschitz constant can significantly improve the speed of convergence.
Optional kwargs
are:
lower_bound
: A valid lower bound for the objective state variable. Can be -Inf
.upper_bound
: A valid upper bound for the objective state variable. Can be +Inf
.Setting tight values for these optional variables can significantly improve the speed of convergence.
If the objective state is N
-dimensional, each keyword argument must be an NTuple{N,Float64}
. For example, initial_value = (0.0, 1.0)
.
SDDP.objective_state
— Functionobjective_state(subproblem::JuMP.Model)
Return the current objective state of the problem.
Can only be called from SDDP.parameterize
.
SDDP.Noise
— TypeNoise(support, probability)
An atom of a discrete random variable at the point of support support
and associated probability probability
.
SDDP.numerical_stability_report
— Functionnumerical_stability_report(
[io::IO = stdout,]
model::PolicyGraph;
by_node::Bool = false,
print::Bool = true,
warn::Bool = true,
-)
Print a report identifying possible numeric stability issues.
Keyword arguments
If by_node
, print a report for each node in the graph.
If print
, print to io
.
If warn
, warn if the coefficients may cause numerical issues.
SDDP.train
— FunctionSDDP.train(model::PolicyGraph; kwargs...)
Train the policy for model
.
Keyword arguments
iteration_limit::Int
: number of iterations to conduct before termination.
time_limit::Float64
: number of seconds to train before termination.
stoping_rules
: a vector of SDDP.AbstractStoppingRule
s. Defaults to SimulationStoppingRule
.
print_level::Int
: control the level of printing to the screen. Defaults to 1
. Set to 0
to disable all printing.
log_file::String
: filepath at which to write a log of the training progress. Defaults to SDDP.log
.
log_frequency::Int
: control the frequency with which the logging is outputted (iterations/log). It must be at least 1
. Defaults to 1
.
log_every_seconds::Float64
: control the frequency with which the logging is outputted (seconds/log). Defaults to 0.0
.
log_every_iteration::Bool
; over-rides log_frequency
and log_every_seconds
to force every iteration to be printed. Defaults to false
.
run_numerical_stability_report::Bool
: generate (and print) a numerical stability report prior to solve. Defaults to true
.
refine_at_similar_nodes::Bool
: if SDDP can detect that two nodes have the same children, it can cheaply add a cut discovered at one to the other. In almost all cases this should be set to true
.
cut_deletion_minimum::Int
: the minimum number of cuts to cache before deleting cuts from the subproblem. The impact on performance is solver specific; however, smaller values result in smaller subproblems (and therefore quicker solves), at the expense of more time spent performing cut selection.
risk_measure
: the risk measure to use at each node. Defaults to Expectation
.
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
.
sampling_scheme
: a sampling scheme to use on the forward pass of the algorithm. Defaults to InSampleMonteCarlo
.
backward_sampling_scheme
: a backward pass sampling scheme to use on the backward pass of the algorithm. Defaults to CompleteSampler
.
cut_type
: choose between SDDP.SINGLE_CUT
and SDDP.MULTI_CUT
versions of SDDP.
dashboard::Bool
: open a visualization of the training over time. Defaults to false
.
parallel_scheme::AbstractParallelScheme
: specify a scheme for solving in parallel. Defaults to Threaded()
.
forward_pass::AbstractForwardPass
: specify a scheme to use for the forward passes.
forward_pass_resampling_probability::Union{Nothing,Float64}
: set to a value in (0, 1)
to enable RiskAdjustedForwardPass
. Defaults to nothing
(disabled).
add_to_existing_cuts::Bool
: set to true
to allow training a model that was previously trained. Defaults to false
.
duality_handler::AbstractDualityHandler
: specify a duality handler to use when creating cuts.
post_iteration_callback::Function
: a callback with the signature post_iteration_callback(::IterationResult)
that is evaluated after each iteration of the algorithm.
There is also a special option for infinite horizon problems
cycle_discretization_delta
: the maximum distance between states allowed on the forward pass. This is for advanced users only and needs to be used in conjunction with a different sampling_scheme
.SDDP.termination_status
— Functiontermination_status(model::PolicyGraph)::Symbol
Query the reason why the training stopped.
SDDP.write_cuts_to_file
— Functionwrite_cuts_to_file(
+)
Print a report identifying possible numeric stability issues.
Keyword arguments
If by_node
, print a report for each node in the graph.
If print
, print to io
.
If warn
, warn if the coefficients may cause numerical issues.
SDDP.train
— FunctionSDDP.train(model::PolicyGraph; kwargs...)
Train the policy for model
.
Keyword arguments
iteration_limit::Int
: number of iterations to conduct before termination.
time_limit::Float64
: number of seconds to train before termination.
stoping_rules
: a vector of SDDP.AbstractStoppingRule
s. Defaults to SimulationStoppingRule
.
print_level::Int
: control the level of printing to the screen. Defaults to 1
. Set to 0
to disable all printing.
log_file::String
: filepath at which to write a log of the training progress. Defaults to SDDP.log
.
log_frequency::Int
: control the frequency with which the logging is outputted (iterations/log). It must be at least 1
. Defaults to 1
.
log_every_seconds::Float64
: control the frequency with which the logging is outputted (seconds/log). Defaults to 0.0
.
log_every_iteration::Bool
; over-rides log_frequency
and log_every_seconds
to force every iteration to be printed. Defaults to false
.
run_numerical_stability_report::Bool
: generate (and print) a numerical stability report prior to solve. Defaults to true
.
refine_at_similar_nodes::Bool
: if SDDP can detect that two nodes have the same children, it can cheaply add a cut discovered at one to the other. In almost all cases this should be set to true
.
cut_deletion_minimum::Int
: the minimum number of cuts to cache before deleting cuts from the subproblem. The impact on performance is solver specific; however, smaller values result in smaller subproblems (and therefore quicker solves), at the expense of more time spent performing cut selection.
risk_measure
: the risk measure to use at each node. Defaults to Expectation
.
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
.
sampling_scheme
: a sampling scheme to use on the forward pass of the algorithm. Defaults to InSampleMonteCarlo
.
backward_sampling_scheme
: a backward pass sampling scheme to use on the backward pass of the algorithm. Defaults to CompleteSampler
.
cut_type
: choose between SDDP.SINGLE_CUT
and SDDP.MULTI_CUT
versions of SDDP.
dashboard::Bool
: open a visualization of the training over time. Defaults to false
.
parallel_scheme::AbstractParallelScheme
: specify a scheme for solving in parallel. Defaults to Threaded()
.
forward_pass::AbstractForwardPass
: specify a scheme to use for the forward passes.
forward_pass_resampling_probability::Union{Nothing,Float64}
: set to a value in (0, 1)
to enable RiskAdjustedForwardPass
. Defaults to nothing
(disabled).
add_to_existing_cuts::Bool
: set to true
to allow training a model that was previously trained. Defaults to false
.
duality_handler::AbstractDualityHandler
: specify a duality handler to use when creating cuts.
post_iteration_callback::Function
: a callback with the signature post_iteration_callback(::IterationResult)
that is evaluated after each iteration of the algorithm.
There is also a special option for infinite horizon problems
cycle_discretization_delta
: the maximum distance between states allowed on the forward pass. This is for advanced users only and needs to be used in conjunction with a different sampling_scheme
.SDDP.termination_status
— Functiontermination_status(model::PolicyGraph)::Symbol
Query the reason why the training stopped.
SDDP.write_cuts_to_file
— Functionwrite_cuts_to_file(
model::PolicyGraph{T},
filename::String;
kwargs...,
-) where {T}
Write the cuts that form the policy in model
to filename
in JSON format.
Keyword arguments
node_name_parser
is a function which converts the name of each node into a string representation. It has the signature: node_name_parser(::T)::String
.
write_only_selected_cuts
write only the selected cuts to the json file. Defaults to false.
See also SDDP.read_cuts_from_file
.
SDDP.read_cuts_from_file
— Functionread_cuts_from_file(
+) where {T}
Write the cuts that form the policy in model
to filename
in JSON format.
Keyword arguments
node_name_parser
is a function which converts the name of each node into a string representation. It has the signature: node_name_parser(::T)::String
.
write_only_selected_cuts
write only the selected cuts to the json file. Defaults to false.
See also SDDP.read_cuts_from_file
.
SDDP.read_cuts_from_file
— Functionread_cuts_from_file(
model::PolicyGraph{T},
filename::String;
kwargs...,
-) where {T}
Read cuts (saved using SDDP.write_cuts_to_file
) from filename
into model
.
Since T
can be an arbitrary Julia type, the conversion to JSON is lossy. When reading, read_cuts_from_file
only supports T=Int
, T=NTuple{N, Int}
, and T=Symbol
. If you have manually created a policy graph with a different node type T
, provide a function node_name_parser
with the signature
Keyword arguments
node_name_parser(T, name::String)::T where {T}
that returns the name of each node given the string name name
. If node_name_parser
returns nothing
, those cuts are skipped.
cut_selection::Bool
run or not the cut selection algorithm when adding the cuts to the model.
See also SDDP.write_cuts_to_file
.
SDDP.write_log_to_csv
— Functionwrite_log_to_csv(model::PolicyGraph, filename::String)
Write the log of the most recent training to a csv for post-analysis.
Assumes that the model has been trained via SDDP.train
.
SDDP.set_numerical_difficulty_callback
— Functionset_numerical_difficulty_callback(
+) where {T}
Read cuts (saved using SDDP.write_cuts_to_file
) from filename
into model
.
Since T
can be an arbitrary Julia type, the conversion to JSON is lossy. When reading, read_cuts_from_file
only supports T=Int
, T=NTuple{N, Int}
, and T=Symbol
. If you have manually created a policy graph with a different node type T
, provide a function node_name_parser
with the signature
Keyword arguments
node_name_parser(T, name::String)::T where {T}
that returns the name of each node given the string name name
. If node_name_parser
returns nothing
, those cuts are skipped.
cut_selection::Bool
run or not the cut selection algorithm when adding the cuts to the model.
See also SDDP.write_cuts_to_file
.
SDDP.write_log_to_csv
— Functionwrite_log_to_csv(model::PolicyGraph, filename::String)
Write the log of the most recent training to a csv for post-analysis.
Assumes that the model has been trained via SDDP.train
.
SDDP.set_numerical_difficulty_callback
— Functionset_numerical_difficulty_callback(
model::PolicyGraph,
callback::Function,
)
Set a callback function callback(::PolicyGraph, ::Node; require_dual::Bool)
that is run when the optimizer terminates without finding a primal solution (and dual solution if require_dual
is true
).
Default callback
The default callback is a small variation of:
function callback(::PolicyGraph, node::Node; require_dual::Bool)
@@ -274,29 +274,29 @@
end
return
end
-SDDP.set_numerical_difficulty_callback(model, callback)
SDDP.AbstractStoppingRule
— TypeAbstractStoppingRule
The abstract type for the stopping-rule interface.
You need to define the following methods:
SDDP.stopping_rule_status
— Functionstopping_rule_status(::AbstractStoppingRule)::Symbol
Return a symbol describing the stopping rule.
SDDP.convergence_test
— Functionconvergence_test(
+SDDP.set_numerical_difficulty_callback(model, callback)
SDDP.AbstractStoppingRule
— TypeAbstractStoppingRule
The abstract type for the stopping-rule interface.
You need to define the following methods:
SDDP.stopping_rule_status
— Functionstopping_rule_status(::AbstractStoppingRule)::Symbol
Return a symbol describing the stopping rule.
SDDP.convergence_test
— Functionconvergence_test(
model::PolicyGraph,
log::Vector{Log},
::AbstractStoppingRule,
-)::Bool
Return a Bool
indicating if the algorithm should terminate the training.
SDDP.IterationLimit
— TypeIterationLimit(limit::Int)
Teriminate the algorithm after limit
number of iterations.
SDDP.TimeLimit
— TypeTimeLimit(limit::Float64)
Teriminate the algorithm after limit
seconds of computation.
SDDP.Statistical
— TypeStatistical(;
+)::Bool
Return a Bool
indicating if the algorithm should terminate the training.
SDDP.IterationLimit
— TypeIterationLimit(limit::Int)
Teriminate the algorithm after limit
number of iterations.
SDDP.TimeLimit
— TypeTimeLimit(limit::Float64)
Teriminate the algorithm after limit
seconds of computation.
SDDP.Statistical
— TypeStatistical(;
num_replications::Int,
iteration_period::Int = 1,
z_score::Float64 = 1.96,
verbose::Bool = true,
disable_warning::Bool = false,
-)
Perform an in-sample Monte Carlo simulation of the policy with num_replications
replications every iteration_period
s and terminate if the deterministic bound (lower if minimizing) falls into the confidence interval for the mean of the simulated cost.
If verbose = true
, print the confidence interval.
If disable_warning = true
, disable the warning telling you not to use this stopping rule (see below).
Why this stopping rule is not good
This stopping rule is one of the most common stopping rules seen in the literature. Don't follow the crowd. It is a poor choice for your model, and should be rarely used. Instead, you should use the default stopping rule, or use a fixed limit like a time or iteration limit.
To understand why this stopping rule is a bad idea, assume we have conducted num_replications
simulations and the objectives are in a vector objectives::Vector{Float64}
.
Our mean is μ = mean(objectives)
and the half-width of the confidence interval is w = z_score * std(objectives) / sqrt(num_replications)
.
Many papers suggest terminating the algorithm once the deterministic bound (lower if minimizing, upper if maximizing) is contained within the confidence interval. That is, if μ - w <= bound <= μ + w
. Even worse, some papers define an optimization gap of (μ + w) / bound
(if minimizing) or (μ - w) / bound
(if maximizing), and they terminate once the gap is less than a value like 1%.
Both of these approaches are misleading, and more often than not, they will result in terminating with a sub-optimal policy that performs worse than expected. There are two main reasons for this:
There is a third, more technical reason which relates to the conditional dependence of constructing multiple confidence intervals.
The default value of z_score = 1.96
corresponds to a 95% confidence interval. You should interpret the interval as "if we re-run this simulation 100 times, then the true mean will lie in the confidence interval 95 times out of 100." But if the bound is within the confidence interval, then we know the true mean cannot be better than the bound. Therfore, there is a more than 95% chance that the mean is within the interval.
A separate problem arises if we simulate, find that the bound is outside the confidence interval, keep training, and then re-simulate to compute a new confidence interval. Because we will terminate when the bound enters the confidence interval, the repeated construction of a confidence interval means that the unconditional probability that we terminate with a false positive is larger than 5% (there are now more chances that the sample mean is optimistic and that the confidence interval includes the bound but not the true mean). One fix is to simulate with a sequentially increasing number of replicates, so that the unconditional probability stays at 95%, but this runs into the problem of computational cost. For more information on sequential sampling, see, for example, Güzin Bayraksan, David P. Morton, (2011) A Sequential Sampling Procedure for Stochastic Programming. Operations Research 59(4):898-913.
SDDP.BoundStalling
— TypeBoundStalling(num_previous_iterations::Int, tolerance::Float64)
Teriminate the algorithm once the deterministic bound (lower if minimizing, upper if maximizing) fails to improve by more than tolerance
in absolute terms for more than num_previous_iterations
consecutve iterations, provided it has improved relative to the bound after the first iteration.
Checking for an improvement relative to the first iteration avoids early termination in a situation where the bound fails to improve for the first N
iterations. This frequently happens in models with a large number of stages, where it takes time for the cuts to propogate backward enough to modify the bound of the root node.
SDDP.StoppingChain
— TypeStoppingChain(rules::AbstractStoppingRule...)
Terminate once all of the rules
are statified.
This stopping rule short-circuits, so subsequent rules are only tested if the previous pass.
Examples
A stopping rule that runs 100 iterations, then checks for the bound stalling:
StoppingChain(IterationLimit(100), BoundStalling(5, 0.1))
SDDP.SimulationStoppingRule
— TypeSimulationStoppingRule(;
+)
Perform an in-sample Monte Carlo simulation of the policy with num_replications
replications every iteration_period
s and terminate if the deterministic bound (lower if minimizing) falls into the confidence interval for the mean of the simulated cost.
If verbose = true
, print the confidence interval.
If disable_warning = true
, disable the warning telling you not to use this stopping rule (see below).
Why this stopping rule is not good
This stopping rule is one of the most common stopping rules seen in the literature. Don't follow the crowd. It is a poor choice for your model, and should be rarely used. Instead, you should use the default stopping rule, or use a fixed limit like a time or iteration limit.
To understand why this stopping rule is a bad idea, assume we have conducted num_replications
simulations and the objectives are in a vector objectives::Vector{Float64}
.
Our mean is μ = mean(objectives)
and the half-width of the confidence interval is w = z_score * std(objectives) / sqrt(num_replications)
.
Many papers suggest terminating the algorithm once the deterministic bound (lower if minimizing, upper if maximizing) is contained within the confidence interval. That is, if μ - w <= bound <= μ + w
. Even worse, some papers define an optimization gap of (μ + w) / bound
(if minimizing) or (μ - w) / bound
(if maximizing), and they terminate once the gap is less than a value like 1%.
Both of these approaches are misleading, and more often than not, they will result in terminating with a sub-optimal policy that performs worse than expected. There are two main reasons for this:
There is a third, more technical reason which relates to the conditional dependence of constructing multiple confidence intervals.
The default value of z_score = 1.96
corresponds to a 95% confidence interval. You should interpret the interval as "if we re-run this simulation 100 times, then the true mean will lie in the confidence interval 95 times out of 100." But if the bound is within the confidence interval, then we know the true mean cannot be better than the bound. Therfore, there is a more than 95% chance that the mean is within the interval.
A separate problem arises if we simulate, find that the bound is outside the confidence interval, keep training, and then re-simulate to compute a new confidence interval. Because we will terminate when the bound enters the confidence interval, the repeated construction of a confidence interval means that the unconditional probability that we terminate with a false positive is larger than 5% (there are now more chances that the sample mean is optimistic and that the confidence interval includes the bound but not the true mean). One fix is to simulate with a sequentially increasing number of replicates, so that the unconditional probability stays at 95%, but this runs into the problem of computational cost. For more information on sequential sampling, see, for example, Güzin Bayraksan, David P. Morton, (2011) A Sequential Sampling Procedure for Stochastic Programming. Operations Research 59(4):898-913.
SDDP.BoundStalling
— TypeBoundStalling(num_previous_iterations::Int, tolerance::Float64)
Teriminate the algorithm once the deterministic bound (lower if minimizing, upper if maximizing) fails to improve by more than tolerance
in absolute terms for more than num_previous_iterations
consecutve iterations, provided it has improved relative to the bound after the first iteration.
Checking for an improvement relative to the first iteration avoids early termination in a situation where the bound fails to improve for the first N
iterations. This frequently happens in models with a large number of stages, where it takes time for the cuts to propogate backward enough to modify the bound of the root node.
SDDP.StoppingChain
— TypeStoppingChain(rules::AbstractStoppingRule...)
Terminate once all of the rules
are statified.
This stopping rule short-circuits, so subsequent rules are only tested if the previous pass.
Examples
A stopping rule that runs 100 iterations, then checks for the bound stalling:
StoppingChain(IterationLimit(100), BoundStalling(5, 0.1))
SDDP.SimulationStoppingRule
— TypeSimulationStoppingRule(;
sampling_scheme::AbstractSamplingScheme = SDDP.InSampleMonteCarlo(),
replications::Int = -1,
period::Int = -1,
distance_tol::Float64 = 1e-2,
bound_tol::Float64 = 1e-4,
-)
Terminate the algorithm using a mix of heuristics. Unless you know otherwise, this is typically a good default.
Termination criteria
First, we check that the deterministic bound has stabilized. That is, over the last five iterations, the deterministic bound has changed by less than an absolute or relative tolerance of bound_tol
.
Then, if we have not done one in the last period
iterations, we perform a primal simulation of the policy using replications
out-of-sample realizations from sampling_scheme
. The realizations are stored and re-used in each simulation. From each simulation, we record the value of the stage objective. We terminate the policy if each of the trajectories in two consecutive simulations differ by less than distance_tol
.
By default, replications
and period
are -1
, and SDDP.jl will guess good values for these. Over-ride the default behavior by setting an appropriate value.
Example
SDDP.train(model; stopping_rules = [SimulationStoppingRule()])
SDDP.FirstStageStoppingRule
— TypeFirstStageStoppingRule(; atol::Float64 = 1e-3, iterations::Int = 50)
Terminate the algorithm when the outgoing values of the first-stage state variables have not changed by more than atol
for iterations
number of consecutive iterations.
Example
SDDP.train(model; stopping_rules = [FirstStageStoppingRule()])
SDDP.AbstractSamplingScheme
— TypeAbstractSamplingScheme
The abstract type for the sampling-scheme interface.
You need to define the following methods:
SDDP.sample_scenario
— Functionsample_scenario(graph::PolicyGraph{T}, ::AbstractSamplingScheme) where {T}
Sample a scenario from the policy graph graph
based on the sampling scheme.
Returns ::Tuple{Vector{Tuple{T, <:Any}}, Bool}
, where the first element is the scenario, and the second element is a Boolean flag indicating if the scenario was terminated due to the detection of a cycle.
The scenario is a list of tuples (type Vector{Tuple{T, <:Any}}
) where the first component of each tuple is the index of the node, and the second component is the stagewise-independent noise term observed in that node.
SDDP.InSampleMonteCarlo
— TypeInSampleMonteCarlo(;
+)
Terminate the algorithm using a mix of heuristics. Unless you know otherwise, this is typically a good default.
Termination criteria
First, we check that the deterministic bound has stabilized. That is, over the last five iterations, the deterministic bound has changed by less than an absolute or relative tolerance of bound_tol
.
Then, if we have not done one in the last period
iterations, we perform a primal simulation of the policy using replications
out-of-sample realizations from sampling_scheme
. The realizations are stored and re-used in each simulation. From each simulation, we record the value of the stage objective. We terminate the policy if each of the trajectories in two consecutive simulations differ by less than distance_tol
.
By default, replications
and period
are -1
, and SDDP.jl will guess good values for these. Over-ride the default behavior by setting an appropriate value.
Example
SDDP.train(model; stopping_rules = [SimulationStoppingRule()])
SDDP.FirstStageStoppingRule
— TypeFirstStageStoppingRule(; atol::Float64 = 1e-3, iterations::Int = 50)
Terminate the algorithm when the outgoing values of the first-stage state variables have not changed by more than atol
for iterations
number of consecutive iterations.
Example
SDDP.train(model; stopping_rules = [FirstStageStoppingRule()])
SDDP.AbstractSamplingScheme
— TypeAbstractSamplingScheme
The abstract type for the sampling-scheme interface.
You need to define the following methods:
SDDP.sample_scenario
— Functionsample_scenario(graph::PolicyGraph{T}, ::AbstractSamplingScheme) where {T}
Sample a scenario from the policy graph graph
based on the sampling scheme.
Returns ::Tuple{Vector{Tuple{T, <:Any}}, Bool}
, where the first element is the scenario, and the second element is a Boolean flag indicating if the scenario was terminated due to the detection of a cycle.
The scenario is a list of tuples (type Vector{Tuple{T, <:Any}}
) where the first component of each tuple is the index of the node, and the second component is the stagewise-independent noise term observed in that node.
SDDP.InSampleMonteCarlo
— TypeInSampleMonteCarlo(;
max_depth::Int = 0,
terminate_on_cycle::Function = false,
terminate_on_dummy_leaf::Function = true,
rollout_limit::Function = (i::Int) -> typemax(Int),
initial_node::Any = nothing,
-)
A Monte Carlo sampling scheme using the in-sample data from the policy graph definition.
If terminate_on_cycle
, terminate the forward pass once a cycle is detected. If max_depth > 0
, return once max_depth
nodes have been sampled. If terminate_on_dummy_leaf
, terminate the forward pass with 1 - probability of sampling a child node.
Note that if terminate_on_cycle = false
and terminate_on_dummy_leaf = false
then max_depth
must be set > 0.
Control which node the trajectories start from using initial_node
. If it is left as nothing
, the root node is used as the starting node.
You can use rollout_limit
to set iteration specific depth limits. For example:
InSampleMonteCarlo(rollout_limit = i -> 2 * i)
SDDP.OutOfSampleMonteCarlo
— TypeOutOfSampleMonteCarlo(
+)
A Monte Carlo sampling scheme using the in-sample data from the policy graph definition.
If terminate_on_cycle
, terminate the forward pass once a cycle is detected. If max_depth > 0
, return once max_depth
nodes have been sampled. If terminate_on_dummy_leaf
, terminate the forward pass with 1 - probability of sampling a child node.
Note that if terminate_on_cycle = false
and terminate_on_dummy_leaf = false
then max_depth
must be set > 0.
Control which node the trajectories start from using initial_node
. If it is left as nothing
, the root node is used as the starting node.
You can use rollout_limit
to set iteration specific depth limits. For example:
InSampleMonteCarlo(rollout_limit = i -> 2 * i)
SDDP.OutOfSampleMonteCarlo
— TypeOutOfSampleMonteCarlo(
f::Function,
graph::PolicyGraph;
use_insample_transition::Bool = false,
@@ -315,7 +315,7 @@
end
end
Given linear policy graph graph
with T
stages:
sampler = OutOfSampleMonteCarlo(graph, use_insample_transition=true) do node
return [SDDP.Noise(node, 0.3), SDDP.Noise(node + 1, 0.7)]
-end
SDDP.Historical
— TypeHistorical(
+end
SDDP.Historical
— TypeHistorical(
scenarios::Vector{Vector{Tuple{T,S}}},
probability::Vector{Float64};
terminate_on_cycle::Bool = false,
@@ -326,17 +326,17 @@
[(1, 1.0), (2, 0.0), (3, 0.0)]
],
[0.2, 0.5, 0.3],
-)
Historical(
+)
Historical(
scenarios::Vector{Vector{Tuple{T,S}}};
terminate_on_cycle::Bool = false,
) where {T,S}
A deterministic sampling scheme that iterates through the vector of provided scenarios
.
Examples
Historical([
[(1, 0.5), (2, 1.0), (3, 0.5)],
[(1, 0.5), (2, 0.0), (3, 1.0)],
[(1, 1.0), (2, 0.0), (3, 0.0)],
-])
Historical(
+])
Historical(
scenario::Vector{Tuple{T,S}};
terminate_on_cycle::Bool = false,
-) where {T,S}
A deterministic sampling scheme that always samples scenario
.
Examples
Historical([(1, 0.5), (2, 1.5), (3, 0.75)])
SDDP.PSRSamplingScheme
— TypePSRSamplingScheme(N::Int; sampling_scheme = InSampleMonteCarlo())
A sampling scheme with N
scenarios, similar to how PSR does it.
SDDP.SimulatorSamplingScheme
— TypeSimulatorSamplingScheme(simulator::Function)
Create a sampling scheme based on a univariate scenario generator simulator
, which returns a Vector{Float64}
when called with no arguments like simulator()
.
This sampling scheme must be used with a Markovian graph constructed from the same simulator
.
The sample space for SDDP.parameterize
must be a tuple with 1 or 2 values, value is the Markov state and the second value is the random variable for the current node. If the node is deterministic, use Ω = [(markov_state,)]
.
This sampling scheme generates a new scenario by calling simulator()
, and then picking the sequence of nodes in the Markovian graph that is closest to the new trajectory.
Example
julia> using SDDP
+) where {T,S}
A deterministic sampling scheme that always samples scenario
.
Examples
Historical([(1, 0.5), (2, 1.5), (3, 0.75)])
SDDP.PSRSamplingScheme
— TypePSRSamplingScheme(N::Int; sampling_scheme = InSampleMonteCarlo())
A sampling scheme with N
scenarios, similar to how PSR does it.
SDDP.SimulatorSamplingScheme
— TypeSimulatorSamplingScheme(simulator::Function)
Create a sampling scheme based on a univariate scenario generator simulator
, which returns a Vector{Float64}
when called with no arguments like simulator()
.
This sampling scheme must be used with a Markovian graph constructed from the same simulator
.
The sample space for SDDP.parameterize
must be a tuple with 1 or 2 values, value is the Markov state and the second value is the random variable for the current node. If the node is deterministic, use Ω = [(markov_state,)]
.
This sampling scheme generates a new scenario by calling simulator()
, and then picking the sequence of nodes in the Markovian graph that is closest to the new trajectory.
Example
julia> using SDDP
julia> import HiGHS
@@ -368,50 +368,50 @@
iteration_limit = 10,
sampling_scheme = SDDP.SimulatorSamplingScheme(simulator),
)
-
SDDP.AbstractParallelScheme
— TypeAbstractParallelScheme
Abstract type for different parallelism schemes.
SDDP.Serial
— TypeSerial()
Run SDDP in serial mode.
SDDP.Threaded
— TypeThreaded()
Run SDDP in multi-threaded mode.
Use julia --threads N
to start Julia with N
threads. In most cases, you should pick N
to be the number of physical cores on your machine.
This plug-in is experimental, and parts of SDDP.jl may not be threadsafe. If you encounter any problems or crashes, please open a GitHub issue.
Example
SDDP.train(model; parallel_scheme = SDDP.Threaded())
-SDDP.simulate(model; parallel_scheme = SDDP.Threaded())
SDDP.Asynchronous
— TypeAsynchronous(
+
SDDP.AbstractParallelScheme
— TypeAbstractParallelScheme
Abstract type for different parallelism schemes.
SDDP.Serial
— TypeSerial()
Run SDDP in serial mode.
SDDP.Threaded
— TypeThreaded()
Run SDDP in multi-threaded mode.
Use julia --threads N
to start Julia with N
threads. In most cases, you should pick N
to be the number of physical cores on your machine.
This plug-in is experimental, and parts of SDDP.jl may not be threadsafe. If you encounter any problems or crashes, please open a GitHub issue.
Example
SDDP.train(model; parallel_scheme = SDDP.Threaded())
+SDDP.simulate(model; parallel_scheme = SDDP.Threaded())
SDDP.Asynchronous
— TypeAsynchronous(
[init_callback::Function,]
slave_pids::Vector{Int} = workers();
use_master::Bool = true,
-)
Run SDDP in asynchronous mode workers with pid's slave_pids
.
After initializing the models on each worker, call init_callback(model)
. Note that init_callback
is run locally on the worker and not on the master thread.
If use_master
is true
, iterations are also conducted on the master process.
Asynchronous(
+)
Run SDDP in asynchronous mode workers with pid's slave_pids
.
After initializing the models on each worker, call init_callback(model)
. Note that init_callback
is run locally on the worker and not on the master thread.
If use_master
is true
, iterations are also conducted on the master process.
Asynchronous(
solver::Any,
slave_pids::Vector{Int} = workers();
use_master::Bool = true,
-)
Run SDDP in asynchronous mode workers with pid's slave_pids
.
Set the optimizer on each worker by calling JuMP.set_optimizer(model, solver)
.
SDDP.AbstractForwardPass
— TypeAbstractForwardPass
Abstract type for different forward passes.
SDDP.DefaultForwardPass
— TypeDefaultForwardPass(; include_last_node::Bool = true)
The default forward pass.
If include_last_node = false
and the sample terminated due to a cycle, then the last node (which forms the cycle) is omitted. This can be useful option to set when training, but it comes at the cost of not knowing which node formed the cycle (if there are multiple possibilities).
SDDP.RevisitingForwardPass
— TypeRevisitingForwardPass(
+)
Run SDDP in asynchronous mode workers with pid's slave_pids
.
Set the optimizer on each worker by calling JuMP.set_optimizer(model, solver)
.
SDDP.AbstractForwardPass
— TypeAbstractForwardPass
Abstract type for different forward passes.
SDDP.DefaultForwardPass
— TypeDefaultForwardPass(; include_last_node::Bool = true)
The default forward pass.
If include_last_node = false
and the sample terminated due to a cycle, then the last node (which forms the cycle) is omitted. This can be useful option to set when training, but it comes at the cost of not knowing which node formed the cycle (if there are multiple possibilities).
SDDP.RevisitingForwardPass
— TypeRevisitingForwardPass(
period::Int = 500;
sub_pass::AbstractForwardPass = DefaultForwardPass(),
-)
A forward pass scheme that generate period
new forward passes (using sub_pass
), then revisits all previously explored forward passes. This can be useful to encourage convergence at a diversity of points in the state-space.
Set period = typemax(Int)
to disable.
For example, if period = 2
, then the forward passes will be revisited as follows: 1, 2, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 1, 2, ...
.
SDDP.RiskAdjustedForwardPass
— TypeRiskAdjustedForwardPass(;
+)
A forward pass scheme that generate period
new forward passes (using sub_pass
), then revisits all previously explored forward passes. This can be useful to encourage convergence at a diversity of points in the state-space.
Set period = typemax(Int)
to disable.
For example, if period = 2
, then the forward passes will be revisited as follows: 1, 2, 1, 2, 3, 4, 1, 2, 3, 4, 5, 6, 1, 2, ...
.
SDDP.RiskAdjustedForwardPass
— TypeRiskAdjustedForwardPass(;
forward_pass::AbstractForwardPass,
risk_measure::AbstractRiskMeasure,
resampling_probability::Float64,
rejection_count::Int = 5,
-)
A forward pass that resamples a previous forward pass with resampling_probability
probability, and otherwise samples a new forward pass using forward_pass
.
The forward pass to revisit is chosen based on the risk-adjusted (using risk_measure
) probability of the cumulative stage objectives.
Note that this objective corresponds to the first time we visited the trajectory. Subsequent visits may have improved things, but we don't have the mechanisms in-place to update it. Therefore, remove the forward pass from resampling consideration after rejection_count
revisits.
SDDP.AlternativeForwardPass
— TypeAlternativeForwardPass(
+)
A forward pass that resamples a previous forward pass with resampling_probability
probability, and otherwise samples a new forward pass using forward_pass
.
The forward pass to revisit is chosen based on the risk-adjusted (using risk_measure
) probability of the cumulative stage objectives.
Note that this objective corresponds to the first time we visited the trajectory. Subsequent visits may have improved things, but we don't have the mechanisms in-place to update it. Therefore, remove the forward pass from resampling consideration after rejection_count
revisits.
SDDP.AlternativeForwardPass
— TypeAlternativeForwardPass(
forward_model::SDDP.PolicyGraph{T};
forward_pass::AbstractForwardPass = DefaultForwardPass(),
-)
A forward pass that simulates using forward_model
, which may be different to the model used in the backwards pass.
When using this forward pass, you should almost always pass SDDP.AlternativePostIterationCallback
to the post_iteration_callback
argument of SDDP.train
.
This forward pass is most useful when the forward_model
is non-convex and we use a convex approximation of the model in the backward pass.
For example, in optimal power flow models, we can use an AC-OPF formulation as the forward_model
and a DC-OPF formulation as the backward model.
For more details see the paper:
Rosemberg, A., and Street, A., and Garcia, J.D., and Valladão, D.M., and Silva, T., and Dowson, O. (2021). Assessing the cost of network simplifications in long-term hydrothermal dispatch planning models. IEEE Transactions on Sustainable Energy. 13(1), 196-206.
SDDP.AlternativePostIterationCallback
— TypeAlternativePostIterationCallback(forward_model::PolicyGraph)
A post-iteration callback that should be used whenever SDDP.AlternativeForwardPass
is used.
SDDP.RegularizedForwardPass
— TypeRegularizedForwardPass(;
+)
A forward pass that simulates using forward_model
, which may be different to the model used in the backwards pass.
When using this forward pass, you should almost always pass SDDP.AlternativePostIterationCallback
to the post_iteration_callback
argument of SDDP.train
.
This forward pass is most useful when the forward_model
is non-convex and we use a convex approximation of the model in the backward pass.
For example, in optimal power flow models, we can use an AC-OPF formulation as the forward_model
and a DC-OPF formulation as the backward model.
For more details see the paper:
Rosemberg, A., and Street, A., and Garcia, J.D., and Valladão, D.M., and Silva, T., and Dowson, O. (2021). Assessing the cost of network simplifications in long-term hydrothermal dispatch planning models. IEEE Transactions on Sustainable Energy. 13(1), 196-206.
SDDP.AlternativePostIterationCallback
— TypeAlternativePostIterationCallback(forward_model::PolicyGraph)
A post-iteration callback that should be used whenever SDDP.AlternativeForwardPass
is used.
SDDP.RegularizedForwardPass
— TypeRegularizedForwardPass(;
rho::Float64 = 0.05,
forward_pass::AbstractForwardPass = DefaultForwardPass(),
-)
A forward pass that regularizes the outgoing first-stage state variables with an L-infty trust-region constraint about the previous iteration's solution. Specifically, the bounds of the outgoing state variable x
are updated from (l, u)
to max(l, x^k - rho * (u - l)) <= x <= min(u, x^k + rho * (u - l))
, where x^k
is the optimal solution of x
in the previous iteration. On the first iteration, the value of the state at the root node is used.
By default, rho
is set to 5%, which seems to work well empirically.
Pass a different forward_pass
to control the forward pass within the regularized forward pass.
This forward pass is largely intended to be used for investment problems in which the first stage makes a series of capacity decisions that then influence the rest of the graph. An error is thrown if the first stage problem is not deterministic, and states are silently skipped if they do not have finite bounds.
SDDP.AbstractRiskMeasure
— TypeAbstractRiskMeasure
The abstract type for the risk measure interface.
You need to define the following methods:
SDDP.adjust_probability
— Functionadjust_probability(
+)
A forward pass that regularizes the outgoing first-stage state variables with an L-infty trust-region constraint about the previous iteration's solution. Specifically, the bounds of the outgoing state variable x
are updated from (l, u)
to max(l, x^k - rho * (u - l)) <= x <= min(u, x^k + rho * (u - l))
, where x^k
is the optimal solution of x
in the previous iteration. On the first iteration, the value of the state at the root node is used.
By default, rho
is set to 5%, which seems to work well empirically.
Pass a different forward_pass
to control the forward pass within the regularized forward pass.
This forward pass is largely intended to be used for investment problems in which the first stage makes a series of capacity decisions that then influence the rest of the graph. An error is thrown if the first stage problem is not deterministic, and states are silently skipped if they do not have finite bounds.
SDDP.AbstractRiskMeasure
— TypeAbstractRiskMeasure
The abstract type for the risk measure interface.
You need to define the following methods:
SDDP.adjust_probability
— Functionadjust_probability(
measure::Expectation
risk_adjusted_probability::Vector{Float64},
original_probability::Vector{Float64},
noise_support::Vector{Noise{T}},
objective_realizations::Vector{Float64},
is_minimization::Bool,
-) where {T}
SDDP.AbstractDualityHandler
— TypeAbstractDualityHandler
The abstract type for the duality handler interface.
SDDP.ContinuousConicDuality
— TypeContinuousConicDuality()
Compute dual variables in the backward pass using conic duality, relaxing any binary or integer restrictions as necessary.
Theory
Given the problem
min Cᵢ(x̄, u, w) + θᵢ
+) where {T}
SDDP.AbstractDualityHandler
— TypeAbstractDualityHandler
The abstract type for the duality handler interface.
SDDP.ContinuousConicDuality
— TypeContinuousConicDuality()
Compute dual variables in the backward pass using conic duality, relaxing any binary or integer restrictions as necessary.
Theory
Given the problem
min Cᵢ(x̄, u, w) + θᵢ
st (x̄, x′, u) in Xᵢ(w) ∩ S
x̄ - x == 0 [λ]
where S ⊆ ℝ×ℤ
, we relax integrality and using conic duality to solve for λ
in the problem:
min Cᵢ(x̄, u, w) + θᵢ
st (x̄, x′, u) in Xᵢ(w)
- x̄ - x == 0 [λ]
SDDP.LagrangianDuality
— TypeLagrangianDuality(;
+ x̄ - x == 0 [λ]
SDDP.LagrangianDuality
— TypeLagrangianDuality(;
method::LocalImprovementSearch.AbstractSearchMethod =
LocalImprovementSearch.BFGS(100),
)
Obtain dual variables in the backward pass using Lagrangian duality.
Arguments
method
: the LocalImprovementSearch
method for maximizing the Lagrangian dual problem.Theory
Given the problem
min Cᵢ(x̄, u, w) + θᵢ
st (x̄, x′, u) in Xᵢ(w) ∩ S
x̄ - x == 0 [λ]
where S ⊆ ℝ×ℤ
, we solve the problem max L(λ)
, where:
L(λ) = min Cᵢ(x̄, u, w) + θᵢ - λ' h(x̄)
- st (x̄, x′, u) in Xᵢ(w) ∩ S
and where h(x̄) = x̄ - x
.
SDDP.StrengthenedConicDuality
— TypeStrengthenedConicDuality()
Obtain dual variables in the backward pass using strengthened conic duality.
Theory
Given the problem
min Cᵢ(x̄, u, w) + θᵢ
+ st (x̄, x′, u) in Xᵢ(w) ∩ S
and where h(x̄) = x̄ - x
.
SDDP.StrengthenedConicDuality
— TypeStrengthenedConicDuality()
Obtain dual variables in the backward pass using strengthened conic duality.
Theory
Given the problem
min Cᵢ(x̄, u, w) + θᵢ
st (x̄, x′, u) in Xᵢ(w) ∩ S
x̄ - x == 0 [λ]
we first obtain an estimate for λ
using ContinuousConicDuality
.
Then, we evaluate the Lagrangian function:
L(λ) = min Cᵢ(x̄, u, w) + θᵢ - λ' (x̄ - x`)
- st (x̄, x′, u) in Xᵢ(w) ∩ S
to obtain a better estimate of the intercept.
SDDP.BanditDuality
— TypeBanditDuality()
Formulates the problem of choosing a duality handler as a multi-armed bandit problem. The arms to choose between are:
Our problem isn't a typical multi-armed bandit for a two reasons:
We choose a very simple heuristic: pick the arm with the best mean + 1 standard deviation. That should ensure we consistently pick the arm with the best likelihood of improving the value function.
In future, we should consider discounting the rewards of earlier iterations, and focus more on the more-recent rewards.
SDDP.simulate
— Functionsimulate(
+ st (x̄, x′, u) in Xᵢ(w) ∩ S
to obtain a better estimate of the intercept.
SDDP.BanditDuality
— TypeBanditDuality()
Formulates the problem of choosing a duality handler as a multi-armed bandit problem. The arms to choose between are:
Our problem isn't a typical multi-armed bandit for a two reasons:
We choose a very simple heuristic: pick the arm with the best mean + 1 standard deviation. That should ensure we consistently pick the arm with the best likelihood of improving the value function.
In future, we should consider discounting the rewards of earlier iterations, and focus more on the more-recent rewards.
SDDP.simulate
— Functionsimulate(
model::PolicyGraph,
number_replications::Int = 1,
variables::Vector{Symbol} = Symbol[];
@@ -426,65 +426,65 @@
custom_recorders = Dict{Symbol, Function}(
:constraint_dual => sp -> JuMP.dual(sp[:my_constraint])
)
-)
The value of the dual in the first stage of the second replication can be accessed as:
simulation_results[2][1][:constraint_dual]
SDDP.calculate_bound
— FunctionSDDP.calculate_bound(
+)
The value of the dual in the first stage of the second replication can be accessed as:
simulation_results[2][1][:constraint_dual]
SDDP.calculate_bound
— FunctionSDDP.calculate_bound(
model::PolicyGraph,
state::Dict{Symbol,Float64} = model.initial_root_state;
risk_measure::AbstractRiskMeasure = Expectation(),
-)
Calculate the lower bound (if minimizing, otherwise upper bound) of the problem model at the point state, assuming the risk measure at the root node is risk_measure.
SDDP.add_all_cuts
— Functionadd_all_cuts(model::PolicyGraph)
Add all cuts that may have been deleted back into the model.
Explanation
During the solve, SDDP.jl may decide to remove cuts for a variety of reasons.
These can include cuts that define the optimal value function, particularly around the extremes of the state-space (e.g., reservoirs empty).
This function ensures that all cuts discovered are added back into the model.
SDDP.DecisionRule
— TypeDecisionRule(model::PolicyGraph{T}; node::T)
Create a decision rule for node node
in model
.
Example
rule = SDDP.DecisionRule(model; node = 1)
SDDP.evaluate
— Functionevaluate(
+)
Calculate the lower bound (if minimizing, otherwise upper bound) of the problem model at the point state, assuming the risk measure at the root node is risk_measure.
SDDP.add_all_cuts
— Functionadd_all_cuts(model::PolicyGraph)
Add all cuts that may have been deleted back into the model.
Explanation
During the solve, SDDP.jl may decide to remove cuts for a variety of reasons.
These can include cuts that define the optimal value function, particularly around the extremes of the state-space (e.g., reservoirs empty).
This function ensures that all cuts discovered are added back into the model.
SDDP.DecisionRule
— TypeDecisionRule(model::PolicyGraph{T}; node::T)
Create a decision rule for node node
in model
.
Example
rule = SDDP.DecisionRule(model; node = 1)
SDDP.evaluate
— Functionevaluate(
rule::DecisionRule;
incoming_state::Dict{Symbol,Float64},
noise = nothing,
controls_to_record = Symbol[],
-)
Evalute the decision rule rule
at the point described by the incoming_state
and noise
.
If the node is deterministic, omit the noise
argument.
Pass a list of symbols to controls_to_record
to save the optimal primal solution corresponding to the names registered in the model.
evaluate(
+)
Evalute the decision rule rule
at the point described by the incoming_state
and noise
.
If the node is deterministic, omit the noise
argument.
Pass a list of symbols to controls_to_record
to save the optimal primal solution corresponding to the names registered in the model.
evaluate(
V::ValueFunction,
point::Dict{Union{Symbol,String},<:Real}
objective_state = nothing,
belief_state = nothing
-)
Evaluate the value function V
at point
in the state-space.
Returns a tuple containing the height of the function, and the subgradient w.r.t. the convex state-variables.
Examples
evaluate(V, Dict(:volume => 1.0))
If the state variable is constructed like @variable(sp, volume[1:4] >= 0, SDDP.State, initial_value = 0.0)
, use [i]
to index the state variable:
evaluate(V, Dict(Symbol("volume[1]") => 1.0))
You can also use strings or symbols for the keys.
evaluate(V, Dict("volume[1]" => 1))
evalute(V::ValueFunction{Nothing, Nothing}; kwargs...)
Evalute the value function V
at the point in the state-space specified by kwargs
.
Examples
evaluate(V; volume = 1)
evaluate(
+)
Evaluate the value function V
at point
in the state-space.
Returns a tuple containing the height of the function, and the subgradient w.r.t. the convex state-variables.
Examples
evaluate(V, Dict(:volume => 1.0))
If the state variable is constructed like @variable(sp, volume[1:4] >= 0, SDDP.State, initial_value = 0.0)
, use [i]
to index the state variable:
evaluate(V, Dict(Symbol("volume[1]") => 1.0))
You can also use strings or symbols for the keys.
evaluate(V, Dict("volume[1]" => 1))
evalute(V::ValueFunction{Nothing, Nothing}; kwargs...)
Evalute the value function V
at the point in the state-space specified by kwargs
.
Examples
evaluate(V; volume = 1)
evaluate(
model::PolicyGraph{T},
validation_scenarios::ValidationScenarios{T,S},
) where {T,S}
Evaluate the performance of the policy contained in model
after a call to train
on the scenarios specified by validation_scenarios
.
Examples
model, validation_scenarios = read_from_file("my_model.sof.json")
train(model; iteration_limit = 100)
-simulations = evaluate(model, validation_scenarios)
SDDP.SpaghettiPlot
— TypeSDDP.SpaghettiPlot(; stages, scenarios)
Initialize a new SpaghettiPlot
with stages
stages and scenarios
number of replications.
SDDP.add_spaghetti
— FunctionSDDP.add_spaghetti(data_function::Function, plt::SpaghettiPlot; kwargs...)
Description
Add a new figure to the SpaghettiPlot plt
, where the y-value of the scenario
th line when x = stage
is given by data_function(plt.simulations[scenario][stage])
.
Keyword arguments
xlabel
: set the xaxis labelylabel
: set the yaxis labeltitle
: set the title of the plotymin
: set the minimum y valueymax
: set the maximum y valuecumulative
: plot the additive accumulation of the value across the stagesinterpolate
: interpolation method for lines between stages.Defaults to "linear"
see the d3 docs for all options.
Examples
simulations = simulate(model, 10)
+simulations = evaluate(model, validation_scenarios)
SDDP.SpaghettiPlot
— TypeSDDP.SpaghettiPlot(; stages, scenarios)
Initialize a new SpaghettiPlot
with stages
stages and scenarios
number of replications.
SDDP.add_spaghetti
— FunctionSDDP.add_spaghetti(data_function::Function, plt::SpaghettiPlot; kwargs...)
Description
Add a new figure to the SpaghettiPlot plt
, where the y-value of the scenario
th line when x = stage
is given by data_function(plt.simulations[scenario][stage])
.
Keyword arguments
xlabel
: set the xaxis labelylabel
: set the yaxis labeltitle
: set the title of the plotymin
: set the minimum y valueymax
: set the maximum y valuecumulative
: plot the additive accumulation of the value across the stagesinterpolate
: interpolation method for lines between stages.Defaults to "linear"
see the d3 docs for all options.
Examples
simulations = simulate(model, 10)
plt = SDDP.spaghetti_plot(simulations)
SDDP.add_spaghetti(plt; title = "Stage objective") do data
return data[:stage_objective]
-end
SDDP.publication_plot
— FunctionSDDP.publication_plot(
+end
SDDP.publication_plot
— FunctionSDDP.publication_plot(
data_function, simulations;
quantile = [0.0, 0.1, 0.25, 0.5, 0.75, 0.9, 1.0],
kwargs...)
Create a Plots.jl
recipe plot of the simulations.
See Plots.jl
for the list of keyword arguments.
Examples
SDDP.publication_plot(simulations; title = "My title") do data
return data[:stage_objective]
-end
SDDP.ValueFunction
— TypeValueFunction
A representation of the value function. SDDP.jl uses the following unique representation of the value function that is undocumented in the literature.
It supports three types of state variables:
In addition, we have three types of cuts:
Therefore, ValueFunction returns a JuMP model of the following form:
V(x, b, y) = min: μᵀb + νᵀy + θ
+end
SDDP.ValueFunction
— TypeValueFunction
A representation of the value function. SDDP.jl uses the following unique representation of the value function that is undocumented in the literature.
It supports three types of state variables:
In addition, we have three types of cuts:
Therefore, ValueFunction returns a JuMP model of the following form:
V(x, b, y) = min: μᵀb + νᵀy + θ
s.t. # "Single" / "Average" cuts
μᵀb(j) + νᵀy(j) + θ >= α(j) + xᵀβ(j), ∀ j ∈ J
# "Multi" cuts
μᵀb(k) + νᵀy(k) + φ(w) >= α(k, w) + xᵀβ(k, w), ∀w ∈ Ω, k ∈ K
# "Risk-set" cuts
- θ ≥ Σ{p(k, w) * φ(w)}_w - μᵀb(k) - νᵀy(k), ∀ k ∈ K
SDDP.evaluate
— Methodevaluate(
+ θ ≥ Σ{p(k, w) * φ(w)}_w - μᵀb(k) - νᵀy(k), ∀ k ∈ K
SDDP.evaluate
— Methodevaluate(
V::ValueFunction,
point::Dict{Union{Symbol,String},<:Real}
objective_state = nothing,
belief_state = nothing
-)
Evaluate the value function V
at point
in the state-space.
Returns a tuple containing the height of the function, and the subgradient w.r.t. the convex state-variables.
Examples
evaluate(V, Dict(:volume => 1.0))
If the state variable is constructed like @variable(sp, volume[1:4] >= 0, SDDP.State, initial_value = 0.0)
, use [i]
to index the state variable:
evaluate(V, Dict(Symbol("volume[1]") => 1.0))
You can also use strings or symbols for the keys.
evaluate(V, Dict("volume[1]" => 1))
SDDP.plot
— Functionplot(plt::SpaghettiPlot[, filename::String]; open::Bool = true)
The SpaghettiPlot plot plt
to filename
. If filename
is not given, it will be saved to a temporary directory. If open = true
, then a browser window will be opened to display the resulting HTML file.
SDDP.write_subproblem_to_file
— Functionwrite_subproblem_to_file(
+)
Evaluate the value function V
at point
in the state-space.
Returns a tuple containing the height of the function, and the subgradient w.r.t. the convex state-variables.
Examples
evaluate(V, Dict(:volume => 1.0))
If the state variable is constructed like @variable(sp, volume[1:4] >= 0, SDDP.State, initial_value = 0.0)
, use [i]
to index the state variable:
evaluate(V, Dict(Symbol("volume[1]") => 1.0))
You can also use strings or symbols for the keys.
evaluate(V, Dict("volume[1]" => 1))
SDDP.plot
— Functionplot(plt::SpaghettiPlot[, filename::String]; open::Bool = true)
The SpaghettiPlot plot plt
to filename
. If filename
is not given, it will be saved to a temporary directory. If open = true
, then a browser window will be opened to display the resulting HTML file.
SDDP.write_subproblem_to_file
— Functionwrite_subproblem_to_file(
node::Node,
filename::String;
throw_error::Bool = false,
-)
Write the subproblem contained in node
to the file filename
.
The throw_error
is an argument used internally by SDDP.jl. If set, an error will be thrown.
Example
SDDP.write_subproblem_to_file(model[1], "subproblem_1.lp")
SDDP.deterministic_equivalent
— Functiondeterministic_equivalent(
+)
Write the subproblem contained in node
to the file filename
.
The throw_error
is an argument used internally by SDDP.jl. If set, an error will be thrown.
Example
SDDP.write_subproblem_to_file(model[1], "subproblem_1.lp")
SDDP.deterministic_equivalent
— Functiondeterministic_equivalent(
pg::PolicyGraph{T},
optimizer = nothing;
time_limit::Union{Real,Nothing} = 60.0,
-)
Form a JuMP model that represents the deterministic equivalent of the problem.
Examples
deterministic_equivalent(model)
deterministic_equivalent(model, HiGHS.Optimizer)
SDDP.write_to_file
— Functionwrite_to_file(
+)
Form a JuMP model that represents the deterministic equivalent of the problem.
Examples
deterministic_equivalent(model)
deterministic_equivalent(model, HiGHS.Optimizer)
SDDP.write_to_file
— Functionwrite_to_file(
model::PolicyGraph,
filename::String;
compression::MOI.FileFormats.AbstractCompressionScheme =
MOI.FileFormats.AutomaticCompression(),
kwargs...
-)
Write model
to filename
in the StochOptFormat file format.
Pass an argument to compression
to override the default of automatically detecting the file compression to use based on the extension of filename
.
See Base.write(::IO, ::PolicyGraph)
for information on the keyword arguments that can be provided.
This function is experimental. See the full warning in Base.write(::IO, ::PolicyGraph)
.
Examples
write_to_file(model, "my_model.sof.json"; validation_scenarios = 10)
SDDP.read_from_file
— Functionread_from_file(
+)
Write model
to filename
in the StochOptFormat file format.
Pass an argument to compression
to override the default of automatically detecting the file compression to use based on the extension of filename
.
See Base.write(::IO, ::PolicyGraph)
for information on the keyword arguments that can be provided.
This function is experimental. See the full warning in Base.write(::IO, ::PolicyGraph)
.
Examples
write_to_file(model, "my_model.sof.json"; validation_scenarios = 10)
SDDP.read_from_file
— Functionread_from_file(
filename::String;
compression::MOI.FileFormats.AbstractCompressionScheme =
MOI.FileFormats.AutomaticCompression(),
kwargs...
-)::Tuple{PolicyGraph, ValidationScenarios}
Return a tuple containing a PolicyGraph
object and a ValidationScenarios
read from filename
in the StochOptFormat file format.
Pass an argument to compression
to override the default of automatically detecting the file compression to use based on the extension of filename
.
See Base.read(::IO, ::Type{PolicyGraph})
for information on the keyword arguments that can be provided.
This function is experimental. See the full warning in Base.read(::IO, ::Type{PolicyGraph})
.
Examples
model, validation_scenarios = read_from_file("my_model.sof.json")
Base.write
— MethodBase.write(
+)::Tuple{PolicyGraph, ValidationScenarios}
Return a tuple containing a PolicyGraph
object and a ValidationScenarios
read from filename
in the StochOptFormat file format.
Pass an argument to compression
to override the default of automatically detecting the file compression to use based on the extension of filename
.
See Base.read(::IO, ::Type{PolicyGraph})
for information on the keyword arguments that can be provided.
This function is experimental. See the full warning in Base.read(::IO, ::Type{PolicyGraph})
.
Examples
model, validation_scenarios = read_from_file("my_model.sof.json")
Base.write
— MethodBase.write(
io::IO,
model::PolicyGraph;
validation_scenarios::Union{Nothing,Int,ValidationScenarios} = nothing,
@@ -500,15 +500,15 @@
date = "2020-07-20",
description = "Example problem for the SDDP.jl documentation",
)
-end
Base.read
— MethodBase.read(
+end
Base.read
— MethodBase.read(
io::IO,
::Type{PolicyGraph};
bound::Float64 = 1e6,
)::Tuple{PolicyGraph,ValidationScenarios}
Return a tuple containing a PolicyGraph
object and a ValidationScenarios
read from io
in the StochOptFormat file format.
See also: evaluate
.
Compatibility
This function is experimental. Things may change between commits. You should not rely on this functionality as a long-term file format (yet).
In addition to potential changes to the underlying format, only a subset of possible modifications are supported. These include:
If your model uses something other than this, this function may throw an error or silently build a non-convex model.
Examples
open("my_model.sof.json", "r") do io
model, validation_scenarios = read(io, PolicyGraph)
-end
SDDP.evaluate
— Methodevaluate(
+end
SDDP.evaluate
— Methodevaluate(
model::PolicyGraph{T},
validation_scenarios::ValidationScenarios{T,S},
) where {T,S}
Evaluate the performance of the policy contained in model
after a call to train
on the scenarios specified by validation_scenarios
.
Examples
model, validation_scenarios = read_from_file("my_model.sof.json")
train(model; iteration_limit = 100)
-simulations = evaluate(model, validation_scenarios)
SDDP.ValidationScenarios
— TypeValidationScenario{T,S}(scenarios::Vector{ValidationScenario{T,S}})
An AbstractSamplingScheme
based on a vector of scenarios.
Each scenario is a vector of Tuple{T, S}
where the first element is the node to visit and the second element is the realization of the stagewise-independent noise term. Pass nothing
if the node is deterministic.
SDDP.ValidationScenario
— TypeValidationScenario{T,S}(scenario::Vector{Tuple{T,S}})
A single scenario for testing.
See also: ValidationScenarios
.
Settings
This document was generated with Documenter.jl version 1.8.0 on Tuesday 3 December 2024. Using Julia version 1.11.2.