Skip to content

Commit

Permalink
Add support for ObjectiveFunction{VectorAffineFunction} (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Jan 3, 2025
1 parent fbf94b5 commit ace16b5
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 1 deletion.
67 changes: 66 additions & 1 deletion src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
is_feasibility::Bool
is_objective_function_set::Bool
is_objective_sense_set::Bool
multi_objective::Union{Nothing,MOI.VectorAffineFunction{Float64}}

# A flag to keep track of whether the objective is linear or quadratic.
hessian::Union{Nothing,SparseArrays.SparseMatrixCSC{Float64,HighsInt}}
Expand Down Expand Up @@ -305,6 +306,7 @@ mutable struct Optimizer <: MOI.AbstractOptimizer
false,
false,
nothing,
nothing,
Set{_VariableInfo}(),
_variable_info_dict(),
_constraint_info_dict(),
Expand Down Expand Up @@ -366,6 +368,7 @@ function MOI.empty!(model::Optimizer)
model.is_feasibility = true
model.is_objective_function_set = false
model.is_objective_sense_set = false
model.multi_objective = nothing
model.hessian = nothing
empty!(model.binaries)
empty!(model.variable_info)
Expand Down Expand Up @@ -871,6 +874,10 @@ function MOI.delete(model::Optimizer, v::MOI.VariableIndex)
other_info.column -= 1
end
end
if model.multi_objective !== nothing
model.multi_objective =
MOI.Utilities.filter_variables(!=(v), model.multi_objective)
end
model.name_to_variable = nothing
model.name_to_constraint_index = nothing
return
Expand Down Expand Up @@ -1017,7 +1024,9 @@ function MOI.supports(
end

function MOI.get(model::Optimizer, ::MOI.ObjectiveFunctionType)
if model.hessian === nothing
if model.multi_objective !== nothing
return MOI.VectorAffineFunction{Float64}
elseif model.hessian === nothing
return MOI.ScalarAffineFunction{Float64}
else
return MOI.ScalarQuadraticFunction{Float64}
Expand All @@ -1029,6 +1038,11 @@ function MOI.set(
::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}},
f::MOI.ScalarAffineFunction{Float64},
)
if model.multi_objective !== nothing
ret = Highs_clearLinearObjectives(model)
_check_ret(ret)
model.multi_objective = nothing
end
num_vars = HighsInt(length(model.variable_info))
obj = zeros(Float64, num_vars)
for term in f.terms
Expand Down Expand Up @@ -1062,6 +1076,11 @@ function MOI.set(
::MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}},
f::MOI.ScalarQuadraticFunction{Float64},
)
if model.multi_objective !== nothing
ret = Highs_clearLinearObjectives(model)
_check_ret(ret)
model.multi_objective = nothing
end
numcol = length(model.variable_info)
obj = zeros(Float64, numcol)
for term in f.affine_terms
Expand Down Expand Up @@ -1228,6 +1247,52 @@ function MOI.modify(
return
end

function MOI.supports(
::Optimizer,
::MOI.ObjectiveFunction{MOI.VectorAffineFunction{Float64}},
)
return true
end

function MOI.get(
model::Optimizer,
::MOI.ObjectiveFunction{MOI.VectorAffineFunction{Float64}},
)
return model.multi_objective
end

function MOI.set(
model::Optimizer,
::MOI.ObjectiveFunction{MOI.VectorAffineFunction{Float64}},
f::MOI.VectorAffineFunction{Float64},
)
num_vars = HighsInt(length(model.variable_info))
O = MOI.output_dimension(f)
obj_coefs = zeros(Float64, O * num_vars)
for term in f.terms
col = column(model, term.scalar_term.variable) + 1
obj_coefs[O*(term.output_index-1)+col] += term.scalar_term.coefficient
end
# senseP will be 1 if MIN and -1 if MAX
senseP = Ref{HighsInt}()
ret = Highs_getObjectiveSense(model, senseP)
_check_ret(ret)
ret = Highs_passLinearObjectives(
model,
O, # num_linear_objective
fill(Float64(senseP[]), O), # weight: set to -1 if maximizing
f.constants, # offset
obj_coefs, # coefficients
zeros(Float64, O), # abs_tolerance
zeros(Float64, O), # rel_tolerance
ones(Cint, O), # priority
)
_check_ret(ret)
model.multi_objective = f
model.is_objective_function_set = true
return
end

###
### VariableIndex-in-Set constraints.
###
Expand Down
44 changes: 44 additions & 0 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,50 @@ function test_dual_objective_value_infeasible()
return
end

function test_ObjectiveFunction_VectorAffineFunction_to_ScalarAffineFunction()
model = HiGHS.Optimizer()
MOI.set(model, MOI.Silent(), true)
x = MOI.add_variables(model, 2)
MOI.add_constraint(model, x[1], MOI.Interval(1.0, 2.0))
MOI.add_constraint(model, x[2], MOI.Interval(3.0, 4.0))
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
f = MOI.Utilities.operate(vcat, Float64, 1.0 * x[1], 1.0 * x[2])
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
MOI.optimize!(model)
@test (MOI.get(model, MOI.ObjectiveValue()), 4.0)
g = 1.0 * x[1] - 2.0 * x[2]
MOI.set(model, MOI.ObjectiveFunction{typeof(g)}(), g)
MOI.optimize!(model)
@test (MOI.get(model, MOI.ObjectiveValue()), -7.0; atol = 1e-6)
@test (MOI.get(model, MOI.VariablePrimal(), x), [1.0, 4.0]; atol = 1e-6)
@test MOI.get(model, MOI.ObjectiveFunctionType()) ==
MOI.ScalarAffineFunction{Float64}
@test model.multi_objective === nothing
return
end

function test_ObjectiveFunction_VectorAffineFunction_to_ScalarQuadraticFunction()
model = HiGHS.Optimizer()
MOI.set(model, MOI.Silent(), true)
x = MOI.add_variables(model, 2)
MOI.add_constraint(model, x[1], MOI.Interval(1.0, 2.0))
MOI.add_constraint(model, x[2], MOI.Interval(3.0, 4.0))
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
f = MOI.Utilities.operate(vcat, Float64, 1.0 * x[1], 1.0 * x[2])
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
MOI.optimize!(model)
@test (MOI.get(model, MOI.ObjectiveValue()), 4.0)
g = 1.0 * x[1] + (1.0 * x[2] * x[2] - 8.0 * x[2] + 16.0)
MOI.set(model, MOI.ObjectiveFunction{typeof(g)}(), g)
MOI.optimize!(model)
@test (MOI.get(model, MOI.ObjectiveValue()), 1.0; atol = 1e-6)
@test (MOI.get(model, MOI.VariablePrimal(), x), [1.0, 4.0]; atol = 1e-6)
@test MOI.get(model, MOI.ObjectiveFunctionType()) ==
MOI.ScalarQuadraticFunction{Float64}
@test model.multi_objective === nothing
return
end

end # module

TestMOIHighs.runtests()

0 comments on commit ace16b5

Please sign in to comment.