Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix MOI.supports and MOI.supports_constraint for DualOptimizer #86

Merged
merged 4 commits into from
Apr 7, 2020
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
2 changes: 0 additions & 2 deletions src/Dualization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ const VQF{T} = MOI.VectorQuadraticFunction{T}
const VI = MOI.VariableIndex
const CI = MOI.ConstraintIndex

import MathOptInterface: dual_set

include("structures.jl")
include("utils.jl")
include("dual_sets.jl")
Expand Down
81 changes: 53 additions & 28 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@ export DualOptimizer, dual_optimizer

dual_optimizer(optimizer_constructor) = () -> DualOptimizer(MOI.instantiate(optimizer_constructor))

# Supported Functions
const SF = Union{MOI.SingleVariable,
MOI.ScalarAffineFunction{Float64},
MOI.VectorOfVariables,
MOI.VectorAffineFunction{Float64}}

# Supported Sets
const SS = Union{MOI.EqualTo{Float64}, MOI.GreaterThan{Float64}, MOI.LessThan{Float64},
MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives,
MOI.SecondOrderCone, MOI.RotatedSecondOrderCone,
MOI.ExponentialCone, MOI.DualExponentialCone,
MOI.PowerCone, MOI.DualPowerCone,
MOI.PositiveSemidefiniteConeTriangle}

struct DualOptimizer{T, OT <: MOI.ModelLike} <: MOI.AbstractOptimizer
dual_problem::DualProblem{T, OT}

Expand Down Expand Up @@ -65,21 +51,60 @@ function DualOptimizer()
end

function MOI.supports(::DualOptimizer,
::Union{MOI.ObjectiveSense,
MOI.ObjectiveFunction{MOI.SingleVariable},
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}},
MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{Float64}},
})
::MOI.ObjectiveSense)
return true
end

function MOI.supports_constraint(optimizer::DualOptimizer, F::Type{<:SF}, S::Type{<:SS})
return MOI.supports_constraint(optimizer.dual_problem.dual_model, F, S)
function MOI.supports(optimizer::DualOptimizer{T},
::MOI.ObjectiveFunction{F}) where {T, F}
# If the objective function is `MOI.SingleVariable` or `MOI.ScalarAffineFunction`,
# a `MOI.ScalarAffineFunction` is set as objective function for the dual problem.
# If it is `MOI.ScalarQuadraticFunction` , a `MOI.ScalarQuadraticFunction` is set as objective function for the dual problem.
G = F <: MOI.ScalarQuadraticFunction ? MOI.ScalarQuadraticFunction{T} : MOI.ScalarAffineFunction{T}
return supported_obj(F) && MOI.supports(optimizer.dual_problem.dual_model, MOI.ObjectiveFunction{G}())
end

function MOI.supports_constraint(
optimizer::DualOptimizer{T},
F::Type{<:Union{MOI.SingleVariable, MOI.ScalarAffineFunction{T}}},
S::Type{<:MOI.AbstractScalarSet}) where T
D = _dual_set_type(S)
if D === nothing
return false
end
if D <: MOI.AbstractVectorSet # The dual of `EqualTo` is `Reals`
return MOI.supports_add_constrained_variables(optimizer.dual_problem.dual_model, D)
else
return MOI.supports_add_constrained_variable(optimizer.dual_problem.dual_model, D)
end
end

function MOI.supports_constraint(::DualOptimizer, ::Type{MOI.AbstractFunction}, ::Type{MOI.AbstractSet})
return false
end
function MOI.supports_constraint(
optimizer::DualOptimizer{T},
F::Type{<:Union{MOI.VectorOfVariables, MOI.VectorAffineFunction{T}}},
S::Type{<:MOI.AbstractVectorSet}) where T
D = _dual_set_type(S)
if D === nothing
return false
end
return MOI.supports_add_constrained_variables(optimizer.dual_problem.dual_model, D)
end

# TODO add this when constrained variables are implemented
#function MOI.supports_add_constrained_variables(
# optimizer::DualOptimizer{T}, S::Type{MOI.Reals}) where T
# return MOI.supports_constraint(optimizer.dual_problem.dual_model,
# MOI.ScalarAffineFunction{T},
# MOI.EqualTo{T}) # If it was `MOI.Zeros`, we would not need this method as special case of the one below
#end
#function MOI.supports_add_constrained_variables(
# optimizer::DualOptimizer{T}, S::Type{<:MOI.AbstractVectorSet}) where T
# D = _dual_set_type(S)
# if D === nothing
# return false
# end
# return MOI.supports_constraint(optimizer.dual_problem.dual_model,
# MOI.VectorAffineFunction{T}, D)
#end

