Skip to content

Commit 76a4d3e

Browse files
authored
Symmetry reduction (#175)
* Symmetry reduction * Switch to Github CI
1 parent 794977e commit 76a4d3e

File tree

12 files changed

+258
-66
lines changed

12 files changed

+258
-66
lines changed

.github/workflows/CompatHelper.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name: CompatHelper
2+
3+
on:
4+
schedule:
5+
- cron: '00 00 * * *'
6+
workflow_dispatch:
7+
8+
jobs:
9+
CompatHelper:
10+
runs-on: ${{ matrix.os }}
11+
strategy:
12+
matrix:
13+
julia-version: [1.2.0]
14+
julia-arch: [x86]
15+
os: [ubuntu-latest]
16+
steps:
17+
- name: Pkg.add("CompatHelper")
18+
run: julia -e 'using Pkg; Pkg.add("CompatHelper")'
19+
- name: CompatHelper.main()
20+
env:
21+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22+
run: julia -e 'using CompatHelper; CompatHelper.main()'

.github/workflows/TagBot.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
name: TagBot
22
on:
3-
schedule:
4-
- cron: 0 * * * *
3+
issue_comment:
4+
types:
5+
- created
6+
workflow_dispatch:
57
jobs:
68
TagBot:
9+
if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'
710
runs-on: ubuntu-latest
811
steps:
912
- uses: JuliaRegistries/TagBot@v1
1013
with:
1114
token: ${{ secrets.GITHUB_TOKEN }}
15+
ssh: ${{ secrets.DOCUMENTER_KEY }}

.github/workflows/ci.yml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: CI
2+
on:
3+
push:
4+
branches: [master]
5+
pull_request:
6+
types: [opened, synchronize, reopened]
7+
jobs:
8+
test:
9+
name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}
10+
runs-on: ${{ matrix.os }}
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
include:
15+
- version: '1.0'
16+
os: ubuntu-latest
17+
arch: x64
18+
- version: '1'
19+
os: ubuntu-latest
20+
arch: x64
21+
- version: '1'
22+
os: ubuntu-latest
23+
arch: x86
24+
- version: 'nightly'
25+
os: ubuntu-latest
26+
arch: x64
27+
steps:
28+
- uses: actions/checkout@v2
29+
- uses: julia-actions/setup-julia@v1
30+
with:
31+
version: ${{ matrix.version }}
32+
arch: ${{ matrix.arch }}
33+
- uses: actions/cache@v1
34+
env:
35+
cache-name: cache-artifacts
36+
with:
37+
path: ~/.julia/artifacts
38+
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
39+
restore-keys: |
40+
${{ runner.os }}-test-${{ env.cache-name }}-
41+
${{ runner.os }}-test-
42+
${{ runner.os }}-
43+
- uses: julia-actions/julia-buildpkg@v1
44+
- uses: julia-actions/julia-runtest@v1
45+
with:
46+
depwarn: error
47+
- uses: julia-actions/julia-processcoverage@v1
48+
- uses: codecov/codecov-action@v1
49+
with:
50+
file: lcov.info

.github/workflows/documentation.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Documentation
2+
on:
3+
push:
4+
branches: [master]
5+
tags: '*'
6+
pull_request:
7+
types: [opened, synchronize, reopened]
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v2
13+
- uses: julia-actions/setup-julia@latest
14+
with:
15+
# Build documentation on the latest Julia 1.x
16+
version: '1'
17+
- name: Install dependencies
18+
run: julia --project=docs/ -e 'using Pkg; Pkg.instantiate();
19+
Pkg.add("DynamicPolynomials");
20+
Pkg.add(PackageSpec(url="https://github.com/blegat/Documenter.jl", rev="latexout"));
21+
Pkg.develop(PackageSpec(path=pwd()))'
22+
- name: Build and deploy
23+
env:
24+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token
25+
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # For authentication with SSH deploy key
26+
run: julia --project=docs/ docs/make.jl

.github/workflows/examples.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Examples
2+
on:
3+
push:
4+
branches: [master]
5+
pull_request:
6+
types: [opened, synchronize, reopened]
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v2
12+
- uses: julia-actions/setup-julia@latest
13+
with:
14+
version: '1'
15+
- name: Install dependencies
16+
run: julia --project=examples -e 'using Pkg; Pkg.instantiate(); Pkg.develop(PackageSpec(path=pwd()))'
17+
- name: Run examples
18+
run: julia --project=examples --color=yes examples/run_examples.jl

.travis.yml

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

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ julia = "1"
3333

