From c6d8dad3d9d5bcf65a18a7a6a888250c4c2e0066 Mon Sep 17 00:00:00 2001 From: odow Date: Fri, 19 Apr 2024 10:59:28 +1200 Subject: [PATCH] [FileFormats.MPS] fix issue #2479 --- src/FileFormats/MPS/MPS.jl | 17 ++++++++ test/FileFormats/MPS/MPS.jl | 39 +++++++++++++++++++ .../MPS/integer_default_bounds_LI.mps | 12 ++++++ .../MPS/integer_default_bounds_MI.mps | 12 ++++++ .../MPS/integer_default_bounds_PL.mps | 12 ++++++ 5 files changed, 92 insertions(+) create mode 100644 test/FileFormats/MPS/integer_default_bounds_LI.mps create mode 100644 test/FileFormats/MPS/integer_default_bounds_MI.mps create mode 100644 test/FileFormats/MPS/integer_default_bounds_PL.mps diff --git a/src/FileFormats/MPS/MPS.jl b/src/FileFormats/MPS/MPS.jl index 21018a81c1..165e9cae6f 100644 --- a/src/FileFormats/MPS/MPS.jl +++ b/src/FileFormats/MPS/MPS.jl @@ -1024,6 +1024,7 @@ mutable struct TempMPSModel obj_constant::Float64 col_lower::Vector{Float64} col_upper::Vector{Float64} + col_bounds_default::Vector{Bool} row_lower::Vector{Float64} row_upper::Vector{Float64} sense::Vector{Sense} @@ -1050,6 +1051,7 @@ function TempMPSModel() 0.0, # obj_constant Float64[], # col_lower Float64[], # col_upper + Bool[], # col_bounds_default Float64[], # row_lower Float64[], # row_upper Sense[], # sense @@ -1465,6 +1467,7 @@ function _add_new_column(data, column_name) push!(data.c, 0.0) push!(data.col_lower, 0.0) push!(data.col_upper, Inf) + push!(data.col_bounds_default, true) push!(data.vtype, VTYPE_CONTINUOUS) return end @@ -1641,6 +1644,13 @@ function _parse_single_bound(data, column_name::String, bound_type::String) if col === nothing error("Column name $(column_name) not found.") end + if data.col_bounds_default[col] && data.vtype[col] == VTYPE_INTEGER + # This column was part of an INTORG...INTEND block, so it gets a default + # bound of [0, 1]. However, since it now has a bound, it reverts to a + # default of [0, inf). + data.col_upper[col] = Inf + end + data.col_bounds_default[col] = false if bound_type == "PL" data.col_upper[col] = Inf elseif bound_type == "MI" @@ -1667,6 +1677,13 @@ function _parse_single_bound( if col === nothing error("Column name $(column_name) not found.") end + if data.col_bounds_default[col] && data.vtype[col] == VTYPE_INTEGER + # This column was part of an INTORG...INTEND block, so it gets a default + # bound of [0, 1]. However, since it now has a bound, it reverts to a + # default of [0, inf). + data.col_upper[col] = Inf + end + data.col_bounds_default[col] = false if bound_type == "FX" data.col_lower[col] = value data.col_upper[col] = value diff --git a/test/FileFormats/MPS/MPS.jl b/test/FileFormats/MPS/MPS.jl index 47a525fbfc..46a72c9b37 100644 --- a/test/FileFormats/MPS/MPS.jl +++ b/test/FileFormats/MPS/MPS.jl @@ -200,6 +200,45 @@ function test_integer_default_bounds() return end +function test_integer_default_bounds_LI() + model = MPS.Model() + filename = joinpath(@__DIR__, "integer_default_bounds_LI.mps") + MOI.read_from_file(model, filename) + x = only(MOI.get(model, MOI.ListOfVariableIndices())) + c_types = MOI.get(model, MOI.ListOfConstraintTypesPresent()) + @test length(c_types) == 2 + @test (MOI.VariableIndex, MOI.Integer) in c_types + F, S = MOI.VariableIndex, MOI.GreaterThan{Float64} + @test (F, S) in c_types + ci = MOI.ConstraintIndex{F,S}(x.value) + @test MOI.get(model, MOI.ConstraintSet(), ci) == MOI.GreaterThan(1.0) + return +end + +function test_integer_default_bounds_MI() + model = MPS.Model() + filename = joinpath(@__DIR__, "integer_default_bounds_MI.mps") + MOI.read_from_file(model, filename) + @test MOI.get(model, MOI.ListOfConstraintTypesPresent()) == + [(MOI.VariableIndex, MOI.Integer)] + return +end + +function test_integer_default_bounds_PL() + model = MPS.Model() + filename = joinpath(@__DIR__, "integer_default_bounds_PL.mps") + MOI.read_from_file(model, filename) + x = only(MOI.get(model, MOI.ListOfVariableIndices())) + c_types = MOI.get(model, MOI.ListOfConstraintTypesPresent()) + @test length(c_types) == 2 + @test (MOI.VariableIndex, MOI.Integer) in c_types + F, S = MOI.VariableIndex, MOI.GreaterThan{Float64} + @test (F, S) in c_types + ci = MOI.ConstraintIndex{F,S}(x.value) + @test MOI.get(model, MOI.ConstraintSet(), ci) == MOI.GreaterThan(0.0) + return +end + function test_free_integer() model = MPS.Model() MOI.read_from_file(model, joinpath(@__DIR__, "free_integer.mps")) diff --git a/test/FileFormats/MPS/integer_default_bounds_LI.mps b/test/FileFormats/MPS/integer_default_bounds_LI.mps new file mode 100644 index 0000000000..9ef3975de2 --- /dev/null +++ b/test/FileFormats/MPS/integer_default_bounds_LI.mps @@ -0,0 +1,12 @@ +NAME +OBJSENSE MIN +ROWS + N obj +COLUMNS + MARKER 'MARKER' 'INTORG' + x obj 1 + MARKER 'MARKER' 'INTEND' +RHS +BOUNDS + LI BND1 x 1 +ENDATA diff --git a/test/FileFormats/MPS/integer_default_bounds_MI.mps b/test/FileFormats/MPS/integer_default_bounds_MI.mps new file mode 100644 index 0000000000..5058efd630 --- /dev/null +++ b/test/FileFormats/MPS/integer_default_bounds_MI.mps @@ -0,0 +1,12 @@ +NAME +OBJSENSE MIN +ROWS + N obj +COLUMNS + MARKER 'MARKER' 'INTORG' + x obj 1 + MARKER 'MARKER' 'INTEND' +RHS +BOUNDS + MI BND1 x +ENDATA diff --git a/test/FileFormats/MPS/integer_default_bounds_PL.mps b/test/FileFormats/MPS/integer_default_bounds_PL.mps new file mode 100644 index 0000000000..7aad9c1408 --- /dev/null +++ b/test/FileFormats/MPS/integer_default_bounds_PL.mps @@ -0,0 +1,12 @@ +NAME +OBJSENSE MIN +ROWS + N obj +COLUMNS + MARKER 'MARKER' 'INTORG' + x obj 1 + MARKER 'MARKER' 'INTEND' +RHS +BOUNDS + PL BND1 x +ENDATA