From e26ff8b80a81d533e0a59bf2ae88e33a1a0c3d55 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 23 Jan 2024 15:16:28 +1300 Subject: [PATCH] Add support for vector-valued MOI.set when setting new ConstraintSet (#196) --- src/MOI_wrapper.jl | 60 ++++++++++++++++ test/MOI_wrapper.jl | 170 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+) diff --git a/src/MOI_wrapper.jl b/src/MOI_wrapper.jl index c4ad69b..74686bc 100644 --- a/src/MOI_wrapper.jl +++ b/src/MOI_wrapper.jl @@ -1427,6 +1427,41 @@ function MOI.set( return end +function MOI.set( + model::Optimizer, + ::MOI.ConstraintSet, + c::Vector{MOI.ConstraintIndex{MOI.VariableIndex,S}}, + s::Vector{S}, +) where {S<:_SCALAR_SETS} + if length(c) != length(s) + msg = "number of constraints does not match number of sets" + throw(DimensionMismatch(msg)) + end + for ci in c + MOI.throw_if_not_valid(model, ci) + end + N = length(c) + columns, lower, upper = zeros(Cint, N), zeros(Cdouble, N), zeros(Cdouble, N) + for (i, (ci, si)) in enumerate(zip(c, s)) + info = _info(model, ci) + columns[i] = info.column + l, u = _bounds(si) + if S == MOI.LessThan{Float64} + info.upper = u + lower[i], upper[i] = info.lower, u + elseif S == MOI.GreaterThan{Float64} + info.lower = l + lower[i], upper[i] = l, info.upper + else + lower[i], upper[i] = l, u + info.lower, info.upper = l, u + end + end + ret = Highs_changeColsBoundsBySet(model, N, columns, lower, upper) + _check_ret(ret) + return +end + function MOI.supports( ::Optimizer, ::MOI.ConstraintName, @@ -1603,6 +1638,31 @@ function MOI.set( return end +function MOI.set( + model::Optimizer, + ::MOI.ConstraintSet, + c::Vector{MOI.ConstraintIndex{MOI.ScalarAffineFunction{Float64},S}}, + s::Vector{S}, +) where {S<:_SCALAR_SETS} + if length(c) != length(s) + msg = "number of constraints does not match number of sets" + throw(DimensionMismatch(msg)) + end + for ci in c + MOI.throw_if_not_valid(model, ci) + end + N = length(c) + rows, lower, upper = zeros(Cint, N), zeros(Cdouble, N), zeros(Cdouble, N) + for (i, (ci, si)) in enumerate(zip(c, s)) + info = _info(model, ci) + rows[i] = info.row + lower[i], upper[i] = info.lower, info.upper = _bounds(si) + end + ret = Highs_changeRowsBoundsBySet(model, N, rows, lower, upper) + _check_ret(ret) + return +end + function MOI.get( model::Optimizer, ::MOI.ConstraintFunction, diff --git a/test/MOI_wrapper.jl b/test/MOI_wrapper.jl index 3b1b786..a7e3055 100644 --- a/test/MOI_wrapper.jl +++ b/test/MOI_wrapper.jl @@ -543,6 +543,176 @@ function test_quadratic_diagonal_modification() return end +function test_change_col_bounds_by_set_dimension_mismatch() + model = HiGHS.Optimizer() + MOI.set(model, MOI.Silent(), true) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint.(model, x, MOI.GreaterThan.(1.0:3.0)) + @test_throws( + DimensionMismatch, + MOI.set(model, MOI.ConstraintSet(), c, MOI.GreaterThan.([4.0, 5.0])), + ) + return +end + +function test_change_col_bounds_by_set_invalid() + model = HiGHS.Optimizer() + MOI.set(model, MOI.Silent(), true) + x = MOI.add_variable(model) + c = MOI.add_constraint(model, x, MOI.GreaterThan(0.0)) + c_invalid = typeof(c)(-123456) + sets = MOI.GreaterThan.(1.0:2.0) + @test_throws( + MOI.InvalidIndex(c_invalid), + MOI.set(model, MOI.ConstraintSet(), [c, c_invalid], sets), + ) + return +end + +function test_change_col_bounds_by_set_greater_than() + model = HiGHS.Optimizer() + MOI.set(model, MOI.Silent(), true) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint.(model, x, MOI.GreaterThan.(1.0:3.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + f = 1.0 * x[1] + x[2] + x[3] + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 6; atol = 1e-6) + MOI.set( + model, + MOI.ConstraintSet(), + [c[1], c[3]], + MOI.GreaterThan.([4.0, 5.0]), + ) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 11; atol = 1e-6) + @test MOI.get(model, MOI.ConstraintSet(), c) == + MOI.GreaterThan.([4.0, 2.0, 5.0]) + return +end + +function test_change_col_bounds_by_set_less_than() + model = HiGHS.Optimizer() + MOI.set(model, MOI.Silent(), true) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint.(model, x, MOI.LessThan.(1.0:3.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + f = 1.0 * x[1] + x[2] + x[3] + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 6; atol = 1e-6) + MOI.set(model, MOI.ConstraintSet(), [c[1], c[3]], MOI.LessThan.([4.0, 5.0])) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 11; atol = 1e-6) + @test MOI.get(model, MOI.ConstraintSet(), c) == + MOI.LessThan.([4.0, 2.0, 5.0]) + return +end + +function test_change_col_bounds_by_set_equal_to() + model = HiGHS.Optimizer() + MOI.set(model, MOI.Silent(), true) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint.(model, x, MOI.EqualTo.(1.0:3.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + f = 1.0 * x[1] + x[2] + x[3] + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 6; atol = 1e-6) + MOI.set(model, MOI.ConstraintSet(), [c[1], c[3]], MOI.EqualTo.([4.0, 5.0])) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 11; atol = 1e-6) + @test MOI.get(model, MOI.ConstraintSet(), c) == + MOI.EqualTo.([4.0, 2.0, 5.0]) + return +end + +function test_change_row_bounds_by_set_dimension_mismatch() + model = HiGHS.Optimizer() + MOI.set(model, MOI.Silent(), true) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint.(model, 1.0 .* x, MOI.GreaterThan.(1.0:3.0)) + @test_throws( + DimensionMismatch, + MOI.set(model, MOI.ConstraintSet(), c, MOI.GreaterThan.([4.0, 5.0])), + ) + return +end + +function test_change_row_bounds_by_set_invalid() + model = HiGHS.Optimizer() + MOI.set(model, MOI.Silent(), true) + x = MOI.add_variable(model) + c = MOI.add_constraint(model, 1.0 .* x, MOI.GreaterThan(0.0)) + c_invalid = typeof(c)(-123456) + sets = MOI.GreaterThan.(1.0:2.0) + @test_throws( + MOI.InvalidIndex(c_invalid), + MOI.set(model, MOI.ConstraintSet(), [c, c_invalid], sets), + ) + return +end + +function test_change_row_bounds_by_set_greater_than() + model = HiGHS.Optimizer() + MOI.set(model, MOI.Silent(), true) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint.(model, 1.0 .* x, MOI.GreaterThan.(1.0:3.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE) + f = 1.0 * x[1] + x[2] + x[3] + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 6; atol = 1e-6) + MOI.set( + model, + MOI.ConstraintSet(), + [c[1], c[3]], + MOI.GreaterThan.([4.0, 5.0]), + ) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 11; atol = 1e-6) + @test MOI.get(model, MOI.ConstraintSet(), c) == + MOI.GreaterThan.([4.0, 2.0, 5.0]) + return +end + +function test_change_row_bounds_by_set_less_than() + model = HiGHS.Optimizer() + MOI.set(model, MOI.Silent(), true) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint.(model, 1.0 .* x, MOI.LessThan.(1.0:3.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + f = 1.0 * x[1] + x[2] + x[3] + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 6; atol = 1e-6) + MOI.set(model, MOI.ConstraintSet(), [c[1], c[3]], MOI.LessThan.([4.0, 5.0])) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 11; atol = 1e-6) + @test MOI.get(model, MOI.ConstraintSet(), c) == + MOI.LessThan.([4.0, 2.0, 5.0]) + return +end + +function test_change_row_bounds_by_set_equal_to() + model = HiGHS.Optimizer() + MOI.set(model, MOI.Silent(), true) + x = MOI.add_variables(model, 3) + c = MOI.add_constraint.(model, 1.0 .* x, MOI.EqualTo.(1.0:3.0)) + MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE) + f = 1.0 * x[1] + x[2] + x[3] + MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 6; atol = 1e-6) + MOI.set(model, MOI.ConstraintSet(), [c[1], c[3]], MOI.EqualTo.([4.0, 5.0])) + MOI.optimize!(model) + @test ≈(MOI.get(model, MOI.ObjectiveValue()), 11; atol = 1e-6) + @test MOI.get(model, MOI.ConstraintSet(), c) == + MOI.EqualTo.([4.0, 2.0, 5.0]) + return +end + end TestMOIHighs.runtests()