3434
[extras]
3535
CSDP = "0a46da34-8e4b-519e-b418-48813639ff34"
36+
Cyclotomics = "da8f5974-afbb-4dc8-91d8-516d5257c83b"
3637
DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07"
3738
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
39+
PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420"
3840
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
3941
PolyJuMP = "ddf597a6-d67e-5340-b84c-e37d84115374"
4042
SCS = "c946c3f1-0d1f-5ce8-9dea-7daa1f7e2d13"

appveyor.yml

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

docs/Project.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
[deps]
22
CSDP = "0a46da34-8e4b-519e-b418-48813639ff34"
3+
Cyclotomics = "da8f5974-afbb-4dc8-91d8-516d5257c83b"
4+
DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07"
35
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
46
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
7+
MultivariateBases = "be282fd4-ad43-11e9-1d11-8bd9d7e43378"
8+
MultivariatePolynomials = "102ac46a-7ee4-5c85-9060-abc95bfdeaa3"
9+
MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
10+
PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420"
511
SumOfSquares = "4b9e565b-77fc-50a5-a571-1244f986bda1"

docs/make.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const EXAMPLES = [
1111
"Sum-of-Squares Matrices.jl",
1212
"Noncommutative variables.jl",
1313
"Sums of Hermitian squares.jl",
14+
"Symmetry reduction.jl",
1415
]
1516

1617
for example in EXAMPLES
@@ -38,6 +39,7 @@ makedocs(
3839
"Sum-of-Squares Matrices" => "generated/Sum-of-Squares Matrices.md",
3940
"Noncommutative variables" => "generated/Noncommutative variables.md",
4041
"Sums of Hermitian squares" => "generated/Sums of Hermitian squares.md",
42+
"Symmetry reduction" => "generated/Symmetry reduction.md",
4143
]
4244
],
4345
# The following ensures that we only include the docstrings from

examples/Project.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
[deps]
22
CSDP = "0a46da34-8e4b-519e-b418-48813639ff34"
3+
Cyclotomics = "da8f5974-afbb-4dc8-91d8-516d5257c83b"
34
DynamicPolynomials = "7c1d4256-1411-5781-91ec-d7bc3513ac07"
45
IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a"
56
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
7+
MultivariateBases = "be282fd4-ad43-11e9-1d11-8bd9d7e43378"
8+
MultivariatePolynomials = "102ac46a-7ee4-5c85-9060-abc95bfdeaa3"
9+
MutableArithmetics = "d8a4904e-b15c-11e9-3269-09a3773c0cb0"
10+
PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420"
611
SumOfSquares = "4b9e565b-77fc-50a5-a571-1244f986bda1"

