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
12 changes: 12 additions & 0 deletions src/Bridges/Bridges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ function full_bridge_optimizer(model::MOI.ModelLike, ::Type{T}) where T
cache = MOIU.UniversalFallback(AllBridgedConstraints{T}())
bridgedmodel = MOIB.LazyBridgeOptimizer(model, cache)
add_bridge(bridgedmodel, MOIB.VectorizeBridge{T})
add_bridge(bridgedmodel, MOIB.ScalarSlackBridge{T})
add_bridge(bridgedmodel, MOIB.VectorSlackBridge{T})
add_bridge(bridgedmodel, MOIB.SplitIntervalBridge{T})
add_bridge(bridgedmodel, MOIB.QuadtoSOCBridge{T})
add_bridge(bridgedmodel, MOIB.GeoMeanBridge{T})
Expand All @@ -59,6 +61,16 @@ end

include("vectorizebridge.jl")
@bridge Vectorize VectorizeBridge () (MOI.EqualTo, MOI.LessThan, MOI.GreaterThan,) () () (MOI.SingleVariable,) (MOI.ScalarAffineFunction, MOI.ScalarQuadraticFunction) () ()
include("slackbridge.jl")
@bridge ScalarSlack ScalarSlackBridge () (MOI.Interval, MOI.LessThan, MOI.GreaterThan) () () () (MOI.ScalarAffineFunction, MOI.ScalarQuadraticFunction) () ()
@bridge(VectorSlack, VectorSlackBridge, (), (),
(MOI.Nonnegatives, MOI.Nonpositives, MOI.SecondOrderCone,
MOI.RotatedSecondOrderCone, MOI.GeometricMeanCone,
MOI.PositiveSemidefiniteConeSquare, MOI.LogDetConeTriangle,
MOI.RootDetConeTriangle),
(MOI.PowerCone, MOI.DualPowerCone, MOI.SOS1, MOI.SOS2), (), (), (),
(MOI.VectorAffineFunction, MOI.VectorQuadraticFunction)
)
include("intervalbridge.jl")
@bridge SplitInterval SplitIntervalBridge () (MOI.Interval,) () () (MOI.SingleVariable,) (MOI.ScalarAffineFunction, MOI.ScalarQuadraticFunction) () ()
include("rsocbridge.jl")
Expand Down
2 changes: 2 additions & 0 deletions src/Bridges/bridgeoptimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,5 @@ end
# Variables
MOI.add_variable(b::AbstractBridgeOptimizer) = MOI.add_variable(b.model)
MOI.add_variables(b::AbstractBridgeOptimizer, n) = MOI.add_variables(b.model, n)

# TODO add transform
176 changes: 176 additions & 0 deletions src/Bridges/slackbridge.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# scalar version

"""
ScalarSlackBridge{T, F, S}

The `ScalarSlackBridge` converts a constraint `G`-in-`S` where `G` is a function different
from `SingleVariable` into the constraints `F`-in-`EqualTo{T}` and `SingleVariable`-in-`S`.
`F` is the result of subtracting a `SingleVariable` from `G`.
Tipically `G` is the same as `F`, but that is not mandatory.
"""
struct ScalarSlackBridge{T, F, S} <: AbstractBridge
slack::MOI.VariableIndex
slack_in_set::CI{MOI.SingleVariable, S}
equality::CI{F, MOI.EqualTo{T}}
end
function ScalarSlackBridge{T, F, S}(model, f::MOI.AbstractScalarFunction, s::S) where {T, F, S}
slack = MOI.add_variable(model)
new_f = MOIU.operate(-, T, f, MOI.SingleVariable(slack))
slack_in_set = MOI.add_constraint(model, MOI.SingleVariable(slack), s)
equality = MOI.add_constraint(model, new_f, MOI.EqualTo(0.0))
return ScalarSlackBridge{T, F, S}(slack, slack_in_set, equality)
end

# start allowing everything (scalar)
MOI.supports_constraint(::Type{ScalarSlackBridge{T, F, S}},
::Type{<:MOI.AbstractScalarFunction},
::Type{<:MOI.AbstractScalarSet}) where {T, F, S} = true
# then restrict (careful with ambiguity)
MOI.supports_constraint(::Type{ScalarSlackBridge{T, F, S}},
::Type{<:MOI.SingleVariable},
::Type{<:MOI.EqualTo}) where {T, F, S} = false
MOI.supports_constraint(::Type{ScalarSlackBridge{T, F, S}},
::Type{<:MOI.SingleVariable},
::Type{<:MOI.AbstractScalarSet}) where {T, F, S} = false
MOI.supports_constraint(::Type{ScalarSlackBridge{T, F, S}},
::Type{<:MOI.AbstractScalarFunction},
::Type{<:MOI.EqualTo}) where {T, F, S} = false
function added_constraint_types(::Type{ScalarSlackBridge{T, F, S}}) where {T, F, S}
return [(F, MOI.EqualTo{T}), (MOI.SingleVariable, S)]
end
function concrete_bridge_type(::Type{<:ScalarSlackBridge{T}},
F::Type{<:MOI.AbstractScalarFunction},
S::Type{<:MOI.AbstractScalarSet}) where T
F2 = MOIU.promote_operation(-, T, F, MOI.SingleVariable)
return ScalarSlackBridge{T, F2, S}
end

