From a3da26dcda4b32bf5d4217282197324d85cb9906 Mon Sep 17 00:00:00 2001 From: Joey Huchette Date: Sun, 20 Dec 2020 23:06:52 -0500 Subject: [PATCH 1/3] Add setters for basis --- src/MOI_wrapper.jl | 54 ++++++++++++++++++++++++++++++++++++++ test/MOI/MOI_wrapper.jl | 58 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index 0cff55a0..d730ab70 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -3181,6 +3181,60 @@ function MOI.get( end end +function MOI.set( + model::Optimizer, + ::MOI.ConstraintBasisStatus, + c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},S}, + bs::MOI.BasisStatusCode +) where {S <: _SCALAR_SETS} + # _update_if_necessary(model) + row = _info(model, c).row + valueP::Cint = + (if bs == MOI.BASIC + Cint(-0) + elseif bs == MOI.NONBASIC + Cint(-1) + else + error("Unsupported constraint basis value $bs.") + end) + ret = GRBsetintattrelement(model, "CBasis", Cint(row - 1), valueP) + _check_ret(model, ret) + _require_update(model) + return +end + +function MOI.set( + model::Optimizer, + ::MOI.ConstraintBasisStatus, + c::MOI.ConstraintIndex{MOI.SingleVariable,S}, + bs::MOI.BasisStatusCode +) where {S <: _SCALAR_SETS} + # _update_if_necessary(model) + valueP::Cint = + (if bs == MOI.BASIC + Cint(0) + elseif bs == MOI.NONBASIC_AT_LOWER + Cint(-1) + elseif bs == MOI.NONBASIC_AT_UPPER + Cint(-2) + elseif bs == MOI.SUPER_BASIC + Cint(-3) + else + @assert bs == MOI.NONBASIC + if S <: MOI.LessThan + Cint(-2) + elseif S <: MOI.GreaterThan + Cint(-1) + else + error("Do not know how to set variable basis status $bs with constraint set $set.") + end + end) + ret = GRBsetintattrelement(model, "VBasis", Cint(column(model, c) - 1), valueP) + _check_ret(model, ret) + _require_update(model) + return +end + function MOI.compute_conflict!(model::Optimizer) ret = GRBcomputeIIS(model) _check_ret(model, ret) diff --git a/test/MOI/MOI_wrapper.jl b/test/MOI/MOI_wrapper.jl index f365b5ed..4ebc4e90 100644 --- a/test/MOI/MOI_wrapper.jl +++ b/test/MOI/MOI_wrapper.jl @@ -1364,13 +1364,69 @@ function test_farkas_dual_max_ii() @show clb_dual, c_dual @test clb_dual[1] < 1e-6 @test clb_dual[2] < 1e-6 - # Confirmed bug in Gurobi <=9.1 and fixed in next release. When updating for new + # Confirmed bug in Gurobi <=9.1 and fixed in next release. When updating for new # release, confirm fixed and add a conditional check on `_GRB_VERSION`. @test_broken c_dual[1] < 1e-6 @test_broken clb_dual[1] ≈ 2 * c_dual atol = 1e-6 @test_broken clb_dual[2] ≈ c_dual atol = 1e-6 end +function test_set_basis() + T = Float64 + model = Gurobi.Optimizer(GRB_ENV) + # MOI.set(model, MOI.Silent(), true) + # Min -x + # s.t. x + y <= 1 + # x, y >= 0 + + x = MOI.add_variable(model) + y = MOI.add_variable(model) + + cf = MOI.ScalarAffineFunction{T}( + MOI.ScalarAffineTerm{T}.([one(T), one(T)], [x, y]), + zero(T), + ) + c = MOI.add_constraint(model, cf, MOI.LessThan(one(T))) + + vc1 = MOI.add_constraint( + model, + MOI.SingleVariable(x), + MOI.GreaterThan(zero(T)), + ) + vc2 = MOI.add_constraint( + model, + MOI.SingleVariable(y), + MOI.GreaterThan(zero(T)), + ) + objf = MOI.ScalarAffineFunction{T}( + MOI.ScalarAffineTerm{T}.([-one(T), zero(T)], [x, y]), + zero(T), + ) + MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(), objf) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + + # The following is an indirect, brittle way to test the desired behavior. + # Gurobi appears to not allow you to query VBasis/CBasis attributes without + # optimizing the problem + # (https://support.gurobi.com/hc/en-us/community/posts/360075729911-Getting-VBasis-CBasis-attributes-without-optimizing). + # The following just verifies that the problem is solved with 0 simplex iterations + # when we seed the optimal solution and 1 simplex iteration when we seed a + # suboptimal feasible basis. Ideally, we would remove the call to MOI.optimize! + # and just verify that, after setting the basis, we can get the same basis + # statuses back. + MOI.set(model, MOI.ConstraintBasisStatus(), c, MOI.NONBASIC) + MOI.set(model, MOI.ConstraintBasisStatus(), vc1, MOI.BASIC) + MOI.set(model, MOI.ConstraintBasisStatus(), vc2, MOI.NONBASIC) + MOI.optimize!(model) + @test MOI.get(model, MOI.SimplexIterations()) == 0 + + MOI.set(model, MOI.ConstraintBasisStatus(), c, MOI.BASIC) + MOI.set(model, MOI.ConstraintBasisStatus(), vc1, MOI.NONBASIC) + MOI.set(model, MOI.ConstraintBasisStatus(), vc2, MOI.NONBASIC) + MOI.optimize!(model) + @test MOI.get(model, MOI.SimplexIterations()) == 0 +end + end runtests(TestMOIWrapper) From 29a5ad97d981e01989b9b1c2b64a0dd68142e6de Mon Sep 17 00:00:00 2001 From: Joey Huchette Date: Sun, 20 Dec 2020 23:15:43 -0500 Subject: [PATCH 2/3] Fix silly error in test --- test/MOI/MOI_wrapper.jl | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/test/MOI/MOI_wrapper.jl b/test/MOI/MOI_wrapper.jl index 4ebc4e90..2e65c65b 100644 --- a/test/MOI/MOI_wrapper.jl +++ b/test/MOI/MOI_wrapper.jl @@ -1369,12 +1369,12 @@ function test_farkas_dual_max_ii() @test_broken c_dual[1] < 1e-6 @test_broken clb_dual[1] ≈ 2 * c_dual atol = 1e-6 @test_broken clb_dual[2] ≈ c_dual atol = 1e-6 -end +# end -function test_set_basis() +function _build_basis_model() T = Float64 model = Gurobi.Optimizer(GRB_ENV) - # MOI.set(model, MOI.Silent(), true) + MOI.set(model, MOI.Silent(), true) # Min -x # s.t. x + y <= 1 # x, y >= 0 @@ -1404,7 +1404,10 @@ function test_set_basis() ) MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(), objf) MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + return model, vc1, vc2, c +end +function test_set_basis() # The following is an indirect, brittle way to test the desired behavior. # Gurobi appears to not allow you to query VBasis/CBasis attributes without # optimizing the problem @@ -1414,17 +1417,23 @@ function test_set_basis() # suboptimal feasible basis. Ideally, we would remove the call to MOI.optimize! # and just verify that, after setting the basis, we can get the same basis # statuses back. - MOI.set(model, MOI.ConstraintBasisStatus(), c, MOI.NONBASIC) - MOI.set(model, MOI.ConstraintBasisStatus(), vc1, MOI.BASIC) - MOI.set(model, MOI.ConstraintBasisStatus(), vc2, MOI.NONBASIC) - MOI.optimize!(model) - @test MOI.get(model, MOI.SimplexIterations()) == 0 + let + model, vc1, vc2, c = _build_basis_model() + MOI.set(model, MOI.ConstraintBasisStatus(), vc1, MOI.BASIC) + MOI.set(model, MOI.ConstraintBasisStatus(), vc2, MOI.NONBASIC) + MOI.set(model, MOI.ConstraintBasisStatus(), c, MOI.NONBASIC) + MOI.optimize!(model) + @test MOI.get(model, MOI.SimplexIterations()) == 0 + end - MOI.set(model, MOI.ConstraintBasisStatus(), c, MOI.BASIC) - MOI.set(model, MOI.ConstraintBasisStatus(), vc1, MOI.NONBASIC) - MOI.set(model, MOI.ConstraintBasisStatus(), vc2, MOI.NONBASIC) - MOI.optimize!(model) - @test MOI.get(model, MOI.SimplexIterations()) == 0 + let + model, vc1, vc2, c = _build_basis_model() + MOI.set(model, MOI.ConstraintBasisStatus(), vc1, MOI.NONBASIC) + MOI.set(model, MOI.ConstraintBasisStatus(), vc2, MOI.NONBASIC) + MOI.set(model, MOI.ConstraintBasisStatus(), c, MOI.BASIC) + MOI.optimize!(model) + @test MOI.get(model, MOI.SimplexIterations()) == 1 + end end end From 31c68a174031609a069579d7570298b8029465af Mon Sep 17 00:00:00 2001 From: Joey Huchette Date: Wed, 23 Dec 2020 14:30:27 -0500 Subject: [PATCH 3/3] Uncomment mysteriously commented end --- test/MOI/MOI_wrapper.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/MOI/MOI_wrapper.jl b/test/MOI/MOI_wrapper.jl index 2e65c65b..a9d74814 100644 --- a/test/MOI/MOI_wrapper.jl +++ b/test/MOI/MOI_wrapper.jl @@ -1369,7 +1369,7 @@ function test_farkas_dual_max_ii() @test_broken c_dual[1] < 1e-6 @test_broken clb_dual[1] ≈ 2 * c_dual atol = 1e-6 @test_broken clb_dual[2] ≈ c_dual atol = 1e-6 -# end +end function _build_basis_model() T = Float64