Skip to content

Commit 68228da

Browse files
authored
[FileFormats.MPS] fix MAX_SENSE with quadratic objective (#2539)
1 parent cb1ad41 commit 68228da

File tree

2 files changed

+33
-12
lines changed

2 files changed

+33
-12
lines changed

src/FileFormats/MPS/MPS.jl

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ function Base.write(io::IO, model::Model)
237237
write_rhs(io, model, obj_const)
238238
write_ranges(io, model)
239239
write_bounds(io, model, var_to_column)
240-
write_quadobj(io, model, var_to_column)
240+
write_quadobj(io, model, flip_obj, var_to_column)
241241
if options.quadratic_format != kQuadraticFormatCPLEX
242242
# Gurobi needs qcons _after_ quadobj and _before_ SOS.
243243
write_quadcons(io, model, var_to_column)
@@ -805,7 +805,7 @@ end
805805
# QUADRATIC OBJECTIVE
806806
# ==============================================================================
807807

808-
function write_quadobj(io::IO, model::Model, var_to_column)
808+
function write_quadobj(io::IO, model::Model, flip_obj::Bool, var_to_column)
809809
f = _get_objective(model)
810810
if isempty(f.quadratic_terms)
811811
return
@@ -822,6 +822,7 @@ function write_quadobj(io::IO, model::Model, var_to_column)
822822
_write_q_matrix(
823823
io,
824824
model,
825+
flip_obj,
825826
f,
826827
var_to_column;
827828
duplicate_off_diagonal = options.quadratic_format ==
@@ -833,6 +834,7 @@ end
833834
function _write_q_matrix(
834835
io::IO,
835836
model::Model,
837+
flip_obj::Bool,
836838
f,
837839
var_to_column;
838840
duplicate_off_diagonal::Bool,
@@ -861,15 +863,13 @@ function _write_q_matrix(
861863
)
862864
x_name = _var_name(model, x, var_to_column[x], options.generic_names)
863865
y_name = _var_name(model, y, var_to_column[y], options.generic_names)
864-
println(
865-
io,
866-
Card(f2 = x_name, f3 = y_name, f4 = _to_string(terms[(x, y)])),
867-
)
866+
coef = terms[(x, y)]
867+
if flip_obj
868+
coef *= -1
869+
end
870+
println(io, Card(f2 = x_name, f3 = y_name, f4 = _to_string(coef)))
868871
if x != y && duplicate_off_diagonal
869-
println(
870-
io,
871-
Card(f2 = y_name, f3 = x_name, f4 = _to_string(terms[(x, y)])),
872-
)
872+
println(io, Card(f2 = y_name, f3 = x_name, f4 = _to_string(coef)))
873873
end
874874
end
875875
return
@@ -899,6 +899,7 @@ function write_quadcons(io::IO, model::Model, var_to_column)
899899
_write_q_matrix(
900900
io,
901901
model,
902+
false, # flip_obj
902903
f,
903904
var_to_column;
904905
duplicate_off_diagonal = options.quadratic_format !=

test/FileFormats/MPS/MPS.jl

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ module TestMPS
99
using Test
1010

1111
import MathOptInterface as MOI
12+
import MathOptInterface.FileFormats: MPS
1213
import DataStructures: OrderedDict
1314

14-
const MPS = MOI.FileFormats.MPS
15-
1615
function runtests()
1716
for name in names(@__MODULE__; all = true)
1817
if startswith("$(name)", "test_")
@@ -1249,6 +1248,27 @@ function test_binary_with_infeasible_bounds()
12491248
return
12501249
end
12511250

1251+
function test_issue_2538()
1252+
model = MPS.Model()
1253+
x = MOI.add_variables(model, 2)
1254+
f = 1.0 * x[1] + 2.0 * x[2] + 3.0 * x[1] * x[2] + 4.0 * x[2] * x[2]
1255+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
1256+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
1257+
io = IOBuffer()
1258+
write(io, model)
1259+
model_2 = MPS.Model()
1260+
seekstart(io)
1261+
read!(io, model_2)
1262+
MOI.get(model_2, MOI.ObjectiveSense()) == MOI.MIN_SENSE
1263+
y = MOI.get(model_2, MOI.ListOfVariableIndices())
1264+
g = MOI.get(model_2, MOI.ObjectiveFunction{typeof(f)}())
1265+
@test isapprox(
1266+
g,
1267+
-1.0 * y[1] - 2.0 * y[2] - 3.0 * y[1] * y[2] - 4.0 * y[2] * y[2],
1268+
)
1269+
return
1270+
end
1271+
12521272
end # TestMPS
12531273

12541274
TestMPS.runtests()

0 commit comments

Comments
 (0)