function MOI.copy_to(dest::DualOptimizer, src::MOI.ModelLike; kwargs...)
# Dualize the original problem
Expand Down Expand Up @@ -171,13 +196,13 @@ function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintPrimal,
return MOI.get(optimizer.dual_problem.dual_model, MOI.ConstraintDual(), ci_dual_problem) - primal_ci_constant
end

function MOI.get(optimizer::DualOptimizer, ::MOI.ConstraintPrimal,
ci::CI{F,S}) where {F <: MOI.AbstractVectorFunction, S <: MOI.AbstractVectorSet}
function MOI.get(optimizer::DualOptimizer{T}, ::MOI.ConstraintPrimal,
ci::CI{F,S}) where {T, F <: MOI.AbstractVectorFunction, S <: MOI.AbstractVectorSet}
# If it has no key than there is no dual constraint
if !haskey(optimizer.dual_problem.primal_dual_map.primal_con_dual_con, ci)
# The number of dual variable associated with the primal constraint is the ci dimension
ci_dimension = length(get_vis_dual_problem(optimizer, ci))
return zeros(Float64, ci_dimension)
return zeros(T, ci_dimension)
end
ci_dual_problem = get_ci_dual_problem(optimizer, ci)
return MOI.get(optimizer.dual_problem.dual_model, MOI.ConstraintDual(), ci_dual_problem)
Expand Down
23 changes: 13 additions & 10 deletions src/add_dual_cone_constraint.jl
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
function add_dual_cone_constraint(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, vi::Vector{VI},
function add_dual_cone_constraint(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike,
ci::CI{F, S}) where {F <: MOI.AbstractScalarFunction, S <: MOI.AbstractScalarSet}
# In this case vi should have only one entry
return MOI.add_constraint(dual_model, SVF(vi[1]), MOI.dual_set(get_set(primal_model, ci)))
vi, con_index = MOI.add_constrained_variable(dual_model, _dual_set(get_set(primal_model, ci)))
return [vi], con_index
end

function add_dual_cone_constraint(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, vi::Vector{VI},
function add_dual_cone_constraint(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike,
ci::CI{F, MOI.EqualTo{T}}) where {T, F <: MOI.AbstractScalarFunction}
return
vi = MOI.add_variable(dual_model)
return [vi], nothing
end

function add_dual_cone_constraint(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, vis::Vector{VI},
function add_dual_cone_constraint(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike,
ci::CI{F, S}) where {F <: MOI.AbstractVectorFunction, S <: MOI.AbstractVectorSet}
return MOI.add_constraint(dual_model, VVF(vis), MOI.dual_set(get_set(primal_model, ci)))
return MOI.add_constrained_variables(dual_model, _dual_set(get_set(primal_model, ci)))
end

function add_dual_cone_constraint(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike, vis::Vector{VI},
function add_dual_cone_constraint(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike,
ci::CI{F, MOI.Zeros}) where {F <: MOI.AbstractVectorFunction}
return
end
# Add as many variables as the dimension of the constraint
vis = MOI.add_variables(dual_model, get_ci_row_dimension(primal_model, ci))
return vis, nothing
end
40 changes: 20 additions & 20 deletions src/dual_equality_constraints.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function add_dual_equality_constraints(dual_model::MOI.ModelLike, primal_model::

# Loop at every constraint to get the scalar affine terms
scalar_affine_terms = get_scalar_affine_terms(primal_model,
primal_dual_map.primal_con_dual_var,
primal_dual_map.primal_con_dual_var,
all_variables, con_types, T)

# get RHS from objective coeficients
Expand All @@ -32,9 +32,9 @@ function add_dual_equality_constraints(dual_model::MOI.ModelLike, primal_model::
MOI.ScalarAffineFunction(scalar_affine_terms[primal_vi], zero(T)),
MOI.EqualTo(sense_change * get(scalar_terms, primal_vi, zero(T))))
#Set constraint name with the name of the associated priaml variable
if !is_empty(dual_names)
set_dual_constraint_name(dual_model, primal_model, primal_vi, dual_ci,
dual_names.dual_constraint_name_prefix)
if !is_empty(dual_names)
set_dual_constraint_name(dual_model, primal_model, primal_vi, dual_ci,
dual_names.dual_constraint_name_prefix)
end
# Add primal variable to dual contraint to the link dictionary
push!(primal_dual_map.primal_var_dual_con, primal_vi => dual_ci)
Expand Down Expand Up @@ -79,11 +79,11 @@ function add_scalar_affine_terms_from_quad_params(
end
end

function set_dual_constraint_name(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike,
function set_dual_constraint_name(dual_model::MOI.ModelLike, primal_model::MOI.ModelLike,
primal_vi::VI, dual_ci::CI, prefix::String)
MOI.set(dual_model, MOI.ConstraintName(), dual_ci,
MOI.set(dual_model, MOI.ConstraintName(), dual_ci,
prefix*MOI.get(primal_model, MOI.VariableName(), primal_vi))
return
return
end

function get_scalar_terms(primal_model::MOI.ModelLike,
Expand All @@ -106,16 +106,16 @@ function get_scalar_affine_terms(primal_model::MOI.ModelLike,
variables::Vector{VI},
con_types::Vector{Tuple{DataType, DataType}},
T::Type)

scalar_affine_terms = Dict{VI,Vector{MOI.ScalarAffineTerm{T}}}()
for vi in variables
scalar_affine_terms[vi] = MOI.ScalarAffineTerm{T}[]
end
for (F, S) in con_types
primal_cis = MOI.get(primal_model, MOI.ListOfConstraintIndices{F,S}()) # Constraints of type {F, S}
for ci in primal_cis
fill_scalar_affine_terms!(scalar_affine_terms, primal_con_dual_var,
primal_model, ci)
fill_scalar_affine_terms!(scalar_affine_terms, primal_con_dual_var,
primal_model, ci)
end
end
return scalar_affine_terms
Expand All @@ -132,7 +132,7 @@ end

function fill_scalar_affine_terms!(scalar_affine_terms::Dict{VI,Vector{MOI.ScalarAffineTerm{T}}},
primal_con_dual_var::Dict{CI, Vector{VI}},
primal_model::MOI.ModelLike, ci::CI{SAF{T}, S},
primal_model::MOI.ModelLike, ci::CI{SAF{T}, S},
) where {T, S <: Union{MOI.GreaterThan{T},
MOI.LessThan{T},
MOI.EqualTo{T}}}
Expand All @@ -143,25 +143,25 @@ function fill_scalar_affine_terms!(scalar_affine_terms::Dict{VI,Vector{MOI.Scala
push_to_scalar_affine_terms!(scalar_affine_terms[term.variable_index],
MOI.coefficient(term), dual_vi)
end
return
return
end

function fill_scalar_affine_terms!(scalar_affine_terms::Dict{VI,Vector{MOI.ScalarAffineTerm{T}}},
primal_con_dual_var::Dict{CI, Vector{VI}},
primal_model::MOI.ModelLike, ci::CI{SVF, S},
primal_model::MOI.ModelLike, ci::CI{SVF, S},
) where {T, S <: Union{MOI.GreaterThan{T},
MOI.LessThan{T},
MOI.EqualTo{T}}}

moi_function = get_function(primal_model, ci)
dual_vi = primal_con_dual_var[ci][1] # In this case we only have one vi
push_to_scalar_affine_terms!(scalar_affine_terms[moi_function.variable], one(T), dual_vi)
return
return
end

function fill_scalar_affine_terms!(scalar_affine_terms::Dict{VI,Vector{MOI.ScalarAffineTerm{T}}},
primal_con_dual_var::Dict{CI, Vector{VI}},
primal_model::MOI.ModelLike, ci::CI{VAF{T}, S},
primal_model::MOI.ModelLike, ci::CI{VAF{T}, S},
) where {T, S <: MOI.AbstractVectorSet}

moi_function = get_function(primal_model, ci)
Expand All @@ -170,25 +170,25 @@ function fill_scalar_affine_terms!(scalar_affine_terms::Dict{VI,Vector{MOI.Scala
dual_vi = primal_con_dual_var[ci][term.output_index]
# term.output_index is the row of the VAF,
# it corresponds to the dual variable associated with this constraint
push_to_scalar_affine_terms!(scalar_affine_terms[term.scalar_term.variable_index],
push_to_scalar_affine_terms!(scalar_affine_terms[term.scalar_term.variable_index],
set_dot(term.output_index, set, T)*MOI.coefficient(term), dual_vi)
end
return
end

function fill_scalar_affine_terms!(scalar_affine_terms::Dict{VI,Vector{MOI.ScalarAffineTerm{T}}},
primal_con_dual_var::Dict{CI, Vector{VI}},
primal_model::MOI.ModelLike, ci::CI{VVF, S},
primal_model::MOI.ModelLike, ci::CI{VVF, S},
) where {T, S <: MOI.AbstractVectorSet}

moi_function = get_function(primal_model, ci)
set = get_set(primal_model, ci)
for (i, variable) in enumerate(moi_function.variables)
dual_vi = primal_con_dual_var[ci][i]
push_to_scalar_affine_terms!(scalar_affine_terms[variable],
push_to_scalar_affine_terms!(scalar_affine_terms[variable],
set_dot(i, set, T)*one(T), dual_vi)
end
return
return
end

function set_dot(i::Int, s::MOI.AbstractVectorSet, T::Type)
Expand All @@ -198,4 +198,4 @@ function set_dot(i::Int, s::MOI.AbstractVectorSet, T::Type)
end
function set_dot(i::Int, s::MOI.AbstractScalarSet, T::Type)
return one(T)
end
end
Loading