Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eeb7bb6

Browse files
authored
Merge pull request #52 from COPT-Public/multi_objective_dev
add multi-objective support
2 parents 31a2e1b + edfceeb commit eeb7bb6

File tree

5 files changed

+416
-0
lines changed

5 files changed

+416
-0
lines changed

src/COPT.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ end
102102
include("MOI/MOI_wrapper.jl")
103103
include("MOI/indicator_constraint.jl")
104104
include("MOI/callback.jl")
105+
include("MOI/multi_objective.jl")
105106

106107
# COPT exports all `COPT_xxx` and `copt_xxx` symbols. If you don't want all of
107108
# these symbols in your environment, then use `import COPT` instead of

src/MOI/MOI_wrapper.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ include("dual_exp_cone_bridge.jl")
3232
_SCALAR_AFFINE,
3333
_SCALAR_QUADRATIC,
3434
_UNSET_OBJECTIVE,
35+
_VECTOR_AFFINE
3536
)
3637

3738
@enum(
@@ -3393,6 +3394,10 @@ end
33933394
function MOI.get(model::Optimizer, attr::MOI.ObjectiveValue)
33943395
_throw_if_optimize_in_progress(model, attr)
33953396
MOI.check_result_index_bounds(model, attr)
3397+
N = MOI.get(model, NumberOfObjectives())
3398+
if N > 1
3399+
return [MOI.get(model, MultiObjectiveValue(i)) for i in 1:N]
3400+
end
33963401
return _copt_get_dbl_attr(
33973402
model,
33983403
model.solved_as_mip ? "BestObj" : "LpObjval",
@@ -3663,6 +3668,8 @@ function MOI.get(model::Optimizer, ::MOI.ObjectiveFunctionType)
36633668
return MOI.VariableIndex
36643669
elseif model.objective_type == _SCALAR_AFFINE
36653670
return MOI.ScalarAffineFunction{Float64}
3671+
elseif model.objective_type == _VECTOR_AFFINE
3672+
return MOI.VectorAffineFunction{Float64}
36663673
else
36673674
@assert model.objective_type == _SCALAR_QUADRATIC
36683675
return MOI.ScalarQuadraticFunction{Float64}

src/MOI/multi_objective.jl

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
# Copyright (c) 2015: Joey Huchette and contributors
2+
# Copyright (c) 2025: COPT-Public
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
8+
function _copt_get_int_attr_multi_obj(model::Optimizer, index::Int, name::String)
9+
p_value = Ref{Cint}()
10+
ret = COPT_MultiObjGetIntAttr(model.prob, Cint(index - 1), name, p_value)
11+
_check_ret(model, ret)
12+
return p_value[]
13+
end
14+
15+
function _copt_get_dbl_attr_multi_obj(model::Optimizer, index::Int, name::String)
16+
p_value = Ref{Cdouble}()
17+
ret = COPT_MultiObjGetDblAttr(model.prob, Cint(index - 1), name, p_value)
18+
_check_ret(model, ret)
19+
return p_value[]
20+
end
21+
22+
# ==============================================================================
23+
# objective function
24+
# ==============================================================================
25+
struct MultiObjectiveFunction <: MOI.AbstractModelAttribute
26+
index::Int
27+
end
28+
29+
function MOI.set(
30+
model::Optimizer,
31+
attr::MultiObjectiveFunction,
32+
f::MOI.ScalarAffineFunction,
33+
)
34+
num_vars = length(model.variable_info)
35+
obj = zeros(Float64, num_vars)
36+
for term in f.terms
37+
col = column(model, term.variable)
38+
obj[col] += term.coefficient
39+
end
40+
ret = COPT_MultiObjSetColObj(model.prob, Cint(attr.index - 1), num_vars, C_NULL, obj)
41+
_check_ret(model, ret)
42+
ret = COPT_MultiObjSetObjConst(model.prob, Cint(attr.index - 1), f.constant)
43+
_check_ret(model, ret)
44+
return
45+
end
46+
47+
function _zero_multiobjective(model::Optimizer, attr::MultiObjectiveFunction)
48+
num_vars = length(model.variable_info)
49+
# COPT returns an error when calling COPT_SetColObj() with no columns.
50+
if num_vars > 0
51+
obj = zeros(Float64, num_vars)
52+
ret = COPT_MultiObjSetColObj(model.prob, Cint(attr.index - 1), num_vars, C_NULL, obj)
53+
_check_ret(model, ret)
54+
end
55+
ret = COPT_MultiObjSetObjConst(model.prob, Cint(attr.index - 1), f.constant)
56+
_check_ret(model, ret)
57+
return
58+
end
59+
60+
function MOI.set(model::Optimizer, attr::MultiObjectiveFunction, ::MOI.ObjectiveSense, sense::MOI.OptimizationSense)
61+
ret = if sense == MOI.MIN_SENSE
62+
COPT_MultiObjSetObjSense(model.prob, Cint(attr.index - 1), COPT_MINIMIZE)
63+
elseif sense == MOI.MAX_SENSE
64+
COPT_MultiObjSetObjSense(model.prob, Cint(attr.index - 1), COPT_MAXIMIZE)
65+
else
66+
@assert sense == MOI.FEASIBILITY_SENSE
67+
_zero_multiobjective(model, attr)
68+
COPT_SetObjSense(model.prob, COPT_MINIMIZE)
69+
end
70+
_check_ret(model, ret)
71+
return
72+
end
73+
74+
function MOI.get(model::Optimizer, attr::MultiObjectiveFunction, ::MOI.ObjectiveSense)
75+
objective_sense = _copt_get_int_attr_multi_obj(model, attr.index, "ObjSense")
76+
if objective_sense == COPT_MINIMIZE
77+
return MOI.MIN_SENSE
78+
else
79+
return MOI.MAX_SENSE
80+
end
81+
end
82+
83+
function MOI.supports(
84+
model::Optimizer,
85+
::MOI.ObjectiveFunction{MOI.VectorAffineFunction{Float64}},
86+
)
87+
return true
88+
end
89+
90+
function MOI.set(
91+
model::Optimizer,
92+
::MOI.ObjectiveFunction{F},
93+
f::F,
94+
) where {F<:MOI.VectorAffineFunction{Float64}}
95+
for (i, fi) in enumerate(MOI.Utilities.eachscalar(f))
96+
MOI.set(model, MultiObjectiveFunction(i), fi)
97+
end
98+
model.objective_type = _VECTOR_AFFINE
99+
return
100+
end
101+
102+
function _get_multiobj_linear_part(model::Optimizer, index::Int)
103+
num_col = length(model.variable_info)
104+
values = Array{Cdouble}(undef, num_col)
105+
ret = COPT_MultiObjGetColObj(model.prob, Cint(index - 1), num_col, C_NULL, values)
106+
_check_ret(model, ret)
107+
return values
108+
end
109+
110+
function MOI.get(
111+
model::Optimizer,
112+
::MOI.ObjectiveFunction{MOI.VectorAffineFunction{Float64}},
113+
)
114+
F = MOI.ScalarAffineFunction{Float64}
115+
f = F[]
116+
for i in 1:MOI.get(model, NumberOfObjectives())
117+
coefficients = _get_multiobj_linear_part(model, i)
118+
terms = MOI.ScalarAffineTerm{Float64}[]
119+
for (index, info) in model.variable_info
120+
coefficient = coefficients[info.column]
121+
if !iszero(coefficient)
122+
push!(terms, MOI.ScalarAffineTerm(coefficient, index))
123+
end
124+
end
125+
constant = _copt_get_dbl_attr_multi_obj(model, i, "ObjConst")
126+
cur_obj = MOI.ScalarAffineFunction(terms, constant[])
127+
push!(f, cur_obj)
128+
end
129+
return MOI.Utilities.operate(vcat, Float64, f...)
130+
end
131+
132+
133+
# ==============================================================================
134+
# model-related parameters and attributes
135+
# ==============================================================================
136+
struct NumberOfObjectives <: MOI.AbstractModelAttribute end
137+
138+
function MOI.get(model::Optimizer, ::NumberOfObjectives)
139+
return _copt_get_int_attr(model, "MultiObjs")
140+
end
141+
142+
struct MultiObjTimeLimit <: MOI.AbstractModelAttribute end
143+
144+
function MOI.set(model::Optimizer, ::MultiObjTimeLimit, value::Real)
145+
MOI.set(model, MOI.RawOptimizerAttribute("MultiObjTimeLimit"), value)
146+
return
147+
end
148+
149+
function MOI.get(model::Optimizer, ::MultiObjTimeLimit)
150+
return MOI.get(model, MOI.RawOptimizerAttribute("MultiObjTimeLimit"))
151+
end
152+
153+
struct MultiObjParamMode <: MOI.AbstractModelAttribute end
154+
155+
function MOI.set(model::Optimizer, ::MultiObjParamMode, value::Int)
156+
MOI.set(model, MOI.RawOptimizerAttribute("MultiObjParamMode"), value)
157+
return
158+
end
159+
160+
function MOI.get(model::Optimizer, ::MultiObjParamMode)
161+
return MOI.get(model, MOI.RawOptimizerAttribute("MultiObjParamMode"))
162+
end
163+
164+
165+
# ==============================================================================
166+
# objective-related parameters
167+
# ==============================================================================
168+
struct MultiObjectiveParams <: MOI.AbstractModelAttribute
169+
index::Int
170+
name::String
171+
end
172+
173+
function MOI.set(model::Optimizer, attr::MultiObjectiveParams, value)
174+
if (attr.name == COPT_MULTIOBJ_PRIORITY || attr.name == COPT_MULTIOBJ_WEIGHT ||
175+
attr.name == COPT_MULTIOBJ_ABSTOL || attr.name == COPT_MULTIOBJ_RELTOL)
176+
ret = COPT_MultiObjSetObjParam(model.prob, Cint(attr.index - 1), attr.name, value)
177+
_check_ret(model, ret)
178+
return
179+
end
180+
181+
param_type = _search_param_attr(model, attr.name)
182+
if param_type == 0
183+
ret = COPT_MultiObjSetDblParam(model.prob, Cint(attr.index - 1), attr.name, value)
184+
_check_ret(model, ret)
185+
elseif param_type == 1
186+
ret = COPT_MultiObjSetIntParam(model.prob, Cint(attr.index - 1), attr.name, value)
187+
_check_ret(model, ret)
188+
else
189+
throw(MOI.UnsupportedAttribute(attr.name))
190+
end
191+
return
192+
end
193+
194+
function MOI.get(model::Optimizer, attr::MultiObjectiveParams)
195+
if (attr.name == COPT_MULTIOBJ_PRIORITY || attr.name == COPT_MULTIOBJ_WEIGHT ||
196+
attr.name == COPT_MULTIOBJ_ABSTOL || attr.name == COPT_MULTIOBJ_RELTOL)
197+
p_value = Ref{Cdouble}()
198+
ret = COPT_MultiObjGetObjParam(model.prob, Cint(attr.index - 1), attr.name, p_value)
199+
_check_ret(model, ret)
200+
return p_value[]
201+
end
202+
203+
param_type = _search_param_attr(model, attr.name)
204+
if param_type == 0
205+
p_value = Ref{Cdouble}()
206+
ret = COPT_MultiObjGetDblParam(model.prob, Cint(attr.index - 1), attr.name, p_value)
207+
_check_ret(model, ret)
208+
return p_value[]
209+
elseif param_type == 1
210+
p_value = Ref{Cint}()
211+
ret = COPT_MultiObjGetIntParam(model.prob, Cint(attr.index - 1), attr.name, p_value)
212+
_check_ret(model, ret)
213+
return p_value[]
214+
else
215+
throw(MOI.UnsupportedAttribute(attr.name))
216+
end
217+
end
218+
219+
struct MultiObjectivePriority <: MOI.AbstractModelAttribute
220+
index::Int
221+
end
222+
223+
function MOI.set(model::Optimizer, attr::MultiObjectivePriority, value::Real)
224+
MOI.set(model, MultiObjectiveParams(attr.index, "MultiObjPriority"), value)
225+
return
226+
end
227+
228+
function MOI.get(model::Optimizer, attr::MultiObjectivePriority)
229+
return MOI.get(model, MultiObjectiveParams(attr.index, "MultiObjPriority"))
230+
end
231+
232+
struct MultiObjectiveWeight <: MOI.AbstractModelAttribute
233+
index::Int
234+
end
235+
236+
function MOI.set(model::Optimizer, attr::MultiObjectiveWeight, weight::Real)
237+
MOI.set(model, MultiObjectiveParams(attr.index, "MultiObjWeight"), weight)
238+
return
239+
end
240+
241+
function MOI.get(model::Optimizer, attr::MultiObjectiveWeight)
242+
return MOI.get(model, MultiObjectiveParams(attr.index, "MultiObjWeight"))
243+
end
244+
245+
struct MultiObjectiveAbsTol <: MOI.AbstractModelAttribute
246+
index::Int
247+
end
248+
249+
function MOI.set(model::Optimizer, attr::MultiObjectiveAbsTol, weight::Real)
250+
MOI.set(model, MultiObjectiveParams(attr.index, "MultiObjAbsTol"), weight)
251+
return
252+
end
253+
254+
function MOI.get(model::Optimizer, attr::MultiObjectiveAbsTol)
255+
return MOI.get(model, MultiObjectiveParams(attr.index, "MultiObjAbsTol"))
256+
end
257+
258+
struct MultiObjectiveRelTol <: MOI.AbstractModelAttribute
259+
index::Int
260+
end
261+
262+
function MOI.set(model::Optimizer, attr::MultiObjectiveRelTol, weight::Real)
263+
MOI.set(model, MultiObjectiveParams(attr.index, "MultiObjRelTol"), weight)
264+
return
265+
end
266+
267+
function MOI.get(model::Optimizer, attr::MultiObjectiveRelTol)
268+
return MOI.get(model, MultiObjectiveParams(attr.index, "MultiObjRelTol"))
269+
end
270+
271+
272+
# ==============================================================================
273+
# objective-related attributes
274+
# ==============================================================================
275+
276+
struct MultiObjectiveValue <: MOI.AbstractModelAttribute
277+
index::Int
278+
end
279+
280+
function MOI.get(model::Optimizer, attr::MultiObjectiveValue)
281+
return _copt_get_dbl_attr_multi_obj(model, attr.index, model.solved_as_mip ? "BestObj" : "LpObjval")
282+
end

test/MathOptInterface/MOI_wrapper.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ function test_runtests()
7474
# New tests for function modifcation, https://github.com/jump-dev/MathOptInterface.jl/pull/2328
7575
"test_basic_VectorAffineFunction_Indicator_LessThan",
7676
"test_basic_VectorAffineFunction_Indicator_GreaterThan",
77+
# COPT does not support delete variables in multi-objective Optimization
78+
"test_multiobjective_vector_affine_function_delete",
79+
"test_multiobjective_vector_quadratic_function_delete",
80+
"test_multiobjective_vector_of_variables_delete"
7781
],
7882
)
7983
return

0 commit comments

Comments
 (0)