Skip to content

Commit 7c7b184

Browse files
authored
Merge pull request #109 from JuliaOpt/bl/psd2x2_variable_bridge
Add PSD2x2 variable bridge
2 parents 780d4f5 + 53e8d6c commit 7c7b184

15 files changed

+144
-74
lines changed

src/SumOfSquares.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const MOIB = MOI.Bridges
3535

3636
# Variable Bridges
3737
include("variable_bridge.jl")
38+
include("psd2x2_variable_bridge.jl")
3839
include("scaled_diagonally_dominant_variable_bridge.jl")
3940
include("generic_variable_bridge.jl")
4041
include("copositive_inner_variable_bridge.jl")

src/attributes.jl

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,46 @@ A constraint attribute for the monomials indexing the
77
struct CertificateMonomials <: MOI.AbstractConstraintAttribute end
88

99
"""
10-
struct GramMatrixAttribute <: MOI.AbstractConstraintAttribute end
10+
GramMatrixAttribute(N)
11+
GramMatrixAttribute()
1112
1213
A constraint attribute for the [`GramMatrix`](@ref) of a constraint, that is,
1314
the positive semidefinite matrix `Q` indexed by the monomials in the vector `X`
1415
such that ``X^\\top Q X`` is the sum-of-squares certificate of the constraint.
15-
The
1616
"""
17-
struct GramMatrixAttribute <: MOI.AbstractConstraintAttribute end
17+
struct GramMatrixAttribute <: MOI.AbstractConstraintAttribute
18+
N::Int
19+
end
20+
GramMatrixAttribute() = GramMatrixAttribute(1)
1821

1922
"""
20-
struct MomentMatrixAttribute <: MOI.AbstractConstraintAttribute end
23+
MomentMatrixAttribute(N)
24+
MomentMatrixAttribute()
2125
2226
A constraint attribute fot the `MomentMatrix` of a constraint.
2327
"""
24-
struct MomentMatrixAttribute <: MOI.AbstractConstraintAttribute end
28+
struct MomentMatrixAttribute <: MOI.AbstractConstraintAttribute
29+
N::Int
30+
end
31+
MomentMatrixAttribute() = MomentMatrixAttribute(1)
2532

2633
"""
27-
struct LagrangianMultipliers <: MOI.AbstractConstraintAttribute end
34+
LagrangianMultipliers(N)
35+
LagrangianMultipliers()
2836
29-
A constraint attribute fot the `LagrangianMultipliers` assiciated to the
37+
A constraint attribute fot the `LagrangianMultipliers` associated to the
3038
inequalities of the domain of a constraint. There is one multiplier per
3139
inequality and no multiplier for equalities as the equalities are handled by
3240
reducing the polynomials over the ideal they generate instead of explicitely
3341
creating multipliers.
3442
"""
35-
struct LagrangianMultipliers <: MOI.AbstractConstraintAttribute end
43+
struct LagrangianMultipliers <: MOI.AbstractConstraintAttribute
44+
N::Int
45+
end
46+
LagrangianMultipliers() = LagrangianMultipliers(1)
3647

