Skip to content
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
1 change: 1 addition & 0 deletions src/Dualization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ include("dualize.jl")
include("MOI_wrapper.jl")
include("vectorize_emulator.jl")
include("attributes.jl")
include("supports.jl")

export dualize
export dual_optimizer
Expand Down
78 changes: 0 additions & 78 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,56 +101,6 @@ end

DualOptimizer() = error("DualOptimizer must have a solver attached")

MOI.supports(::DualOptimizer, ::MOI.ObjectiveSense) = true

function MOI.supports(
optimizer::DualOptimizer{T},
::MOI.ObjectiveFunction{F},
) where {T,F}
# If the objective function is `MOI.VariableIndex` or
# `MOI.ScalarAffineFunction`, then a `MOI.ScalarAffineFunction` is set as
# the objective function for the dual problem.
# If it is `MOI.ScalarQuadraticFunction` , a `MOI.ScalarQuadraticFunction`
# is set as objective function for the dual problem.
attr = if F <: MOI.ScalarQuadraticFunction
MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}()
else
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}()
end
return supported_objective(F) &&
MOI.supports(optimizer.dual_problem.dual_model, attr)
end

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

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

function _change_constant(
model,
ci::MOI.ConstraintIndex{<:MOI.ScalarAffineFunction,S},
Expand Down Expand Up @@ -205,34 +155,6 @@ function MOI.modify(
return
end

function MOI.supports_add_constrained_variables(
optimizer::DualOptimizer{T},
::Type{MOI.Reals},
) where {T}
return MOI.supports_constraint(
optimizer.dual_problem.dual_model,
MOI.ScalarAffineFunction{T},
MOI.EqualTo{T},
)
# If `_dual_set_type(MOI.Reals)` 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)
MOI.empty!(dest)
dualize(
Expand Down
146 changes: 146 additions & 0 deletions src/supports.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
MOI.supports(::DualOptimizer, ::MOI.ObjectiveSense) = true

function MOI.supports(
optimizer::DualOptimizer{T},
::MOI.ObjectiveFunction{F},
) where {T,F}
# If the objective function is `MOI.VariableIndex` or
# `MOI.ScalarAffineFunction`, then a `MOI.ScalarAffineFunction` is set as
# the objective function for the dual problem.
# If it is `MOI.ScalarQuadraticFunction` , a `MOI.ScalarQuadraticFunction`
# is set as objective function for the dual problem.
attr = if F <: MOI.ScalarQuadraticFunction
MOI.ObjectiveFunction{MOI.ScalarQuadraticFunction{T}}()
else
MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}()
end
return supported_objective(F) &&
MOI.supports(optimizer.dual_problem.dual_model, attr)
end

# Generic fallback for `VariableBridgingCost`/`ConstraintBridgingCost`
# calls `dual_attribute` which won't work for sets that don't implement `dual_set_type`
# so we define custom `get` methods instead.

function MOI.get(
optimizer::DualOptimizer,
attr::Union{MOI.VariableBridgingCost,MOI.ConstraintBridgingCost},
)
return MOI.get_fallback(optimizer, attr)
end

function MOI.get(
optimizer::DualOptimizer{T},
::MOI.ConstraintBridgingCost{
<:Union{MOI.VariableIndex,MOI.ScalarAffineFunction{T}},
S,
},
) where {T,S<:MOI.AbstractScalarSet}
D = _dual_set_type(S)
if D === nothing
return Inf
end
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.VariableBridgingCost{D}(),
)
end

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

function MOI.get(
optimizer::DualOptimizer{T},
::MOI.ConstraintBridgingCost{
<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}},
S,
},
) where {T,S<:MOI.AbstractVectorSet}
D = _dual_set_type(S)
if D === nothing
return Inf
end
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.VariableBridgingCost{D}(),
)
end

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

function MOI.get(
optimizer::DualOptimizer{T},
::MOI.VariableBridgingCost{MOI.Reals},
) where {T}
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.ConstraintBridgingCost{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}(),
)
end

