Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
dce45e9
add L1 and Linfinity norm cones and tests
chriscoey Aug 7, 2019
5feb57f
add news item
chriscoey Aug 7, 2019
0772963
fix incorrect name
chriscoey Aug 7, 2019
a9c1c6e
add new cones in bridge tests
chriscoey Aug 7, 2019
930327a
start on adding NormOne to LP bridge
chriscoey Aug 8, 2019
93ca1bd
start adding NormInfinityCone bridge
chriscoey Aug 8, 2019
1bf304a
update NEWS
chriscoey Aug 8, 2019
5467abf
fix bridge tests
chriscoey Aug 8, 2019
c1594b8
address some comments
chriscoey Aug 8, 2019
1e73162
disallow VQF; start on duals thru new bridges
chriscoey Aug 9, 2019
ca19287
get dual bridge tests working
chriscoey Aug 9, 2019
58b33ec
cleanup and support VQF
chriscoey Aug 10, 2019
20397c6
merge master
chriscoey Aug 10, 2019
09ed86a
parametrize new bridges with function types
chriscoey Aug 10, 2019
a8fe952
use normalize_and_add_constraint; fix test failures
chriscoey Aug 10, 2019
7806da8
address comments
chriscoey Aug 10, 2019
bce186a
address comments
chriscoey Aug 12, 2019
913ffcb
add basic constraint tests; add test_models_equal tests
chriscoey Aug 14, 2019
be11ace
start on adding constraint function and set getters for NormInfinityCone
chriscoey Aug 14, 2019
913e3db
add get constraint function/set for NormOneCone; cleanup
chriscoey Aug 14, 2019
a30a695
merge in master; update NEWS
chriscoey Aug 14, 2019
e9f60c9
Merge branch 'master' into addL1Linf
chriscoey Aug 15, 2019
2fc69c7
use VAF in test instead of VOV
chriscoey Aug 15, 2019
49c87ff
fix some tests
chriscoey Aug 15, 2019
a5a479c
use MOIU.convert_approx
chriscoey Aug 15, 2019
459972c
canonicalize in convert_approx; use VAQ in unsafe_add when needed; ne…
chriscoey Aug 15, 2019
4d28606
correct type signature
chriscoey Aug 15, 2019
786a246
remove new variables to make NormOneBridge tests pass
chriscoey Aug 16, 2019
cd3758f
merge master
chriscoey Aug 16, 2019
6e6d5db
remove conversion no longer needed
chriscoey Aug 16, 2019
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
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
MathOptInterface (MOI) release notes
====================================

v0.9.1 (?)
---------------------

- L_1 and L_∞ norm epigraph cones and corresponding bridges to LP were added (#818).

v0.9.0 (August 13, 2019)
---------------------

Expand Down
5 changes: 5 additions & 0 deletions src/Bridges/Constraint/Constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ include("rsoc.jl")
const RSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCBridge{T}, OT}
include("quad_to_soc.jl")
const QuadtoSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{QuadtoSOCBridge{T}, OT}
include("norm_to_lp.jl")
const NormInfinity{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBridge{T}, OT}
const NormOne{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T}, OT}
include("geomean.jl")
const GeoMean{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeanBridge{T}, OT}
include("square.jl")
Expand Down Expand Up @@ -69,6 +72,8 @@ function add_all_bridges(bridged_model, T::Type)
MOIB.add_bridge(bridged_model, VectorFunctionizeBridge{T})
MOIB.add_bridge(bridged_model, SplitIntervalBridge{T})
MOIB.add_bridge(bridged_model, QuadtoSOCBridge{T})
MOIB.add_bridge(bridged_model, NormInfinityBridge{T})
MOIB.add_bridge(bridged_model, NormOneBridge{T})
MOIB.add_bridge(bridged_model, GeoMeanBridge{T})
MOIB.add_bridge(bridged_model, SquareBridge{T})
MOIB.add_bridge(bridged_model, LogDetBridge{T})
Expand Down
151 changes: 151 additions & 0 deletions src/Bridges/Constraint/norm_to_lp.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
"""
NormInfinityBridge{T}

The `NormInfinityCone` is representable with LP constraints, since
``t \\ge \\max_i \\lvert x_i \\rvert`` if and only if
``t \\ge x_i`` and ``t \\ge -x_i`` for all ``i``.
"""
struct NormInfinityBridge{T, F, G} <: AbstractBridge
nn_index::CI{F, MOI.Nonnegatives}
end
function bridge_constraint(::Type{NormInfinityBridge{T, F, G}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormInfinityCone) where {T, F, G}
f_scalars = MOIU.eachscalar(f)
t = f_scalars[1]
d = MOI.dimension(s)
lb = f_scalars[2:d]
ub = MOIU.operate(-, T, lb)
f_new = MOIU.operate(vcat, T, ub, lb)
for i in 1:MOI.output_dimension(f_new)
MOIU.operate_output_index!(+, T, i, f_new, t)
end
nn_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(MOI.output_dimension(f_new)))
return NormInfinityBridge{T, F, G}(nn_index)
end