48+
# Needs to declare it set by optimize that it is not queried in the Caching
49+
# optimize, even of `CertificateMonomials` which is set befor optimize.
3750
function MOI.is_set_by_optimize(::Union{CertificateMonomials,
3851
GramMatrixAttribute,
3952
MomentMatrixAttribute,

src/copositive_inner.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ which is the same as, using the `domain` keyword,
4444
@constraint(model, x^2 - 2x*y + y^2 in SOSCone(), domain = @set x*y ≥ 0)
4545
```
4646
47-
For consistency with its equivalent forms, the [`GramMatrixAttribute`](@ref) for
48-
this constraint is given by the gram matrix in the `psd_inner` cone, i.e. which
49-
should be equal to `Q - Λ`.
47+
As an important difference with its equivalent forms, the
48+
[`GramMatrixAttribute`](@ref) for the copositive constraint is given by matrix
49+
`Q` while for the equivalent form using the domain` keyword, the value
50+
of the attribute would correspond to the the gram matrix in the `psd_inner`
51+
cone, i.e. which should be equal to `Q - Λ`.
5052
5153
[BPT12] Blekherman, G.; Parrilo, P. A. & Thomas, R. R.
5254
*Semidefinite Optimization and Convex Algebraic Geometry*.

src/copositive_inner_variable_bridge.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function MOI.delete(model::MOI.ModelLike, bridge::CopositiveInnerVariableBridge)
4040
end
4141

4242
function MOI.get(model::MOI.ModelLike,
43-
attr::Union{MomentMatrixAttribute, GramMatrixAttribute},
43+
attr::Union{MOI.ConstraintDual, MOI.ConstraintPrimal},
4444
bridge::CopositiveInnerVariableBridge)
4545
return MOI.get(model, attr, bridge.variable_bridge)
4646
end

src/generic_variable_bridge.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,13 @@ function MOI.delete(model::MOI.ModelLike, bridge::GenericVariableBridge)
4242
end
4343
end
4444

45-
function MOI.get(model::MOI.ModelLike, ::MomentMatrixAttribute,
45+
function MOI.get(model::MOI.ModelLike,
46+
attr::MOI.ConstraintPrimal,
4647
bridge::GenericVariableBridge)
47-
return MOI.get(model, MOI.ConstraintDual(), bridge.gram_constraint)
48+
return MOI.get(model, MOI.VariablePrimal(attr.N), bridge.gram_matrix)
4849
end
49-
50-
function MOI.get(model::MOI.ModelLike, ::GramMatrixAttribute,
50+
function MOI.get(model::MOI.ModelLike,
51+
attr::MOI.ConstraintDual,
5152
bridge::GenericVariableBridge)
52-
return MOI.get(model, MOI.VariablePrimal(), bridge.gram_matrix)
53+
return MOI.get(model, attr, bridge.gram_constraint)
5354
end

src/psd2x2_variable_bridge.jl

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
struct PositiveSemidefinite2x2VariableBridge{T} <: AbstractVariableBridge
2+
variables::Vector{MOI.VariableIndex}
3+
rsoc::MOI.ConstraintIndex{MOI.VectorOfVariables,
4+
MOI.RotatedSecondOrderCone}
5+
end
6+
7+
function add_variable_bridge(
8+
::Type{PositiveSemidefinite2x2VariableBridge{T}}, model::MOI.ModelLike,
9+
s::PositiveSemidefinite2x2ConeTriangle) where {T}
10+
x = MOI.add_variables(model, 3)
11+
rsoc = MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.RotatedSecondOrderCone(3))
12+
Q12 = MOIU.operate(/, T, MOI.SingleVariable(x[3]), convert(T, 2))
13+
g = typeof(Q12)[MOI.SingleVariable(x[1]), Q12, MOI.SingleVariable(x[2])]
14+
return g, PositiveSemidefinite2x2VariableBridge{T}(x, rsoc)
15+
end
16+
17+
function MOIB.added_constraint_types(
18+
::Type{PositiveSemidefinite2x2VariableBridge{T}}) where {T}
19+
return [(MOI.VectorOfVariables, MOI.RotatedSecondOrderCone)]
20+
end
21+
22+
function variable_bridge_type(::Type{PositiveSemidefinite2x2ConeTriangle},
23+
T::Type)
24+
return PositiveSemidefinite2x2VariableBridge{T}
25+
end
26+
27+
28+
# Attributes, VariableBridge acting as an model
29+
function MOI.get(::PositiveSemidefinite2x2VariableBridge,
30+
::MOI.NumberOfVariables)
31+
return 3
32+
end
33+
function MOI.get(
34+
::PositiveSemidefinite2x2VariableBridge,
35+
::MOI.NumberOfConstraints{MOI.VectorOfVariables,
36+
MOI.RotatedSecondOrderCone})
37+
return 1
38+
end
39+
function MOI.get(
40+
bridge::PositiveSemidefinite2x2VariableBridge,
41+
::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,
42+
MOI.RotatedSecondOrderCone})
43+
return [bridge.rsoc]
44+
end
45+
46+
# Indices
47+
function MOI.delete(model::MOI.ModelLike,
48+
bridge::PositiveSemidefinite2x2VariableBridge)
49+
MOI.delete(model, bridge.rsoc)
50+
for vi in bridge.variables
51+
MOI.delete(model, vi)
52+
end
53+
end
54+
55+
function MOI.get(model::MOI.ModelLike,
56+
attr::MOI.ConstraintPrimal,
57+
bridge::PositiveSemidefinite2x2VariableBridge)
58+
value = MOI.get(model, attr, bridge.rsoc)
59+
return [value[1], value[3] / 2, value[2]]
60+
end
61+
function MOI.get(model::MOI.ModelLike,
62+
attr::MOI.ConstraintDual,
63+
bridge::PositiveSemidefinite2x2VariableBridge)
64+
dual = MOI.get(model, attr, bridge.rsoc)
65+
# / 2 (because of different scalar product) * √2 (because of A^{-*} = 1 / √2
66+
return [dual[1], dual[3] / 2, dual[2]]
67+
end
Lines changed: 27 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
struct ScaledDiagonallyDominantVariableBridge{T} <: AbstractVariableBridge
2-
side_dimension::Int
3-
variables::Vector{NTuple{3, MOI.VariableIndex}}
4-
psd2x2::Vector{MOI.ConstraintIndex{MOI.VectorOfVariables,
5-
PositiveSemidefinite2x2ConeTriangle}}
1+
struct ScaledDiagonallyDominantVariableBridge{T, VBT} <: AbstractVariableBridge
2+
psd2x2::Vector{VBT}
63
end
74

85
function add_variable_bridge(
9-
::Type{ScaledDiagonallyDominantVariableBridge{T}}, model::MOI.ModelLike,
10-
s::ScaledDiagonallyDominantConeTriangle) where {T}
6+
::Type{ScaledDiagonallyDominantVariableBridge{T, VBT}}, model::MOI.ModelLike,
7+
s::ScaledDiagonallyDominantConeTriangle) where {T, VBT}
118
# `p.Q` is SDD iff it is the sum of psd matrices Mij that are zero except
129
# for entries ii, ij and jj [Lemma 9, AM17].
1310
n = s.side_dimension
@@ -17,62 +14,53 @@ function add_variable_bridge(
1714
F = MOI.ScalarAffineFunction{T}
1815
g = F[zero(F) for i in 1:MOI.dimension(s)]
1916
num_off_diag = MOI.dimension(s) - n
20-
variables = Vector{NTuple{3, MOI.VariableIndex}}(undef, num_off_diag)
21-
psd2x2 = Vector{MOI.ConstraintIndex{
22-
MOI.VectorOfVariables, PositiveSemidefinite2x2ConeTriangle}}(
23-
undef, num_off_diag)
17+
psd2x2 = Vector{VBT}(undef, num_off_diag)
2418
diag_idx(i) = div(i * (i + 1), 2)
2519
k = 0
2620
k2x2 = 0
2721
for j in 1:n
2822
for i in 1:(j-1)
2923
k += 1
3024
k2x2 += 1
31-
vii, vij, vjj = MOI.add_variables(model, 3)
32-
variables[k2x2] = (vii, vij, vjj)
33-
Mii = MOI.SingleVariable(vii)
34-
MOIU.operate!(+, T, g[diag_idx(i)], Mii)
35-
Mij = MOI.SingleVariable(vij)
36-
MOIU.operate!(+, T, g[k], Mij)
37-
Mjj = MOI.SingleVariable(vjj)
38-
MOIU.operate!(+, T, g[diag_idx(j)], Mjj)
3925
# PSD constraints on 2x2 matrices are SOC representable
40-
psd2x2[k2x2] = MOI.add_constraint(
41-
model, MOI.VectorOfVariables([vii, vij, vjj]),
42-
PositiveSemidefinite2x2ConeTriangle())
26+
x, psd2x2[k2x2] = add_variable_bridge(VBT, model, PositiveSemidefinite2x2ConeTriangle())
27+
MOIU.operate!(+, T, g[diag_idx(i)], x[1])
28+
MOIU.operate!(+, T, g[k], x[2])
29+
MOIU.operate!(+, T, g[diag_idx(j)], x[3])
4330
end
4431
k += 1 # diagonal entry `(i, i)`
4532
end
46-
return g, ScaledDiagonallyDominantVariableBridge{T}(n, variables, psd2x2)
33+
return g, ScaledDiagonallyDominantVariableBridge{T, VBT}(psd2x2)
4734
end
4835

4936
function MOIB.added_constraint_types(
50-
::Type{ScaledDiagonallyDominantVariableBridge{T}}) where {T}
51-
return [(MOI.VectorOfVariables, PositiveSemidefinite2x2ConeTriangle)]
37+
::Type{ScaledDiagonallyDominantVariableBridge{T, VBT}}) where {T, VBT}
38+
added = Tuple{DataType, DataType}[]
39+
return append!(added, MOIB.added_constraint_types(VBT))
5240
end
5341

5442
function variable_bridge_type(::Type{ScaledDiagonallyDominantConeTriangle},
5543
T::Type)
56-
return ScaledDiagonallyDominantVariableBridge{T}
44+
VBT = variable_bridge_type(PositiveSemidefinite2x2ConeTriangle, T)
45+
return ScaledDiagonallyDominantVariableBridge{T, VBT}
5746
end
5847

5948

6049
# Attributes, VariableBridge acting as an model
61-
function MOI.get(bridge::ScaledDiagonallyDominantVariableBridge,
62-
::MOI.NumberOfVariables)
63-
return 3 * length(bridge.variables)
50+
function MOI.get(bridge::ScaledDiagonallyDominantVariableBridge, attr::MOI.NumberOfVariables)
51+
return reduce(+, MOI.get.(bridge.psd2x2, attr), init=0)
6452
end
65-
function MOI.get(
66-
bridge::ScaledDiagonallyDominantVariableBridge,
67-
::MOI.NumberOfConstraints{MOI.VectorOfVariables,
68-
ScaledDiagonallyDominantConeTriangle})
69-
return length(bridge.psd2x2)
53+
function MOI.get(bridge::ScaledDiagonallyDominantVariableBridge,
54+
attr::MOI.NumberOfConstraints)
55+
return reduce(+, MOI.get.(bridge.psd2x2, attr), init=0)
7056
end
71-
function MOI.get(
72-
bridge::ScaledDiagonallyDominantVariableBridge,
73-
::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,
74-
ScaledDiagonallyDominantConeTriangle})
75-
return bridge.psd2x2
57+
function MOI.get(bridge::ScaledDiagonallyDominantVariableBridge,
58+
attr::MOI.ListOfConstraintIndices{F, S}) where {F, S}
59+
list = MOI.ConstraintIndex{F, S}[]
60+
for variable_bridge in bridge.psd2x2
61+
append!(list, MOI.get(variable_bridge, attr))
62+
end
63+
return list
7664
end
7765

