Skip to content

Commit 90ddcb0

Browse files
committed
Fix missing MOI methods and update tests
1 parent 438a77b commit 90ddcb0

File tree

6 files changed

+140
-157
lines changed

6 files changed

+140
-157
lines changed

src/Interfaces/MOI/MOI_wrapper.jl

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import MathOptInterface
2-
const MOI = MathOptInterface
1+
import MathOptInterface as MOI
32

43
# ==============================================================================
54
# HELPER FUNCTIONS
@@ -84,8 +83,8 @@ Wrapper for MOI.
8483
mutable struct Optimizer{T} <: MOI.AbstractOptimizer
8584
inner::Model{T}
8685

87-
is_feas::Bool # Model is feasibility problem if true
88-
_obj_type::ObjType
86+
objective_sense::Union{Nothing,MOI.OptimizationSense}
87+
_obj_type::Union{Nothing,ObjType}
8988

9089
# Map MOI Variable/Constraint indices to internal indices
9190
var_counter::Int # Should never be reset
@@ -98,10 +97,6 @@ mutable struct Optimizer{T} <: MOI.AbstractOptimizer
9897
# Variable and constraint names
9998
name2var::Dict{String, Set{MOI.VariableIndex}}
10099
name2con::Dict{String, Set{MOI.ConstraintIndex}}
101-
# MOIIndex -> name mapping for SingleVariable constraints
102-
# Will be dropped with MOI 0.10
103-
# => (https://github.com/jump-dev/MathOptInterface.jl/issues/832)
104-
bnd2name::Dict{MOI.ConstraintIndex, String}
105100

106101
# Keep track of bound constraints
107102
var2bndtype::Dict{MOI.VariableIndex, Set{Type{<:MOI.AbstractScalarSet}}}
@@ -111,7 +106,9 @@ mutable struct Optimizer{T} <: MOI.AbstractOptimizer
111106

112107
function Optimizer{T}(;kwargs...) where{T}
113108
m = new{T}(
114-
Model{T}(), false, _SCALAR_AFFINE,
109+
Model{T}(),
110+
nothing, # objective_sense
111+
nothing, # _obj_type
115112
# Variable and constraint counters
116113
0, 0,
117114
# Index mapping
@@ -120,7 +117,6 @@ mutable struct Optimizer{T} <: MOI.AbstractOptimizer
120117
# Name -> index mapping
121118
Dict{String, Set{MOI.VariableIndex}}(),
122119
Dict{String, Set{MOI.ConstraintIndex}}(),
123-
Dict{MOI.ConstraintIndex, String}(), # Variable bounds tracking
124120
Dict{MOI.VariableIndex, Set{Type{<:MOI.AbstractScalarSet}}}(),
125121
0.0
126122
)
@@ -138,6 +134,8 @@ Optimizer(;kwargs...) = Optimizer{Float64}(;kwargs...)
138134
function MOI.empty!(m::Optimizer)
139135
# Inner model
140136
empty!(m.inner)
137+
m.objective_sense = nothing
138+
m._obj_type = nothing
141139
# Reset index mappings
142140
m.var_indices_moi = MOI.VariableIndex[]
143141
m.con_indices_moi = MOI.ConstraintIndex[]
@@ -149,14 +147,15 @@ function MOI.empty!(m::Optimizer)
149147
m.name2con = Dict{String, Set{MOI.ConstraintIndex}}()
150148

151149
# Reset bound tracking
152-
m.bnd2name = Dict{MOI.ConstraintIndex, String}()
153150
m.var2bndtype = Dict{MOI.VariableIndex, Set{MOI.ConstraintIndex}}()
154151

155152
m.solve_time = 0.0
156153
return nothing
157154
end
158155

159156
function MOI.is_empty(m::Optimizer)
157+
m.objective_sense === nothing || return false
158+
m._obj_type === nothing || return false
160159
m.inner.pbdata.nvar == 0 || return false
161160
m.inner.pbdata.ncon == 0 || return false
162161

@@ -168,7 +167,6 @@ function MOI.is_empty(m::Optimizer)
168167
length(m.name2var) == 0 || return false
169168
length(m.name2con) == 0 || return false
170169

171-
length(m.bnd2name) == 0 || return false
172170
length(m.var2bndtype) == 0 || return false
173171

174172
return true
@@ -182,8 +180,8 @@ end
182180

183181
MOI.supports_incremental_interface(::Optimizer) = true
184182

185-
function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike; kwargs...)
186-
return MOI.Utilities.default_copy_to(dest, src; kwargs...)
183+
function MOI.copy_to(dest::Optimizer, src::MOI.ModelLike)
184+
return MOI.Utilities.default_copy_to(dest, src)
187185
end
188186