MOI.supports_constraint(::Type{NormInfinityBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T = true
MOIB.added_constrained_variable_types(::Type{<:NormInfinityBridge}) = Tuple{DataType}[]
MOIB.added_constraint_types(::Type{NormInfinityBridge{T, F, G}}) where {T, F, G} = [(F, MOI.Nonnegatives)]
function concrete_bridge_type(::Type{<:NormInfinityBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormInfinityCone}) where T
F = MOIU.promote_operation(+, T, G, G)
return NormInfinityBridge{T, F, G}
end

# Attributes, Bridge acting as a model
MOI.get(b::NormInfinityBridge{T, F, G}, ::MOI.NumberOfConstraints{F, MOI.Nonnegatives}) where {T, F, G} = 1
MOI.get(b::NormInfinityBridge{T, F, G}, ::MOI.ListOfConstraintIndices{F, MOI.Nonnegatives}) where {T, F, G} = [b.nn_index]

# References
MOI.delete(model::MOI.ModelLike, c::NormInfinityBridge) = MOI.delete(model, c.nn_index)

# Attributes, Bridge acting as a constraint
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormInfinityBridge{T, F, G}) where {T, F, G}
nn_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), c.nn_index))
t = MOIU.operate!(/, T, sum(nn_func), T(length(nn_func)))
d = div(length(nn_func), 2)
x = MOIU.operate!(/, T, MOIU.operate!(-, T, nn_func[(d + 1):end], nn_func[1:d]), T(2))
return MOIU.convert_approx(G, MOIU.operate(vcat, T, t, x))
end
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormInfinityBridge)
dim = 1 + div(MOI.dimension(MOI.get(model, MOI.ConstraintSet(), c.nn_index)), 2)
return MOI.NormInfinityCone(dim)
end
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormInfinityBridge)
nn_primal = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index)
t = sum(nn_primal) / length(nn_primal)
d = div(length(nn_primal), 2)
x = (nn_primal[(d + 1):end] - nn_primal[1:d]) / 2
return vcat(t, x)
end
# Given a_i is dual on t - x_i >= 0 and b_i is dual on t + x_i >= 0,
# the dual on (t, x) in NormInfinityCone is (u, v) in NormOneCone, where
# v_i = -a_i + b_i and u = sum(a) + sum(b).
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormInfinityBridge)
nn_dual = MOI.get(model, MOI.ConstraintDual(), c.nn_index)
t = sum(nn_dual)
d = div(length(nn_dual), 2)
x = (nn_dual[(d + 1):end] - nn_dual[1:d])
return vcat(t, x)
end

