From 8e8d04a097b7814f7b65d6633d4fc1ff97c5d5b5 Mon Sep 17 00:00:00 2001 From: odow Date: Mon, 16 Dec 2019 17:09:26 -0600 Subject: [PATCH 1/2] Write MOF files by default --- Project.toml | 2 +- src/algorithm.jl | 53 ++++++++++++++----------------------------- src/modeling_aids.jl | 13 +++++++---- test/algorithm.jl | 17 ++++++++++---- test/modeling_aids.jl | 4 +--- 5 files changed, 39 insertions(+), 50 deletions(-) diff --git a/Project.toml b/Project.toml index 8af27258a..61eb1f520 100644 --- a/Project.toml +++ b/Project.toml @@ -23,7 +23,7 @@ HTTP = "0.8.1" JSON = "0.21" JuMP = "~0.20" JuliaFormatter = "0.1.37" -MathOptFormat = "0.2" +MathOptFormat = "0.4" RecipesBase = "0.7" Reexport = "0.2" TimerOutputs = "0.5" diff --git a/src/algorithm.jl b/src/algorithm.jl index 44d9d6f28..e44a2de19 100644 --- a/src/algorithm.jl +++ b/src/algorithm.jl @@ -209,46 +209,23 @@ stage_objective_value(stage_objective::Real) = stage_objective stage_objective_value(stage_objective) = JuMP.value(stage_objective) """ - write_subproblem_to_file(node::Node, filename::String; format=:both) + write_subproblem_to_file(node::Node, filename::String; throw_error::Bool = false) Write the subproblem contained in `node` to the file `filename`. - -`format` should be one of `:mps`, `:lp`, or `:both`. """ -function write_subproblem_to_file( - node::Node, - filename::String; - format::Symbol = :both, - throw_error::Bool = false, -) - if format ∉ (:mps, :lp, :both) - error("Invalid `format=$(format)`. Must be `:mps`, `:lp`, or `:both`.") - end - if format == :mps || format == :both - mps = MathOptFormat.MPS.Model() - MOI.copy_to(mps, JuMP.backend(node.subproblem)) - MOI.write_to_file(mps, filename * ".mps") - end - if format == :lp || format == :both - lp = MathOptFormat.LP.Model() - MOI.copy_to(lp, JuMP.backend(node.subproblem)) - MOI.write_to_file(lp, filename * ".lp") - end +function write_subproblem_to_file(node::Node, filename::String; throw_error::Bool = false) + model = MathOptFormat.Model(filename = filename) + MOI.copy_to(model, JuMP.backend(node.subproblem)) + MOI.write_to_file(model, filename) if throw_error error( - "Unable to retrieve dual solution from ", - node.index, - ".", - "\n Termination status: ", - JuMP.termination_status(node.subproblem), - "\n Primal status: ", - JuMP.primal_status(node.subproblem), - "\n Dual status: ", - JuMP.dual_status(node.subproblem), - ".\n An MPS file was written to `subproblem.mps` and an LP file ", - "written to `subproblem.lp`. See ", - "https://odow.github.io/SDDP.jl/latest/tutorial/06_warnings/#Numerical-stability-1", - " for more information.", + "Unable to retrieve solution from $(node.index).\n", + " Termination status: $(JuMP.termination_status(node.subproblem))\n", + " Primal status: $(JuMP.primal_status(node.subproblem))\n", + " Dual status: $(JuMP.dual_status(node.subproblem)).\n", + "A MathOptFormat file was written to `$(filename)`.\n", + "See https://odow.github.io/SDDP.jl/latest/tutorial/06_warnings/#Numerical-stability-1", + "\nfor more information.", ) end end @@ -295,7 +272,11 @@ function solve_subproblem( # Test for primal feasibility. if JuMP.primal_status(node.subproblem) != JuMP.MOI.FEASIBLE_POINT - write_subproblem_to_file(node, "subproblem", throw_error = true) + write_subproblem_to_file( + node, + "subproblem_$(node.index).mof.json", + throw_error = true, + ) end # If require_duals = true, check for dual feasibility and return a dict with # the dual on the fixed constraint associated with each incoming state diff --git a/src/modeling_aids.jl b/src/modeling_aids.jl index 9a22fe6d3..8487ac74e 100644 --- a/src/modeling_aids.jl +++ b/src/modeling_aids.jl @@ -24,7 +24,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -function find_min(x::Vector{T}, y::T) where {T <: Real} +function find_min(x::Vector{T}, y::T) where {T<:Real} best_i = 0 best_z = Inf for i = 1:length(x) @@ -40,7 +40,7 @@ end function lattice_approximation(f::Function, states::Vector{Int}, scenarios::Int) path = f()::Vector{Float64} support = [fill(path[t], states[t]) for t = 1:length(states)] - probability = [zeros(states[t - 1], states[t]) for t = 2:length(states)] + probability = [zeros(states[t-1], states[t]) for t = 2:length(states)] prepend!(probability, Ref(zeros(1, states[1]))) distance = 0.0 for n = 1:scenarios @@ -55,7 +55,8 @@ function lattice_approximation(f::Function, states::Vector{Int}, scenarios::Int) min_dist, best_idx = find_min(support[t], path[t]) dist += min_dist^2 probability[t][last_index, best_idx] += 1.0 - support[t][best_idx] -= min_dist * (support[t][best_idx] - path[t]) / (3000 + n)^0.75 + support[t][best_idx] -= + min_dist * (support[t][best_idx] - path[t]) / (3000 + n)^0.75 last_index = best_idx end distance = (distance * (n - 1) + dist) / n @@ -114,7 +115,9 @@ nodes between stages. Alternatively, `budget` can be a `Vector{Int}`, which deta number of Markov state to have in each stage. """ function MarkovianGraph( - simulator::Function; budget::Union{Int, Vector{Int}}, scenarios::Int = 1000 + simulator::Function; + budget::Union{Int,Vector{Int}}, + scenarios::Int = 1000, ) scenarios = max(scenarios, 10) states = allocate_support_budget(simulator, budget, scenarios) @@ -127,7 +130,7 @@ function MarkovianGraph( for t = 2:length(support) for (j, sj) in enumerate(support[t]) add_node(g, (t, sj)) - for (i, si) in enumerate(support[t - 1]) + for (i, si) in enumerate(support[t-1]) add_edge(g, (t - 1, si) => (t, sj), probability[t][i, j]) end end diff --git a/test/algorithm.jl b/test/algorithm.jl index aa439dd84..bf822dc55 100644 --- a/test/algorithm.jl +++ b/test/algorithm.jl @@ -161,11 +161,18 @@ end @constraint(node, x.out <= -1) @stageobjective(node, x.out) end - @test_throws Exception SDDP.train(model; iteration_limit = 1, print_level = 0) - @test isfile("subproblem.mps") - rm("subproblem.mps") - @test isfile("subproblem.lp") - rm("subproblem.lp") + ex = ErrorException(""" + Unable to retrieve solution from 1. + Termination status: INFEASIBLE + Primal status: NO_SOLUTION + Dual status: INFEASIBILITY_CERTIFICATE. + A MathOptFormat file was written to `subproblem_1.mof.json`. + See https://odow.github.io/SDDP.jl/latest/tutorial/06_warnings/#Numerical-stability-1 + for more information.""") + + @test_throws ex SDDP.train(model; iteration_limit = 1, print_level = 0) + @test isfile("subproblem_1.mof.json") + rm("subproblem_1.mof.json") end @testset "refine_at_similar_nodes" begin diff --git a/test/modeling_aids.jl b/test/modeling_aids.jl index 7750323bf..43f614515 100644 --- a/test/modeling_aids.jl +++ b/test/modeling_aids.jl @@ -34,9 +34,7 @@ end end @testset "lattice_approximation" begin - support, probability = SDDP.lattice_approximation( - () -> rand(5), [1, 2, 3, 4, 5], 100 - ) + support, probability = SDDP.lattice_approximation(() -> rand(5), [1, 2, 3, 4, 5], 100) for (t, s) in enumerate(support) @test length(s) == t @test all(x -> 0 <= x <= 1, s) From 5622ce8fb9d6340998ba86071382296b0e8913fb Mon Sep 17 00:00:00 2001 From: odow Date: Tue, 17 Dec 2019 08:27:36 -0600 Subject: [PATCH 2/2] Fix docs --- docs/src/guides/debug_a_model.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/src/guides/debug_a_model.md b/docs/src/guides/debug_a_model.md index 6f67c7735..39bc64561 100644 --- a/docs/src/guides/debug_a_model.md +++ b/docs/src/guides/debug_a_model.md @@ -53,13 +53,8 @@ Now to write out the problem to a file. We'll get a few warnings because some variables and constraints don't have names. They don't matter, so ignore them. ```jldoctest tutorial_eight; filter=r"MathOptFormat\ .+?MathOptFormat\.jl" -julia> SDDP.write_subproblem_to_file(model[1], "subproblem", format=:lp) +julia> SDDP.write_subproblem_to_file(model[1], "subproblem.lp") -``` - -We can check the file by reading it back in again. - -```jldoctest tutorial_eight julia> read("subproblem.lp") |> String |> print minimize obj: 1.1 x_out + 1 x2 @@ -80,8 +75,7 @@ are not in the original problem formulation. ```jldoctest tutorial_eight; filter=r"MathOptFormat\ .+?MathOptFormat\.jl" julia> SDDP.parameterize(model[1], 3.3) -julia> SDDP.write_subproblem_to_file(model[1], "subproblem", format=:lp) - +julia> SDDP.write_subproblem_to_file(model[1], "subproblem.lp") julia> read("subproblem.lp") |> String |> print minimize