Skip to content

Commit 9e3fe6b

Browse files
committed
Add tests
1 parent 046e0a3 commit 9e3fe6b

File tree

10 files changed

+212
-43
lines changed

10 files changed

+212
-43
lines changed

docs/src/background/duality.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ and similarly, the dual is:
114114
The scalar product is different from the canonical one for the sets
115115
[`PositiveSemidefiniteConeTriangle`](@ref), [`LogDetConeTriangle`](@ref),
116116
[`RootDetConeTriangle`](@ref),
117-
[`SetWithDotProducts`](@ref) and
117+
[`SetDotProducts`](@ref) and
118118
[`LinearCombinationInSet`](@ref).
119119

120120
If the set ``C_i`` of the section [Duality](@ref) is one of these three cones,

docs/src/manual/standard_form.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ The vector-valued set types implemented in MathOptInterface.jl are:
8282
| [`RelativeEntropyCone(d)`](@ref MathOptInterface.RelativeEntropyCone) | ``\{ (u, v, w) \in \mathbb{R}^{d} : u \ge \sum_i w_i \log (\frac{w_i}{v_i}), v_i \ge 0, w_i \ge 0 \}`` |
8383
| [`HyperRectangle(l, u)`](@ref MathOptInterface.HyperRectangle) | ``\{x \in \bar{\mathbb{R}}^d: x_i \in [l_i, u_i] \forall i=1,\ldots,d\}`` |
8484
| [`NormCone(p, d)`](@ref MathOptInterface.NormCone) | ``\{ (t,x) \in \mathbb{R}^{d} : t \ge \left(\sum\limits_i \lvert x_i \rvert^p\right)^{\frac{1}{p}} \}`` |
85-
| [`SetWithDotProducts(s, v)`](@ref MathOptInterface.SetWithDotProducts) | The cone `s` with dot products with the fixed vectors `v`. |
85+
| [`SetDotProducts(s, v)`](@ref MathOptInterface.SetDotProducts) | The cone `s` with dot products with the fixed vectors `v`. |
8686
| [`LinearCombinationInSet(s, v)`](@ref MathOptInterface.LinearCombinationInSet) | The cone of vector `(y, x)` such that ``\sum_i y_i v_i + x`` belongs to `s`. |
8787

8888
## Matrix cones

