1
0
mirror of https://github.com/kalmarek/PropertyT.jl.git synced 2024-11-24 16:35:28 +01:00

Compare commits

...

6 Commits

4 changed files with 78 additions and 48 deletions

View File

@ -83,26 +83,29 @@ Base.@propagate_inbounds function Base.getindex(
return pos - neg return pos - neg
end end
struct NZPairsIter{T} struct NZPairsIter{T,I}
m::ConstraintMatrix{T} m::ConstraintMatrix{T,I}
end end
Base.eltype(::Type{NZPairsIter{T}}) where {T} = Pair{Int,T} Base.eltype(::Type{NZPairsIter{T}}) where {T} = Pair{Int,T}
Base.IteratorSize(::Type{<:NZPairsIter}) = Base.SizeUnknown() Base.IteratorSize(::Type{<:NZPairsIter}) = Base.SizeUnknown()
# TODO: iterate over (idx=>val) pairs combining vals # TODO: iterate over (idx=>val) pairs combining vals
function Base.iterate(itr::NZPairsIter, state::Tuple{Int,Int} = (1, 1)) function Base.iterate(
itr::NZPairsIter,
state::Tuple{Int,Nothing} = (1, nothing),
)
k = iterate(itr.m.pos, state[1]) k = iterate(itr.m.pos, state[1])
isnothing(k) && return iterate(itr, state[2]) isnothing(k) && return iterate(itr, (nothing, 1))
idx, st = k idx, st = k
return idx => itr.m.val, (st, 1) return idx => itr.m.val, (st, nothing)
end end
function Base.iterate(itr::NZPairsIter, state::Int) function Base.iterate(itr::NZPairsIter, state::Tuple{Nothing,Int})
k = iterate(itr.m.neg, state[1]) k = iterate(itr.m.neg, state[2])
isnothing(k) && return nothing isnothing(k) && return nothing
idx, st = k idx, st = k
return idx => -itr.m.val, st return idx => -itr.m.val, (nothing, st)
end end
""" """

View File

@ -12,9 +12,10 @@ function reconstruct(
n = __outer_dim(wbdec) n = __outer_dim(wbdec)
res = sum(zip(Ms, SymbolicWedderburn.direct_summands(wbdec))) do (M, ds) res = sum(zip(Ms, SymbolicWedderburn.direct_summands(wbdec))) do (M, ds)
res = similar(M, n, n) res = similar(M, n, n)
res = _reconstruct!(res, M, ds, __group_of(wbdec), wbdec.hom) res = _reconstruct!(res, M, ds)
return res return res
end end
res = average!(zero(res), res, __group_of(wbdec), wbdec.hom)
return res return res
end end
@ -22,16 +23,14 @@ function _reconstruct!(
res::AbstractMatrix, res::AbstractMatrix,
M::AbstractMatrix, M::AbstractMatrix,
ds::SymbolicWedderburn.DirectSummand, ds::SymbolicWedderburn.DirectSummand,
G,
hom,
) )
U = SymbolicWedderburn.image_basis(ds)
d = SymbolicWedderburn.degree(ds)
Θπ = (U' * M * U) .* d
res .= zero(eltype(res)) res .= zero(eltype(res))
Θπ = average!(res, Θπ, G, hom) if !iszero(M)
return Θπ U = SymbolicWedderburn.image_basis(ds)
d = SymbolicWedderburn.degree(ds)
res = (U' * M * U) .* d
end
return res
end end
function __droptol!(M::AbstractMatrix, tol) function __droptol!(M::AbstractMatrix, tol)
@ -52,18 +51,18 @@ function average!(
<:SymbolicWedderburn.ByPermutations, <:SymbolicWedderburn.ByPermutations,
}, },
) )
res .= zero(eltype(res))
@assert size(M) == size(res) @assert size(M) == size(res)
o = Groups.order(Int, G)
for g in G for g in G
p = SymbolicWedderburn.induce(hom, g) p = SymbolicWedderburn.induce(hom, g)
Threads.@threads for c in axes(res, 2) Threads.@threads for c in axes(res, 2)
# note: p is a permutation,
# so accesses below are guaranteed to be disjoint
for r in axes(res, 1) for r in axes(res, 1)
res[r^p, c^p] += M[r, c] if !iszero(M[r, c])
res[r^p, c^p] += M[r, c] / o
end
end end
end end
end end
o = Groups.order(Int, G)
res ./= o
return res return res
end end

View File

