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

Commit 4c08533

Browse files
authored
Merge pull request #69 from JuliaOpt/od/performance
Performance improvements
2 parents dce0136 + 2ff3462 commit 4c08533

File tree

5 files changed

+190
-94
lines changed

5 files changed

+190
-94
lines changed

src/LinQuadOptInterface.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,8 @@ end
199199
# Abstract + macro
200200
abstract type LinQuadOptimizer <: MOI.AbstractOptimizer end
201201

202+
@enum(VariableType, Continuous, Binary, Integer, Semiinteger, Semicontinuous)
203+
202204
macro LinQuadOptimizerBase(inner_model_type=Any)
203205
esc(quote
204206
inner::$inner_model_type
@@ -214,6 +216,7 @@ macro LinQuadOptimizerBase(inner_model_type=Any)
214216
variable_names::Dict{MOI.VariableIndex, String}
215217
variable_names_rev::Dict{String, MOI.VariableIndex}
216218
variable_references::Vector{MOI.VariableIndex}
219+
variable_type::Dict{MOI.VariableIndex, LinQuadOptInterface.VariableType}
217220

218221
variable_primal_solution::Vector{Float64}
219222
variable_dual_solution::Vector{Float64}
@@ -256,6 +259,7 @@ function MOI.is_empty(m::LinQuadOptimizer)
256259
ret = ret && isempty(m.variable_names)
257260
ret = ret && isempty(m.variable_names_rev)
258261
ret = ret && isempty(m.variable_references)
262+
ret = ret && isempty(m.variable_type)
259263
ret = ret && isempty(m.variable_primal_solution)
260264
ret = ret && isempty(m.variable_dual_solution)
261265
ret = ret && m.last_constraint_reference == 0
@@ -291,7 +295,7 @@ function MOI.empty!(m::M, env = nothing) where M<:LinQuadOptimizer
291295
m.variable_names = Dict{MathOptInterface.VariableIndex, String}()
292296
m.variable_names_rev = Dict{String, MathOptInterface.VariableIndex}()
293297
m.variable_references = MathOptInterface.VariableIndex[]
294-
298+
m.variable_type = Dict{MathOptInterface.VariableIndex, VariableType}()
295299
m.variable_primal_solution = Float64[]
296300
m.variable_dual_solution = Float64[]
297301

src/constraints/singlevariable.jl

Lines changed: 109 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -21,97 +21,96 @@ constrdict(model::LinQuadOptimizer, ::SVCI{MOI.Integer}) = cmap(model).integer
2121
constrdict(model::LinQuadOptimizer, ::SVCI{MOI.Semicontinuous{Float64}}) = cmap(model).semicontinuous
2222
constrdict(model::LinQuadOptimizer, ::SVCI{MOI.Semiinteger{Float64}}) = cmap(model).semiinteger
2323

24-
function set_variable_bound(model::LinQuadOptimizer, v::SinVar, set::LE)
24+
"""
25+
change_both_variable_bounds!(model::LinQuadOptimizer, columns::Vector{Int},
26+
lower_bounds::Vector{Float64}, upper_bounds::Vector{Float64})
27+
28+
Set the lower bound of column `columns[i]` to `lower_bounds[i]` and the upper
29+
bound to `upper_bounds[i]`. Alternatively, the lower or upper bound can be left
30+
blank by passing an array of length 0 instead.
31+
32+
Examples:
33+
change_both_variable_bounds!(model, [1, 2], [-0.5, 0.0], [1.0, 2.0])
34+
change_both_variable_bounds!(model, [1, 2], [-0.5, 0.0], Float64[])
35+
change_both_variable_bounds!(model, [1, 2], Float64[], [1.0, 2.0])
36+
"""
37+
function change_both_variable_bounds!(
38+
model::LinQuadOptimizer,
39+
columns::Vector{Int},
40+
lower_bounds::Vector{Float64},
41+
upper_bounds::Vector{Float64})
42+
if length(lower_bounds) > 0 && length(upper_bounds) > 0
43+
columns = vcat(columns, columns)
44+
end
2545
change_variable_bounds!(model,
26-
[get_column(model, v)],
27-
[set.upper],
28-
[backend_type(model, Val{:Upperbound}())]
46+
columns,
47+
vcat(lower_bounds, upper_bounds),
48+
vcat(
49+
fill(backend_type(model, Val{:Lowerbound}()), length(lower_bounds)),
50+
fill(backend_type(model, Val{:Upperbound}()), length(upper_bounds))
51+
)
2952
)
3053
end
3154

55+
function set_variable_bound(model::LinQuadOptimizer, v::SinVar, set::LE)
56+
change_both_variable_bounds!(
57+
model, [get_column(model, v)], Float64[], [set.upper])
58+
end
59+
3260
function set_variable_bound(model::LinQuadOptimizer, v::SinVar, set::GE)
33-
change_variable_bounds!(model,
34-
[get_column(model, v)],
35-
[set.lower],
36-
[backend_type(model, Val{:Lowerbound}())]
37-
)
61+
change_both_variable_bounds!(
62+
model, [get_column(model, v)], [set.lower], Float64[])
3863
end
3964

4065
function set_variable_bound(model::LinQuadOptimizer, v::SinVar, set::EQ)
41-
change_variable_bounds!(model,
42-
[get_column(model, v), get_column(model, v)],
43-
[set.value, set.value],
44-
[backend_type(model, Val{:Upperbound}()),
45-
backend_type(model, Val{:Lowerbound}())
46-
]
47-
)
66+
change_both_variable_bounds!(
67+
model, [get_column(model, v)], [set.value], [set.value])
4868
end
4969

5070
function set_variable_bound(model::LinQuadOptimizer, v::SinVar, set::IV)
51-
change_variable_bounds!(model,
52-
[get_column(model, v), get_column(model, v)],
53-
[set.upper, set.lower],
54-
[backend_type(model, Val{:Upperbound}()),
55-
backend_type(model, Val{:Lowerbound}())
56-
]
57-
)
71+
change_both_variable_bounds!(
72+
model, [get_column(model, v)], [set.lower], [set.upper])
5873
end
5974

60-
"""
61-
has_value(dict::Dict{K, V}, value::V) where {K, V}
62-
63-
Return true if `dict` has a `key=>value` pair with `value`.
64-
"""
65-
function has_value(dict::Dict{K, V}, value::V) where {K, V}
66-
return value in values(dict)
75+
function set_variable_bounds(
76+
model::LinQuadOptimizer, vars::Vector{SinVar}, sets::Vector{LE})
77+
change_both_variable_bounds!(model, get_column.(model, vars), Float64[],
78+
[set.upper for set in sets])
6779
end
6880

69-
"""
70-
__check_for_conflicting__(model::LinQuadOptimizer, variable::SinVar, set,
71-
conflicting_type::Type{<:AbstractSet})
72-
73-
Throw an error if `variable` is already constrained to be in a set of type
74-
`conflicting_type`.
75-
"""
76-
function __check_for_conflicting__(model::LinQuadOptimizer, variable::SinVar, set,
77-
conflict_type::Type{<:MOI.AbstractSet})
78-
if has_value(constrdict(model, SVCI{conflict_type}(0)), variable.variable)
79-
error("Cannot add constraint $(variable)-in-$(set) as it is already " *
80-
"constrained by a set of type $(conflict_type).")
81-
end
81+
function set_variable_bounds(
82+
model::LinQuadOptimizer, vars::Vector{SinVar}, sets::Vector{GE})
83+
change_both_variable_bounds!(model, get_column.(model, vars),
84+
[set.lower for set in sets], Float64[])
8285
end
8386

84-
function __check_for_conflicting__(model::LinQuadOptimizer, variable::SinVar, set,
85-
conflict_type::Type{MOI.ZeroOne})
86-
for (index, lower, upper) in values(constrdict(model, SVCI{conflict_type}(0)))
87-
if index == variable.variable
88-
error("Cannot add constraint $(variable)-in-$(set) as it is already " *
89-
"constrained by a set of type $(conflict_type).")
90-
end
91-
end
87+
function set_variable_bounds(
88+
model::LinQuadOptimizer, vars::Vector{SinVar}, sets::Vector{EQ})
89+
values = [set.value for set in sets]
90+
change_both_variable_bounds!(model, get_column.(model, vars), values, values)
9291
end
9392

93+
function set_variable_bounds(
94+
model::LinQuadOptimizer, vars::Vector{SinVar}, sets::Vector{IV})
95+
change_both_variable_bounds!(model, get_column.(model, vars),
96+
[set.lower for set in sets], [set.upper for set in sets])
97+
end
9498

9599
"""
96-
__check_for_conflicting__(model::LinQuadOptimizer, variable::SinVar, set,
97-
conflicting_types::AbstractSet...)
100+
has_value(dict::Dict{K, V}, value::V) where {K, V}
98101
99-
Throw an error if `variable` is constrained to be in a set whose type is one of
100-
`conflicting_types`.
102+
Return true if `dict` has a `key=>value` pair with `value`.
101103
"""
102-
function __check_for_conflicting__(model::LinQuadOptimizer, variable::SinVar, set,
103-
conflicting_types...)
104-
for conflict_type in conflicting_types
105-
__check_for_conflicting__(model, variable, set, conflict_type)
106-
end
104+
function has_value(dict::Dict{K, V}, value::V) where {K, V}
105+
return value in values(dict)
107106
end
108107

109108
function MOI.add_constraint(model::LinQuadOptimizer, variable::SinVar, set::S) where S <: LinSets
110109
__assert_supported_constraint__(model, SinVar, S)
111-
# Since the following "variable type" sets also define bounds (implicitly or explicitly),
112-
# they may conflict with other bound constraints.
113-
__check_for_conflicting__(model, variable, set,
114-
S, MOI.Semicontinuous{Float64}, MOI.Semiinteger{Float64}, MOI.ZeroOne)
110+
variable_type = model.variable_type[variable.variable]
111+
if !(variable_type == Continuous || variable_type == Integer)
112+
error("Cannot set bounds because variable is of type: $(variable_type).")
113+
end
115114
set_variable_bound(model, variable, set)
116115
model.last_constraint_reference += 1
117116
index = SVCI{S}(model.last_constraint_reference)
@@ -120,13 +119,36 @@ function MOI.add_constraint(model::LinQuadOptimizer, variable::SinVar, set::S) w
120119
return index
121120
end
122121

122+
function MOI.add_constraints(model::LinQuadOptimizer, variables::Vector{SinVar},
123+
sets::Vector{S}) where S <: LinSets
124+
__assert_supported_constraint__(model, SinVar, S)
125+
for variable in variables
126+
variable_type = model.variable_type[variable.variable]
127+
if !(variable_type == Continuous || variable_type == Integer)
128+
error("Cannot set bounds because variable is of type: $(variable_type).")
129+
end
130+
end
131+
set_variable_bounds(model, variables, sets)
132+
indices = SVCI{S}[]
133+
for variable in variables
134+
model.last_constraint_reference += 1
135+
index = SVCI{S}(model.last_constraint_reference)
136+
dict = constrdict(model, index)
137+
dict[index] = variable.variable
138+
push!(indices, index)
139+
end
140+
return indices
141+
end
142+
123143
function MOI.delete(model::LinQuadOptimizer, index::SVCI{S}) where S <: LinSets
124144
__assert_valid__(model, index)
125145
delete_constraint_name(model, index)
126146
dict = constrdict(model, index)
127-
variable_index = dict[index]
128-
set_variable_bound(model, SinVar(variable_index), IV(-Inf, Inf))
147+
variable = dict[index]
148+
model.variable_type[variable] = Continuous
149+
set_variable_bound(model, SinVar(variable), IV(-Inf, Inf))
129150
delete!(dict, index)
151+
return
130152
end
131153

132154
# constraint set
@@ -174,8 +196,11 @@ user does.
174196
=#
175197
function MOI.add_constraint(model::LinQuadOptimizer, variable::SinVar, set::MOI.ZeroOne)
176198
__assert_supported_constraint__(model, SinVar, MOI.ZeroOne)
177-
__check_for_conflicting__(model, variable, set, MOI.ZeroOne, MOI.Integer,
178-
MOI.Semicontinuous{Float64}, MOI.Semiinteger{Float64})
199+
variable_type = model.variable_type[variable.variable]
200+
if variable_type != Continuous
201+
error("Cannot make variable binary because it is $(variable_type).")
202+
end
203+
model.variable_type[variable.variable] = Binary
179204
model.last_constraint_reference += 1
180205
index = SVCI{MOI.ZeroOne}(model.last_constraint_reference)
181206
column = get_column(model, variable)
@@ -198,6 +223,7 @@ function MOI.delete(model::LinQuadOptimizer, index::SVCI{MOI.ZeroOne})
198223
delete_constraint_name(model, index)
199224
dict = constrdict(model, index)
200225
(variable, lower, upper) = dict[index]
226+
model.variable_type[variable] = Continuous
201227
column = get_column(model, variable)
202228
change_variable_types!(
203229
model, [column], [backend_type(model, Val{:Continuous}())])
@@ -227,8 +253,11 @@ end
227253

228254
function MOI.add_constraint(model::LinQuadOptimizer, variable::SinVar, set::MOI.Integer)
229255
__assert_supported_constraint__(model, SinVar, MOI.Integer)
230-
__check_for_conflicting__(model, variable, set, MOI.ZeroOne,
231-
MOI.Semicontinuous{Float64}, MOI.Semiinteger{Float64})
256+
variable_type = model.variable_type[variable.variable]
257+
if variable_type != Continuous
258+
error("Cannot make variable integer because it is $(variable_type).")
259+
end
260+
model.variable_type[variable.variable] = Integer
232261
change_variable_types!(model, [get_column(model, variable)],
233262
[backend_type(model, set)])
234263
model.last_constraint_reference += 1
@@ -244,6 +273,7 @@ function MOI.delete(model::LinQuadOptimizer, index::SVCI{MOI.Integer})
244273
delete_constraint_name(model, index)
245274
dict = constrdict(model, index)
246275
variable = dict[index]
276+
model.variable_type[variable] = Continuous
247277
change_variable_types!(model, [get_column(model, variable)],
248278
[backend_type(model, Val{:Continuous}())])
249279
delete!(dict, index)
@@ -265,14 +295,15 @@ end
265295
Semicontinuous / Semiinteger constraints
266296
=#
267297
const SEMI_TYPES = Union{MOI.Semicontinuous{Float64}, MOI.Semiinteger{Float64}}
298+
variable_type_(::Type{<:MOI.Semicontinuous}) = Semicontinuous
299+
variable_type_(::Type{<:MOI.Semiinteger}) = Semiinteger
268300
function MOI.add_constraint(model::LinQuadOptimizer, variable::SinVar, set::S) where S <: SEMI_TYPES
269301
__assert_supported_constraint__(model, SinVar, S)
270-
__check_for_conflicting__(model, variable, set, S, MOI.ZeroOne, MOI.Integer)
271-
if S == MOI.Semicontinuous{Float64}
272-
__check_for_conflicting__(model, variable, set, MOI.Semiinteger{Float64})
273-
else
274-
__check_for_conflicting__(model, variable, set, MOI.Semicontinuous{Float64})
302+
variable_type = model.variable_type[variable.variable]
303+
if variable_type != Continuous
304+
error("Cannot make variable $(S) because it is $(variable_type).")
275305
end
306+
model.variable_type[variable.variable] = variable_type_(S)
276307
column = get_column(model, variable)
277308
change_variable_types!(model, [column], [backend_type(model, set)])
278309
change_variable_bounds!(model,
@@ -293,7 +324,9 @@ function MOI.delete(model::LinQuadOptimizer, index::SVCI{<:SEMI_TYPES})
293324
__assert_valid__(model, index)
294325
delete_constraint_name(model, index)
295326
dict = constrdict(model, index)
296-
column = get_column(model, dict[index])
327+
variable = dict[index]
328+
column = get_column(model, variable)
329+
model.variable_type[variable] = Continuous
297330
change_variable_types!(model, [column], [backend_type(model, Val{:Continuous}())])
298331
change_variable_bounds!(model,
299332
[column, column],

src/mockoptimizer.jl

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ const SUPPORTED_CONSTRAINTS = [
137137
(LQOI.SinVar, LQOI.IV),
138138
(LQOI.SinVar, MOI.ZeroOne),
139139
(LQOI.SinVar, MOI.Integer),
140+
(LQOI.SinVar, MOI.Semiinteger{Float64}),
141+
(LQOI.SinVar, MOI.Semicontinuous{Float64}),
140142
(LQOI.VecVar, LQOI.SOS1),
141143
(LQOI.VecVar, LQOI.SOS2),
142144
(LQOI.VecVar, MOI.Nonnegatives),
@@ -542,19 +544,9 @@ function LQOI.delete_quadratic_constraints!(instance::MockLinQuadOptimizer, rowb
542544
end
543545
end
544546

545-
# TODO fix types
546-
function variabletype(::MockLinQuadOptimizer, typeval)
547-
if typeval == 'B'
548-
return 'B'
549-
elseif typeval == 'I'
550-
return 'I'
551-
else
552-
return 'C'
553-
end
554-
end
555547
function LQOI.change_variable_types!(instance::MockLinQuadOptimizer, colvec, typevec)
556548
for i in eachindex(colvec)
557-
instance.inner.vartype[colvec[i]] = variabletype(instance, typevec[i])
549+
instance.inner.vartype[colvec[i]] = typevec[i]
558550
end
559551
end
560552

src/variables.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ function MOI.add_variable(model::LinQuadOptimizer)
9292
push!(model.variable_references, index)
9393
push!(model.variable_primal_solution, NaN)
9494
push!(model.variable_dual_solution, NaN)
95+
model.variable_type[index] = Continuous
9596
return index
9697
end
9798

@@ -115,6 +116,7 @@ function MOI.add_variables(model::LinQuadOptimizer, number_to_add::Int)
115116
push!(model.variable_references, index)
116117
push!(model.variable_primal_solution, NaN)
117118
push!(model.variable_dual_solution, NaN)
119+
model.variable_type[index] = Continuous
118120
end
119121
return variable_indices
120122
end
@@ -143,6 +145,7 @@ function MOI.delete(model::LinQuadOptimizer, index::VarInd)
143145
delete_variables!(model, column, column)
144146
# delete from problem
145147
deleteat!(model.variable_references, column)
148+
delete!(model.variable_type, index)
146149
deleteat!(model.variable_primal_solution, column)
147150
deleteat!(model.variable_dual_solution, column)
148151
deleteref!(model.variable_mapping, column, index)

0 commit comments

Comments
 (0)