diff --git a/experimental/LieAlgebras/docs/src/introduction.md b/experimental/LieAlgebras/docs/src/introduction.md index 6ab3f70f4793..76a9bbead968 100644 --- a/experimental/LieAlgebras/docs/src/introduction.md +++ b/experimental/LieAlgebras/docs/src/introduction.md @@ -18,6 +18,7 @@ This part of OSCAR is in an experimental state; please see [Adding new projects Please direct questions about this part of OSCAR to the following people: * [Lars Göttgens](https://lgoe.li/) +* [Laura Voggesberger](https://www.ruhr-uni-bochum.de/ffm/Lehrstuehle/Lehrstuhl-VI/voggesberger.html) You can ask questions in the [OSCAR Slack](https://www.oscar-system.org/community/#slack). diff --git a/experimental/LieAlgebras/docs/src/lie_algebras.md b/experimental/LieAlgebras/docs/src/lie_algebras.md index 70c3dd958867..a40cda0b2a94 100644 --- a/experimental/LieAlgebras/docs/src/lie_algebras.md +++ b/experimental/LieAlgebras/docs/src/lie_algebras.md @@ -24,6 +24,7 @@ coefficients(::LieAlgebraElem) coeff(::LieAlgebraElem, ::Int) getindex(::LieAlgebraElem, ::Int) symbols(::LieAlgebra) +characteristic(L::LieAlgebra) ``` ## Special functions for `LinearLieAlgebra`s diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index f82583821247..d9c6ba931665 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -57,6 +57,13 @@ function basis(L::LieAlgebra, i::Int) return L([(j == i ? one(R) : zero(R)) for j in 1:dim(L)]) end +@doc raw""" + characteristic(L::LieAlgebra) -> Int + +Return the characteristic of the coefficient ring of the Lie algebra `L`. +""" +characteristic(L::LieAlgebra) = characteristic(coefficient_ring(L)) + @doc raw""" zero(L::LieAlgebra{C}) -> LieAlgebraElem{C} diff --git a/experimental/LieAlgebras/src/LieAlgebras.jl b/experimental/LieAlgebras/src/LieAlgebras.jl index 9200fa18e83e..e8f51c3c181d 100644 --- a/experimental/LieAlgebras/src/LieAlgebras.jl +++ b/experimental/LieAlgebras/src/LieAlgebras.jl @@ -21,6 +21,7 @@ import ..Oscar: canonical_projections, center, centralizer, + characteristic, coeff, coefficient_ring, coefficients, @@ -72,6 +73,8 @@ export LieSubalgebra export LieAlgebraModule, LieAlgebraModuleElem export LieAlgebraModuleHom export LinearLieAlgebra, LinearLieAlgebraElem +export RootSystem +export SimpleLieAlgebra, SimpleLieAlgebraElem export abelian_lie_algebra export abstract_module @@ -79,10 +82,14 @@ export base_lie_algebra export base_module export base_modules export bracket +export cartan_matrix +export chevalley_basis export coefficient_vector export coerce_to_lie_algebra_elem export combinations export derived_algebra +export dynkin_diagram +export exterior_power export general_linear_lie_algebra export highest_weight_module export hom_direct_sum @@ -97,8 +104,11 @@ export is_tensor_product export lie_algebra export matrix_repr_basis export multicombinations +export number_of_roots export permutations export permutations_with_sign +export root_system +export root_system_type export special_linear_lie_algebra export special_orthogonal_lie_algebra export standard_module @@ -120,6 +130,8 @@ include("LieAlgebraModuleHom.jl") include("iso_oscar_gap.jl") include("iso_gap_oscar.jl") include("GapWrapper.jl") +include("root_systems.jl") +include("simple_lie_algebra.jl") end @@ -133,6 +145,8 @@ export LieAlgebraModule, LieAlgebraModuleElem export LieAlgebraModuleHom export LieSubalgebra export LinearLieAlgebra, LinearLieAlgebraElem +export RootSystem +export SimpleLieAlgebra, SimpleLieAlgebraElem export abelian_lie_algebra export abstract_module @@ -140,8 +154,12 @@ export base_lie_algebra export base_module export base_modules export bracket +export cartan_matrix +export chevalley_basis export coerce_to_lie_algebra_elem export derived_algebra +export dynkin_diagram +export exterior_power export general_linear_lie_algebra export highest_weight_module export hom_direct_sum @@ -155,6 +173,9 @@ export is_tensor_power export is_tensor_product export lie_algebra export matrix_repr_basis +export number_of_roots +export root_system +export root_system_type export special_linear_lie_algebra export special_orthogonal_lie_algebra export standard_module diff --git a/experimental/LieAlgebras/src/root_systems.jl b/experimental/LieAlgebras/src/root_systems.jl new file mode 100644 index 000000000000..5630a19aa958 --- /dev/null +++ b/experimental/LieAlgebras/src/root_systems.jl @@ -0,0 +1,237 @@ +mutable struct RootSystem + roots::Vector{Vector{Int}} + simple_roots::Vector{Vector{Int}} + positive_roots::Vector{Vector{Int}} + root_system_type::Tuple{Symbol,Int} + GAP_root_system::GAP.GapObj + function RootSystem(S::Symbol, n::Int) + # S is a symbol detailing the type of the indecomposable root system + # e.g. "A", "B", "C",... and n is an integer for the number of simple roots + S1 = GAP.Obj(S) + RS = GAP.Globals.RootSystem(S1, n) + sR = Vector{Vector{Int}}(GAP.Globals.SimpleSystem(RS)) + Ro1 = Vector{Vector{Int}}(GAP.Globals.PositiveRoots(RS)) + Ro2 = Vector{Vector{Int}}(GAP.Globals.NegativeRoots(RS)) + Ro = vcat(Ro1, Ro2) + t = (S, n) + return new(Ro, sR, Ro1, t, RS) + end +end + +############################################################################### +# +# Basic manipulation +# +############################################################################### +@doc raw""" + number_of_roots(R::RootSystem) + +Return the numbers of roots in the root system `R`. +""" +number_of_roots(R::RootSystem) = size(R.roots)[1] + +@doc raw""" + number_of_roots(S::Symbol, n::Int) + +Return the numbers of roots in the root system of type `S` +""" +number_of_roots(S::Symbol, n::Int) = number_of_roots(root_system(S, n)) + +@doc raw""" + getindex(R::RootSystem, r::Int) + +Return the `r`-th root of the root system `R`. +""" +getindex(R::RootSystem, r::Int) = getindex(R.roots, r) + +@doc raw""" + root_system_type(R::RootSystem) + +Return the Dynkin type of the root system `R`. +""" +root_system_type(R::RootSystem) = R.root_system_type + +root_system_type_string(R::RootSystem) = + string(R.root_system_type[1]) * string(R.root_system_type[2]) + +############################################################################### +# +# String I/O +# +############################################################################### + +function Base.show(io::IO, R::RootSystem) + print(io, "Root system of type $(root_system_type_string(R))") +end + +############################################################################### +# +# Comparison +# +############################################################################### + +function Base.:(==)(R1::RootSystem, R2::RootSystem) + return R1.root_system_type == R2.root_system_type && R1.roots == R2.roots +end + +function Base.hash(R::RootSystem, h::UInt) + b = 0x9d96557cb5f07773 % UInt + h = hash(R.root_system_type, h) + h = hash(R.roots, h) + return xor(h, b) +end +############################################################################### +# +# Constructor +# +############################################################################### + +@doc raw""" + root_system(S::Symbol, n::Int) -> RootSystem + +Return the root system of type `Sn` where `S` is a symbol consisting out of +a single letter `A`, `B`, `C`, `D`, `E`, `F`, `G`. +The allowed values for `n` depend on the choice of `S`. +""" +function root_system(S::Symbol, n::Int) + @req _root_system_type_supported_by_GAP(S, n) "Unknown Dynkin type or not supported by GAP" + return RootSystem(S, n) +end + +############################################################################### +# +# further functions +# +############################################################################### + +function _root_system_type_supported_by_GAP(S, n) + S in [:A, :B, :C, :D, :E, :F, :G] || return false + n >= 1 || return false + S == :D && n < 4 && return false + S == :E && !(n in [6, 7, 8]) && return false + S == :F && n != 4 && return false + S == :G && n != 2 && return false + return true +end + +@doc raw""" + cartan_matrix(S::Symbol, n::Int) -> Matrix{QQFieldElem} + +Return the Cartan matrix of the root system of type `Sn`. +For the semantics of the arguments, refer to `root_system(S::Symbol, n::Int)` ref. +""" +function cartan_matrix(S::Symbol, n::Int) + return cartan_matrix(root_system(S, n)) +end + +@doc raw""" + cartan_matrix(R::RootSystem) -> Matrix{QQFieldElem} + +Return the Cartan matrix of the root system `R`. +""" +function cartan_matrix(R::RootSystem) + RS = R.GAP_root_system + CG = GAP.Globals.CartanMatrix(RS) + C = matrix(QQ, CG) + return C +end + +@doc raw""" + dynkin_diagram(S::Symbol, n::Int) + +Return the Dynkin diagram of the root system of type `Sn`. +For the semantics of the arguments, refer to [root_system(S::Symbol, n::Int)` ref. +""" +function dynkin_diagram(S::Symbol, n::Int) + @req _root_system_type_supported_by_GAP(S, n) "Unknown Dynkin type or not supported by GAP" + D = "" + + if S == :A + for i in 1:(n - 1) + D = D * string(i) * " - " + end + D = D * string(n) + + elseif S == :B + if n == 1 + D = string(n) + else + for i in 1:(n - 2) + D = D * string(i) * " - " + end + D = D * string(n - 1) * " >=> " * string(n) + end + + elseif S == :C + if n == 1 + D = string(n) + else + for i in 1:(n - 2) + D = D * string(i) * " - " + end + D = D * string(n - 1) * " <=< " * string(n) + end + + elseif S == :D + if n >= 4 + for i in 1:(4 * n - 10) + D = D * " " + end + D = D * string(n - 1) * "\n" + for i in 1:(4 * n - 11) + D = D * " " + end + D = D * "/\n" + for i in 1:(n - 3) + D = D * string(i) * " - " + end + D = D * string(n - 2) * "\n" + for i in 1:(4 * n - 12) + D = D * " " + end + D = D * " \\ \n" + for i in 1:(4 * n - 10) + D = D * " " + end + D = D * string(n) + else + error("This root system doesn't exist.") + end + + elseif S == :E + if n == 6 + D = "1 - 3 - 4 - 5 - 6\n |\n 2" + elseif n == 7 + D = "1 - 3 - 4 - 5 - 6 - 7\n |\n 2" + elseif n == 8 + D = "1 - 3 - 4 - 5 - 6 - 7 - 8\n |\n 2" + else + error("This root system doesn't exist.") + end + + elseif S == :F + if n == 4 + D = "1 - 2 >=> 3 - 4" + else + error("This root system doesn't exist.") + end + elseif S == :G + if n == 2 + D = "1 >>> 2" + else + error("This root system doesn't exist.") + end + else + error("This root system doesn't exist.") + end + print(D) +end + +@doc raw""" + dynkin_diagram(R::RootSystem) + +Return the Dynkin diagram of the root system `R` +""" +function dynkin_diagram(R::RootSystem) + return dynkin_diagram(R.root_system_type...) +end diff --git a/experimental/LieAlgebras/src/simple_lie_algebra.jl b/experimental/LieAlgebras/src/simple_lie_algebra.jl new file mode 100644 index 000000000000..50af8073c0ff --- /dev/null +++ b/experimental/LieAlgebras/src/simple_lie_algebra.jl @@ -0,0 +1,158 @@ +#Construct a simple Lie algebra over a given field with a given root system +@attributes mutable struct SimpleLieAlgebra{C<:FieldElem} <: LieAlgebra{C} + R::Field + root_system::RootSystem + dim::Int + s::Vector{Symbol} + root_system_type::Tuple{Symbol,Int} + struct_consts::Matrix{SRow{C}} + function SimpleLieAlgebra{C}( + R::Field, S::Symbol, n::Int; cached::Bool=true + ) where {C<:FieldElem} + RS = root_system(S, n) + return get_cached!(SimpleLieAlgebraDict, (R, RS), cached) do + dimL = number_of_roots(RS) + length(RS.simple_roots) + s = [Symbol("e_$i") for i in 1:dimL] + st = root_system_type(RS) + #get the structure constants of the Lie algebra L + #note that it is enough to do this over QQ, as we can later coerce the constants + #into the field R + coeffs_iso = inv(Oscar.iso_oscar_gap(QQ)) + LG = GAP.Globals.SimpleLieAlgebra(GAP.Obj(S), n, domain(coeffs_iso)) + sc_table_G = + ( + entry -> (entry[1], Vector{elem_type(QQ)}(map(coeffs_iso, entry[2]))) + ).( + Matrix{Tuple{Vector{Int},Vector{GAP.Obj}}}( + (GAP.Globals.StructureConstantsTable(GAPWrap.Basis(LG)))[1:dimL] + ) + ) + struct_consts = Matrix{SRow{elem_type(R)}}(undef, dimL, dimL) + for i in 1:dimL, j in 1:dimL + struct_consts[i, j] = sparse_row( + R, Tuple{Int,elem_type(R)}[(k, R(c)) for (k, c) in zip(sc_table_G[i, j]...)] + ) + end + new{C}(R, RS, dimL, s, st, struct_consts) + end::SimpleLieAlgebra{C} + end +end + +const SimpleLieAlgebraDict = CacheDictType{Tuple{Field,RootSystem},SimpleLieAlgebra}() + +mutable struct SimpleLieAlgebraElem{C<:FieldElem} <: LieAlgebraElem{C} + parent::SimpleLieAlgebra{C} + mat::MatElem{C} +end + +############################################################################### +# +# Basic manipulation +# +############################################################################### + +parent_type(::Type{SimpleLieAlgebraElem{C}}) where {C<:FieldElem} = SimpleLieAlgebra{C} + +elem_type(::Type{SimpleLieAlgebra{C}}) where {C<:FieldElem} = SimpleLieAlgebraElem{C} + +parent(x::SimpleLieAlgebraElem) = x.parent + +coefficient_ring(L::SimpleLieAlgebra{C}) where {C<:FieldElem} = L.R::parent_type(C) + +dim(L::SimpleLieAlgebra) = L.dim + +root_system(L::SimpleLieAlgebra) = L.root_system + +root_system_type(L::SimpleLieAlgebra) = L.root_system_type + +function symbols(L::SimpleLieAlgebra) + return L.s +end + +############################################################################### +# +# String I/O +# +############################################################################### + +function Base.show(io::IO, ::MIME"text/plain", L::SimpleLieAlgebra) + io = pretty(io) + println(io, "Simple Lie algebra") + println(io, Indent(), "of type $(root_system_type_string(root_system(L)))", Dedent()) + println(io, Indent(), "of dimension $(dim(L))", Dedent()) + print(io, "over ") + print(io, Lowercase(), coefficient_ring(L)) +end + +function Base.show(io::IO, L::SimpleLieAlgebra) + if get(io, :supercompact, false) + print(io, "Simple Lie algebra $(root_system_type_string(root_system(L)))") + else + io = pretty(io) + print( + io, "Simple Lie algebra $(root_system_type_string(root_system(L))) over ", Lowercase() + ) + print(IOContext(io, :supercompact => true), coefficient_ring(L)) + end +end + +############################################################################### +# +# Arithmetic operations +# +############################################################################### + +# Binary operations + +function bracket( + x::SimpleLieAlgebraElem{C}, y::SimpleLieAlgebraElem{C} +) where {C<:FieldElem} + check_parent(x, y) + L = parent(x) + mat = sum( + cxi * cyj * L.struct_consts[i, j] for (i, cxi) in enumerate(coefficients(x)), + (j, cyj) in enumerate(coefficients(y)); + init=sparse_row(coefficient_ring(L)), + ) + return L(mat) +end +############################################################################### +# +# Constructors +# +############################################################################### + +@doc raw""" + lie_algebra(R::Field, S::Symbol, n::Int; cached::Bool=true) -> SimpleLieAlgebra{elem_type(R)} + +Construct the simple Lie algebra over the field `R` with root system of type `Sn` (see `root_system(S::Symbol, n::Int)` ref). +The internally used basis of this Lie algebra is the Chevalley basis. +""" +function lie_algebra(R::Field, S::Symbol, n::Int; cached::Bool=true) + return SimpleLieAlgebra{elem_type(R)}(R, S, n; cached) +end + +############################################################################### +# +# Chevalley basis +# +############################################################################### + +@doc raw""" + chevalley_basis(L::SimpleLieAlgebra{T}) -> NTuple{3,Vector{SimpleLieAlgebraElem{T}}} + +Return the Chevalley basis of the simple Lie algebra `L` in three vectors, stating first the positive root vectors, +then the negative root vectors, and finally the basis of the Cartan subalgebra. The order of root vectors corresponds +to the order of the roots in the root system. +""" +function chevalley_basis(L::SimpleLieAlgebra) + RS = root_system(L) + n = length(RS.positive_roots) + B = basis(L) + # root vectors + r_plus = B[1:n] + r_minus = B[(n + 1):(2 * n)] + # basis for cartan algebra + h = B[(2 * n + 1):dim(L)] + return (r_plus, r_minus, h) +end diff --git a/experimental/LieAlgebras/test/LieAlgebra-test.jl b/experimental/LieAlgebras/test/LieAlgebra-test.jl index 3588eb5c1e7b..8e8eb23c72bc 100644 --- a/experimental/LieAlgebras/test/LieAlgebra-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebra-test.jl @@ -18,6 +18,8 @@ function lie_algebra_conformance_test( @test coefficient_ring(x) === coefficient_ring(L) @test elem_type(coefficient_ring(L)) == C + @test characteristic(L) == characteristic(coefficient_ring(L)) + # this block stays only as long as `ngens` and `gens` are not specialized for Lie algebras @test dim(L) == ngens(L) @test basis(L) == gens(L) @@ -26,6 +28,8 @@ function lie_algebra_conformance_test( @test dim(L) == length(basis(L)) @test all(i -> basis(L, i) == basis(L)[i], 1:dim(L)) + @test dim(L) == length(symbols(L)) + @test iszero(zero(L)) @test coefficients(x) == [coeff(x, i) for i in 1:dim(L)] @@ -102,6 +106,7 @@ end include("AbstractLieAlgebra-test.jl") include("LinearLieAlgebra-test.jl") +include("simple_lie_algebra-test.jl") @testset "LieAlgebras.LieAlgebra" begin @testset "universal_enveloping_algebra" begin diff --git a/experimental/LieAlgebras/test/root_systems-test.jl b/experimental/LieAlgebras/test/root_systems-test.jl new file mode 100644 index 000000000000..e74f44b3ec43 --- /dev/null +++ b/experimental/LieAlgebras/test/root_systems-test.jl @@ -0,0 +1,7 @@ +@testset "LieAlgebras.RootSystem" begin + R = root_system(:F, 4) + @test cartan_matrix(R) == + matrix(QQ, 4, 4, [2, -1, 0, 0, -1, 2, -2, 0, 0, -1, 2, -1, 0, 0, -1, 2]) + @test number_of_roots(R) == 48 + @test number_of_roots(:F, 4) == number_of_roots(R) +end diff --git a/experimental/LieAlgebras/test/runtests.jl b/experimental/LieAlgebras/test/runtests.jl index f13f718ec084..f6216cc2fb20 100644 --- a/experimental/LieAlgebras/test/runtests.jl +++ b/experimental/LieAlgebras/test/runtests.jl @@ -7,3 +7,4 @@ include("LieAlgebraIdeal-test.jl") include("LieAlgebraHom-test.jl") include("LieAlgebraModule-test.jl") include("LieAlgebraModuleHom-test.jl") +include("root_systems-test.jl") diff --git a/experimental/LieAlgebras/test/simple_lie_algebra-test.jl b/experimental/LieAlgebras/test/simple_lie_algebra-test.jl new file mode 100644 index 000000000000..69b0b5cdc017 --- /dev/null +++ b/experimental/LieAlgebras/test/simple_lie_algebra-test.jl @@ -0,0 +1,29 @@ +@testset "LieAlgebras.SimpleLieAlgebra" begin + @testset "conformance tests" begin + @testset "B2(QQ)" begin + L = lie_algebra(QQ, :B, 2) + lie_algebra_conformance_test( + L, SimpleLieAlgebra{QQFieldElem}, SimpleLieAlgebraElem{QQFieldElem} + ) + end + + @testset "A3(CF(4))" begin + L = lie_algebra(cyclotomic_field(4)[1], :A, 3) + lie_algebra_conformance_test( + L, SimpleLieAlgebra{nf_elem}, SimpleLieAlgebraElem{nf_elem} + ) + end + end + + @testset "constructors and basic properties" begin + L = lie_algebra(QQ, :A, 2) + @test dim(L) == 8 + @test coefficient_ring(L) == QQ + @test root_system(L) == RootSystem(:A, 2) + @test root_system_type(L) == (:A, 2) + @test characteristic(L) == 0 + @test chevalley_basis(L) == ( + [basis(L, i) for i in 1:3], [basis(L, i) for i in 4:6], [basis(L, i) for i in 7:8] + ) + end +end diff --git a/src/Oscar.jl b/src/Oscar.jl index c5124855181a..579e22327d28 100644 --- a/src/Oscar.jl +++ b/src/Oscar.jl @@ -90,6 +90,7 @@ function __init__() "forms", # bilinear/sesquilinear/quadratic forms "primgrp", # primitive groups library "repsn", # constructing representations of finite groups + "sla", # computing with simple Lie algebras "smallgrp", # small groups library "transgrp", # transitive groups library "wedderga", # provides a function to compute Schur indices