Skip to content

Commit

Permalink
Add SOCRBridge and disable SOCtoPSDBridge
Browse files Browse the repository at this point in the history
  • Loading branch information
blegat committed Aug 12, 2019
1 parent d9673e8 commit 6585286
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 11 deletions.
1 change: 1 addition & 0 deletions docs/src/apireference.md
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,7 @@ Bridges.Constraint.ScalarFunctionizeBridge
Bridges.Constraint.VectorFunctionizeBridge
Bridges.Constraint.SplitIntervalBridge
Bridges.Constraint.RSOCBridge
Bridges.Constraint.SOCRBridge
Bridges.Constraint.QuadtoSOCBridge
Bridges.Constraint.GeoMeanBridge
Bridges.Constraint.SquareBridge
Expand Down
6 changes: 5 additions & 1 deletion src/Bridges/Constraint/Constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ include("interval.jl")
const SplitInterval{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SplitIntervalBridge{T}, OT}
include("rsoc.jl")
const RSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCBridge{T}, OT}
include("socr.jl")
const SOCR{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCRBridge{T}, OT}
include("quad_to_soc.jl")
const QuadtoSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{QuadtoSOCBridge{T}, OT}
include("geomean.jl")
Expand Down Expand Up @@ -74,8 +76,10 @@ function add_all_bridges(bridged_model, T::Type)
MOIB.add_bridge(bridged_model, LogDetBridge{T})
MOIB.add_bridge(bridged_model, RootDetBridge{T})
MOIB.add_bridge(bridged_model, RSOCBridge{T})
MOIB.add_bridge(bridged_model, SOCRBridge{T})
# We do not add `SOCtoPSDBridge` as transforming the `SOC` to `RSOC` and
# then to `PSD` produces a smaller SDP constraint.
MOIB.add_bridge(bridged_model, RSOCtoPSDBridge{T})
MOIB.add_bridge(bridged_model, SOCtoPSDBridge{T})
MOIB.add_bridge(bridged_model, IndicatorActiveOnFalseBridge{T})
return
end
Expand Down
30 changes: 20 additions & 10 deletions src/Bridges/Constraint/rsoc.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function concrete_bridge_type(::Type{<:RSOCBridge{T}},
Y = MOIU.promote_operation(-, T, S, S)
Z = MOIU.promote_operation(+, T, S, S)
F = MOIU.promote_operation(vcat, T, Z, Y, G)
RSOCBridge{T, F, G}
return RSOCBridge{T, F, G}
end

# Attributes, Bridge acting as a model
Expand All @@ -72,8 +72,8 @@ function MOI.get(b::RSOCBridge{T, F},
end

# References
function MOI.delete(model::MOI.ModelLike, c::RSOCBridge)
MOI.delete(model, c.soc)
function MOI.delete(model::MOI.ModelLike, bridge::RSOCBridge)
MOI.delete(model, bridge.soc)
end

# Attributes, Bridge acting as a constraint
Expand All @@ -88,12 +88,22 @@ function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet, bridge::RSOCBrid
return MOI.RotatedSecondOrderCone(MOI.dimension(set))
end
# As the linear transformation is a symmetric involution,
# the constraint primal and dual both need to be processed by reapplying the same transformation
function _get(model, attr::Union{MOI.ConstraintPrimal, MOI.ConstraintDual}, c::RSOCBridge)
x = MOI.get(model, attr, c.soc)
# the constraint primal and dual both need to be processed by reapplying the
# same transformation
function rotate_result(model,
attr::Union{MOI.ConstraintPrimal, MOI.ConstraintDual},
ci::MOI.ConstraintIndex)
x = MOI.get(model, attr, ci)
s2 = 2
[x[1]/s2+x[2]/s2; x[1]/s2-x[2]/s2; x[3:end]]
return [x[1]/s2 + x[2]/s2; x[1]/s2 - x[2]/s2; x[3:end]]
end
# Need to define both `get` methods and redirect to `rotate_result` to avoid
# ambiguity in dispatch
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal,
bridge::RSOCBridge)
return rotate_result(model, attr, bridge.soc)
end
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintDual,
bridge::RSOCBridge)
return rotate_result(model, attr, bridge.soc)
end
# Need to define both `get` methods and redirect to `_get` to avoid ambiguity in dispatch
MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal, c::RSOCBridge) = _get(model, attr, c)
MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintDual, c::RSOCBridge) = _get(model, attr, c)
4 changes: 4 additions & 0 deletions src/Bridges/Constraint/soc_to_psd.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ which is equivalent to
t^2 & > x^\\top x
\\end{align*}
```
This bridge is not added by default by [`full_bridge_optimizer`](@ref) as
bridging second order cone constraints to semidefinite constraints can be
achieved by the [`SOCRBridge`](@ref) followed by the [`RSOCtoPSDBridge`](@ref)
while creating a smaller semidefinite constraint.
"""
struct SOCtoPSDBridge{T} <: AbstractBridge
dim::Int
Expand Down
74 changes: 74 additions & 0 deletions src/Bridges/Constraint/socr.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
"""
SOCRBridge{T, F, G}
The `SecondOrderCone` is `RotatedSecondOrderCone`. We simply do the inverse
transformation of [`RSOCBridge`](@ref). In fact, as the transformation is an
involution, we do the same transformation.
"""
struct SOCRBridge{T, F, G} <: AbstractBridge
rsoc::CI{F, MOI.RotatedSecondOrderCone}
end
function bridge_constraint(::Type{SOCRBridge{T, F, G}}, model,
f::MOI.AbstractVectorFunction,
s::MOI.SecondOrderCone) where {T, F, G}
soc = MOI.add_constraint(model, rotate_function(f, T),
MOI.RotatedSecondOrderCone(MOI.dimension(s)))
return SOCRBridge{T, F, G}(soc)
end

function MOI.supports_constraint(::Type{SOCRBridge{T}},
::Type{<:MOI.AbstractVectorFunction},
::Type{MOI.SecondOrderCone}) where T
return true
end
MOIB.added_constrained_variable_types(::Type{<:SOCRBridge}) = Tuple{DataType}[]
function MOIB.added_constraint_types(::Type{<:SOCRBridge{T, F}}) where {T, F}
return [(F, MOI.RotatedSecondOrderCone)]
end
function concrete_bridge_type(::Type{<:SOCRBridge{T}},
G::Type{<:MOI.AbstractVectorFunction},
::Type{MOI.SecondOrderCone}) where T
S = MOIU.promote_operation(/, T, MOIU.scalar_type(G), T)
Y = MOIU.promote_operation(-, T, S, S)
Z = MOIU.promote_operation(+, T, S, S)
F = MOIU.promote_operation(vcat, T, Z, Y, G)
return SOCRBridge{T, F, G}
end

# Attributes, Bridge acting as an model
function MOI.get(b::SOCRBridge{T, F},
::MOI.NumberOfConstraints{F, MOI.RotatedSecondOrderCone}) where {T, F}
return 1
end
function MOI.get(b::SOCRBridge{T, F},
::MOI.ListOfConstraintIndices{F, MOI.RotatedSecondOrderCone}) where {T, F}
return [b.rsoc]
end

# References
function MOI.delete(model::MOI.ModelLike, bridge::SOCRBridge)
MOI.delete(model, bridge.rsoc)
end

# Attributes, Bridge acting as a constraint
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction,
bridge::SOCRBridge{T, F, G}) where {T, F, G}
# As it is an involution, we can just reapply the same transformation
func = MOI.get(model, attr, bridge.rsoc)
return MOIU.convert_approx(G, rotate_function(func, T))
end
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet, bridge::SOCRBridge)
set = MOI.get(model, attr, bridge.rsoc)
return MOI.SecondOrderCone(MOI.dimension(set))
end
# As the linear transformation is a symmetric involution,
# the constraint primal and dual both need to be processed by reapplying the same transformation
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintPrimal,
bridge::SOCRBridge)
return rotate_result(model, attr, bridge.rsoc)
end
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintDual,
bridge::SOCRBridge)
return rotate_result(model, attr, bridge.rsoc)
end
1 change: 1 addition & 0 deletions test/Bridges/Constraint/Constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ include("slack.jl")
include("functionize.jl")
include("interval.jl")
include("rsoc.jl")
include("socr.jl")
include("quad_to_soc.jl")
include("geomean.jl")
include("square.jl")
Expand Down
39 changes: 39 additions & 0 deletions test/Bridges/Constraint/socr.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using Test

using MathOptInterface
const MOI = MathOptInterface
const MOIT = MathOptInterface.Test
const MOIU = MathOptInterface.Utilities
const MOIB = MathOptInterface.Bridges

include("utilities.jl")

mock = MOIU.MockOptimizer(MOIU.Model{Float64}())
config = MOIT.TestConfig()

@testset "RSOC" begin
bridged_mock = MOIB.SOCR{Float64}(mock)

MOIT.basic_constraint_tests(
bridged_mock, config,
include = [(F, S)
for F in [MOI.VectorOfVariables, MOI.VectorAffineFunction{Float64},
MOI.VectorQuadraticFunction{Float64}]
for S in [MOI.SecondOrderCone]])

mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1/√2, 1/√2],
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [[1 - 1/√2, 1 + 1/√2, -1]],
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]])
MOIT.soc1vtest(bridged_mock, config)
mock.optimize! = (mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(mock, [1.0, 1/√2, 1/√2],
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [[1 - 1/√2, 1 + 1/√2, -1]],
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[-√2]])
MOIT.soc1ftest(bridged_mock, config)
ci = first(MOI.get(
bridged_mock,
MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{Float64},
MOI.SecondOrderCone}()))
test_delete_bridge(bridged_mock, ci, 3,
((MOI.VectorAffineFunction{Float64},
MOI.RotatedSecondOrderCone, 0),))
end

0 comments on commit 6585286

Please sign in to comment.