Skip to content

Commit ace16b5

Browse files
authored
Add support for ObjectiveFunction{VectorAffineFunction} (#263)
1 parent fbf94b5 commit ace16b5

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

src/MOI_wrapper.jl

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
270270
is_feasibility::Bool
271271
is_objective_function_set::Bool
272272
is_objective_sense_set::Bool
273+
multi_objective::Union{Nothing,MOI.VectorAffineFunction{Float64}}
273274

274275
# A flag to keep track of whether the objective is linear or quadratic.
275276
hessian::Union{Nothing,SparseArrays.SparseMatrixCSC{Float64,HighsInt}}
@@ -305,6 +306,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
305306
false,
306307
false,
307308
nothing,
309+
nothing,
308310
Set{_VariableInfo}(),
309311
_variable_info_dict(),
310312
_constraint_info_dict(),
@@ -366,6 +368,7 @@ function MOI.empty!(model::Optimizer)
366368
model.is_feasibility = true
367369
model.is_objective_function_set = false
368370
model.is_objective_sense_set = false
371+
model.multi_objective = nothing
369372
model.hessian = nothing
370373
empty!(model.binaries)
371374
empty!(model.variable_info)
@@ -871,6 +874,10 @@ function MOI.delete(model::Optimizer, v::MOI.VariableIndex)
871874
other_info.column -= 1
872875
end
873876
end
877+
if model.multi_objective !== nothing
878+
model.multi_objective =
879+
MOI.Utilities.filter_variables(!=(v), model.multi_objective)
880+
end
874881
model.name_to_variable = nothing
875882
model.name_to_constraint_index = nothing
876883
return
@@ -1017,7 +1024,9 @@ function MOI.supports(
10171024
end
10181025

10191026
function MOI.get(model::Optimizer, ::MOI.ObjectiveFunctionType)
1020-
if model.hessian === nothing
1027+
if model.multi_objective !== nothing
1028+
return MOI.VectorAffineFunction{Float64}
1029+
elseif model.hessian === nothing
10211030
return MOI.ScalarAffineFunction{Float64}
10221031
else
10231032
return MOI.ScalarQuadraticFunction{Float64}
@@ -1029,6 +1038,11 @@ function MOI.set(
10291038
::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}},
10301039
f::MOI.ScalarAffineFunction{Float64},
10311040
)
1041+
if model.multi_objective !== nothing
1042+
ret = Highs_clearLinearObjectives(model)
1043+
_check_ret(ret)
1044+
model.multi_objective = nothing
1045+
end
10321046
num_vars = HighsInt(length(model.variable_info))
10331047
obj = zeros(Float64, num_vars)
10341048
for term in f.terms
@@ -1062,6 +1076,11 @@ function MOI.set(
10621076
::MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}},
10631077
f::MOI.ScalarQuadraticFunction{Float64},
10641078
)
1079+
if model.multi_objective !== nothing
1080+
ret = Highs_clearLinearObjectives(model)
1081+
_check_ret(ret)
1082+
model.multi_objective = nothing
1083+
end
10651084
numcol = length(model.variable_info)
10661085
obj = zeros(Float64, numcol)
10671086
for term in f.affine_terms
@@ -1228,6 +1247,52 @@ function MOI.modify(
12281247
return
12291248
end
12301249

1250+
function MOI.supports(
1251+
::Optimizer,
1252+
::MOI.ObjectiveFunction{MOI.VectorAffineFunction{Float64}},
1253+
)
1254+
return true
1255+
end
1256+
1257+
function MOI.get(
1258+
model::Optimizer,
1259+
::MOI.ObjectiveFunction{MOI.VectorAffineFunction{Float64}},
1260+
)
1261+
return model.multi_objective
1262+
end
1263+
1264+
function MOI.set(
1265+
model::Optimizer,
1266+
::MOI.ObjectiveFunction{MOI.VectorAffineFunction{Float64}},
1267+
f::MOI.VectorAffineFunction{Float64},
1268+
)
1269+
num_vars = HighsInt(length(model.variable_info))
1270+
O = MOI.output_dimension(f)
1271+
obj_coefs = zeros(Float64, O * num_vars)
1272+
for term in f.terms
1273+
col = column(model, term.scalar_term.variable) + 1
1274+
obj_coefs[O*(term.output_index-1)+col] += term.scalar_term.coefficient
1275+
end
1276+
# senseP will be 1 if MIN and -1 if MAX
1277+
senseP = Ref{HighsInt}()
1278+
ret = Highs_getObjectiveSense(model, senseP)
1279+
_check_ret(ret)
1280+
ret = Highs_passLinearObjectives(
1281+
model,
1282+
O, # num_linear_objective
1283+
fill(Float64(senseP[]), O), # weight: set to -1 if maximizing
1284+
f.constants, # offset
1285+
obj_coefs, # coefficients
1286+
zeros(Float64, O), # abs_tolerance
1287+
zeros(Float64, O), # rel_tolerance
1288+
ones(Cint, O), # priority
1289+
)
1290+
_check_ret(ret)
1291+
model.multi_objective = f
1292+
model.is_objective_function_set = true
1293+
return
1294+
end
1295+
12311296
###
12321297
### VariableIndex-in-Set constraints.
12331298
###

test/MOI_wrapper.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,50 @@ function test_dual_objective_value_infeasible()
986986
return
987987
end
988988

989+
function test_ObjectiveFunction_VectorAffineFunction_to_ScalarAffineFunction()
990+
model = HiGHS.Optimizer()
991+
MOI.set(model, MOI.Silent(), true)
992+
x = MOI.add_variables(model, 2)
993+
MOI.add_constraint(model, x[1], MOI.Interval(1.0, 2.0))
994+
MOI.add_constraint(model, x[2], MOI.Interval(3.0, 4.0))
995+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
996+
f = MOI.Utilities.operate(vcat, Float64, 1.0 * x[1], 1.0 * x[2])
997+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
998+
MOI.optimize!(model)
999+
@test (MOI.get(model, MOI.ObjectiveValue()), 4.0)
1000+
g = 1.0 * x[1] - 2.0 * x[2]
1001+
MOI.set(model, MOI.ObjectiveFunction{typeof(g)}(), g)
1002+
MOI.optimize!(model)
1003+
@test (MOI.get(model, MOI.ObjectiveValue()), -7.0; atol = 1e-6)
1004+
@test (MOI.get(model, MOI.VariablePrimal(), x), [1.0, 4.0]; atol = 1e-6)
1005+
@test MOI.get(model, MOI.ObjectiveFunctionType()) ==
1006+
MOI.ScalarAffineFunction{Float64}
1007+
@test model.multi_objective === nothing
1008+
return
1009+
end
1010+
1011+
function test_ObjectiveFunction_VectorAffineFunction_to_ScalarQuadraticFunction()
1012+
model = HiGHS.Optimizer()
1013+
MOI.set(model, MOI.Silent(), true)
1014+
x = MOI.add_variables(model, 2)
1015+
MOI.add_constraint(model, x[1], MOI.Interval(1.0, 2.0))
1016+
MOI.add_constraint(model, x[2], MOI.Interval(3.0, 4.0))
1017+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
1018+
f = MOI.Utilities.operate(vcat, Float64, 1.0 * x[1], 1.0 * x[2])
1019+
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
1020+
MOI.optimize!(model)
1021+
@test (MOI.get(model, MOI.ObjectiveValue()), 4.0)
1022+
g = 1.0 * x[1] + (1.0 * x[2] * x[2] - 8.0 * x[2] + 16.0)
1023+
MOI.set(model, MOI.ObjectiveFunction{typeof(g)}(), g)
1024+
MOI.optimize!(model)
1025+
@test (MOI.get(model, MOI.ObjectiveValue()), 1.0; atol = 1e-6)
1026+
@test (MOI.get(model, MOI.VariablePrimal(), x), [1.0, 4.0]; atol = 1e-6)
1027+
@test MOI.get(model, MOI.ObjectiveFunctionType()) ==
1028+
MOI.ScalarQuadraticFunction{Float64}
1029+
@test model.multi_objective === nothing
1030+
return
1031+
end
1032+
9891033
end # module
9901034

9911035
TestMOIHighs.runtests()

0 commit comments

Comments
 (0)