Skip to content

Commit 452ee2b

Browse files
authored
Add WeightedSOSCone (#351)
* Add WeightedSOSCone * Fix * Fix format * Fix * fix * Add docstrings in docs * Fix * Fix * rename * Add tests * Fix format * Update ci.yml * include tests * MOI master
1 parent 84d1207 commit 452ee2b

File tree

10 files changed

+287
-2
lines changed

10 files changed

+287
-2
lines changed

.github/workflows/ci.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ jobs:
3232
version: ${{ matrix.version }}
3333
arch: ${{ matrix.arch }}
3434
- uses: julia-actions/cache@v1
35+
- name: MOI
36+
shell: julia --project=@. {0}
37+
run: |
38+
using Pkg
39+
Pkg.add([
40+
PackageSpec(name="MultivariateBases", rev="master"),
41+
PackageSpec(name="MathOptInterface", rev="master"),
42+
])
3543
- uses: julia-actions/julia-buildpkg@v1
3644
- uses: julia-actions/julia-runtest@v1
3745
with:

docs/src/reference/standard_form.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ SumOfSquares.PolyJuMP.SAGE.AGEBridge
4343

4444
## MOI Sets
4545

46+
Sum-of-Squares cones:
47+
```@docs
48+
SumOfSquares.SOSPolynomialSet
49+
SumOfSquares.WeightedSOSCone
50+
```
51+
4652
Special cases of positive semidefinite cones:
4753
```@docs
4854
SumOfSquares.EmptyCone

docs/src/references.bib

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ @Article{Hesse1973
6161
publisher = {Institute for Operations Research and the Management Sciences (INFORMS)},
6262
}
6363

64+
@article{Kapelevich2023,
65+
title = {Sum of Squares Generalizations for Conic Sets},
66+
author = {Kapelevich, Lea and Coey, Chris and Vielma, Juan Pablo},
67+
year = {2023},
68+
month = may,
69+
journal = {Mathematical Programming},
70+
volume = {199},
71+
number = {1},
72+
pages = {1417--1429},
73+
issn = {1436-4646},
74+
doi = {10.1007/s10107-022-01831-6},
75+
}
76+
6477
@Book{Lasserre2009,
6578
author = {Lasserre, Jean Bernard},
6679
publisher = {Imperial College Press},
@@ -98,6 +111,18 @@ @Article{Murty1987
98111
publisher = {Springer-Verlag Berlin, Heidelberg}
99112
}
100113

114+
@Article{Papp2017,
115+
title = {Sum-of-Squares Optimization without Semidefinite Programming},
116+
author = {Papp, D. and Y{\i}ld{\i}z, S.},
117+
year = {2017},
118+
month = dec,
119+
journal = {ArXiv e-prints},
120+
eprint = {1712.01792},
121+
primaryclass = {math.OC},
122+
adsnote = {Provided by the SAO/NASA Astrophysics Data System},
123+
archiveprefix = {arxiv},
124+
}
125+
101126
@Article{Parrilo2003,
102127
title = {Semidefinite Programming Relaxations for Semialgebraic Problems},
103128
author = {Parrilo, Pablo A},

src/Bridges/Constraint/utilities.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function union_set_types(MCT)
1414
}
1515
end
1616
function constrained_variable_types(MCT)
17-
return [
17+
return Tuple{Type}[
1818
(typeof(SOS.matrix_cone(MCT, 0)),),
1919
(typeof(SOS.matrix_cone(MCT, 1)),),
2020
(typeof(SOS.matrix_cone(MCT, 2)),),

src/Bridges/Variable/Variable.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
module Variable
22

3+
import MutableArithmetics as MA
34
import MathOptInterface as MOI
4-
5+
import MultivariatePolynomials as MP
56
import SumOfSquares as SOS
67

78
include("psd2x2.jl")
89
include("scaled_diagonally_dominant.jl")
910
include("copositive_inner.jl")
11+
include("kernel.jl")
1012

1113
end

src/Bridges/Variable/kernel.jl

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
struct KernelBridge{T,M} <: MOI.Bridges.Variable.AbstractBridge
2+
affine::Vector{MOI.ScalarAffineFunction{T}}
3+
variables::Vector{Vector{MOI.VariableIndex}}
4+
constraints::Vector{MOI.ConstraintIndex{MOI.VectorOfVariables}}
5+
set::SOS.WeightedSOSCone{M}
6+
end
7+
8+
function MOI.Bridges.Variable.bridge_constrained_variable(
9+
::Type{KernelBridge{T}},
10+
model::MOI.ModelLike,
11+
set::SOS.WeightedSOSCone{M},
12+
) where {T,M}
13+
variables = Vector{MOI.VariableIndex}[]
14+
constraints = MOI.ConstraintIndex{MOI.VectorOfVariables}[]
15+
acc = MA.Zero()
16+
for (gram_basis, weight) in zip(set.gram_bases, set.weights)
17+
gram, vars, con = SOS.add_gram_matrix(model, M, gram_basis, T)
18+
push!(variables, vars)
19+
push!(constraints, con)
20+
acc = MA.add_mul!!(acc, weight, gram)
21+
end
22+
affine = MP.coefficients(acc, set.basis)
23+
return KernelBridge{T,M}(affine, variables, constraints, set)
24+
end
25+
26+
function MOI.Bridges.Variable.supports_constrained_variable(
27+
::Type{<:KernelBridge},
28+
::Type{<:SOS.WeightedSOSCone},
29+
)
30+
return true
31+
end
32+
33+
function MOI.Bridges.added_constrained_variable_types(
34+
::Type{KernelBridge{T,M}},
35+
) where {T,M}
36+
return SOS.Bridges.Constraint.constrained_variable_types(M)
37+
end
38+
39+
function MOI.Bridges.added_constraint_types(::Type{<:KernelBridge})
40+
return Tuple{Type,Type}[]
41+
end
42+
43+
# Attributes, Bridge acting as a model
44+
function MOI.get(bridge::KernelBridge, ::MOI.NumberOfVariables)
45+
return sum(length, bridge.variables)
46+
end
47+
48+
function MOI.get(bridge::KernelBridge, ::MOI.ListOfVariableIndices)
49+
return reduce(vcat, bridge.variables)
50+
end
51+
52+
function MOI.get(
53+
bridge::KernelBridge,
54+
::MOI.NumberOfConstraints{MOI.VectorOfVariables,S},
55+
) where {S<:MOI.AbstractVectorSet}
56+
return count(bridge.constraints) do ci
57+
return ci isa MOI.ConstraintIndex{MOI.VectorOfVariables,S}
58+
end
59+
end
60+
61+
function MOI.get(
62+
bridge::KernelBridge,
63+
::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,S},
64+
) where {S}
65+
return [
66+
ci for ci in bridge.constraints if
67+
ci isa MOI.ConstraintIndex{MOI.VectorOfVariables,S}
68+
]
69+
end
70+
71+
# Indices
72+
function MOI.delete(model::MOI.ModelLike, bridge::KernelBridge)
73+
for vars in bridge.variables
74+
MOI.delete(model, vars)
75+
end
76+
return
77+
end
78+
79+
# Attributes, Bridge acting as a constraint
80+
81+
function MOI.get(::MOI.ModelLike, ::MOI.ConstraintSet, bridge::KernelBridge)
82+
return bridge.set
83+
end
84+
85+
function MOI.get(
86+
model::MOI.ModelLike,
87+
attr::MOI.ConstraintPrimal,
88+
bridge::KernelBridge,
89+
)
90+
return [
91+
MOI.get(
92+
model,
93+
MOI.VariablePrimal(attr.result_index),
94+
bridge,
95+
MOI.Bridges.IndexInVector(i),
96+
) for i in eachindex(bridge.affine)
97+
]
98+
end
99+
100+
function MOI.get(
101+
model::MOI.ModelLike,
102+
attr::MOI.VariablePrimal,
103+
bridge::KernelBridge,
104+
i::MOI.Bridges.IndexInVector,
105+
)
106+
return MOI.Utilities.eval_variable(bridge.affine[i.value]) do
107+
return vi -> MOI.get(model, MOI.VariablePrimal(attr.result_index), vi)
108+
end
109+
end
110+
111+
function MOI.Bridges.bridged_function(
112+
bridge::KernelBridge,
113+
i::MOI.Bridges.IndexInVector,
114+
)
115+
return bridge.affine[i.value]
116+
end
117+
118+
function MOI.Bridges.Variable.unbridged_map(
119+
bridge::KernelBridge{T},
120+
coefs::Vector{MOI.VariableIndex},
121+
) where {T}
122+
F = MOI.ScalarAffineFunction{T}
123+
map = Pair{MOI.VariableIndex,F}[]
124+
return nothing
125+
end

src/sets.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,66 @@ function Base.copy(
108108
return set
109109
end
110110

111+
"""
112+
struct WeightedSOSCone{
113+
M,
114+
B<:AbstractPolynomialBasis,
115+
G<:AbstractPolynomialBasis,
116+
W<:MP.AbstractPolynomialLike,
117+
} <: MOI.AbstractVectorSet
118+
basis::B
119+
gram_bases::Vector{G}
120+
weights::Vector{W}
121+
end
122+
123+
The weighted sum-of-squares cone is the set of vectors of coefficients `a` in `basis`
124+
that are the sum of `weights[i]` multiplied by a gram matrix with basis `gram_bases[i]`.
125+
The matrix cone type `M` is used to decide in which cone the gram matrix is constrained,
126+
e.g., `MOI.PositiveSemidefiniteConeTriangle` or
127+
[`SumOfSquares.ScaledDiagonallyDominantConeTriangle`](@ref).
128+
129+
See [Papp2017; Section 1.1](@cite) and [Kapelevich2023; Section 1](@cite).
130+
"""
131+
struct WeightedSOSCone{
132+
M,
133+
B<:AbstractPolynomialBasis,
134+
G<:AbstractPolynomialBasis,
135+
W<:MP.AbstractPolynomialLike,
136+
} <: MOI.AbstractVectorSet
137+
basis::B
138+
gram_bases::Vector{G}
139+
weights::Vector{W}
140+
end
141+
function WeightedSOSCone{M}(
142+
basis::AbstractPolynomialBasis,
143+
gram_bases::Vector{G},
144+
weights::Vector{W},
145+
) where {M,G<:AbstractPolynomialBasis,W<:MP.AbstractPolynomialLike}
146+
return WeightedSOSCone{M,typeof(basis),G,W}(basis, gram_bases, weights)
147+
end
148+
MOI.dimension(set::WeightedSOSCone) = length(set.basis)
149+
Base.copy(set::WeightedSOSCone) = set
150+
function Base.:(==)(a::WeightedSOSCone, b::WeightedSOSCone)
151+
return a.basis == b.basis &&
152+
a.gram_bases == b.gram_bases &&
153+
a.weights == b.weights
154+
end
155+
156+
"""
157+
struct SOSPolynomialSet{
158+
DT<:AbstractSemialgebraicSet,
159+
MT<:MP.AbstractMonomial,
160+
MVT<:AbstractVector{MT},
161+
CT<:Certificate.AbstractCertificate,
162+
} <: MOI.AbstractVectorSet
163+
domain::DT
164+
monomials::MVT
165+
certificate::CT
166+
end
167+
168+
The sum-of-squares cone is the set of vectors of coefficients `a` for monomials `monomials`
169+
for which the polynomial is a sum-of-squares `certificate` over `domain`.
170+
"""
111171
struct SOSPolynomialSet{
112172
DT<:AbstractSemialgebraicSet,
113173
MT<:MP.AbstractMonomial,

test/Bridges/Bridges.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Test
2+
3+
@testset "$(dir)" for dir in ["Variable"]
4+
@testset "$(file)" for file in readdir(joinpath(@__DIR__, dir))
5+
if !endswith(file, ".jl")
6+
continue
7+
elseif dir == "." && file == "Bridges.jl"
8+
continue
9+
end
10+
include(joinpath(@__DIR__, dir, file))
11+
end
12+
end

test/Bridges/Variable/kernel.jl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
module TestVariableKernel
2+
3+
using Test
4+
using DynamicPolynomials
5+
using SumOfSquares
6+
7+
function runtests()
8+
for name in names(@__MODULE__; all = true)
9+
if startswith("$(name)", "test_")
10+
@testset "$(name)" begin
11+
getfield(@__MODULE__, name)()
12+
end
13+
end
14+
end
15+
return
16+
end
17+
18+
function test_runtests()
19+
@polyvar x y
20+
MOI.Bridges.runtests(
21+
SumOfSquares.Bridges.Variable.KernelBridge,
22+
model -> begin
23+
MOI.add_constrained_variables(
24+
model,
25+
SumOfSquares.WeightedSOSCone{
26+
MOI.PositiveSemidefiniteConeTriangle,
27+
}(
28+
MonomialBasis([x^4, x^3 * y, x^2 * y^2, y^4]),
29+
[MonomialBasis([x^2, y^2, x * y])],
30+
[1.0 * x^0 * y^0],
31+
),
32+
)
33+
end,
34+
model -> begin
35+
Q, _ = MOI.add_constrained_variables(
36+
model,
37+
MOI.PositiveSemidefiniteConeTriangle(3),
38+
)
39+
end,
40+
)
41+
return
42+
end
43+
44+
end # module
45+
46+
TestVariableKernel.runtests()

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ include("gram_matrix.jl")
3232

3333
include("variable.jl")
3434
include("constraint.jl")
35+
include("Bridges/Bridges.jl")
3536

3637
include("Mock/mock_tests.jl")
3738

0 commit comments

Comments
 (0)