From 3c4bfe9c8ddf04b364304fdef5f072837cc7de09 Mon Sep 17 00:00:00 2001 From: odow Date: Thu, 4 Feb 2021 15:24:08 +1300 Subject: [PATCH 1/2] [MOI] Allow accessing multiple results --- Project.toml | 2 +- src/MOI_wrapper.jl | 45 +++++++++++++++++++++++++++------- test/MOI/MOI_wrapper.jl | 54 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 90 insertions(+), 11 deletions(-) diff --git a/Project.toml b/Project.toml index 076ac333..33694f41 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Gurobi" uuid = "2e9cd046-0924-5485-92f1-d5272153d98b" repo = "https://github.com/jump-dev/Gurobi.jl" -version = "0.9.7" +version = "0.9.8" [deps] CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 46ecac1c..13a120d7 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -2533,10 +2533,9 @@ function _has_primal_ray(model::Optimizer) return ret == 0 end -function MOI.get(model::Optimizer, attr::MOI.VariablePrimal, x::MOI.VariableIndex) - _throw_if_optimize_in_progress(model, attr) - MOI.check_result_index_bounds(model, attr) - key = model.has_unbounded_ray ? "UnbdRay" : "X" +function _get_dbl_attr_variable( + model::Optimizer, key::String, x::MOI.VariableIndex +) col = Cint(column(model, x) - 1) valueP = Ref{Cdouble}() ret = GRBgetdblattrelement(model, key, col, valueP) @@ -2544,6 +2543,24 @@ function MOI.get(model::Optimizer, attr::MOI.VariablePrimal, x::MOI.VariableInde return valueP[] end +function MOI.get( + model::Optimizer, attr::MOI.VariablePrimal, x::MOI.VariableIndex +) + _throw_if_optimize_in_progress(model, attr) + if model.has_unbounded_ray + return _get_dbl_attr_variable(model, "UnbdRay", x) + end + result_count = MOI.get(model, MOI.ResultCount()) + if !(1 <= attr.N <= result_count) + throw(MOI.ResultIndexBoundsError(attr, result_count)) + elseif attr.N == 1 + return _get_dbl_attr_variable(model, "X", x) + else + MOI.set(model, MOI.RawParameter("SolutionNumber"), attr.N - 1) + return _get_dbl_attr_variable(model, "Xn", x) + end +end + function MOI.get( model::Optimizer, attr::MOI.ConstraintPrimal, c::MOI.ConstraintIndex{MOI.SingleVariable, <:Any} @@ -2760,11 +2777,21 @@ end function MOI.get(model::Optimizer, attr::MOI.ObjectiveValue) _throw_if_optimize_in_progress(model, attr) - MOI.check_result_index_bounds(model, attr) - valueP = Ref{Cdouble}() - ret = GRBgetdblattr(model, "ObjVal", valueP) - _check_ret(model, ret) - return valueP[] + result_count = MOI.get(model, MOI.ResultCount()) + if !(1 <= attr.result_index <= result_count) + throw(MOI.ResultIndexBoundsError(attr, result_count)) + elseif attr.result_index == 1 + valueP = Ref{Cdouble}() + ret = GRBgetdblattr(model, "ObjVal", valueP) + _check_ret(model, ret) + return valueP[] + else + MOI.set(model, MOI.RawParameter("SolutionNumber"), attr.result_index - 1) + valueP = Ref{Cdouble}() + ret = GRBgetdblattr(model, "PoolObjVal", valueP) + _check_ret(model, ret) + return valueP[] + end end function MOI.get(model::Optimizer, attr::MOI.ObjectiveBound) diff --git a/test/MOI/MOI_wrapper.jl b/test/MOI/MOI_wrapper.jl index 4d169fc5..9961df1b 100644 --- a/test/MOI/MOI_wrapper.jl +++ b/test/MOI/MOI_wrapper.jl @@ -530,7 +530,7 @@ end function test_Add_and_delete_sos_constraints() model = Gurobi.Optimizer(GRB_ENV) MOI.set(model, MOI.Silent(), true) - # SOS2 to model piecewise linear function + # SOS2 to model piecewise linear function xp1 = [1.0, 2.0, 3.0] yp1 = [3.0, 2.0, 3.0] λ = MOI.add_variables(model, length(xp1)) @@ -1532,6 +1532,58 @@ function test_add_constrained_variables() @test MOI.get(model, MOI.ConstraintSet(), ci) == set end +function _is_binary(x; atol = 1e-6) + return isapprox(x, 0; atol = atol) || isapprox(x, 1; atol = atol) +end + +function test_multiple_solutions() + model = Gurobi.Optimizer(GRB_ENV) + MOI.set(model, MOI.RawParameter("OutputFlag"), 0) + MOI.set(model, MOI.RawParameter("Cuts"), 0) + MOI.set(model, MOI.RawParameter("Presolve"), 0) + MOI.set(model, MOI.RawParameter("PreCrush"), 0) + MOI.set(model, MOI.RawParameter("Heuristics"), 0) + N = 30 + x = MOI.add_variables(model, N) + MOI.add_constraints(model, MOI.SingleVariable.(x), MOI.ZeroOne()) + MOI.set.(model, MOI.VariablePrimalStart(), x, 0.0) + Random.seed!(1) + item_weights, item_values = rand(N), rand(N) + MOI.add_constraint( + model, + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(item_weights, x), 0.0), + MOI.LessThan(10.0) + ) + MOI.set( + model, + MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}(), + MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.(item_values, x), 0.0) + ) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + MOI.optimize!(model) + RC = MOI.get(model, MOI.ResultCount()) + @test RC > 1 + for n in [0, RC + 1] + @test_throws( + MOI.ResultIndexBoundsError, + MOI.get(model, MOI.VariablePrimal(n), x), + ) + @test_throws( + MOI.ResultIndexBoundsError, + MOI.get(model, MOI.ObjectiveValue(n)), + ) + end + for n = 1:RC + xn = MOI.get(model, MOI.VariablePrimal(n), x) + @test all(_is_binary, xn) + @test isapprox( + MOI.get(model, MOI.ObjectiveValue(n)), + item_values' * xn, + atol=1e-6, + ) + end +end + end runtests(TestMOIWrapper) From 2903483fb596437a21ef2c64149b1046daf6dbfa Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 5 Feb 2021 14:15:57 +1300 Subject: [PATCH 2/2] Refactor implementation --- src/MOI_wrapper.jl | 49 +++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 13a120d7..e7cc4d9b 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -2533,32 +2533,21 @@ function _has_primal_ray(model::Optimizer) return ret == 0 end -function _get_dbl_attr_variable( - model::Optimizer, key::String, x::MOI.VariableIndex -) - col = Cint(column(model, x) - 1) - valueP = Ref{Cdouble}() - ret = GRBgetdblattrelement(model, key, col, valueP) - _check_ret(model, ret) - return valueP[] -end - function MOI.get( model::Optimizer, attr::MOI.VariablePrimal, x::MOI.VariableIndex ) _throw_if_optimize_in_progress(model, attr) - if model.has_unbounded_ray - return _get_dbl_attr_variable(model, "UnbdRay", x) - end - result_count = MOI.get(model, MOI.ResultCount()) - if !(1 <= attr.N <= result_count) - throw(MOI.ResultIndexBoundsError(attr, result_count)) - elseif attr.N == 1 - return _get_dbl_attr_variable(model, "X", x) - else + MOI.check_result_index_bounds(model, attr) + key = model.has_unbounded_ray ? "UnbdRay" : "X" + if attr.N > 1 MOI.set(model, MOI.RawParameter("SolutionNumber"), attr.N - 1) - return _get_dbl_attr_variable(model, "Xn", x) + key = "Xn" end + col = Cint(column(model, x) - 1) + valueP = Ref{Cdouble}() + ret = GRBgetdblattrelement(model, key, col, valueP) + _check_ret(model, ret) + return valueP[] end function MOI.get( @@ -2777,21 +2766,15 @@ end function MOI.get(model::Optimizer, attr::MOI.ObjectiveValue) _throw_if_optimize_in_progress(model, attr) - result_count = MOI.get(model, MOI.ResultCount()) - if !(1 <= attr.result_index <= result_count) - throw(MOI.ResultIndexBoundsError(attr, result_count)) - elseif attr.result_index == 1 - valueP = Ref{Cdouble}() - ret = GRBgetdblattr(model, "ObjVal", valueP) - _check_ret(model, ret) - return valueP[] - else + MOI.check_result_index_bounds(model, attr) + if attr.result_index > 1 MOI.set(model, MOI.RawParameter("SolutionNumber"), attr.result_index - 1) - valueP = Ref{Cdouble}() - ret = GRBgetdblattr(model, "PoolObjVal", valueP) - _check_ret(model, ret) - return valueP[] end + valueP = Ref{Cdouble}() + key = attr.result_index == 1 ? "ObjVal" : "PoolObjVal" + ret = GRBgetdblattr(model, key, valueP) + _check_ret(model, ret) + return valueP[] end function MOI.get(model::Optimizer, attr::MOI.ObjectiveBound)