function MOI.supports_add_constrained_variables(
optimizer::DualOptimizer{T},
::Type{MOI.Reals},
) where {T}
return MOI.supports_constraint(
optimizer.dual_problem.dual_model,
MOI.ScalarAffineFunction{T},
MOI.EqualTo{T},
)
# If `_dual_set_type(MOI.Reals)` was `MOI.Zeros`,
# we would not need this method as special case of the one below
end

function MOI.get(
optimizer::DualOptimizer{T},
::MOI.VariableBridgingCost{S},
) where {T,S<:MOI.AbstractVectorSet}
D = _dual_set_type(S)
if D === nothing
return Inf
end
return MOI.get(
optimizer.dual_problem.dual_model,
MOI.ConstraintBridgingCost{MOI.VectorAffineFunction{T},D}(),
)
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
36 changes: 36 additions & 0 deletions test/Solvers/hypatia_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,42 @@

import Hypatia

@testset "VariableBridgingCost / ConstraintBridgingCost" begin
opt = MOI.instantiate(dual_optimizer(Hypatia.Optimizer))
# Specialized `VariableBridgingCost{MOI.Reals}`
@test MOI.supports_add_constrained_variables(opt, MOI.Reals)
@test MOI.get(opt, MOI.VariableBridgingCost{MOI.Reals}()) == 1
# Specialized `VariableBridgingCost{S<:AbstractVectorSet}`, supported branch
@test MOI.supports_add_constrained_variables(opt, MOI.Nonnegatives)
@test MOI.get(opt, MOI.VariableBridgingCost{MOI.Nonnegatives}()) == 0
# Same dispatch, supported with nonzero cost
@test MOI.get(opt, MOI.VariableBridgingCost{MOI.Zeros}()) == 1
# Same dispatch, `_dual_set_type` returns `nothing`
@test !MOI.supports_add_constrained_variables(opt, MOI.SOS1{Float64})
@test MOI.get(opt, MOI.VariableBridgingCost{MOI.SOS1{Float64}}()) == Inf
# Specialized scalar `ConstraintBridgingCost`, supported branch
@test MOI.get(
opt,
MOI.ConstraintBridgingCost{
MOI.ScalarAffineFunction{Float64},
MOI.GreaterThan{Float64},
}(),
) == 1
# Specialized vector `ConstraintBridgingCost`, `_dual_set_type` returns `nothing`
@test !MOI.supports_constraint(
opt,
MOI.VectorOfVariables,
MOI.SOS1{Float64},
)
@test MOI.get(
opt,
MOI.ConstraintBridgingCost{MOI.VectorOfVariables,MOI.SOS1{Float64}}(),
) == Inf
# Generic fallback (no specialized method matches)
@test MOI.get(opt, MOI.VariableBridgingCost{MOI.GreaterThan{Float64}}()) ==
0
end

@testset "Solve problems with different coefficient_type" begin
function mineig(::Type{T}) where {T}
model = GenericModel{T}(
Expand Down
11 changes: 11 additions & 0 deletions test/Tests/test_MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@
@testset "support" begin
for opt in dual_linear_optimizer
@test !MOI.supports_constraint(opt, MOI.VariableIndex, MOI.Integer)
@test MOI.get(
opt,
MOI.ConstraintBridgingCost{MOI.VariableIndex,MOI.Integer}(),
) == Inf
@test MOI.supports(opt, MOI.ObjectiveSense())
end
for opt in dual_conic_optimizer
Expand All @@ -104,6 +108,13 @@
MOI.VectorOfVariables,
MOI.PositiveSemidefiniteConeTriangle,
)
@test MOI.get(
opt,
MOI.ConstraintBridgingCost{
MOI.VectorOfVariables,
MOI.PositiveSemidefiniteConeTriangle,
}(),
) == 0
end
end

Expand Down
Loading