"""
NormOneBridge{T}

The `NormOneCone` is representable with LP constraints, since
``t \\ge \\sum_i \\lvert x_i \\rvert`` if and only if there exists a vector y such that
``t \\ge \\sum_i y_i`` and ``y_i \\ge x_i``, ``y_i \\ge -x_i`` for all ``i``.
"""
struct NormOneBridge{T, F, G, H} <: AbstractBridge
y::Vector{MOI.VariableIndex}
ge_index::CI{F, MOI.GreaterThan{T}}
nn_index::CI{G, MOI.Nonnegatives}
end
function bridge_constraint(::Type{NormOneBridge{T, F, G, H}}, model::MOI.ModelLike, f::MOI.AbstractVectorFunction, s::MOI.NormOneCone) where {T, F, G, H}
f_scalars = MOIU.eachscalar(f)
d = MOI.dimension(s)
y = MOI.add_variables(model, d - 1)
ge_index = MOIU.normalize_and_add_constraint(model, MOIU.operate(-, T, f_scalars[1], MOIU.operate(sum, T, y)), MOI.GreaterThan(zero(T)), allow_modify_function=true)
lb = f_scalars[2:d]
ub = MOIU.operate(-, T, lb)
lb = MOIU.operate!(+, T, lb, MOI.VectorOfVariables(y))
ub = MOIU.operate!(+, T, ub, MOI.VectorOfVariables(y))
f_new = MOIU.operate(vcat, T, ub, lb)
nn_index = MOI.add_constraint(model, f_new, MOI.Nonnegatives(2d - 2))
return NormOneBridge{T, F, G, H}(y, ge_index, nn_index)
end

