From 84945d3b5cc956f76009418301aa86e78c33c132 Mon Sep 17 00:00:00 2001 From: kalmarek Date: Wed, 3 Apr 2019 00:47:40 +0200 Subject: [PATCH] add Roots.jl, SpNs.jl, actions and their tests --- src/Roots.jl | 59 ++++++++++++++++++++++ src/SpN_actions.jl | 39 +++++++++++++++ src/SpNs.jl | 119 +++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 108 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 325 insertions(+) create mode 100644 src/Roots.jl create mode 100644 src/SpN_actions.jl create mode 100644 src/SpNs.jl create mode 100644 test/runtests.jl diff --git a/src/Roots.jl b/src/Roots.jl new file mode 100644 index 0000000..c339419 --- /dev/null +++ b/src/Roots.jl @@ -0,0 +1,59 @@ +module Roots + +using StaticArrays +using LinearAlgebra + +export Root, isproportional, isorthogonal, ~, ⟂ + +abstract type AbstractRoot end + +struct Root{N} <: AbstractRoot + coord::SVector{N, Float64} + + Root(a::SVector{N}) where N = new{N}(a) +end + +Root(a) = Root(SVector(a...)) + +function Base.:(==)(r::Root{N}, s::Root{M}) where {M, N} + M == N || return false + r.coord == s.coord || return false + return true +end + +Base.hash(r::Root, h::UInt) = hash(Root, hash(r.coord, h)) + +Base.:+(r::Root{N}, s::Root{N}) where N = Root(r.coord+s.coord) +Base.:-(r::Root{N}, s::Root{N}) where N = Root(r.coord-s.coord) +Base.:-(r::Root{N}) where N = Root(-r.coord) + +Base.:*(a::Number, r::Root) = Root(a*r.coord) +Base.:*(r::Root, a::Number) = a*r + +Base.length(r::Root) = norm(r, 2) + +LinearAlgebra.norm(r::Root, p::Real=2) = norm(r.coord, p) + +LinearAlgebra.dot(r::Root{N}, s::Root{N}) where N = dot(r.coord, s.coord) + +cos_angle(a,b) = dot(a, b) / (norm(a) * norm(b)) + +function isproportional(α::Root{N}, β::Root{N}) where N + return isapprox(abs(cos_angle(α, β)), 1.0, atol=eps(1.0)) +end + +Base.:~(α::Root{N}, β::Root{N}) where N = isproportional(α, β) + +function isorthogonal(α::Root{N}, β::Root{N}) where N + return isapprox(cos_angle(α, β), 0.0, atol=eps(1.0)) +end + +⟂(α::Root{N}, β::Root{N}) where N = isorthogonal(α, β) + +function Base.show(io::IO, r::Root{N}) where N + print(io, "$(r.coord): root of length $(norm(r))") +end + +E(N, i::Integer) = Root(ntuple(k -> k == i ? 1 : 0, N)) + +end # of module RootSystems diff --git a/src/SpN_actions.jl b/src/SpN_actions.jl new file mode 100644 index 0000000..caecb2f --- /dev/null +++ b/src/SpN_actions.jl @@ -0,0 +1,39 @@ +function blkdiag(A, B) + new_size = (size(A,1) + size(B,1), size(A,2) + size(B,2)) + res = zeros(eltype(A), new_size) + res[1:size(A,1), 1:size(A,2)] .= A + res[size(A,1)+1:end, size(A,2)+1:end] .= B + return res +end + +function matrix_emb(n::DirectPowerGroupElem, p::perm) + Id = parent(n.elts[1])() + elt = Diagonal([(-1)^(el == Id ? 0 : 1) for el in n.elts]) + elt = elt[:, p.d] + return blkdiag(elt, elt) +end + +function (g::WreathProductElem)(A::MatElem) + g_inv = inv(g) + G = matrix_emb(g.n, g_inv.p) + G_inv = matrix_emb(g_inv.n, g.p) + M = parent(A) + return M(G)*A*M(G_inv) +end + +function Base.:*(x::AbstractAlgebra.MatElem, P::Generic.perm) + z = similar(x) + m = nrows(x) + n = ncols(x) + for i = 1:m + for j = 1:n + z[i, j] = x[i,P[j]] + end + end + return z +end + +function (p::perm)(A::MatElem) + length(p.d) == A.r == A.c || throw("Can't act via $p on matrix of size ($(A.r), $(A.c))") + return p*A*inv(p) +end diff --git a/src/SpNs.jl b/src/SpNs.jl new file mode 100644 index 0000000..c7b9878 --- /dev/null +++ b/src/SpNs.jl @@ -0,0 +1,119 @@ +module SpNs + +using LinearAlgebra +using AbstractAlgebra +using Nemo +using Groups +using GroupRings + +include("Roots.jl") +using .Roots + +export Sp, isproportional, ~, isorthogonal, ⟂, positive + +# two families of generators + +function a(i::Int, j::Int, M::MatSpace, val=one(M.base_ring)) # short roots + @assert i ≠ j + m = one(M) + N = size(m,2)÷2 + + m[i,j] = val + m[N+j, N+i] = -val + @assert issymplectic(m) + return m +end + +function b(i::Int, j::Int, M::MatSpace, val=one(M.base_ring)) # long roots + m = one(M) + N = size(m,2)÷2 + m[i, N+j] = val + m[j, N+i] = val + @assert issymplectic(m) + return m +end + +# symplectic generator with associated root datum: + +struct Sp{N} + root::Root{N} + matrix::Nemo.fmpz_mat +end + +function Sp{N}(typ::Val{:a}, i::Int, j::Int) where N + @assert i≠j + r = Roots.E(N, i) - Roots.E(N, j) + M = Nemo.MatrixSpace(Nemo.ZZ, 2N, 2N) + m = a(i,j, M) + return Sp{N}(r, m) +end + +function Sp{N}(typ::Val{:b}, i::Int, j::Int=i) where N + r = Roots.E(N, i) + Roots.E(N, j) + M = Nemo.MatrixSpace(Nemo.ZZ, 2N, 2N) + m = b(i,j, M) + return Sp{N}(r, m) +end + +Sp{N}(s::Symbol, i::Int, j::Int=i) where N = Sp{N}(Val(s), i, j) + +Base.inv(s::Sp) = Sp(s.root, inv(s.matrix)) +Base.:-(s::Sp) = Sp(-s.root, transpose(s.matrix)) + +Roots.isproportional(x::Sp, y::Sp) = isproportional(x.root, y.root) +Roots.:~(x::Sp,y::Sp) = x.root ~ y.root + +Roots.isorthogonal(x::Sp, y::Sp) = isorthogonal(x.root, y.root) +Roots.:⟂(x::Sp,y::Sp) = x.root ⟂ y.root + +function issymplectic(M) + r = nrows(M) + c = ncols(M) + @assert r == c + @assert iseven(r) + n = r÷2 + I = Diagonal(ones(Int, n)) + + O = zeros(Int,n,n); + Ω = parent(M)([O I; -I O]) + return Ω == transpose(M)*Ω*M +end + +indexing(n) = [(i,j) for i in 1:n for j in i+1:n] + +function gens_roots(N) + a_ijs = [Sp{N}(:a, i,j) for (i,j) in indexing(N)] + b_is = [Sp{N}(:b, i) for i in 1:N] + c_ijs = [Sp{N}(:b, i,j) for (i,j) in indexing(N)] + root_system = [a_ijs; b_is; c_ijs] + root_system = [root_system; [-r for r in root_system]]; + root_system = [root_system; inv.(root_system)] + return root_system +end + +function positive(root_system::Vector{SP}) where SP + roots = [r.root for r in root_system] + + pos_roots = eltype(roots)[] + for (idx,γ) in enumerate(roots) + if any(isproportional(s, γ) for s in roots[1:idx-1]) + continue + end + push!(pos_roots, γ) + @info "" positive_root = γ + end + # TODO: what about their sums?? + + return pos_roots +end + +function laplacians(RG::GroupRing, root_system::Vector{SpNs.Sp{N}}) where N + positive_roots = positive(root_system) + Sαs = Dict(α => [w.matrix for w in root_system if isproportional(w.root, α)] for α in positive_roots) + Δs = Dict(α => RG(length(S)) - RG(S) for (α, S) in Sαs) + return Δs +end + +include("SpN_actions.jl") + +end # of module SPNs diff --git a/test/runtests.jl b/test/runtests.jl new file mode 100644 index 0000000..4334e57 --- /dev/null +++ b/test/runtests.jl @@ -0,0 +1,108 @@ +using Test + +using PropertyT +include("../src/SpNs.jl") +using .SpNs +using .SpNs.Roots + +@testset "Roots" begin + @test Root((2,1)) isa Root + r = Root((2,1)) + @test -r isa Root + @test 2r isa Root + @test length(2r) == 2length(r) + + @test r+r isa Root + @test length(r+r) == 2length(r) + @test r-r isa Root; + @test length(r-r) == 0.0 + + r = Root((1,1)); + s = Root((-1,1)); + @test string(Root((1,1))) == "[1.0, 1.0]: root of length 1.4142135623730951" + + @test isproportional(r, r) + @test isproportional(r, -r) + @test isproportional(r, 2r) + @test isproportional(-r, 2r) + @test isproportional(r+s, -s-r) + + @test isorthogonal(r, s) + @test isorthogonal(r, -s) + @test isorthogonal(r, 2s) + @test isorthogonal(-r, 2s) + + @test !isorthogonal(r, r+s) + @test !isorthogonal(r, -r) + @test !isorthogonal(r+s, 2s) + @test !isorthogonal(-r, 2s+r) + + @test isorthogonal(r+s, -s+r) + + N = 3 + @test length(Roots.E(N, 1) - Roots.E(N,2)) == sqrt(2) +end + + +@testset "Symplectic Generators" begin + + s = Sp{2}(:a, 1, 2) + @test -s isa Sp + @test inv(s) isa Sp + t = Sp{2}(:b, 1, 2) + @test -t isa Sp + @test inv(t) isa Sp + + @test isproportional(s, -s) + @test isproportional(s, -s) + @test isproportional(s, inv(s)) + + @test isproportional(t, -t) + @test isproportional(t, inv(t)) + @test !isproportional(s, t) + @test !isproportional(inv(s), -t) + +end + +@testset "Symplectic Group" begin + + @testset "Symplectic Root System" begin + rs = SpNs.gens_roots(2) + S = [r.matrix for r in rs]; + @test all(SpNs.issymplectic.(S)) + @test length(S) == 16 + + rs = SpNs.gens_roots(3) + S = [r.matrix for r in rs]; + @test all(SpNs.issymplectic.(S)) + @test length(S) == 36 + end + + @testset "Sp(4,Z)" begin + + N = 2 + root_system = SpNs.gens_roots(N) + S = [g.matrix for g in root_system] + + Δ = PropertyT.Laplacian(S, 2) + RG = parent(Δ) + + Δs = SpNs.laplacians(RG, root_system) + + @test sum(Δα for (α, Δα) in Δs) == Δ + + @testset "Orthogonality & commutation" begin + r = Root([2, 0]) + s = Root([0, 2]) + + a = Δs[r] + b = Δs[s] + @test a*b == b*a + end + + Sq = sum( Δα^2 for (α, Δα) in Δs ); + Adj = sum( Δα * sum(Δβ for (β, Δβ) in Δs if !isproportional(α, β)) for (α, Δα) in Δs ); + + @test Sq + Adj == Δ^2 + end +end