diff --git a/src/plugins/stopping_rules.jl b/src/plugins/stopping_rules.jl index 520813c19..e13877c52 100644 --- a/src/plugins/stopping_rules.jl +++ b/src/plugins/stopping_rules.jl @@ -171,14 +171,9 @@ function convergence_test( if graph.objective_sense == MOI.MIN_SENSE return sample_mean - sample_ci <= current_bound - elseif graph.objective_sense == MOI.MAX_SENSE - return current_bound <= sample_mean + sample_ci else - # If sense is none of the above for some awkward reason, return to - # previous criteria - return sample_mean - sample_ci <= - current_bound <= - sample_mean + sample_ci + @assert graph.objective_sense == MOI.MAX_SENSE + return current_bound <= sample_mean + sample_ci end end @@ -281,10 +276,6 @@ mutable struct SimulationStoppingRule{F} <: AbstractStoppingRule bound_tol::Float64 end -function _get_state_variable_value(key) - return sp -> JuMP.value(JuMP.variable_by_name(sp, "$(key)_out")) -end - """ SimulationStoppingRule(; sampling_scheme::AbstractSamplingScheme = SDDP.InSampleMonteCarlo(), diff --git a/src/visualization/spaghetti_plot.jl b/src/visualization/spaghetti_plot.jl index f7ca6dd80..07ae7808f 100644 --- a/src/visualization/spaghetti_plot.jl +++ b/src/visualization/spaghetti_plot.jl @@ -152,15 +152,14 @@ function save(p::SpaghettiPlot, args...; kwargs...) return plot(p, args...; kwargs...) end -function launch_file(filename) +function launch_file(filename, fn = run) if Sys.iswindows() - run(`$(ENV["COMSPEC"]) /c start $(filename)`) + return fn(`$(ENV["COMSPEC"]) /c start $(filename)`) elseif Sys.isapple() - run(`open $(filename)`) + return fn(`open $(filename)`) elseif Sys.islinux() || Sys.isbsd() - run(`xdg-open $(filename)`) + return fn(`xdg-open $(filename)`) else error("Unable to show plot. Try opening the file $(filename) manually.") end - return end diff --git a/test/binary_expansion.jl b/test/binary_expansion.jl index 0ba56a8be..9a3a28822 100644 --- a/test/binary_expansion.jl +++ b/test/binary_expansion.jl @@ -71,6 +71,7 @@ function test_Binary_Expansion() @test bincontract([0, 1, 0], 0.1) ≈ 0.2 @test bincontract([1, 1, 0], 0.1) ≈ 0.3 @test bincontract([1, 0, 1], 0.1) ≈ 0.5 + return end end # module diff --git a/test/plugins/bellman_functions.jl b/test/plugins/bellman_functions.jl index 2adf25df8..8464f86c0 100644 --- a/test/plugins/bellman_functions.jl +++ b/test/plugins/bellman_functions.jl @@ -362,16 +362,25 @@ function test_biobjective_cut_selection() ) end end - SDDP.train_biobjective( + ret = SDDP.train_biobjective( model; solution_limit = 10, iteration_limit = 10, print_level = 0, ) + @test ret isa Dict{Float64,Float64} n_cuts = count(model[1].bellman_function.global_theta.cuts) do cut return cut.constraint_ref !== nothing end @test n_cuts < 100 + ret = SDDP.train_biobjective( + model; + solution_limit = 10, + iteration_limit = 10, + print_level = 0, + include_timing = true, + ) + @test ret isa Dict{Float64,Tuple{Float64,Float64}} return end diff --git a/test/plugins/parallel_schemes.jl b/test/plugins/parallel_schemes.jl index 81a64e569..129ce4ef7 100644 --- a/test/plugins/parallel_schemes.jl +++ b/test/plugins/parallel_schemes.jl @@ -45,6 +45,41 @@ function test_Asynchronous_optimizer() a = SDDP.Asynchronous(HiGHS.Optimizer) a.init_callback(model) @test solver_name(model[2].subproblem) == "HiGHS" + model = SDDP.LinearPolicyGraph(; + stages = 3, + lower_bound = 0.0, + direct_mode = true, + optimizer = HiGHS.Optimizer, + ) do sp, stage + @variable(sp, 0 <= x <= 100, SDDP.State, initial_value = 0) + @stageobjective(sp, x.in) + end + scheme = SDDP.Asynchronous() + @test_throws( + ErrorException( + "Cannot use asynchronous solver with optimizers in direct mode.", + ), + scheme.init_callback(model), + ) + @test_throws( + ErrorException( + "Cannot use asynchronous solver with optimizers in direct mode.", + ), + SDDP._uninitialize_solver(model; throw_error = true), + ) + model = SDDP.LinearPolicyGraph(; stages = 3, lower_bound = 0.0) do sp, stage + @variable(sp, 0 <= x <= 100, SDDP.State, initial_value = 0) + @stageobjective(sp, x.in) + end + scheme = SDDP.Asynchronous() + @test_throws( + ErrorException( + "You must supply an optimizer for the policy graph, either by passing\n" * + "one to the `optimizer` keyword argument to `PolicyGraph`, or by\n" * + "using `JuMP.set_optimizer(model, optimizer)`.\n", + ), + scheme.init_callback(model), + ) return end diff --git a/test/plugins/stopping_rules.jl b/test/plugins/stopping_rules.jl index f715c7057..69bd055dc 100644 --- a/test/plugins/stopping_rules.jl +++ b/test/plugins/stopping_rules.jl @@ -134,6 +134,12 @@ function test_Statistical() [SDDP.Log(1, 12.0, 9.0, 1.0, 1, 1, " ", false)], rule, ) + rule = SDDP.Statistical(; num_replications = 20, iteration_period = 2) + @test !SDDP.convergence_test( + model, + [SDDP.Log(1, 6.0, 9.0, 1.0, 1, 1, " ", false)], + rule, + ) return end @@ -194,6 +200,17 @@ function test_BoundStalling() ], rule, ) + rule = SDDP.BoundStalling(5, 1.0) + @test !SDDP.convergence_test( + graph, + [ + SDDP.Log(1, 0.0, 0.0, 1.0, 1, 1, " ", false), + SDDP.Log(2, 0.0, 0.0, 1.0, 1, 1, " ", false), + SDDP.Log(3, 0.0, 0.0, 1.0, 1, 1, " ", false), + SDDP.Log(4, 0.0, 0.0, 1.0, 1, 1, " ", false), + ], + rule, + ) return end @@ -276,6 +293,10 @@ function test_SimulationStoppingRule() @test !SDDP.convergence_test(graph, log[1:10], rule) @test !SDDP.convergence_test(graph, log[1:19], rule) @test SDDP.convergence_test(graph, log[1:20], rule) + rule = SDDP.SimulationStoppingRule(; period = 1) + rule.last_iteration = 20 + @test rule.period == 1 + @test !SDDP.convergence_test(graph, log, rule) return end diff --git a/test/user_interface.jl b/test/user_interface.jl index 6bff8d58c..e83c3e33a 100644 --- a/test/user_interface.jl +++ b/test/user_interface.jl @@ -453,13 +453,31 @@ function test_numerical_stability_report() lower_bound = -1e10, direct_mode = false, ) do subproblem, t - @variable(subproblem, x >= -1e7, SDDP.State, initial_value = 1e-5) + @variable(subproblem, x >= -1e-7, SDDP.State, initial_value = 1e-5) @variable(subproblem, 1 <= y <= 5, Int) # Note: this is just to test range fallback - @constraint(subproblem, 1e9 * x.out >= 1e-6 * x.in + 1e-8) + @constraint(subproblem, 1e-8 <= x.in + y <= 10) + @constraint(subproblem, [x.in, x.out, y] in MOI.ExponentialCone()) + @constraint(subproblem, 1e9 * x.out >= 1e-6 * x.in + 0) @stageobjective(subproblem, 1e9 * x.out) end + mktempdir() do dir + filename = joinpath(dir, "log.stdout") + open(filename, "w") do io + redirect_stdout(io) do + @test SDDP.numerical_stability_report(model) === nothing + return + end + return + end + contents = read(filename, String) + @test occursin("WARNING", contents) + @test occursin("numerical stability report", contents) + return + end report = sprint(SDDP.numerical_stability_report, model) @test occursin("WARNING", report) + @test occursin("[1e-07, 1e+10]", report) + @test occursin("[1e-08, 1e+01]", report) report_2 = sprint(io -> SDDP.numerical_stability_report(io, model; by_node = true)) @test occursin("numerical stability report for node: 1", report_2) @@ -518,6 +536,30 @@ function test_objective_state() return end +function test_objective_state_two() + model = + SDDP.LinearPolicyGraph(; stages = 2, lower_bound = 0) do subproblem, t + @variable(subproblem, x, SDDP.State, initial_value = 0) + SDDP.add_objective_state( + subproblem; + initial_value = (1.5, 0.0), + lower_bound = (0.75, 0.0), + upper_bound = (2.25, 0.0), + lipschitz = (100.0, 100.0), + ) do y, ω + return (y + ω, y) + end + SDDP.parameterize(subproblem, [1, 2]) do ω + ret = SDDP.objective_state(subproblem) + @test ret isa Tuple{Float64,Float64} + @stageobjective(subproblem, ret[1] * x.out) + return + end + end + SDDP.parameterize(model[1], 2) + return +end + function test_belief_updater() graph = SDDP.LinearGraph(2) SDDP.add_edge(graph, 2 => 1, 0.9) @@ -839,6 +881,36 @@ function test_policy_graph_sense_error() return end +function test_print_problem_statistics() + graph = SDDP.Graph(0) + for i in 1:4 + SDDP.add_node(graph, i) + end + SDDP.add_edge(graph, 0 => 1, 1.0) + SDDP.add_edge(graph, 0 => 4, 0.0) + SDDP.add_edge(graph, 1 => 2, 0.2) + SDDP.add_edge(graph, 1 => 3, 0.3) + SDDP.add_edge(graph, 1 => 4, 0.5) + SDDP.add_edge(graph, 2 => 3, 0.5) + SDDP.add_edge(graph, 2 => 4, 0.5) + SDDP.add_edge(graph, 3 => 4, 1.0) + graph + model = SDDP.PolicyGraph(graph; lower_bound = 0.0) do sp, t + @variable(sp, x, SDDP.State, initial_value = 1.0) + @constraint(sp, x.in == x.out) + end + contents = sprint( + SDDP.print_problem_statistics, + model, + false, + nothing, + nothing, + nothing, + ) + @test occursin("scenarios : 4.00000e+00", contents) + return +end + end # module TestUserInterface.runtests() diff --git a/test/visualization/visualization.jl b/test/visualization/visualization.jl index fb632a5e8..da64166df 100644 --- a/test/visualization/visualization.jl +++ b/test/visualization/visualization.jl @@ -55,6 +55,7 @@ function test_SpaghettiPlot() @test read("test.html", String) == read(control, String) end rm("test.html") + @test SDDP.launch_file("test.html", identity) isa Cmd return end