189187

src/Interfaces/MOI/attributes.jl

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,25 @@ const SUPPORTED_MODEL_ATTR = Union{
104104

105105
MOI.supports(::Optimizer, ::SUPPORTED_MODEL_ATTR) = true
106106

107+
#
108+
# ListOfModelAttributesSet
109+
#
110+
function MOI.get(m::Optimizer{T}, ::MOI.ListOfModelAttributesSet) where {T}
111+
ret = MOI.AbstractModelAttribute[]
112+
if !isempty(m.inner.pbdata.name)
113+
push!(ret, MOI.Name())
114+
end
115+
if m.objective_sense !== nothing
116+
push!(ret, MOI.ObjectiveSense())
117+
end
118+
if m._obj_type == _SINGLE_VARIABLE
119+
push!(ret, MOI.ObjectiveFunction{MOI.VariableIndex}())
120+
elseif m._obj_type == _SCALAR_AFFINE
121+
push!(ret, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}())
122+
end
123+
return ret
124+
end
125+
107126
#
108127
# ListOfVariableIndices
109128
#
@@ -125,36 +144,28 @@ MOI.get(m::Optimizer, ::MOI.NumberOfVariables) = m.inner.pbdata.nvar
125144
#
126145
# ObjectiveFunctionType
127146
#
128-
function MOI.get(
129-
m::Optimizer{T}, ::MOI.ObjectiveFunctionType
130-
) where{T}
147+
function MOI.get(m::Optimizer{T}, ::MOI.ObjectiveFunctionType) where{T}
131148
if m._obj_type == _SINGLE_VARIABLE
132149
return MOI.VariableIndex
133-
else
134-
return MOI.ScalarAffineFunction{T}
135150
end
151+
return MOI.ScalarAffineFunction{T}
136152
end
137153

138154
#
139155
# ObjectiveSense
140156
#
141157
function MOI.get(m::Optimizer, ::MOI.ObjectiveSense)
142-
m.is_feas && return MOI.FEASIBILITY_SENSE
143-
144-
return m.inner.pbdata.objsense ? MOI.MIN_SENSE : MOI.MAX_SENSE
158+
return something(m.objective_sense, MOI.FEASIBILITY_SENSE)
145159
end
146160

147161
function MOI.set(m::Optimizer, ::MOI.ObjectiveSense, s::MOI.OptimizationSense)
148-
m.is_feas = (s == MOI.FEASIBILITY_SENSE)
149-
162+
m.objective_sense = s
150163
if s == MOI.MIN_SENSE || s == MOI.FEASIBILITY_SENSE
151164
m.inner.pbdata.objsense = true
152-
elseif s == MOI.MAX_SENSE
153-
m.inner.pbdata.objsense = false
154165
else
155-
error("Objetive sense not supported: $s")
166+
@assert s == MOI.MAX_SENSE
167+
m.inner.pbdata.objsense = false
156168
end
157-
158169
return nothing
159170
end
160171

@@ -164,7 +175,8 @@ end
164175
function MOI.get(m::Optimizer{T}, attr::MOI.ObjectiveValue) where{T}
165176
MOI.check_result_index_bounds(m, attr)
166177
raw_z = get_attribute(m.inner, ObjectiveValue())
167-
return raw_z * !m.is_feas
178+
is_feas = MOI.get(m, MOI.ObjectiveSense()) == MOI.FEASIBILITY_SENSE
179+
return raw_z * !is_feas
168180
end
169181

170182
#