docs/src/reference/standard_form.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,6 @@ LogDetConeTriangle
153153
LogDetConeSquare
154154
RootDetConeTriangle
155155
RootDetConeSquare
156-
SetWithDotProducts
156+
SetDotProducts
157157
LinearCombinationInSet
158158
```

src/Bridges/Constraint/Constraint.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T}
108108
MOI.Bridges.add_bridge(bridged_model, SOS1ToMILPBridge{T})
109109
MOI.Bridges.add_bridge(bridged_model, SOS2ToMILPBridge{T})
110110
MOI.Bridges.add_bridge(bridged_model, IndicatorToMILPBridge{T})
111+
MOI.Bridges.add_bridge(bridged_model, LinearCombinationBridge{T})
111112
return
112113
end
113114

src/Bridges/Variable/bridges/set_dot.jl

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,47 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
17
struct DotProductsBridge{T,S,A,V} <:
2-
SetMapBridge{T,S,MOI.SetWithDotProducts{S,A,V}}
8+
SetMapBridge{T,S,MOI.SetDotProducts{S,A,V}}
39
variables::Vector{MOI.VariableIndex}
410
constraint::MOI.ConstraintIndex{MOI.VectorOfVariables,S}
5-
set::MOI.SetWithDotProducts{S,A,V}
11+
set::MOI.SetDotProducts{S,A,V}
612
end
713

814
function supports_constrained_variable(
915
::Type{<:DotProductsBridge},
10-
::Type{<:MOI.SetWithDotProducts},
16+
::Type{<:MOI.SetDotProducts},
1117
)
1218
return true
1319
end
1420

1521
function concrete_bridge_type(
1622
::Type{<:DotProductsBridge{T}},
17-
::Type{MOI.SetWithDotProducts{S,A,V}},
23+
::Type{MOI.SetDotProducts{S,A,V}},
1824
) where {T,S,A,V}
1925
return DotProductsBridge{T,S,A,V}
2026
end
2127

2228
function bridge_constrained_variable(
2329
BT::Type{DotProductsBridge{T,S,A,V}},
2430
model::MOI.ModelLike,
25-
set::MOI.SetWithDotProducts{S,A,V},
31+
set::MOI.SetDotProducts{S,A,V},
2632
) where {T,S,A,V}
2733
variables, constraint =
2834
_add_constrained_var(model, MOI.Bridges.inverse_map_set(BT, set))
2935
return BT(variables, constraint, set)
3036
end
3137

32-
function MOI.Bridges.map_set(bridge::DotProductsBridge{T,S}, set::S) where {T,S}
38+
function MOI.Bridges.map_set(bridge::DotProductsBridge{T,S}, ::S) where {T,S}
3339
return bridge.set
3440
end
3541

3642
function MOI.Bridges.inverse_map_set(
3743
::Type{<:DotProductsBridge},
38-
set::MOI.SetWithDotProducts,
44+
set::MOI.SetDotProducts,
3945
)
4046
return set.set
4147
end
@@ -53,6 +59,21 @@ function MOI.Bridges.map_function(
5359
)
5460
end
5561

62+
function MOI.Bridges.map_function(
63+
bridge::DotProductsBridge{T},
64+
func,
65+
) where {T}
66+
scalars = MOI.Utilities.eachscalar(func)
67+
return MOI.Utilities.vectorize([
68+
MOI.Utilities.set_dot(
69+
vector,
70+
scalars,
71+
bridge.set.set,
72+
)
73+
for vector in bridge.set.vectors
74+
])
75+
end
76+
5677
# This returns `true` by default for `SetMapBridge`
5778
# but is is not supported for this bridge because `inverse_map_function`
5879
# is not implemented

src/Bridges/Variable/set_map.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ function MOI.get(
149149
bridge::SetMapBridge,
150150
)
151151
value = MOI.get(model, attr, bridge.constraint)
152-
return MOI.Bridges.map_function(typeof(bridge), value)
152+
return MOI.Bridges.map_function(bridge, value)
153153
end
154154

155155
function MOI.get(
@@ -224,7 +224,7 @@ function unbridged_map(
224224
func = MOI.VectorOfVariables(vis)
225225
funcs = MOI.Bridges.inverse_map_function(bridge, func)
226226
scalars = MOI.Utilities.eachscalar(funcs)
227-
# FIXME not correct for SetWithDotProducts, it won't recover the dot product variables
227+
# FIXME not correct for SetDotProducts, it won't recover the dot product variables
228228
return Pair{MOI.VariableIndex,F}[
229229
bridge.variables[i] => scalars[i] for i in eachindex(bridge.variables)
230230
]

src/Test/test_conic.jl

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5770,6 +5770,165 @@ function setup_test(
57705770
return
57715771
end
57725772

5773+
"""
5774+
The goal is to find the maximum lower bound `γ` for the polynomial `x^2 - 2x`.
5775+
Using samples `-1` and `1`, the polynomial `x^2 - 2x - γ` evaluates at `-γ`
5776+
and `2 - γ` respectively.
5777+
The dot product with the gram matrix is the evaluation of `[1; x] * [1 x]` hence
5778+
`[1; -1] * [1 -1]` and `[1; 1] * [1 1]` respectively.
5779+
5780+
The polynomial version is:
5781+
max γ
5782+
s.t. [-γ, 2 - γ] in SetDotProducts(
5783+
PSD(2),
5784+
[[1; -1] * [1 -1], [1; 1] * [1 1]],
5785+
)
5786+
Its dual (moment version) is:
5787+
min -y[1] - y[2]
5788+
s.t. [-γ, 2 - γ] in LinearCombinationInSet(
5789+
PSD(2),
5790+
[[1; -1] * [1 -1], [1; 1] * [1 1]],
5791+
)
5792+
"""
5793+
function test_conic_PositiveSemidefinite_RankOne_polynomial(
5794+
model::MOI.ModelLike,
5795+
config::Config{T},
5796+
) where {T}
5797+
set = MOI.SetDotProducts(
5798+
MOI.PositiveSemidefiniteConeTriangle(2),
5799+
MOI.TriangleVectorization.([
5800+
MOI.PositiveSemidefiniteFactorization(T[1, -1]),
5801+
MOI.PositiveSemidefiniteFactorization(T[1, 1]),
5802+
]),
5803+
)
5804+
@requires MOI.supports_constraint(model, MOI.VectorAffineFunction{T}, typeof(set))
5805+
@requires MOI.supports_incremental_interface(model)
5806+
@requires MOI.supports(model, MOI.ObjectiveSense())
5807+
@requires MOI.supports(model, MOI.ObjectiveFunction{MOI.VariableIndex}())
5808+
γ = MOI.add_variable(model)
5809+
c = MOI.add_constraint(
5810+
model,
5811+
MOI.Utilities.operate(vcat, T, T(3) - T(1) * γ, T(-1) - T(1) * γ),
5812+
set,
5813+
)
5814+
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
5815+
MOI.set(model, MOI.ObjectiveFunction{MOI.VariableIndex}(), γ)
5816+
if _supports(config, MOI.optimize!)
5817+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
5818+
MOI.optimize!(model)
5819+
@test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
5820+
@test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
5821+
if _supports(config, MOI.ConstraintDual)
5822+
@test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
5823+
end
5824+
@test (MOI.get(model, MOI.ObjectiveValue()), T(-1), config)
5825+
if _supports(config, MOI.DualObjectiveValue)
5826+
@test (MOI.get(model, MOI.DualObjectiveValue()), T(-1), config)
5827+
end
5828+
@test (MOI.get(model, MOI.VariablePrimal(), γ), T(-1), config)
5829+
@test (MOI.get(model, MOI.ConstraintPrimal(), c), T[4, 0], config)
5830+
if _supports(config, MOI.ConstraintDual)
5831+
@test (MOI.get(model, MOI.ConstraintDual(), c), T[0, 1], config)
5832+
end
5833+
end
5834+
return
5835+
end
5836+
5837+
function setup_test(
5838+
::typeof(test_conic_PositiveSemidefinite_RankOne_polynomial),
5839+
model::MOIU.MockOptimizer,
5840+
::Config{T},
5841+
) where {T<:Real}
5842+
A = MOI.TriangleVectorization{T,MOI.PositiveSemidefiniteFactorization{T,Vector{T}}}
5843+
MOIU.set_mock_optimize!(
5844+
model,
5845+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
5846+
mock,
5847+
T[-1],
5848+
(MOI.VectorAffineFunction{T}, MOI.SetDotProducts{
5849+
MOI.PositiveSemidefiniteConeTriangle,
5850+
A,
5851+
Vector{A},
5852+
}) => [T[0, 1]],
5853+
),
5854+
)
5855+
return
5856+
end
5857+
5858+
"""
5859+
The moment version of `test_conic_PositiveSemidefinite_RankOne_polynomial`
5860+
5861+
We look for a measure `μ = y1 * δ_{-1} + y2 * δ_{1}` where `δ_{c}` is the Dirac
5862+
measure centered at `c`. The objective is
5863+
`⟨μ, x^2 - 2x⟩ = y1 * ⟨δ_{-1}, x^2 - 2x⟩ + y2 * ⟨δ_{1}, x^2 - 2x⟩ = 3y1 - y2`.
5864+
We want `μ` to be a probability measure so `1 = ⟨μ, 1⟩ = y1 + y2`.
5865+
"""
5866+
function test_conic_PositiveSemidefinite_RankOne_moment(
5867+
model::MOI.ModelLike,
5868+
config::Config{T},
5869+
) where {T}
5870+
set = MOI.LinearCombinationInSet(
5871+
MOI.PositiveSemidefiniteConeTriangle(2),
5872+
MOI.TriangleVectorization.([
5873+
MOI.PositiveSemidefiniteFactorization(T[1, -1]),
5874+
MOI.PositiveSemidefiniteFactorization(T[1, 1]),
5875+
]),
5876+
)
5877+
@requires MOI.supports_add_constrained_variables(model, typeof(set))
5878+
@requires MOI.supports_incremental_interface(model)
5879+
@requires MOI.supports(model, MOI.ObjectiveSense())
5880+
@requires MOI.supports(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}())
5881+
y, cy = MOI.add_constrained_variables(
5882+
model,
5883+
set,
5884+
)
5885+
c = MOI.add_constraint(model, T(1) * y[1] + T(1) * y[2], MOI.EqualTo(T(1)))
5886+
MOI.set(model, MOI.ObjectiveSense(), MOI.MIN_SENSE)
5887+
MOI.set(model, MOI.ObjectiveFunction{MOI.ScalarAffineFunction{T}}(), T(3) * y[1] - T(1) * y[2])
5888+
if _supports(config, MOI.optimize!)
5889+
@test MOI.get(model, MOI.TerminationStatus()) == MOI.OPTIMIZE_NOT_CALLED
5890+
MOI.optimize!(model)
5891+
@test MOI.get(model, MOI.TerminationStatus()) == config.optimal_status
5892+
@test MOI.get(model, MOI.PrimalStatus()) == MOI.FEASIBLE_POINT
5893+
if _supports(config, MOI.ConstraintDual)
5894+
@test MOI.get(model, MOI.DualStatus()) == MOI.FEASIBLE_POINT
5895+
end
5896+
@test (MOI.get(model, MOI.ObjectiveValue()), T(-1), config)
5897+
if _supports(config, MOI.DualObjectiveValue)
5898+
@test (MOI.get(model, MOI.DualObjectiveValue()), T(-1), config)
5899+
end
5900+
@test (MOI.get(model, MOI.VariablePrimal(), y), T[0, 1], config)
5901+
@test (MOI.get(model, MOI.ConstraintPrimal(), c), T(1), config)
5902+
if _supports(config, MOI.ConstraintDual)
5903+
@test (MOI.get(model, MOI.ConstraintDual(), cy), T[4, 0], config)
5904+
@test (MOI.get(model, MOI.ConstraintDual(), c), T(-1), config)
5905+
end
5906+
end
5907+
return
5908+
end
5909+
5910+
function setup_test(
5911+
::typeof(test_conic_PositiveSemidefinite_RankOne_moment),
5912+
model::MOIU.MockOptimizer,
5913+
::Config{T},
5914+
) where {T<:Real}
5915+
A = MOI.TriangleVectorization{T,MOI.PositiveSemidefiniteFactorization{T,Vector{T}}}
5916+
MOIU.set_mock_optimize!(
5917+
model,
5918+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
5919+
mock,
5920+
T[0, 1],
5921+
(MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}) => [T(-1)],
5922+
(MOI.VectorOfVariables, MOI.LinearCombinationInSet{
5923+
MOI.PositiveSemidefiniteConeTriangle,
5924+
A,
5925+
Vector{A},
5926+
}) => [T[4, 0]],
5927+
),
5928+
)
5929+
return
5930+
end
5931+
57735932
"""
57745933
_test_det_cone_helper_ellipsoid(
57755934
model::MOI.ModelLike,

src/Utilities/set_dot.jl

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,6 @@ function set_dot(
8686
return x[1] * y[1] + x[2] * y[2] + triangle_dot(x, y, set.side_dimension, 2)
8787
end
8888

89-
function set_dot(
90-
x::AbstractVector,
91-
y::AbstractVector,
92-
set::Union{MOI.SetWithDotProducts,MOI.LinearCombinationInSet},
93-
)
94-
m = length(set.matrices)
95-
return LinearAlgebra.dot(view(x, 1:m), view(y, 1:m)) +
96-
set_dot(view(x, (m+1):length(x)), view(y, (m+1):length(y)), set.set)
97-
end
98-
9989
"""
10090
dot_coefficients(a::AbstractVector, set::AbstractVectorSet)
10191
@@ -155,16 +145,6 @@ function dot_coefficients(a::AbstractVector, set::MOI.LogDetConeTriangle)
155145
return b
156146
end
157147

158-
function dot_coefficients(
159-
a::AbstractVector,
160-
set::Union{MOI.SetWithDotProducts,MOI.LinearCombinationInSet},
161-
)
162-
b = copy(a)
163-
m = length(set.vectors)
164-
b[(m+1):end] = dot_coefficients(b[(m+1):end], set.set)
165-
return b
166-
end
167-
168148
# For `SetDotScalingVector`, we would like to compute the dot product
169149
# of canonical vectors in O(1) instead of O(n)
170150
# See https://github.com/jump-dev/Dualization.jl/pull/135

src/sets.jl

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1804,31 +1804,31 @@ function Base.getproperty(
18041804
end
18051805

18061806
"""
1807-
SetWithDotProducts(set::MOI.AbstractSet, vectors::AbstractVector)
1807+
SetDotProducts(set::MOI.AbstractSet, vectors::AbstractVector)
18081808
18091809
Given a set `set` of dimension `d` and `m` vectors `a_1`, ..., `a_m` given in `vectors`, this is the set:
18101810
``\\{ ((\\langle a_1, x \\rangle, ..., \\langle a_m, x \\rangle) \\in \\mathbb{R}^{m} : x \\in \\text{set} \\}.``
18111811
"""
1812-
struct SetWithDotProducts{S,A,V<:AbstractVector{A}} <: AbstractVectorSet
1812+
struct SetDotProducts{S,A,V<:AbstractVector{A}} <: AbstractVectorSet
18131813
set::S
18141814
vectors::V
18151815
end
18161816

1817-
function Base.:(==)(s1::SetWithDotProducts, s2::SetWithDotProducts)
1817+
function Base.:(==)(s1::SetDotProducts, s2::SetDotProducts)
18181818
return s1.set == s2.set && s1.vectors == s2.vectors
18191819
end
18201820

1821-
function Base.copy(s::SetWithDotProducts)
1822-
return SetWithDotProducts(copy(s.set), copy(s.vectors))
1821+
function Base.copy(s::SetDotProducts)
1822+
return SetDotProducts(copy(s.set), copy(s.vectors))
18231823
end
18241824

1825-
dimension(s::SetWithDotProducts) = length(s.vectors)
1825+
dimension(s::SetDotProducts) = length(s.vectors)
18261826

1827-
function dual_set(s::SetWithDotProducts)
1827+
function dual_set(s::SetDotProducts)
18281828
return LinearCombinationInSet(s.set, s.vectors)
18291829
end
18301830

1831-
function dual_set_type(::Type{SetWithDotProducts{S,A,V}}) where {S,A,V}
1831+
function dual_set_type(::Type{SetDotProducts{S,A,V}}) where {S,A,V}
18321832
return LinearCombinationInSet{S,A,V}
18331833
end
18341834

@@ -1843,14 +1843,22 @@ struct LinearCombinationInSet{S,A,V<:AbstractVector{A}} <: AbstractVectorSet
18431843
vectors::V
18441844
end
18451845

1846+
function Base.:(==)(s1::LinearCombinationInSet, s2::LinearCombinationInSet)
1847+
return s1.set == s2.set && s1.vectors == s2.vectors
1848+
end
1849+
1850+
function Base.copy(s::LinearCombinationInSet)
1851+
return LinearCombinationInSet(copy(s.set), copy(s.vectors))
1852+
end
1853+
18461854
dimension(s::LinearCombinationInSet) = length(s.vectors)
18471855

18481856
function dual_set(s::LinearCombinationInSet)
1849-
return SetWithDotProducts(s.side_dimension, s.vectors)
1857+
return SetDotProducts(s.side_dimension, s.vectors)
18501858
end
18511859

18521860
function dual_set_type(::Type{LinearCombinationInSet{S,A,V}}) where {S,A,V}
1853-
return SetWithDotProducts{S,A,V}
1861+
return SetDotProducts{S,A,V}
18541862
end
18551863

18561864
abstract type AbstractFactorization{T,F} <: AbstractMatrix{T} end

0 commit comments

Comments
 (0)