# Attributes, Bridge acting as an model
MOI.get(b::ScalarSlackBridge{T, F, S}, ::MOI.NumberOfVariables) where {T, F, S} = 1
MOI.get(b::ScalarSlackBridge{T, F, S}, ::MOI.NumberOfConstraints{F, MOI.EqualTo{T}}) where {T, F, S} = 1
MOI.get(b::ScalarSlackBridge{T, F, S}, ::MOI.NumberOfConstraints{MOI.SingleVariable, S}) where {T, F, S} = 1
MOI.get(b::ScalarSlackBridge{T, F, S}, ::MOI.ListOfConstraintIndices{F, MOI.EqualTo{T}}) where {T, F, S} = [b.equality]
MOI.get(b::ScalarSlackBridge{T, F, S}, ::MOI.ListOfConstraintIndices{MOI.SingleVariable, S}) where {T, F, S} = [b.slack_in_set]

# Indices
function MOI.delete(model::MOI.ModelLike, c::ScalarSlackBridge)
MOI.delete(model, c.equality)
MOI.delete(model, c.slack_in_set)
MOI.delete(model, c.slack)
return
end

# Attributes, Bridge acting as a constraint
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, c::ScalarSlackBridge)
# due to equality, slack should have the same value as original affine function
return MOI.get(model, attr, c.slack_in_set)
end
function MOI.get(model::MOI.ModelLike, a::MOI.ConstraintDual, c::ScalarSlackBridge)
# The dual constraint on slack (since it is free) is
# -dual_slack_in_set + dual_equality = 0 so the two duals are
# equal and we can return either one of them.
return MOI.get(model, a, c.slack_in_set)
end

# Constraints
function MOI.modify(model::MOI.ModelLike, c::ScalarSlackBridge, change::MOI.AbstractFunctionModification)
MOI.modify(model, c.equality, change)
end

function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintFunction,
c::ScalarSlackBridge{T, F, S}, func::F) where {T, F, S}
new_func = MOIU.operate(-, T, func, MOI.SingleVariable(c.slack))
MOI.set(model, MOI.ConstraintFunction(), c.equality, new_func)
end

function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintSet, c::ScalarSlackBridge{T, F, S}, change::S) where {T, F, S}
MOI.set(model, MOI.ConstraintSet(), c.slack_in_set, change)
end

# vector version

"""
VectorSlackBridge{T, F, S}

The `VectorSlackBridge` converts a constraint `G`-in-`S` where `G` is a function different
from `VectorOfVariables` into the constraints `F`in-`Zeros` and `VectorOfVariables`-in-`S`.
`F` is the result of subtracting a `VectorOfVariables` from `G`.
Tipically `G` is the same as `F`, but that is not mandatory.
"""
struct VectorSlackBridge{T, F, S} <: AbstractBridge
slacks::Vector{MOI.VariableIndex}
slacks_in_set::CI{MOI.VectorOfVariables, S}
equality::CI{F, MOI.Zeros}
end
function VectorSlackBridge{T, F, S}(model, f::MOI.AbstractVectorFunction, s::S) where {T, F, S}
d = MOI.dimension(s)
slacks = MOI.add_variables(model, d)
new_f = MOIU.operate(-, T, f, MOI.VectorAffineFunction{T}(MOI.VectorOfVariables(slacks)))
slacks_in_set = MOI.add_constraint(model, MOI.VectorOfVariables(slacks), s)
equality = MOI.add_constraint(model, new_f, MOI.Zeros(d))
return VectorSlackBridge{T, F, S}(slacks, slacks_in_set, equality)
end

MOI.supports_constraint(::Type{VectorSlackBridge{T, F, S}},
::Type{<:MOI.AbstractVectorFunction},
::Type{<:MOI.AbstractVectorSet}) where {T, F, S} = true
MOI.supports_constraint(::Type{VectorSlackBridge{T, F, S}},
::Type{<:MOI.VectorOfVariables},
::Type{<:MOI.Zeros}) where {T, F, S} = false
MOI.supports_constraint(::Type{VectorSlackBridge{T, F, S}},
::Type{<:MOI.AbstractVectorFunction},
::Type{<:MOI.Zeros}) where {T, F, S} = false
MOI.supports_constraint(::Type{VectorSlackBridge{T, F, S}},
::Type{<:MOI.VectorOfVariables},
::Type{<:MOI.AbstractVectorSet}) where {T, F, S} = false
function added_constraint_types(::Type{VectorSlackBridge{T, F, S}}) where {T, F<:MOI.AbstractVectorFunction, S}
return [(F, MOI.Zeros), (MOI.VectorOfVariables, S)]
end
function concrete_bridge_type(::Type{<:VectorSlackBridge{T}},
F::Type{<:MOI.AbstractVectorFunction},
S::Type{<:MOI.AbstractVectorSet}) where T
F2 = MOIU.promote_operation(-, T, F, MOI.VectorOfVariables)
return VectorSlackBridge{T, F2, S}
end

