Skip to content

Commit

Permalink
Merge pull request #86 from JuliaOpt/bl/supports
Browse files Browse the repository at this point in the history
Fix MOI.supports and MOI.supports_constraint for DualOptimizer
  • Loading branch information
blegat authored Apr 7, 2020
2 parents c97f1db + 01419d8 commit f920ba6
Show file tree
Hide file tree
Showing 11 changed files with 200 additions and 206 deletions.
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

0 comments on commit f920ba6

Please sign in to comment.