Skip to content

Commit 780d4f5

Browse files
authored
Merge pull request #108 from JuliaOpt/bl/moments
Support MomentsAttribute
2 parents d8b1f4e + 26c04c0 commit 780d4f5

17 files changed

+264
-103
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ JuMP = "0.19"
2121
MathOptInterface = "~0.8.2"
2222
MultivariateMoments = "≥ 0.2.1"
2323
MultivariatePolynomials = "~0.2.7"
24-
PolyJuMP = "0.3"
24+
PolyJuMP = "0.3.1"
2525
SemialgebraicSets = "0.1"
2626
julia = "1"
2727

src/attributes.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ struct CertificateMonomials <: MOI.AbstractConstraintAttribute end
1010
struct GramMatrixAttribute <: MOI.AbstractConstraintAttribute end
1111
1212
A constraint attribute for the [`GramMatrix`](@ref) of a constraint, that is,
13-
the positive semidefinte matrix `Q` indexed by the monomials in the vector `X`
13+
the positive semidefinite matrix `Q` indexed by the monomials in the vector `X`
1414
such that ``X^\\top Q X`` is the sum-of-squares certificate of the constraint.
1515
The
1616
"""

src/sos_polynomial_bridge.jl

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ struct SOSPolynomialBridge{T, F <: MOI.AbstractVectorFunction,
77
variable_bridge::VBS
88
certificate_monomials::MVT
99
zero_constraint::MOI.ConstraintIndex{F, PolyJuMP.ZeroPolynomialSet{DT, BT, MT, MVT}}
10+
domain::DT
11+
monomials::MVT
1012
end
1113

1214
function SOSPolynomialBridge{T, F, DT, VBS, MCT, BT, MT, MVT}(
@@ -30,7 +32,7 @@ function SOSPolynomialBridge{T, F, DT, VBS, MCT, BT, MT, MVT}(
3032
coefs = MOIU.vectorize(MP.coefficients(q))
3133
zero_constraint = MOI.add_constraint(model, coefs, set)
3234
return SOSPolynomialBridge{T, F, DT, VBS, MCT, BT, MT, MVT}(
33-
variable_bridge, X, zero_constraint)
35+
variable_bridge, X, zero_constraint, s.domain, s.monomials)
3436
end
3537

3638
function MOI.supports_constraint(::Type{SOSPolynomialBridge{T}},
@@ -116,16 +118,25 @@ function MOI.get(::MOI.ModelLike,
116118
::SOSPolynomialBridge)
117119
throw(ValueNotSupported())
118120
end
119-
function MOI.get(::MOI.ModelLike,
120-
::MOI.ConstraintDual,
121-
::SOSPolynomialBridge)
122-
throw(DualNotSupported())
121+
122+
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintDual,
123+
bridge::SOSPolynomialBridge)
124+
dual = MOI.get(model, attr, bridge.zero_constraint)
125+
set = MOI.get(model, MOI.ConstraintSet(), bridge.zero_constraint)
126+
μ = measure(dual, set.monomials)
127+
I = ideal(bridge.domain)
128+
return [dot(rem(mono, I), μ) for mono in bridge.monomials]
123129
end
124130
function MOI.get(model::MOI.ModelLike,
125131
attr::MOI.ConstraintDual,
126132
bridge::SOSPolynomialBridge{T, <:MOI.AbstractVectorFunction, FullSpace}) where {T}
127133
return MOI.get(model, attr, bridge.zero_constraint)
128134
end
135+
function MOI.get(model::MOI.ModelLike, attr::PolyJuMP.MomentsAttribute,
136+
bridge::SOSPolynomialBridge)
137+
return MOI.get(model, attr, bridge.zero_constraint)
138+
end
139+
129140
function MOI.get(::MOI.ModelLike, ::CertificateMonomials,
130141
bridge::SOSPolynomialBridge)
131142
return bridge.certificate_monomials

src/sos_polynomial_in_semialgebraic_set_bridge.jl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct SOSPolynomialInSemialgebraicSetBridge{
2626
lagrangian_monomials::Vector{MVT}
2727
lagrangian_bridges::Vector{VBS}
2828
constraint::MOI.ConstraintIndex{F, SOSPolynomialSet{DT, CT, BT, MT, MVT, NPT}}
29+
monomials::MVT
2930
end
3031

3132
function SOSPolynomialInSemialgebraicSetBridge{T, F, DT, CT, VBS, BT, MT, MVT, NPT}(
@@ -55,7 +56,7 @@ function SOSPolynomialInSemialgebraicSetBridge{T, F, DT, CT, VBS, BT, MT, MVT, N
5556
new_set)
5657

5758
return SOSPolynomialInSemialgebraicSetBridge{
58-
T, F, DT, CT, VBS, BT, MT, MVT, NPT}(λ_monos, λ_bridges, constraint)
59+
T, F, DT, CT, VBS, BT, MT, MVT, NPT}(λ_monos, λ_bridges, constraint, set.monomials)
5960
end
6061

6162
function MOI.supports_constraint(::Type{SOSPolynomialInSemialgebraicSetBridge{T}},
@@ -107,10 +108,17 @@ function MOI.get(::MOI.ModelLike,
107108
::SOSPolynomialInSemialgebraicSetBridge)
108109
throw(ValueNotSupported())
109110
end
110-
function MOI.get(::MOI.ModelLike,
111-
::MOI.ConstraintDual,
112-
::SOSPolynomialInSemialgebraicSetBridge)
113-
throw(DualNotSupported())
111+
112+
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintDual,
113+
bridge::SOSPolynomialInSemialgebraicSetBridge)
114+
dual = MOI.get(model, attr, bridge.constraint)
115+
set = MOI.get(model, MOI.ConstraintSet(), bridge.constraint)
116+
μ = measure(dual, set.monomials)
117+
return [dot(mono, μ) for mono in bridge.monomials]
118+
end
119+
function MOI.get(model::MOI.ModelLike, attr::PolyJuMP.MomentsAttribute,
120+
bridge::SOSPolynomialInSemialgebraicSetBridge)
121+
return MOI.get(model, attr, bridge.constraint)
114122
end
115123

116124
function MOI.get(model::MOI.ModelLike,

test/BPT12e399.jl

Lines changed: 0 additions & 32 deletions
This file was deleted.

test/Mock/BPT12e399.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
config = MOI.Test.TestConfig()
2+
optimize!_max(mock) = MOIU.mock_optimize!(mock, [ 6.0, 1.0, -3.0, 9.0],
3+
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[3, 1, 1/3]],
4+
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [[3, 1/3, 2]]
5+
)
6+
optimize!_min(mock) = MOIU.mock_optimize!(mock, [-6.0, 1.0, 3.0, 9.0],
7+
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[3, -1, 1/3]],
8+
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [[3, 1/3, -√2]]
9+
)
10+
mock = bridged_mock(optimize!_max, optimize!_min)
11+
Tests.BPT12e399_test(mock, config)
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
11
config = MOI.Test.TestConfig()
2-
optimize!(mock) = MOIU.mock_optimize!(mock, [1.0; zeros(5); 6.0; zeros(12); 6.0; zeros(10)])
2+
optimize!(mock) = MOIU.mock_optimize!(mock, [1.0; zeros(5); 6.0; zeros(12); 6.0; zeros(10)],
3+
(MOI.ScalarAffineFunction{Float64}, MOI.GreaterThan{Float64}) => zeros(26),
4+
(MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => zeros(4),
5+
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [zeros(7), zeros(7)],
6+
)
37
mock = bridged_mock(optimize!)
48
Tests.dsos_concave_then_convex_cubic_test(mock, config)
5-
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)])
9+
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)],
10+
(MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => zeros(4),
11+
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [zeros(7), zeros(7)],
12+
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [zeros(3) for i in 1:8],
13+
)
614
mock = bridged_mock(optimize!)
715
Tests.sdsos_concave_then_convex_cubic_test(mock, config)
8-
optimize!(mock) = MOIU.mock_optimize!(mock, [1.0; zeros(5); 6.0; zeros(8); 6.0; zeros(6)])
16+
optimize!(mock) = MOIU.mock_optimize!(mock, [1.0; zeros(5); 6.0; zeros(8); 6.0; zeros(6)],
17+
(MOI.ScalarAffineFunction{Float64}, MOI.EqualTo{Float64}) => zeros(4),
18+
(MOI.VectorOfVariables, MOI.PositiveSemidefiniteConeTriangle) => [zeros(6), zeros(6)],
19+
(MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [zeros(7), zeros(7)],
20+
(MOI.VectorAffineFunction{Float64}, MOI.RotatedSecondOrderCone) => [zeros(3), zeros(3)],
21+
)
922
mock = bridged_mock(optimize!)
1023
Tests.sos_concave_then_convex_cubic_test(mock, config)

test/Mock/mock_tests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,6 @@ end
1818
@testset "Lyapunov Switched System" begin
1919
include("lyapunov_switched_system.jl")
2020
end
21+
@testset "BPT12e399" begin
22+
include("BPT12e399.jl")
23+
end

test/Tests/BPT12e399.jl

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Adapted from Example 3.99 of [BPT12].
2+
#
3+
# [BPT12] Blekherman, G.; Parrilo, P. & Thomas, R.
4+
# Semidefinite Optimization and Convex Algebraic Geometry
5+
# Society for Industrial and Applied Mathematics, 2012
6+
7+
using Test
8+
using SumOfSquares
9+
using DynamicPolynomials
10+
11+
function BPT12e399_test(optimizer, config::MOIT.TestConfig)
12+
atol = config.atol
13+
rtol = config.rtol
14+
15+
model = _model(optimizer)
16+
17+
@variable(model, α)
18+
19+
@polyvar x y
20+
cref = @constraint(model, 10 - (x^2 + α*y) in SOSCone(),
21+
domain = @set x^2 + y^2 == 1)
22+
23+
@objective(model, Max, α)
24+
JuMP.optimize!(model)
25+
26+
@test termination_status(model) == MOI.OPTIMAL
27+
@test objective_value(model) 6.0 atol=atol rtol=rtol
28+
29+
@test JuMP.primal_status(model) == MOI.FEASIBLE_POINT
30+
@test JuMP.value(α) 6 atol=1e-6
31+
32+
@test_throws SumOfSquares.ValueNotSupported value(cref)
33+
p = gram_matrix(cref)
34+
@test getmat(p) [1 -3; -3 9] atol=atol rtol=rtol
35+
@test p.x == [y, 1]
36+
37+
@test dual_status(model) == MOI.FEASIBLE_POINT
38+
μ = dual(cref)
39+
@test μ isa AbstractMeasure{Float64}
40+
@test length(moments(μ)) == 3
41+
@test moment_value(moments(μ)[1]) -8/3 atol=atol rtol=rtol
42+
@test monomial(moments(μ)[1]) == x^2
43+
@test moment_value(moments(μ)[2]) 1 atol=atol rtol=rtol
44+
@test monomial(moments(μ)[2]) == y
45+
@test moment_value(moments(μ)[3]) 1/3 atol=atol rtol=rtol
46+
@test monomial(moments(μ)[3]) == 1
47+
48+
μ = moments(cref)
49+
@test μ isa AbstractMeasure{Float64}
50+
@test length(moments(μ)) == 3
51+
@test moment_value(moments(μ)[1]) 3 atol=atol rtol=rtol
52+
@test monomial(moments(μ)[1]) == y^2
53+
@test moment_value(moments(μ)[2]) 1 atol=atol rtol=rtol
54+
@test monomial(moments(μ)[2]) == y
55+
@test moment_value(moments(μ)[3]) 1/3 atol=atol rtol=rtol
56+
@test monomial(moments(μ)[3]) == 1
57+
58+
@objective(model, Min, α)
59+
JuMP.optimize!(model)
60+
61+
@test termination_status(model) == MOI.OPTIMAL
62+
@test objective_value(model) -6.0 atol=atol rtol=rtol
63+
64+
@test JuMP.primal_status(model) == MOI.FEASIBLE_POINT
65+
@test JuMP.value(α) -6 atol=1e-6
66+
67+
@test_throws SumOfSquares.ValueNotSupported value(cref)
68+
p = gram_matrix(cref)
69+
@test getmat(p) [1 3; 3 9] atol=atol rtol=rtol
70+
@test p.x == [y, 1]
71+
72+
@test dual_status(model) == MOI.FEASIBLE_POINT
73+
μ = dual(cref)
74+
@test μ isa AbstractMeasure{Float64}
75+
@test length(moments(μ)) == 3
76+
@test moment_value(moments(μ)[1]) -8/3 atol=atol rtol=rtol
77+
@test monomial(moments(μ)[1]) == x^2
78+
@test moment_value(moments(μ)[2]) -1 atol=atol rtol=rtol
79+
@test monomial(moments(μ)[2]) == y
80+
@test moment_value(moments(μ)[3]) 1/3 atol=atol rtol=rtol
81+
@test monomial(moments(μ)[3]) == 1
82+
83+
μ = moments(cref)
84+
@test μ isa AbstractMeasure{Float64}
85+
@test length(moments(μ)) == 3
86+
@test moment_value(moments(μ)[1]) 3 atol=atol rtol=rtol
87+
@test monomial(moments(μ)[1]) == y^2
88+
@test moment_value(moments(μ)[2]) -1 atol=atol rtol=rtol
89+
@test monomial(moments(μ)[2]) == y
90+
@test moment_value(moments(μ)[3]) 1/3 atol=atol rtol=rtol
91+
@test monomial(moments(μ)[3]) == 1
92+
end
93+
94+
sd_tests["BPT12e399"] = BPT12e399_test

test/Tests/Tests.jl

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,16 @@ module Tests
22

33
include("utilities.jl")
44

5-
include("term_test.jl")
6-
include("bivariate_quadratic_test.jl")
7-
include("concave_then_convex_cubic_test.jl")
8-
include("horn_test.jl")
9-
10-
const linear_tests = Dict(
11-
"dsos_term" => dsos_term_test,
12-
"dsos_bivariate_quadratic" => dsos_bivariate_quadratic_test,
13-
"dsos_concave_then_convex_cubic" => dsos_concave_then_convex_cubic_test,
14-
"dsos_horn" => dsos_horn_test
15-
)
16-
17-
const soc_tests = Dict(
18-
"sdsos_term" => sdsos_term_test,
19-
"sdsos_bivariate_quadratic" => sdsos_bivariate_quadratic_test,
20-
"sdsos_concave_then_convex_cubic" => sdsos_concave_then_convex_cubic_test,
21-
"sdsos_horn" => sdsos_horn_test
22-
)
23-
24-
const sd_tests = Dict(
25-
"sos_term" => sos_term_test,
26-
"sos_bivariate_quadratic" => sos_bivariate_quadratic_test,
27-
"sos_concave_then_convex_cubic" => sos_concave_then_convex_cubic_test,
28-
"sos_horn" => sos_horn_test,
29-
)
30-
5+
const linear_tests = Dict{String, Function}()
6+
const soc_tests = Dict{String, Function}()
7+
const sd_tests = Dict{String, Function}()
8+
9+
include("term.jl")
10+
include("bivariate_quadratic.jl")
11+
include("horn.jl")
12+
include("concave_then_convex_cubic.jl")
3113
include("lyapunov_switched_system.jl")
14+
include("BPT12e399.jl")
3215

3316
@test_suite linear
3417
@test_suite soc

test/Tests/bivariate_quadratic_test.jl renamed to test/Tests/bivariate_quadratic.jl

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@ function bivariate_quadratic_test(optimizer,
3030
@test getmat(p) ones(2, 2) atol=atol rtol=rtol
3131
@test p.x == [x, 1]
3232

33+
a = moment_value.(moments(dual(cref)))
34+
@test a[2] -1.0 atol=atol rtol=rtol
35+
@test a[1] + a[3] 2.0 atol=atol rtol=rtol
36+
3337
@test dual_status(model) == MOI.FEASIBLE_POINT
34-
μ = dual(cref)
35-
@test μ isa AbstractMeasure{Float64}
36-
@test length(moments(μ)) == 3
37-
a = moment_value.(moments(μ))
38-
@test a[2] -1.0 atol=atol rtol=rtol
39-
@test a[1] + a[3] 2.0 atol=atol rtol=rtol
40-
@test monomial.(moments(μ)) == [x^2, x, 1]
38+
for μ in [dual(cref), moments(cref)]
39+
@test μ isa AbstractMeasure{Float64}
40+
@test length(moments(μ)) == 3
41+
@test a moment_value.(moments(μ)) atol=atol rtol=rtol
42+
@test monomial.(moments(μ)) == [x^2, x, 1]
43+
end
4144

4245
ν = moment_matrix(cref)
4346
@test getmat(ν) [a[1] a[2]
@@ -60,5 +63,8 @@ function bivariate_quadratic_test(optimizer,
6063
0)))
6164
end
6265
sos_bivariate_quadratic_test(optimizer, config) = bivariate_quadratic_test(optimizer, config, SOSCone())
66+
sd_tests["sos_bivariate_quadratic"] = sos_bivariate_quadratic_test
6367
sdsos_bivariate_quadratic_test(optimizer, config) = bivariate_quadratic_test(optimizer, config, SDSOSCone())
68+
soc_tests["sdsos_bivariate_quadratic"] = sdsos_bivariate_quadratic_test
6469
dsos_bivariate_quadratic_test(optimizer, config) = bivariate_quadratic_test(optimizer, config, DSOSCone())
70+
linear_tests["dsos_bivariate_quadratic"] = dsos_bivariate_quadratic_test

test/Tests/concave_then_convex_cubic_test.jl renamed to test/Tests/concave_then_convex_cubic.jl

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,30 @@ function concave_then_convex_cubic_test(optimizer, config::MOIT.TestConfig,
2929
@test_throws SumOfSquares.ValueNotSupported value(cref_convex)
3030
@test_throws SumOfSquares.ValueNotSupported value(cref_concave)
3131

32-
@test_throws SumOfSquares.DualNotSupported dual(cref_convex)
33-
@test_throws SumOfSquares.DualNotSupported dual(cref_concave)
32+
# The monomials contain the variables created for the Hessian so we cannot
33+
# check them easily
34+
for μ in [dual(cref_convex), dual(cref_concave)]
35+
@test μ isa AbstractMeasure{Float64}
36+
@test length(moments(μ)) == 2
37+
end
38+
for μ in [moments(cref_convex), moments(cref_concave)]
39+
@test μ isa AbstractMeasure{Float64}
40+
@test length(moments(μ)) == 7
41+
end
3442
end
3543

3644
function sos_concave_then_convex_cubic_test(optimizer, config)
3745
concave_then_convex_cubic_test(optimizer, config,
3846
MOI.PositiveSemidefiniteConeTriangle)
3947
end
48+
sd_tests["sos_concave_then_convex_cubic"] = sos_concave_then_convex_cubic_test
4049
function sdsos_concave_then_convex_cubic_test(optimizer, config)
4150
concave_then_convex_cubic_test(optimizer, config,
4251
SumOfSquares.ScaledDiagonallyDominantConeTriangle)
4352
end
53+
soc_tests["sdsos_concave_then_convex_cubic"] = sdsos_concave_then_convex_cubic_test
4454
function dsos_concave_then_convex_cubic_test(optimizer, config)
4555
concave_then_convex_cubic_test(optimizer, config,
4656
SumOfSquares.DiagonallyDominantConeTriangle)
4757
end
58+
linear_tests["dsos_concave_then_convex_cubic"] = dsos_concave_then_convex_cubic_test

test/Tests/horn_test.jl renamed to test/Tests/horn.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,8 @@ function horn_test(optimizer,
5959
end
6060
end
6161
sos_horn_test(optimizer, config) = horn_test(optimizer, config, SOSCone())
62+
sd_tests["sos_horn"] = sos_horn_test
6263
sdsos_horn_test(optimizer, config) = horn_test(optimizer, config, SDSOSCone())
64+
soc_tests["sdsos_horn"] = sdsos_horn_test
6365
dsos_horn_test(optimizer, config) = horn_test(optimizer, config, DSOSCone())
66+
linear_tests["dsos_horn"] = dsos_horn_test

0 commit comments

Comments
 (0)