src/Interfaces/MOI/constraints.jl

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,34 @@ const SUPPORTED_CONSTR_ATTR = Union{
2020

2121
MOI.supports(::Optimizer, ::A, ::Type{<:MOI.ConstraintIndex}) where{A<:SUPPORTED_CONSTR_ATTR} = true
2222

23+
function MOI.get(
24+
m::Optimizer,
25+
::MOI.ListOfConstraintAttributesSet{F,S},
26+
) where {F,S}
27+
ret = MOI.AbstractConstraintAttribute[]
28+
for set in values(m.name2con)
29+
if any(ci -> ci isa MOI.ConstraintIndex{F,S}, set)
30+
push!(ret, MOI.ConstraintName())
31+
break
32+
end
33+
end
34+
return ret
35+
end
36+
37+
_type_tuple(::MOI.ConstraintIndex{F,S}) where {F,S} = (F, S)
38+
39+
function MOI.get(m::Optimizer, ::MOI.ListOfConstraintTypesPresent)
40+
ret = Tuple{Type,Type}[]
41+
append!(ret, unique!(_type_tuple.(m.con_indices_moi)))
42+
for set in values(m.var2bndtype)
43+
for S in set
44+
push!(ret, (MOI.VariableIndex, S))
45+
end
46+
end
47+
unique!(ret)
48+
return ret
49+
end
50+
2351
# MOI boilerplate
2452
function MOI.supports(::Optimizer, ::MOI.ConstraintName, ::Type{<:MOI.ConstraintIndex{<:MOI.VariableIndex}})
2553
throw(MOI.VariableIndexConstraintNameError())
@@ -241,15 +269,6 @@ function MOI.delete(
241269
set_attribute(m.inner, VariableUpperBound(), j, T(Inf))
242270
end
243271

244-
# Update name tracking
245-
old_name = get(m.bnd2name, c, "")
246-
if old_name != "" && haskey(m.name2con, old_name)
247-
s = m.name2con[old_name]
248-
delete!(s, c)
249-
length(s) == 0 && delete!(m.name2con, old_name)
250-
end
251-
delete!(m.bnd2name, c)
252-
253272
# Delete tracking of bounds
254273
delete!(m.var2bndtype[v], S)
255274

@@ -367,15 +386,6 @@ end
367386
# ConstraintName
368387
#
369388

370-
function MOI.get(
371-
m::Optimizer{T}, ::MOI.ConstraintName,
372-
c::MOI.ConstraintIndex{MOI.VariableIndex, S}
373-
) where {T, S<:SCALAR_SETS{T}}
374-
MOI.throw_if_not_valid(m, c)
375-
376-
return get(m.bnd2name, c, "")
377-
end
378-
379389
function MOI.get(
380390
m::Optimizer{T}, ::MOI.ConstraintName,
381391
c::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T}, S}

src/Interfaces/MOI/objective.jl

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ end
1313
# =============================================
1414
function MOI.get(
1515
m::Optimizer{T},
16-
::MOI.ObjectiveFunction{MOI.VariableIndex}
17-
) where{T}
16+
::MOI.ObjectiveFunction{F}
17+
) where{T,F}
1818
obj = MOI.get(m, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}())
19-
return convert(MOI.VariableIndex, obj)
19+
return convert(F, obj)
2020
end
2121

2222
function MOI.get(
@@ -77,7 +77,7 @@ end
7777

7878
# =============================================
7979
# 3. Modify objective
80-
# =============================================
80+
# =============================================
8181
function MOI.modify(
8282
m::Optimizer{T},
8383
c::MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}},
@@ -90,6 +90,7 @@ function MOI.modify(
9090
# Update inner model
9191
j = m.var_indices[v]
9292
m.inner.pbdata.obj[j] = chg.new_coefficient # TODO: use inner API
93+
m._obj_type = _SCALAR_AFFINE
9394
return nothing
9495
end
9596

@@ -100,5 +101,6 @@ function MOI.modify(
100101
) where{T}
101102
isfinite(chg.new_constant) || error("Objective constant term must be finite")
102103
m.inner.pbdata.obj0 = chg.new_constant
104+
m._obj_type = _SCALAR_AFFINE
103105
return nothing
104-
end
106+
end

src/Interfaces/MOI/variables.jl

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,22 +38,6 @@ function MOI.add_variable(m::Optimizer{T}) where{T}
3838
return x
3939
end
4040

41-
# TODO: dispatch to inner model
42-
function MOI.add_variables(m::Optimizer, N::Int)
43-
N >= 0 || error("Cannot add negative number of variables")
44-
45-
N == 0 && return MOI.VariableIndex[]
46-
47-
vars = Vector{MOI.VariableIndex}(undef, N)
48-
for j in 1:N
49-
x = MOI.add_variable(m)
50-
vars[j] = x
51-
end
52-
53-
return vars
54-
end
55-
56-
5741
# =============================================
5842
# 3. Delete variables
5943
# =============================================
@@ -111,13 +95,16 @@ function MOI.set(m::Optimizer, ::MOI.VariableName, v::MOI.VariableIndex, name::S
11195
# Check that variable does exist
11296
MOI.throw_if_not_valid(m, v)
11397

114-
s = get!(m.name2var, name, Set{MOI.VariableIndex}())
115-
11698
# Update inner model
11799
j = m.var_indices[v]
118100
old_name = get_attribute(m.inner, VariableName(), j)
101+
if name == old_name
102+
return # It's the same name!
103+
end
119104
set_attribute(m.inner, VariableName(), j, name)
120105

106+
s = get!(m.name2var, name, Set{MOI.VariableIndex}())
107+
121108
# Update names mapping
122109
push!(s, v)
123110
# Delete old name

0 commit comments

Comments
 (0)