examples/Symmetry reduction.jl

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# # Lyapunov Function Search
2+
3+
#md # [![](https://mybinder.org/badge_logo.svg)](@__BINDER_ROOT_URL__/generated/Lyapunov Function Search.ipynb)
4+
#md # [![](https://img.shields.io/badge/show-nbviewer-579ACA.svg)](@__NBVIEWER_ROOT_URL__/generated/Lyapunov Function Search.ipynb)
5+
# **Adapted from**: SOSTOOLS' SOSDEMO2 (See Section 4.2 of [SOSTOOLS User's Manual](http://sysos.eng.ox.ac.uk/sostools/sostools.pdf))
6+
7+
using Pkg
8+
pkg"add https://github.com/kalmarek/SymbolicWedderburn.jl#tw/ex_sos"
9+
10+
import MutableArithmetics
11+
const MA = MutableArithmetics
12+
using MultivariatePolynomials
13+
const MP = MultivariatePolynomials
14+
using MultivariateBases
15+
const MB = MultivariateBases
16+
17+
using Test #src
18+
using DynamicPolynomials
19+
@polyvar x[1:4]
20+
21+
# We would like to find the minimum value of the polynomial
22+
23+
poly = sum(x) + sum(x.^2)
24+
25+
# As we can decouple the problem for each `x[i]` for which `x[i] + x[i]^2` has
26+
# minimum value 0.25, we would expect to get `-1` as answer.
27+
# Can this decoupling be exploited by SumOfSquares as well ?
28+
# For this, we need to use a certificate that can exploit the permutation symmetry of the polynomial.
29+
# This is still a work in progress in SumOfSquares, so we need to define things here:
30+
31+
using SymbolicWedderburn
32+
using PermutationGroups
33+
using Cyclotomics
34+
using SumOfSquares
35+
36+
function SymbolicWedderburn.OnPoints(basis::Union{MonomialVector, AbstractVector{<:Monomial}})
37+
basis_exps = Vector{Vector{Int}}(undef, length(basis))
38+
basis_dict = Dict{Vector{Int}, Int}()
39+
sizehint!(basis_dict, length(basis))
40+
41+
for (i, b) in enumerate(basis)
42+
e = MP.exponents(b) # so that we allocate exponents only once
43+
basis_exps[i] = e
44+
basis_dict[e] = i
45+
end
46+
47+
return SymbolicWedderburn.OnPoints(basis_exps, basis_dict)
48+
end
49+
function symmetry_adapted_basis(G, mvec)
50+
chars_vars = SymbolicWedderburn.characters_dixon(G)
51+
52+
chars = chars_vars
53+
basis = mvec
54+
55+
@assert all.inv_of == first(chars).inv_of for χ in chars)
56+
57+
induced_action = SymbolicWedderburn.OnPoints(basis)
58+
ccG_large = induced_action.(conjugacy_classes(first(chars)))
59+
60+
let ccls = ccG_large, large_gens = induced_action.(gens(G)) # double check:
61+
G_large = PermGroup(large_gens)
62+
ccG_large = conjugacy_classes(G_large)
63+
@assert all(Set.(collect.(ccG_large)) .== Set.(collect.(ccls)))
64+
end
65+
66+
chars_mvec = [SymbolicWedderburn.Character(values(χ), χ.inv_of, ccG_large) for χ in chars]
67+
68+
vr_chars = SymbolicWedderburn.real_vchars(chars_mvec)
69+
U = filter!(x->all(!iszero, x), [SymbolicWedderburn.matrix_projection(χ) for χ in vr_chars])
70+
R = map(U) do c_u
71+
u = last(c_u)
72+
if all(isreal, u)
73+
image_coeffs, pivots = SymbolicWedderburn.row_echelon_form(float.(u))
74+
dim = length(pivots)
75+
image_coeffs[1:dim, :]
76+
else
77+
throw("Not Implemented")
78+
end
79+
end
80+
81+
return map(R) do Ri
82+
FixedPolynomialBasis(Ri * mvec)
83+
end
84+
end
85+
function MP.polynomialtype(::Type{<:MB.AbstractPolynomialVectorBasis{PT}}, T::Type) where PT
86+
C = MP.coefficienttype(PT)
87+
U = MA.promote_operation(*, C, T)
88+
V = MA.promote_operation(+, U, U)
89+
return MP.polynomialtype(PT, V)
90+
end
91+
struct SymmetricIdeal{CT, GT} <: Certificate.AbstractIdealCertificate
92+
cone::CT
93+
group::GT
94+
end
95+
SumOfSquares.matrix_cone_type(::Type{<:SymmetricIdeal{CT}}) where {CT} = SumOfSquares.matrix_cone_type(CT)
96+
Certificate.get(::Type{<:SymmetricIdeal}, ::SumOfSquares.Certificate.GramBasisType) = Vector{MB.FixedPolynomialBasis}
97+
Certificate.zero_basis_type(::Type{<:SymmetricIdeal}) = MB.MonomialBasis
98+
Certificate.zero_basis(::SymmetricIdeal) = MB.MonomialBasis
99+
Certificate.get(::SymmetricIdeal, ::Certificate.ReducedPolynomial, poly, domain) = poly
100+
function Certificate.get(cert::SymmetricIdeal, ::Certificate.GramBasis, poly)
101+
basis = Certificate.maxdegree_gram_basis(MB.MonomialBasis, MP.variables(poly), MP.maxdegree(poly))
102+
return symmetry_adapted_basis(cert.group, basis.monomials)
103+
end
104+
G = PermGroup([perm"(1,2,3,4)"])
105+
106+
# We can use this certificate as follows:
107+
108+
import CSDP
109+
solver = CSDP.Optimizer
110+
model = Model(solver)
111+
@variable(model, t)
112+
@objective(model, Max, t)
113+
con_ref = @constraint(model, sum(x) + sum(x.^2) - t in SOSCone(), ideal_certificate = SymmetricIdeal(SOSCone(), G))
114+
optimize!(model)
115+
@test value(t) -1 #src
116+
value(t)
117+
118+
# We indeed find `-1`, let's verify that symmetry was exploited:
119+
120+
@test length(gram_matrix(con_ref).sub_gram_matrices) == 3 #src
121+
gram_matrix(con_ref)

0 commit comments

Comments
 (0)