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

Performance improvements #69

Merged
merged 4 commits into from
Nov 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/LinQuadOptInterface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ end
# Abstract + macro
abstract type LinQuadOptimizer <: MOI.AbstractOptimizer end

@enum(VariableType, Continuous, Binary, Integer, Semiinteger, Semicontinuous)

macro LinQuadOptimizerBase(inner_model_type=Any)
esc(quote
inner::$inner_model_type
Expand All @@ -214,6 +216,7 @@ macro LinQuadOptimizerBase(inner_model_type=Any)
variable_names::Dict{MOI.VariableIndex, String}
variable_names_rev::Dict{String, MOI.VariableIndex}
variable_references::Vector{MOI.VariableIndex}
variable_type::Dict{MOI.VariableIndex, LinQuadOptInterface.VariableType}

variable_primal_solution::Vector{Float64}
variable_dual_solution::Vector{Float64}
Expand Down Expand Up @@ -256,6 +259,7 @@ function MOI.is_empty(m::LinQuadOptimizer)
ret = ret && isempty(m.variable_names)
ret = ret && isempty(m.variable_names_rev)
ret = ret && isempty(m.variable_references)
ret = ret && isempty(m.variable_type)
ret = ret && isempty(m.variable_primal_solution)
ret = ret && isempty(m.variable_dual_solution)
ret = ret && m.last_constraint_reference == 0
Expand Down Expand Up @@ -291,7 +295,7 @@ function MOI.empty!(m::M, env = nothing) where M<:LinQuadOptimizer
m.variable_names = Dict{MathOptInterface.VariableIndex, String}()
m.variable_names_rev = Dict{String, MathOptInterface.VariableIndex}()
m.variable_references = MathOptInterface.VariableIndex[]

m.variable_type = Dict{MathOptInterface.VariableIndex, VariableType}()
m.variable_primal_solution = Float64[]
m.variable_dual_solution = Float64[]

Expand Down
185 changes: 109 additions & 76 deletions src/constraints/singlevariable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,97 +21,96 @@ constrdict(model::LinQuadOptimizer, ::SVCI{MOI.Integer}) = cmap(model).integer
constrdict(model::LinQuadOptimizer, ::SVCI{MOI.Semicontinuous{Float64}}) = cmap(model).semicontinuous
constrdict(model::LinQuadOptimizer, ::SVCI{MOI.Semiinteger{Float64}}) = cmap(model).semiinteger

function set_variable_bound(model::LinQuadOptimizer, v::SinVar, set::LE)
"""
change_both_variable_bounds!(model::LinQuadOptimizer, columns::Vector{Int},
lower_bounds::Vector{Float64}, upper_bounds::Vector{Float64})

Set the lower bound of column `columns[i]` to `lower_bounds[i]` and the upper
bound to `upper_bounds[i]`. Alternatively, the lower or upper bound can be left
blank by passing an array of length 0 instead.

Examples:
change_both_variable_bounds!(model, [1, 2], [-0.5, 0.0], [1.0, 2.0])
change_both_variable_bounds!(model, [1, 2], [-0.5, 0.0], Float64[])
change_both_variable_bounds!(model, [1, 2], Float64[], [1.0, 2.0])
"""
function change_both_variable_bounds!(
model::LinQuadOptimizer,
columns::Vector{Int},
lower_bounds::Vector{Float64},
upper_bounds::Vector{Float64})
if length(lower_bounds) > 0 && length(upper_bounds) > 0
columns = vcat(columns, columns)
end
change_variable_bounds!(model,
[get_column(model, v)],
[set.upper],
[backend_type(model, Val{:Upperbound}())]
columns,
vcat(lower_bounds, upper_bounds),
vcat(
fill(backend_type(model, Val{:Lowerbound}()), length(lower_bounds)),
fill(backend_type(model, Val{:Upperbound}()), length(upper_bounds))
)
)
end

function set_variable_bound(model::LinQuadOptimizer, v::SinVar, set::LE)
change_both_variable_bounds!(
model, [get_column(model, v)], Float64[], [set.upper])
end

function set_variable_bound(model::LinQuadOptimizer, v::SinVar, set::GE)
change_variable_bounds!(model,
[get_column(model, v)],
[set.lower],
[backend_type(model, Val{:Lowerbound}())]
)
change_both_variable_bounds!(
model, [get_column(model, v)], [set.lower], Float64[])
end

function set_variable_bound(model::LinQuadOptimizer, v::SinVar, set::EQ)
change_variable_bounds!(model,
[get_column(model, v), get_column(model, v)],
[set.value, set.value],
[backend_type(model, Val{:Upperbound}()),
backend_type(model, Val{:Lowerbound}())
]
)
change_both_variable_bounds!(
model, [get_column(model, v)], [set.value], [set.value])
end

function set_variable_bound(model::LinQuadOptimizer, v::SinVar, set::IV)
change_variable_bounds!(model,
[get_column(model, v), get_column(model, v)],
[set.upper, set.lower],
[backend_type(model, Val{:Upperbound}()),
backend_type(model, Val{:Lowerbound}())
]
)
change_both_variable_bounds!(
model, [get_column(model, v)], [set.lower], [set.upper])
end

"""
has_value(dict::Dict{K, V}, value::V) where {K, V}

Return true if `dict` has a `key=>value` pair with `value`.
"""
function has_value(dict::Dict{K, V}, value::V) where {K, V}
return value in values(dict)
function set_variable_bounds(
model::LinQuadOptimizer, vars::Vector{SinVar}, sets::Vector{LE})
change_both_variable_bounds!(model, get_column.(model, vars), Float64[],
[set.upper for set in sets])
end

"""
__check_for_conflicting__(model::LinQuadOptimizer, variable::SinVar, set,
conflicting_type::Type{<:AbstractSet})

Throw an error if `variable` is already constrained to be in a set of type
`conflicting_type`.
"""
function __check_for_conflicting__(model::LinQuadOptimizer, variable::SinVar, set,
conflict_type::Type{<:MOI.AbstractSet})
if has_value(constrdict(model, SVCI{conflict_type}(0)), variable.variable)
error("Cannot add constraint $(variable)-in-$(set) as it is already " *
"constrained by a set of type $(conflict_type).")
end
function set_variable_bounds(
model::LinQuadOptimizer, vars::Vector{SinVar}, sets::Vector{GE})
change_both_variable_bounds!(model, get_column.(model, vars),
[set.lower for set in sets], Float64[])
end

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

function set_variable_bounds(
model::LinQuadOptimizer, vars::Vector{SinVar}, sets::Vector{IV})
change_both_variable_bounds!(model, get_column.(model, vars),
[set.lower for set in sets], [set.upper for set in sets])
end

"""
__check_for_conflicting__(model::LinQuadOptimizer, variable::SinVar, set,
conflicting_types::AbstractSet...)
has_value(dict::Dict{K, V}, value::V) where {K, V}

Throw an error if `variable` is constrained to be in a set whose type is one of
`conflicting_types`.
Return true if `dict` has a `key=>value` pair with `value`.
"""
function __check_for_conflicting__(model::LinQuadOptimizer, variable::SinVar, set,
conflicting_types...)
for conflict_type in conflicting_types
__check_for_conflicting__(model, variable, set, conflict_type)
end
function has_value(dict::Dict{K, V}, value::V) where {K, V}
return value in values(dict)
end

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

function MOI.add_constraints(model::LinQuadOptimizer, variables::Vector{SinVar},
sets::Vector{S}) where S <: LinSets
__assert_supported_constraint__(model, SinVar, S)
for variable in variables
variable_type = model.variable_type[variable.variable]
if !(variable_type == Continuous || variable_type == Integer)
error("Cannot set bounds because variable is of type: $(variable_type).")
end
end
set_variable_bounds(model, variables, sets)
indices = SVCI{S}[]
for variable in variables
model.last_constraint_reference += 1
index = SVCI{S}(model.last_constraint_reference)
dict = constrdict(model, index)
dict[index] = variable.variable
push!(indices, index)
end
return indices
end

function MOI.delete(model::LinQuadOptimizer, index::SVCI{S}) where S <: LinSets
__assert_valid__(model, index)
delete_constraint_name(model, index)
dict = constrdict(model, index)
variable_index = dict[index]
set_variable_bound(model, SinVar(variable_index), IV(-Inf, Inf))
variable = dict[index]
model.variable_type[variable] = Continuous
set_variable_bound(model, SinVar(variable), IV(-Inf, Inf))
delete!(dict, index)
return
end

# constraint set
Expand Down Expand Up @@ -174,8 +196,11 @@ user does.
=#
function MOI.add_constraint(model::LinQuadOptimizer, variable::SinVar, set::MOI.ZeroOne)
__assert_supported_constraint__(model, SinVar, MOI.ZeroOne)
__check_for_conflicting__(model, variable, set, MOI.ZeroOne, MOI.Integer,
MOI.Semicontinuous{Float64}, MOI.Semiinteger{Float64})
variable_type = model.variable_type[variable.variable]
if variable_type != Continuous
error("Cannot make variable binary because it is $(variable_type).")
end
model.variable_type[variable.variable] = Binary
model.last_constraint_reference += 1
index = SVCI{MOI.ZeroOne}(model.last_constraint_reference)
column = get_column(model, variable)
Expand All @@ -198,6 +223,7 @@ function MOI.delete(model::LinQuadOptimizer, index::SVCI{MOI.ZeroOne})
delete_constraint_name(model, index)
dict = constrdict(model, index)
(variable, lower, upper) = dict[index]
model.variable_type[variable] = Continuous
column = get_column(model, variable)
change_variable_types!(
model, [column], [backend_type(model, Val{:Continuous}())])
Expand Down Expand Up @@ -227,8 +253,11 @@ end

function MOI.add_constraint(model::LinQuadOptimizer, variable::SinVar, set::MOI.Integer)
__assert_supported_constraint__(model, SinVar, MOI.Integer)
__check_for_conflicting__(model, variable, set, MOI.ZeroOne,
MOI.Semicontinuous{Float64}, MOI.Semiinteger{Float64})
variable_type = model.variable_type[variable.variable]
if variable_type != Continuous
error("Cannot make variable integer because it is $(variable_type).")
end
model.variable_type[variable.variable] = Integer
change_variable_types!(model, [get_column(model, variable)],
[backend_type(model, set)])
model.last_constraint_reference += 1
Expand All @@ -244,6 +273,7 @@ function MOI.delete(model::LinQuadOptimizer, index::SVCI{MOI.Integer})
delete_constraint_name(model, index)
dict = constrdict(model, index)
variable = dict[index]
model.variable_type[variable] = Continuous
change_variable_types!(model, [get_column(model, variable)],
[backend_type(model, Val{:Continuous}())])
delete!(dict, index)
Expand All @@ -265,14 +295,15 @@ end
Semicontinuous / Semiinteger constraints
=#
const SEMI_TYPES = Union{MOI.Semicontinuous{Float64}, MOI.Semiinteger{Float64}}
variable_type_(::Type{<:MOI.Semicontinuous}) = Semicontinuous
variable_type_(::Type{<:MOI.Semiinteger}) = Semiinteger
function MOI.add_constraint(model::LinQuadOptimizer, variable::SinVar, set::S) where S <: SEMI_TYPES
__assert_supported_constraint__(model, SinVar, S)
__check_for_conflicting__(model, variable, set, S, MOI.ZeroOne, MOI.Integer)
if S == MOI.Semicontinuous{Float64}
__check_for_conflicting__(model, variable, set, MOI.Semiinteger{Float64})
else
__check_for_conflicting__(model, variable, set, MOI.Semicontinuous{Float64})
variable_type = model.variable_type[variable.variable]
if variable_type != Continuous
error("Cannot make variable $(S) because it is $(variable_type).")
end
model.variable_type[variable.variable] = variable_type_(S)
column = get_column(model, variable)
change_variable_types!(model, [column], [backend_type(model, set)])
change_variable_bounds!(model,
Expand All @@ -293,7 +324,9 @@ function MOI.delete(model::LinQuadOptimizer, index::SVCI{<:SEMI_TYPES})
__assert_valid__(model, index)
delete_constraint_name(model, index)
dict = constrdict(model, index)
column = get_column(model, dict[index])
variable = dict[index]
column = get_column(model, variable)
model.variable_type[variable] = Continuous
change_variable_types!(model, [column], [backend_type(model, Val{:Continuous}())])
change_variable_bounds!(model,
[column, column],
Expand Down
14 changes: 3 additions & 11 deletions src/mockoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ const SUPPORTED_CONSTRAINTS = [
(LQOI.SinVar, LQOI.IV),
(LQOI.SinVar, MOI.ZeroOne),
(LQOI.SinVar, MOI.Integer),
(LQOI.SinVar, MOI.Semiinteger{Float64}),
(LQOI.SinVar, MOI.Semicontinuous{Float64}),
(LQOI.VecVar, LQOI.SOS1),
(LQOI.VecVar, LQOI.SOS2),
(LQOI.VecVar, MOI.Nonnegatives),
Expand Down Expand Up @@ -542,19 +544,9 @@ function LQOI.delete_quadratic_constraints!(instance::MockLinQuadOptimizer, rowb
end
end

# TODO fix types
function variabletype(::MockLinQuadOptimizer, typeval)
if typeval == 'B'
return 'B'
elseif typeval == 'I'
return 'I'
else
return 'C'
end
end
function LQOI.change_variable_types!(instance::MockLinQuadOptimizer, colvec, typevec)
for i in eachindex(colvec)
instance.inner.vartype[colvec[i]] = variabletype(instance, typevec[i])
instance.inner.vartype[colvec[i]] = typevec[i]
end
end

Expand Down
3 changes: 3 additions & 0 deletions src/variables.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ function MOI.add_variable(model::LinQuadOptimizer)
push!(model.variable_references, index)
push!(model.variable_primal_solution, NaN)
push!(model.variable_dual_solution, NaN)
model.variable_type[index] = Continuous
return index
end

Expand All @@ -115,6 +116,7 @@ function MOI.add_variables(model::LinQuadOptimizer, number_to_add::Int)
push!(model.variable_references, index)
push!(model.variable_primal_solution, NaN)
push!(model.variable_dual_solution, NaN)
model.variable_type[index] = Continuous
end
return variable_indices
end
Expand Down Expand Up @@ -143,6 +145,7 @@ function MOI.delete(model::LinQuadOptimizer, index::VarInd)
delete_variables!(model, column, column)
# delete from problem
deleteat!(model.variable_references, column)
delete!(model.variable_type, index)
deleteat!(model.variable_primal_solution, column)
deleteat!(model.variable_dual_solution, column)
deleteref!(model.variable_mapping, column, index)
Expand Down
Loading