Skip to content
This repository was archived by the owner on Jun 14, 2020. It is now read-only.

Commit 38befcb

Browse files
authored
Allow setting of duplicate variable and constraint names (#72)
* Allow setting of duplicate variable and constraint names. Error on get if a duplicate exists. * Disable nametests until new MOI tag
1 parent 4c08533 commit 38befcb

File tree

4 files changed

+95
-51
lines changed

4 files changed

+95
-51
lines changed

src/LinQuadOptInterface.jl

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ macro LinQuadOptimizerBase(inner_model_type=Any)
214214
last_variable_reference::UInt64
215215
variable_mapping::Dict{MOI.VariableIndex, Int}
216216
variable_names::Dict{MOI.VariableIndex, String}
217-
variable_names_rev::Dict{String, MOI.VariableIndex}
217+
variable_names_rev::Dict{String, Set{MOI.VariableIndex}}
218218
variable_references::Vector{MOI.VariableIndex}
219219
variable_type::Dict{MOI.VariableIndex, LinQuadOptInterface.VariableType}
220220

@@ -231,9 +231,8 @@ macro LinQuadOptimizerBase(inner_model_type=Any)
231231
qconstraint_primal_solution::Vector{Float64}
232232
qconstraint_dual_solution::Vector{Float64}
233233

234-
# TODO(odow): temp hack for constraint names
235-
constraint_names::Dict{Any, String}
236-
constraint_names_rev::Dict{String, Any}
234+
constraint_names::Dict{CI, String}
235+
constraint_names_rev::Dict{String, Set{CI}}
237236

238237
objective_constant::Float64
239238

@@ -293,7 +292,7 @@ function MOI.empty!(m::M, env = nothing) where M<:LinQuadOptimizer
293292
m.last_variable_reference = 0
294293
m.variable_mapping = Dict{MathOptInterface.VariableIndex, Int}()
295294
m.variable_names = Dict{MathOptInterface.VariableIndex, String}()
296-
m.variable_names_rev = Dict{String, MathOptInterface.VariableIndex}()
295+
m.variable_names_rev = Dict{String, Set{MathOptInterface.VariableIndex}}()
297296
m.variable_references = MathOptInterface.VariableIndex[]
298297
m.variable_type = Dict{MathOptInterface.VariableIndex, VariableType}()
299298
m.variable_primal_solution = Float64[]
@@ -309,8 +308,8 @@ function MOI.empty!(m::M, env = nothing) where M<:LinQuadOptimizer
309308
m.qconstraint_primal_solution = Float64[]
310309
m.qconstraint_dual_solution = Float64[]
311310

312-
m.constraint_names = Dict{Any, String}()
313-
m.constraint_names_rev = Dict{String, Any}()
311+
m.constraint_names = Dict{CI, String}()
312+
m.constraint_names_rev = Dict{String, Set{CI}}()
314313

315314
m.objective_constant = 0.0
316315

src/constraints.jl

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ function Base.getindex(model::LinQuadOptimizer, index::CI)
99
return constrdict(model, index)[index]
1010
end
1111

12-
function delete_constraint_name(model::LinQuadOptimizer, index)
12+
# Internal function: delete the constraint name.
13+
function delete_constraint_name(model::LinQuadOptimizer, index::CI)
1314
if haskey(model.constraint_names, index)
1415
name = model.constraint_names[index]
15-
delete!(model.constraint_names_rev, name)
16+
reverse_map = model.constraint_names_rev[name]
17+
if index in reverse_map
18+
pop!(reverse_map, index)
19+
end
1620
delete!(model.constraint_names, index)
1721
end
22+
return
1823
end
1924

2025
"""
@@ -99,30 +104,44 @@ end
99104
Get constraint names
100105
=#
101106

102-
function MOI.get(model::LinQuadOptimizer, ::MOI.ConstraintName, index::MOI.ConstraintIndex)
107+
function MOI.get(model::LinQuadOptimizer, ::MOI.ConstraintName, index::CI)
103108
if haskey(model.constraint_names, index)
104109
return model.constraint_names[index]
105110
else
106111
return ""
107112
end
108113
end
109-
function MOI.supports(::LinQuadOptimizer, ::MOI.ConstraintName, ::Type{<:MOI.ConstraintIndex})
114+
function MOI.supports(
115+
::LinQuadOptimizer, ::MOI.ConstraintName, ::Type{<:MOI.ConstraintIndex})
110116
return true
111117
end
118+
119+
# The rules for MOI are:
120+
# - Allow setting duplicate variable names
121+
# - Throw an error on get if the name is a duplicate
122+
# So we need to store two things.
123+
# 1. a mapping from ConstraintIndex -> Name
124+
# 2. a reverse mapping from Name -> set of ConstraintIndex's with that name
112125
function MOI.set(model::LinQuadOptimizer, ::MOI.ConstraintName,
113126
index::MOI.ConstraintIndex, name::String)
114-
if haskey(model.constraint_names_rev, name)
115-
if model.constraint_names_rev[name] != index
116-
error("Duplicate constraint name: $(name)")
117-
end
118-
elseif name != ""
119-
if haskey(model.constraint_names, index)
120-
# we're renaming an existing constraint
121-
old_name = model.constraint_names[index]
122-
delete!(model.constraint_names_rev, old_name)
123-
end
127+
if haskey(model.constraint_names, index)
128+
# This constraint already has a name, we must be changing it.
129+
current_name = model.constraint_names[index]
130+
# Remove `index` from the set of current name.
131+
pop!(model.constraint_names_rev[current_name], index)
132+
end
133+
if name != ""
134+
# We're changing the name to something non-default, so store it.
124135
model.constraint_names[index] = name
125-
model.constraint_names_rev[name] = index
136+
if !haskey(model.constraint_names_rev, name)
137+
model.constraint_names_rev[name] = Set{MOI.ConstraintIndex}()
138+
end
139+
push!(model.constraint_names_rev[name], index)
140+
else
141+
# We're changing the name to the default, so we don't store it. Note
142+
# that if `model.constraint_names` doesn't have a key `index`, this does
143+
# nothing.
144+
delete!(model.constraint_names, index)
126145
end
127146
return
128147
end
@@ -131,24 +150,32 @@ end
131150
Get constraint by name
132151
=#
133152

134-
function MOI.get(model::LinQuadOptimizer, ::Type{<:MOI.ConstraintIndex},
135-
name::String)
153+
function MOI.get(
154+
model::LinQuadOptimizer, ::Type{<:MOI.ConstraintIndex}, name::String)
136155
if haskey(model.constraint_names_rev, name)
137-
return model.constraint_names_rev[name]
138-
else
139-
return nothing
156+
if length(model.constraint_names_rev[name]) == 1
157+
return first(model.constraint_names_rev[name])
158+
elseif length(model.constraint_names_rev[name]) > 1
159+
error("Cannot get constraint because the name $(name) is a duplicate.")
160+
end
140161
end
162+
return nothing
141163
end
142164

143165
# this covers the type-stable get(m, ConstraintIndex{F,S}, name)::CI{F,S} case
144-
function MOI.get(model::LinQuadOptimizer, ::Type{MOI.ConstraintIndex{F,S}},
166+
function MOI.get(model::LinQuadOptimizer, ::Type{MOI.ConstraintIndex{F,S}},
145167
name::String) where F where S
146-
if haskey(model.constraint_names_rev, name) &&
147-
isa(model.constraint_names_rev[name], MOI.ConstraintIndex{F,S})
148-
return model.constraint_names_rev[name]::MOI.ConstraintIndex{F,S}
149-
else
150-
return nothing
168+
if haskey(model.constraint_names_rev, name)
169+
if length(model.constraint_names_rev[name]) == 1
170+
index = first(model.constraint_names_rev[name])
171+
if isa(index, MOI.ConstraintIndex{F, S})
172+
return index::MOI.ConstraintIndex{F, S}
173+
end
174+
elseif length(model.constraint_names_rev[name]) > 1
175+
error("Cannot get constraint because the name $(name) is a duplicate.")
176+
end
151177
end
178+
return nothing
152179
end
153180

154181

src/variables.jl

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,32 @@ function MOI.get(model::LinQuadOptimizer, ::MOI.VariableName, index::VarInd)
3232
end
3333
end
3434

35-
function MOI.set(model::LinQuadOptimizer, ::MOI.VariableName, index::VarInd, name::String)
36-
if haskey(model.variable_names_rev, name)
37-
if model.variable_names_rev[name] != index
38-
error("Duplicate variable name: $(name)")
39-
end
40-
elseif name != ""
41-
if haskey(model.variable_names, index)
42-
# we're renaming an existing variable
43-
old_name = model.variable_names[index]
44-
delete!(model.variable_names_rev, old_name)
45-
end
35+
# The rules for MOI are:
36+
# - Allow setting duplicate variable names
37+
# - Throw an error on get if the name is a duplicate
38+
# So we need to store two things.
39+
# 1. a mapping from VariableIndex -> Name
40+
# 2. a reverse mapping from Name -> set of VariableIndex's with that name
41+
function MOI.set(model::LinQuadOptimizer,
42+
::MOI.VariableName, index::VarInd, name::String)
43+
if haskey(model.variable_names, index)
44+
# This variable already has a name, we must be changing it.
45+
current_name = model.variable_names[index]
46+
# Remove `index` from the set of current name.
47+
pop!(model.variable_names_rev[current_name], index)
48+
end
49+
if name != ""
50+
# We're changing the name to something non-default, so store it.
4651
model.variable_names[index] = name
47-
model.variable_names_rev[name] = index
52+
if !haskey(model.variable_names_rev, name)
53+
model.variable_names_rev[name] = Set{VarInd}()
54+
end
55+
push!(model.variable_names_rev[name], index)
56+
else
57+
# We're changing the name to the default, so we don't store it. Note
58+
# that if `model.variable_names` doesn't have a key `index`, this does
59+
# nothing.
60+
delete!(model.variable_names, index)
4861
end
4962
return
5063
end
@@ -55,10 +68,14 @@ end
5568

5669
function MOI.get(model::LinQuadOptimizer, ::Type{MOI.VariableIndex}, name::String)
5770
if haskey(model.variable_names_rev, name)
58-
return model.variable_names_rev[name]
59-
else
60-
return nothing
71+
variable_set = model.variable_names_rev[name]
72+
if length(variable_set) == 1
73+
return first(model.variable_names_rev[name])
74+
elseif length(variable_set) > 1
75+
error("Cannot get variable because the name $(name) is a duplicate.")
76+
end
6177
end
78+
return nothing
6279
end
6380

6481
#=

test/runtests.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,10 @@ const LQOI = LinQuadOptInterface
119119

120120
@testset "ModelLike tests" begin
121121
config = MOIT.TestConfig(solve=false)
122-
@testset "nametest" begin
123-
MOIT.nametest(LQOI.MockLinQuadOptimizer())
124-
end
122+
# TODO(odow): re-enable once MOI v0.6.3+ is tagged.
123+
# @testset "nametest" begin
124+
# MOIT.nametest(LQOI.MockLinQuadOptimizer())
125+
# end
125126
@testset "validtest" begin
126127
MOIT.validtest(LQOI.MockLinQuadOptimizer())
127128
end

0 commit comments

Comments
 (0)