From f82c7e94acbdbaf059e45e7da06147f19816a2ed Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Tue, 8 Oct 2024 09:25:17 +1300 Subject: [PATCH] [FileFormats] read double-sided variable bounds separately (#2548) --- src/FileFormats/LP/LP.jl | 5 ++++- src/FileFormats/MPS/MPS.jl | 7 ++++++- test/FileFormats/LP/LP.jl | 19 +++++++++++++++---- test/FileFormats/MPS/MPS.jl | 19 ++++++++++++++----- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/FileFormats/LP/LP.jl b/src/FileFormats/LP/LP.jl index 3c9dd7a128..d6b447c78d 100644 --- a/src/FileFormats/LP/LP.jl +++ b/src/FileFormats/LP/LP.jl @@ -916,7 +916,10 @@ function _parse_section( return elseif -Inf < lb < ub < Inf _delete_default_lower_bound_if_present(model, cache, x) - MOI.add_constraint(model, x, MOI.Interval(lb, ub)) + # Do not add MOI.Interval constraints because we want to follow + # JuMP's convention of adding separate lower and upper bounds. + MOI.add_constraint(model, x, MOI.GreaterThan(lb)) + MOI.add_constraint(model, x, MOI.LessThan(ub)) return elseif lb == -Inf _delete_default_lower_bound_if_present(model, cache, x) diff --git a/src/FileFormats/MPS/MPS.jl b/src/FileFormats/MPS/MPS.jl index e1b2267dad..263e29ed26 100644 --- a/src/FileFormats/MPS/MPS.jl +++ b/src/FileFormats/MPS/MPS.jl @@ -1287,7 +1287,12 @@ function _add_variable(model, data, variable_map, i, name) variable_map[name] = x MOI.set(model, MOI.VariableName(), x, name) set = bounds_to_set(data.col_lower[i], data.col_upper[i]) - if set !== nothing + if set isa MOI.Interval + # Do not add MOI.Interval constraints because we want to follow JuMP's + # convention of adding separate lower and upper bounds. + MOI.add_constraint(model, x, MOI.GreaterThan(set.lower::Float64)) + MOI.add_constraint(model, x, MOI.LessThan(set.upper::Float64)) + elseif set !== nothing MOI.add_constraint(model, x, set) end if data.vtype[i] == VTYPE_INTEGER diff --git a/test/FileFormats/LP/LP.jl b/test/FileFormats/LP/LP.jl index 0a1f8a9dbb..37ad33c699 100644 --- a/test/FileFormats/LP/LP.jl +++ b/test/FileFormats/LP/LP.jl @@ -9,7 +9,8 @@ module TestLP using Test import MathOptInterface as MOI -const LP = MOI.FileFormats.LP +import MathOptInterface.FileFormats: LP + const LP_TEST_FILE = "test.lp" function test_show() @@ -464,7 +465,8 @@ function test_read_example_lo1() @test (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) in constraints @test (MOI.VariableIndex, MOI.GreaterThan{Float64}) in constraints - @test (MOI.VariableIndex, MOI.Interval{Float64}) in constraints + @test (MOI.VariableIndex, MOI.LessThan{Float64}) in constraints + @test !((MOI.VariableIndex, MOI.Interval{Float64}) in constraints) io = IOBuffer() write(io, model) seekstart(io) @@ -525,7 +527,8 @@ function test_read_model1() @test (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) in constraints @test (MOI.VariableIndex, MOI.GreaterThan{Float64}) in constraints - @test (MOI.VariableIndex, MOI.Interval{Float64}) in constraints + @test (MOI.VariableIndex, MOI.LessThan{Float64}) in constraints + @test !((MOI.VariableIndex, MOI.Interval{Float64}) in constraints) @test (MOI.VariableIndex, MOI.Integer) in constraints @test (MOI.VariableIndex, MOI.ZeroOne) in constraints @test (MOI.VectorOfVariables, MOI.SOS1{Float64}) in constraints @@ -543,9 +546,17 @@ function test_read_model2() @test (MOI.ScalarAffineFunction{Float64}, MOI.LessThan{Float64}) in constraints @test (MOI.VariableIndex, MOI.GreaterThan{Float64}) in constraints - @test (MOI.VariableIndex, MOI.Interval{Float64}) in constraints + @test (MOI.VariableIndex, MOI.LessThan{Float64}) in constraints + @test !((MOI.VariableIndex, MOI.Interval{Float64}) in constraints) @test (MOI.VariableIndex, MOI.Integer) in constraints @test (MOI.VariableIndex, MOI.ZeroOne) in constraints + @test MOI.get(model, MOI.VariableName(), MOI.VariableIndex(2)) == "V5" + ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}(2) + @test MOI.get(model, MOI.ConstraintSet(), ci) == MOI.LessThan(1.0) + ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(2) + @test MOI.get(model, MOI.ConstraintSet(), ci) == MOI.GreaterThan(0.0) + ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{Float64}}(2) + @test !MOI.is_valid(model, ci) @test MOI.get(model, MOI.VariableName(), MOI.VariableIndex(8)) == "V8" @test model.variables.lower[8] == -Inf @test model.variables.upper[8] == -3 diff --git a/test/FileFormats/MPS/MPS.jl b/test/FileFormats/MPS/MPS.jl index 8081411113..fafddbb147 100644 --- a/test/FileFormats/MPS/MPS.jl +++ b/test/FileFormats/MPS/MPS.jl @@ -170,7 +170,8 @@ function test_stacked_data() con3: 1.0 * x in Interval(3.0, 7.0) con4: 2.0 * x in Interval(4.0, 8.0) y in Integer() - y in Interval(1.0, 4.0) + y >= 1.0 + y <= 4.0 z in ZeroOne() """, ) @@ -182,7 +183,8 @@ function test_stacked_data() ["blank_obj", "con1", "con2", "con3", "con4"], [ ("y", MOI.Integer()), - ("y", MOI.Interval{Float64}(1.0, 4.0)), + ("y", MOI.GreaterThan{Float64}(1.0)), + ("y", MOI.LessThan{Float64}(4.0)), ("z", MOI.ZeroOne()), ], ) @@ -193,8 +195,13 @@ function test_integer_default_bounds() model = MPS.Model() MOI.read_from_file(model, joinpath(@__DIR__, "integer_default_bounds.mps")) x = only(MOI.get(model, MOI.ListOfVariableIndices())) + ci = + MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(x.value) + @test MOI.get(model, MOI.ConstraintSet(), ci) == MOI.GreaterThan(0.0) + ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}(x.value) + @test MOI.get(model, MOI.ConstraintSet(), ci) == MOI.LessThan(1.0) ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{Float64}}(x.value) - @test MOI.get(model, MOI.ConstraintSet(), ci) == MOI.Interval(0.0, 1.0) + @test !MOI.is_valid(model, ci) return end @@ -482,7 +489,8 @@ function test_nonzero_variable_bounds() x == 1.0 y >= 2.0 z <= 3.0 - w in Interval(4.0, 5.0) + w >= 4.0 + w <= 5.0 """, ) io = IOBuffer() @@ -499,7 +507,8 @@ function test_nonzero_variable_bounds() ("x", MOI.EqualTo{Float64}(1.0)), ("y", MOI.GreaterThan{Float64}(2.0)), ("z", MOI.LessThan{Float64}(3.0)), - ("w", MOI.Interval{Float64}(4.0, 5.0)), + ("w", MOI.GreaterThan{Float64}(4.0)), + ("w", MOI.LessThan{Float64}(5.0)), ], ) return