# Attributes, Bridge acting as an model
MOI.get(b::VectorSlackBridge{T, F, S}, ::MOI.NumberOfVariables) where {T, F, S} = length(b.slacks)
MOI.get(b::VectorSlackBridge{T, F, S}, ::MOI.NumberOfConstraints{F, MOI.Zeros}) where {T, F, S} = 1
MOI.get(b::VectorSlackBridge{T, F, S}, ::MOI.NumberOfConstraints{MOI.VectorOfVariables, S}) where {T, F, S} = 1
MOI.get(b::VectorSlackBridge{T, F, S}, ::MOI.ListOfConstraintIndices{F, MOI.Zeros}) where {T, F, S} = [b.equality]
MOI.get(b::VectorSlackBridge{T, F, S}, ::MOI.ListOfConstraintIndices{MOI.VectorOfVariables, S}) where {T, F, S} = [b.slacks_in_set]

# Indices
function MOI.delete(model::MOI.ModelLike, c::VectorSlackBridge)
MOI.delete(model, c.equality)
MOI.delete(model, c.slacks_in_set)
MOI.delete(model, c.slacks)
return
end

# Attributes, Bridge acting as a constraint
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, c::VectorSlackBridge)
# due to equality, slacks should have the same value as original affine function
return MOI.get(model, attr, c.slacks_in_set)
end
function MOI.get(model::MOI.ModelLike, a::MOI.ConstraintDual, c::VectorSlackBridge)
# The dual constraint on slack (since it is free) is
# -dual_slack_in_set + dual_equality = 0 so the two duals are
# equal and we can return either one of them.
return MOI.get(model, a, c.slacks_in_set)
end

# Constraints
function MOI.modify(model::MOI.ModelLike, c::VectorSlackBridge, change::MOI.AbstractFunctionModification)
MOI.modify(model, c.equality, change)
end

function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintFunction,
c::VectorSlackBridge{T, F, S}, func::F) where {T, F, S}
new_func = MOIU.operate(-, T, func, MOI.VectorAffineFunction{T}(MOI.VectorOfVariables(c.slacks)))
MOI.set(model, MOI.ConstraintFunction(), c.equality, new_func)
end

function MOI.set(model::MOI.ModelLike, ::MOI.ConstraintSet, c::VectorSlackBridge{T,F,S}, change::S) where {T, F, S}
MOI.set(model, MOI.ConstraintSet(), c.slacks_in_set, change)
end
44 changes: 44 additions & 0 deletions src/Test/contlinear.jl
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,23 @@ function linear7test(model::MOI.ModelLike, config::TestConfig)
atol = config.atol
rtol = config.rtol

# Min x - y
# s.t. bx <= x (c1)
# y <= by (c2)
#
# or, in more detail,
#
# Min 1 x - 1 y
# s.t. - 1 x <= - bx (z) (c1)
# 1 y <= by (w) (c2)
#
# with generic dual
#
# Max - bx z + by w
# s.t. - z == - 1 (c1)
# w == 1 (c2)
# i.e. z == w == 1

@test MOIU.supports_default_copy_to(model, #=copy_names=# false)
@test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}())
@test MOI.supports(model, MOI.ObjectiveSense())
Expand Down Expand Up @@ -1171,9 +1188,36 @@ function linear11test(model::MOI.ModelLike, config::TestConfig)
atol = config.atol
rtol = config.rtol
# simple 2 variable, 1 constraint problem
#
# starts with
#
# min x + y
# st x + y >= 1
# x + y >= 2
# sol: x+y = 2 (degenerate)
#
# with dual
#
# max w + 2z
# st w + z == 1
# w + z == 1
# w, z >= 0
# sol: z = 1, w = 0
#
# tranforms problem into:
#
# min x + y
# st x + y >= 1
# x + y <= 2
# sol: x+y = 1 (degenerate)
#
# with dual
#
# max w + 2z
# st w + z == 1
# w + z == 1
# w >= 0, z <= 0
# sol: w = 1, z = 0

@test MOIU.supports_default_copy_to(model, #=copy_names=# false)
@test MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{Float64}}())
Expand Down
Loading