@ -99,7 +99,7 @@ function invariant_constraint!(
invariant_vec::SparseVector, invariant_vec::SparseVector,
) where {K} ) where {K}
result .= zero(eltype(result)) result .= zero(eltype(result))
for i in SparseArrays.nonzeroinds(invariant_vec) @inbounds for i in SparseArrays.nonzeroinds(invariant_vec)
g = basis[i] g = basis[i]
A = cnstrs[g] A = cnstrs[g]
for (idx, v) in nzpairs(A) for (idx, v) in nzpairs(A)
@ -109,6 +109,25 @@ function invariant_constraint!(
return result return result
end end
function invariant_constraint(basis, cnstrs, invariant_vec)
I = UInt32[]
J = UInt32[]
V = Float64[]
_M = first(values(cnstrs))
CI = CartesianIndices(_M)
@inbounds for i in SparseArrays.nonzeroinds(invariant_vec)
g = basis[i]
A = cnstrs[g]
for (idx, v) in nzpairs(A)
ci = CI[idx]
push!(I, ci[1])
push!(J, ci[2])
push!(V, invariant_vec[i] * v)
end
end
return sparse(I, J, V, size(_M)...)
end
function isorth_projection(ds::SymbolicWedderburn.DirectSummand) function isorth_projection(ds::SymbolicWedderburn.DirectSummand)
U = SymbolicWedderburn.image_basis(ds) U = SymbolicWedderburn.image_basis(ds)
return isapprox(U * U', I) return isapprox(U * U', I)
@ -175,8 +194,23 @@ function sos_problem_primal(
end end
end end
id_one = findfirst(invariant_vectors(wedderburn)) do v
b = basis(parent(elt))
return sparsevec([b[one(first(b))]], [1 // 1], length(v)) == v
end
prog = ProgressMeter.Progress(
length(invariant_vectors(wedderburn));
dt = 1,
desc = "Adding constraints: ",
enabled = show_progress,
barlen = 60,
showspeed = true,
)
feasibility_problem = iszero(orderunit) feasibility_problem = iszero(orderunit)
# problem creation starts here
model = JuMP.Model() model = JuMP.Model()
if !feasibility_problem # add λ or not? if !feasibility_problem # add λ or not?
λ = JuMP.@variable(model, λ) λ = JuMP.@variable(model, λ)
@ -190,58 +224,52 @@ function sos_problem_primal(
end end
end end
P = map(direct_summands(wedderburn)) do ds # semidefinite constraints as described by wedderburn
Ps = map(direct_summands(wedderburn)) do ds
dim = size(ds, 1) dim = size(ds, 1)
P = JuMP.@variable(model, [1:dim, 1:dim], Symmetric) P = JuMP.@variable(model, [1:dim, 1:dim], Symmetric)
JuMP.@constraint(model, P in PSDCone()) JuMP.@constraint(model, P in PSDCone())
return P return P
end end
begin # preallocating begin # Ms are preallocated for the constraints loop
T = eltype(wedderburn) T = eltype(wedderburn)
Ms = [spzeros.(T, size(p)...) for p in P] Ms = [spzeros.(T, size(p)...) for p in Ps]
M_orb = zeros(T, size(parent(elt).mstructure)...) _eps = 10 * eps(T) * max(size(parent(elt).mstructure)...)
end end
X = convert(Vector{T}, StarAlgebras.coeffs(elt)) X = StarAlgebras.coeffs(elt)
U = convert(Vector{T}, StarAlgebras.coeffs(orderunit)) U = StarAlgebras.coeffs(orderunit)
# defining constraints based on the multiplicative structure # defining constraints based on the multiplicative structure
cnstrs = constraints(parent(elt); augmented = augmented) cnstrs = constraints(parent(elt); augmented = augmented)
prog = ProgressMeter.Progress( # adding linear constraints: one per orbit
length(invariant_vectors(wedderburn));
dt = 1,
desc = "Adding constraints: ",
enabled = show_progress,
barlen = 60,
showspeed = true,
)
for (i, iv) in enumerate(invariant_vectors(wedderburn)) for (i, iv) in enumerate(invariant_vectors(wedderburn))
ProgressMeter.next!(prog; showvalues = __show_itrs(i, prog.n)) ProgressMeter.next!(prog; showvalues = __show_itrs(i, prog.n))
augmented && i == id_one && continue
# i == 500 && break
x = dot(X, iv) x = dot(X, iv)
u = dot(U, iv) u = dot(U, iv)
M_orb = invariant_constraint!(M_orb, basis(parent(elt)), cnstrs, iv) spM_orb = invariant_constraint(basis(parent(elt)), cnstrs, iv)
Ms = SymbolicWedderburn.diagonalize!( Ms = SymbolicWedderburn.diagonalize!(
Ms, Ms,
M_orb, spM_orb,
wedderburn; wedderburn;
trace_preserving = true, trace_preserving = true,
) )
# SparseArrays.droptol!.(Ms, 10 * eps(T) * max(size(M_orb)...)) for M in Ms
SparseArrays.droptol!(M, _eps)
# @info [nnz(m) / length(m) for m in Ms] end
if feasibility_problem if feasibility_problem
JuMP.@constraint(model, x == _dot(P, Ms)) JuMP.@constraint(model, x == _dot(Ps, Ms))
else else
JuMP.@constraint(model, x - λ * u == _dot(P, Ms)) JuMP.@constraint(model, x - λ * u == _dot(Ps, Ms))
end end
end end
ProgressMeter.finish!(prog) ProgressMeter.finish!(prog)
return model, P return model, Ps
end end

View File

@ -176,7 +176,7 @@ end
wd; wd;
upper_bound = UB, upper_bound = UB,
halfradius = 2, halfradius = 2,
optimizer = cosmo_optimizer(; accel = 50, alpha = 1.9), optimizer = scs_optimizer(; accel = 50, alpha = 1.9),
) )
@test status == JuMP.OPTIMAL @test status == JuMP.OPTIMAL
@test !certified @test !certified