7866
# Indices
@@ -81,9 +69,4 @@ function MOI.delete(model::MOI.ModelLike,
8169
for ci in bridge.psd2x2
8270
MOI.delete(model, ci)
8371
end
84-
for (vii, vij, vjj) in bridge.variables
85-
MOI.delete(model, vii)
86-
MOI.delete(model, vij)
87-
MOI.delete(model, vjj)
88-
end
8972
end

src/sos_polynomial_bridge.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ function MOI.get(bridge::SOSPolynomialBridge,
9090
return MOI.get(bridge.variable_bridge, attr)
9191
end
9292

93-
9493
# Indices
9594
function MOI.delete(model::MOI.ModelLike, bridge::SOSPolynomialBridge)
9695
# First delete the constraints in which the Gram matrix appears
@@ -144,12 +143,13 @@ end
144143
function MOI.get(model::MOI.ModelLike,
145144
attr::GramMatrixAttribute,
146145
bridge::SOSPolynomialBridge)
147-
return build_gram_matrix(MOI.get(model, attr, bridge.variable_bridge),
146+
return build_gram_matrix(MOI.get(model, MOI.ConstraintPrimal(attr.N), bridge.variable_bridge),
148147
bridge.certificate_monomials)
149148
end
150149
function MOI.get(model::MOI.ModelLike,
151150
attr::MomentMatrixAttribute,
152151
bridge::SOSPolynomialBridge)
153-
return build_moment_matrix(MOI.get(model, attr, bridge.variable_bridge),
152+
return build_moment_matrix(MOI.get(model, MOI.ConstraintDual(attr.N),
153+
bridge.variable_bridge),
154154
bridge.certificate_monomials)
155155
end

src/sos_polynomial_in_semialgebraic_set_bridge.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ end
130130
function MOI.get(model::MOI.ModelLike, ::LagrangianMultipliers,
131131
bridge::SOSPolynomialInSemialgebraicSetBridge)
132132
@assert eachindex(bridge.lagrangian_bridges) == eachindex(bridge.lagrangian_monomials)
133-
map(i -> build_gram_matrix(MOI.get(model, GramMatrixAttribute(),
133+
map(i -> build_gram_matrix(MOI.get(model, MOI.ConstraintPrimal(),
134134
bridge.lagrangian_bridges[i]),
135135
bridge.lagrangian_monomials[i]),
136136
eachindex(bridge.lagrangian_bridges))

test/Mock/BPT12e399.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
config = MOI.Test.TestConfig()
2-
optimize!_max(mock) = MOIU.mock_optimize!(mock, [ 6.0, 1.0, -3.0, 9.0],
2+
optimize!_max(mock) = MOIU.mock_optimize!(mock, [ 6.0, 1.0, 9.0, -3.0*√2],
33
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[3, 1, 1/3]],
4-
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [[3, 1/3, 2]]
4+
(MOI.VectorOfVariables, MOI.RotatedSecondOrderCone) => [[3, 1/3, 2]]
55
)
6-
optimize!_min(mock) = MOIU.mock_optimize!(mock, [-6.0, 1.0, 3.0, 9.0],
6+
optimize!_min(mock) = MOIU.mock_optimize!(mock, [-6.0, 1.0, 9.0, 3.0*√2],
77
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[3, -1, 1/3]],
8-
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [[3, 1/3, -√2]]
8+
(MOI.VectorOfVariables, MOI.RotatedSecondOrderCone) => [[3, 1/3, -√2]]
99
)
1010
mock = bridged_mock(optimize!_max, optimize!_min)
1111
Tests.BPT12e399_test(mock, config)

test/Mock/bivariate_quadratic.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
config = MOI.Test.TestConfig()
2-
optimize!(mock) = MOIU.mock_optimize!(mock, [2.0, 1.0, 1.0, 1.0],
3-
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [[1.0, 1.0, -√2]],
2+
optimize!(mock) = MOIU.mock_optimize!(mock, [2.0, 1.0, 1.0, 2],
3+
(MOI.VectorOfVariables, MOI.RotatedSecondOrderCone) => [[1.0, 1.0, -√2]],
44
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[1.0, -1.0, 1.0]])
55
mock = bridged_mock(optimize!)
66
Tests.sos_bivariate_quadratic_test(mock, config)

test/Mock/concave_then_convex_cubic.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ Tests.dsos_concave_then_convex_cubic_test(mock, config)
99
optimize!(mock) = MOIU.mock_optimize!(mock, [1.0; zeros(5); 3.0; zeros(3); 3.0; zeros(7); 3.0; zeros(3); 3.0; zeros(5)],
1010
(MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => zeros(4),
1111
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [zeros(7), zeros(7)],
12-
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [zeros(3) for i in 1:8],
12+
(MOI.VectorOfVariables, MOI.RotatedSecondOrderCone) => [zeros(3) for i in 1:8],
1313
)
1414
mock = bridged_mock(optimize!)
1515
Tests.sdsos_concave_then_convex_cubic_test(mock, config)
1616
optimize!(mock) = MOIU.mock_optimize!(mock, [1.0; zeros(5); 6.0; zeros(8); 6.0; zeros(6)],
1717
(MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => zeros(4),
1818
(MOI.VectorOfVariables, MOI.PositiveSemidefiniteConeTriangle) => [zeros(6), zeros(6)],
1919
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [zeros(7), zeros(7)],
20-
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [zeros(3), zeros(3)],
20+
(MOI.VectorOfVariables, MOI.RotatedSecondOrderCone) => [zeros(3), zeros(3)],
2121
)
2222
mock = bridged_mock(optimize!)
2323
Tests.sos_concave_then_convex_cubic_test(mock, config)

test/Mock/lyapunov_switched_system.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ config = MOI.Test.TestConfig(atol=1e-5, rtol=1e-5)
1212
# 0 1+α]
1313
# P - A2'P*A*2 = [1+α 0
1414
# 0 β]
15-
optimize!(mock) = MOIU.mock_optimize!(mock, [α, 0.0, α, 1 + α, 0.0, β, 1 + α, β, 0.0])
15+
optimize!(mock) = MOIU.mock_optimize!(mock, [α, α, 0.0, β, 1 + α, 0.0, 1 + α, β, 0.0])
1616
mock = bridged_mock(optimize!)
1717
Tests.quadratic_feasible_lyapunov_switched_system_test(mock, config)
1818
# TODO quadratic infeasible and quartic

test/Tests/bivariate_quadratic.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ function bivariate_quadratic_test(optimizer,
1919
@objective(model, Max, α + 1)
2020
optimize!(model)
2121

22+
@test certificate_monomials(cref) == [x, 1]
23+
2224
@test termination_status(model) == MOI.OPTIMAL
2325
@test objective_value(model) 3.0 atol=atol rtol=rtol
2426

test/Tests/utilities.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ end
136136
# Constraint dual values for inner bridged model
137137
function inner_inspect(model, atol=1e-4)
138138
inner = _inner(backend(model))
139+
@show MOI.get(inner, MOI.NumberOfVariables())
139140
for (F, S) in MOI.get(inner, MOI.ListOfConstraints())
140141
@show F
141142
@show S

0 commit comments

Comments
 (0)