MOI.supports_constraint(::Type{NormOneBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T = true
MOIB.added_constrained_variable_types(::Type{<:NormOneBridge}) = Tuple{DataType}[]
MOIB.added_constraint_types(::Type{NormOneBridge{T, F, G, H}}) where {T, F, G, H} = [(F, MOI.GreaterThan{T}), (G, MOI.Nonnegatives)]
function concrete_bridge_type(::Type{<:NormOneBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, ::Type{MOI.NormOneCone}) where T
S = MOIU.scalar_type(H)
F = MOIU.promote_operation(+, T, S, S)
G = MOIU.promote_operation(+, T, H, H)
return NormOneBridge{T, F, G, H}
end

# Attributes, Bridge acting as a model
MOI.get(b::NormOneBridge, ::MOI.NumberOfVariables) = length(b.y)
MOI.get(b::NormOneBridge, ::MOI.ListOfVariableIndices) = b.y
MOI.get(b::NormOneBridge{T, F, G, H}, ::MOI.NumberOfConstraints{F, MOI.GreaterThan{T}}) where {T, F, G, H} = 1
MOI.get(b::NormOneBridge{T, F, G, H}, ::MOI.NumberOfConstraints{G, MOI.Nonnegatives}) where {T, F, G, H} = 1
MOI.get(b::NormOneBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{F, MOI.GreaterThan{T}}) where {T, F, G, H} = [b.ge_index]
MOI.get(b::NormOneBridge{T, F, G, H}, ::MOI.ListOfConstraintIndices{G, MOI.Nonnegatives}) where {T, F, G, H} = [b.nn_index]

# References
function MOI.delete(model::MOI.ModelLike, c::NormOneBridge)
MOI.delete(model, c.nn_index)
MOI.delete(model, c.ge_index)
MOI.delete(model, c.y)
end

# Attributes, Bridge acting as a constraint
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintFunction, c::NormOneBridge{T, F, G, H}) where {T, F, G, H}
ge_func = MOI.get(model, MOI.ConstraintFunction(), c.ge_index)
nn_func = MOIU.eachscalar(MOI.get(model, MOI.ConstraintFunction(), c.nn_index))
t = MOIU.operate!(+, T, ge_func, MOIU.operate!(/, T, sum(nn_func), T(2)))
d = div(length(nn_func), 2)
x = MOIU.operate!(/, T, MOIU.operate!(-, T, nn_func[(d + 1):end], nn_func[1:d]), T(2))
return MOIU.convert_approx(H, MOIU.remove_variable(MOIU.operate(vcat, T, t, x), c.y))
end
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormOneBridge)
dim = 1 + div(MOI.dimension(MOI.get(model, MOI.ConstraintSet(), c.nn_index)), 2)
return MOI.NormOneCone(dim)
end
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintPrimal, c::NormOneBridge)
ge_primal = MOI.get(model, MOI.ConstraintPrimal(), c.ge_index)
nn_primal = MOI.get(model, MOI.ConstraintPrimal(), c.nn_index)
t = ge_primal + sum(nn_primal) / 2
d = length(c.y)
x = (nn_primal[(d + 1):end] - nn_primal[1:d]) / 2
return vcat(t, x)
end
# Given a_i is dual on y_i - x_i >= 0 and b_i is dual on y_i + x_i >= 0 and c is dual on t - sum(y) >= 0,
# the dual on (t, x) in NormOneCone is (u, v) in NormInfinityCone, where
# v_i = -a_i + b_i and u = c.
function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintDual, c::NormOneBridge)
t = MOI.get(model, MOI.ConstraintDual(), c.ge_index)
nn_dual = MOI.get(model, MOI.ConstraintDual(), c.nn_index)
d = div(length(nn_dual), 2)
x = (nn_dual[(d + 1):end] - nn_dual[1:d])
return vcat(t, x)
end
6 changes: 6 additions & 0 deletions src/Test/UnitTests/basic_constraint_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const BasicConstraintTests = Dict(
(MOI.VectorOfVariables, MOI.Nonpositives) => ( dummy_vectorofvariables, 2, MOI.Nonpositives(2) ),
(MOI.VectorOfVariables, MOI.Nonnegatives) => ( dummy_vectorofvariables, 2, MOI.Nonnegatives(2) ),

(MOI.VectorOfVariables, MOI.NormInfinityCone) => ( dummy_vectorofvariables, 3, MOI.NormInfinityCone(3) ),
(MOI.VectorOfVariables, MOI.NormOneCone) => ( dummy_vectorofvariables, 3, MOI.NormOneCone(3) ),
(MOI.VectorOfVariables, MOI.SecondOrderCone) => ( dummy_vectorofvariables, 3, MOI.SecondOrderCone(3) ),
(MOI.VectorOfVariables, MOI.RotatedSecondOrderCone) => ( dummy_vectorofvariables, 3, MOI.RotatedSecondOrderCone(3) ),
(MOI.VectorOfVariables, MOI.GeometricMeanCone) => ( dummy_vectorofvariables, 3, MOI.GeometricMeanCone(3) ),
Expand Down Expand Up @@ -63,13 +65,17 @@ const BasicConstraintTests = Dict(
(MOI.VectorAffineFunction{Float64}, MOI.Nonpositives) => ( dummy_vector_affine, 2, MOI.Nonpositives(2) ),
(MOI.VectorAffineFunction{Float64}, MOI.Nonnegatives) => ( dummy_vector_affine, 2, MOI.Nonnegatives(2) ),

(MOI.VectorAffineFunction{Float64}, MOI.NormInfinityCone) => ( dummy_vector_affine, 3, MOI.NormInfinityCone(3) ),
(MOI.VectorAffineFunction{Float64}, MOI.NormOneCone) => ( dummy_vector_affine, 3, MOI.NormOneCone(3) ),
(MOI.VectorAffineFunction{Float64}, MOI.SecondOrderCone) => ( dummy_vector_affine, 3, MOI.SecondOrderCone(3) ),
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => ( dummy_vector_affine, 3, MOI.RotatedSecondOrderCone(3) ),

(MOI.VectorQuadraticFunction{Float64}, MOI.Zeros) => ( dummy_vector_quadratic, 2, MOI.Zeros(2) ),
(MOI.VectorQuadraticFunction{Float64}, MOI.Nonpositives) => ( dummy_vector_quadratic, 2, MOI.Nonpositives(2) ),
(MOI.VectorQuadraticFunction{Float64}, MOI.Nonnegatives) => ( dummy_vector_quadratic, 2, MOI.Nonnegatives(2) ),

(MOI.VectorQuadraticFunction{Float64}, MOI.NormInfinityCone) => ( dummy_vector_quadratic, 3, MOI.NormInfinityCone(3) ),
(MOI.VectorQuadraticFunction{Float64}, MOI.NormOneCone) => ( dummy_vector_quadratic, 3, MOI.NormOneCone(3) ),
(MOI.VectorQuadraticFunction{Float64}, MOI.SecondOrderCone) => ( dummy_vector_quadratic, 3, MOI.SecondOrderCone(3) ),
(MOI.VectorQuadraticFunction{Float64}, MOI.RotatedSecondOrderCone) => ( dummy_vector_quadratic, 3, MOI.RotatedSecondOrderCone(3) )
)
Expand Down
Loading