From aaf486ffbc4abf18dc3ce8a3a779e0ba8bb0c235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Fri, 6 Sep 2024 13:10:47 +0200 Subject: [PATCH 01/17] Add `adjoint_matrix` --- experimental/LieAlgebras/src/LieAlgebra.jl | 27 +++++++++++++++++++ experimental/LieAlgebras/src/exports.jl | 1 + .../LieAlgebras/test/LieAlgebra-test.jl | 9 +++++++ 3 files changed, 37 insertions(+) diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index 1ed5624f7324..49daa3aee9b3 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -432,6 +432,33 @@ Return `true` if `L` is solvable, i.e. the derived series of `L` terminates in $ return dim(derived_series(L)[end]) == 0 end +############################################################################### +# +# Adjoint elements +# +############################################################################### + +@attr Vector{dense_matrix_type(C)} function adjoint_matrices( + L::LieAlgebra{C} +) where {C<:FieldElem} + return map(1:dim(L)) do i + x = basis(L, i) + A = zero_matrix(coefficient_ring(L), dim(L), dim(L)) + for (j, bj) in enumerate(basis(L)) + A[j, :] = _matrix(bracket(x, bj)) + end + return A + end +end + +function adjoint_matrix(x::LieAlgebraElem{C}) where {C<:FieldElem} + L = parent(x) + return sum( + c * g for (c, g) in zip(coefficients(x), adjoint_matrices(L)); + init=zero_matrix(coefficient_ring(L), dim(L), dim(L)), + ) +end + ############################################################################### # # Root system getters diff --git a/experimental/LieAlgebras/src/exports.jl b/experimental/LieAlgebras/src/exports.jl index 18a021ab3bff..56ea2eca6632 100644 --- a/experimental/LieAlgebras/src/exports.jl +++ b/experimental/LieAlgebras/src/exports.jl @@ -20,6 +20,7 @@ export WeylOrbitIterator export abelian_lie_algebra export abstract_module +export adjoint_matrix export base_lie_algebra export bilinear_form export bracket diff --git a/experimental/LieAlgebras/test/LieAlgebra-test.jl b/experimental/LieAlgebras/test/LieAlgebra-test.jl index 121d73446ccb..65aa0d63560b 100644 --- a/experimental/LieAlgebras/test/LieAlgebra-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebra-test.jl @@ -93,4 +93,13 @@ @test !is_nilpotent(L) @test !is_solvable(L) end + + @testset "adjoint_matrix" begin + @testset for n in 2:4, F in [QQ, GF(2), GF(3)] + L = general_linear_lie_algebra(F, n) + x = L(rand(-10:10, dim(L))) + y = L(rand(-10:10, dim(L))) + @test coefficients(x * y) == coefficients(y) * adjoint_matrix(x) + end + end end From 16d1a8bb3e9737a9431e962c686644aa68aac4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Fri, 6 Sep 2024 14:01:21 +0200 Subject: [PATCH 02/17] Add `killing_matrix` --- experimental/LieAlgebras/src/LieAlgebra.jl | 13 +++++++++++++ experimental/LieAlgebras/src/exports.jl | 1 + experimental/LieAlgebras/test/LieAlgebra-test.jl | 12 ++++++++++++ 3 files changed, 26 insertions(+) diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index 49daa3aee9b3..b570855983bc 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -459,6 +459,19 @@ function adjoint_matrix(x::LieAlgebraElem{C}) where {C<:FieldElem} ) end +@attr dense_matrix_type(C) function killing_matrix(L::LieAlgebra{C}) where {C<:FieldElem} + R = coefficient_ring(L) + A = zero_matrix(R, dim(L), dim(L)) + for (i, adxi) in enumerate(adjoint_matrices(L)) + for (j, adxj) in enumerate(adjoint_matrices(L)) + i > j && continue # killing form is symmetric + val = tr(adxi * adxj) + A[j, i] = A[i, j] = val + end + end + return A +end + ############################################################################### # # Root system getters diff --git a/experimental/LieAlgebras/src/exports.jl b/experimental/LieAlgebras/src/exports.jl index 56ea2eca6632..cb07ee4db216 100644 --- a/experimental/LieAlgebras/src/exports.jl +++ b/experimental/LieAlgebras/src/exports.jl @@ -66,6 +66,7 @@ export is_simple_coroot export is_simple_coroot_with_index export is_simple_root export is_simple_root_with_index +export killing_matrix export lie_algebra export lmul, lmul! export longest_element diff --git a/experimental/LieAlgebras/test/LieAlgebra-test.jl b/experimental/LieAlgebras/test/LieAlgebra-test.jl index 65aa0d63560b..00d07c14bbf0 100644 --- a/experimental/LieAlgebras/test/LieAlgebra-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebra-test.jl @@ -102,4 +102,16 @@ @test coefficients(x * y) == coefficients(y) * adjoint_matrix(x) end end + + @testset "Killing matrix" begin + # Example from Hum72, Ch. 5.1 + F = QQ + L = lie_algebra( + F, + 2, + [matrix(F, [0 1; 0 0]), matrix(F, [1 0; 0 -1]), matrix(F, [0 0; 1 0])], + [:x, :h, :y], + ) + @test killing_matrix(L) == matrix(F, [0 0 4; 0 8 0; 4 0 0]) + end end From aee118ac4935699f4d311dfb9a993be75b50eb9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Fri, 6 Sep 2024 18:02:58 +0200 Subject: [PATCH 03/17] Add `any_non_ad_nilpotent_element` --- Project.toml | 2 +- docs/oscar_references.bib | 11 +++++ experimental/LieAlgebras/src/LieAlgebra.jl | 46 +++++++++++++++++++ experimental/LieAlgebras/src/LieSubalgebra.jl | 12 +++++ experimental/LieAlgebras/src/exports.jl | 2 + 5 files changed, 72 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index ad8dff36a42c..65d51ce667e6 100644 --- a/Project.toml +++ b/Project.toml @@ -25,7 +25,7 @@ UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" cohomCalg_jll = "5558cf25-a90e-53b0-b813-cadaa3ae7ade" [compat] -AbstractAlgebra = "0.42.3" +AbstractAlgebra = "0.42.5" AlgebraicSolving = "0.5.1" Distributed = "1.6" GAP = "0.11.3" diff --git a/docs/oscar_references.bib b/docs/oscar_references.bib index 3e4463c66139..068a9f9c2e78 100644 --- a/docs/oscar_references.bib +++ b/docs/oscar_references.bib @@ -1091,6 +1091,17 @@ @InProceedings{GHJ16 year = {2016} } +@Article{GIR96, + author = {de Graaf, Willem A. and Ivanyos, Gábor and Rónyai, Lajos}, + title = {Computing Cartan subalgebras of Lie algebras}, + journal = {Appl. Algebra Eng. Commun. Comput.}, + volume = {7}, + number = {5}, + pages = {339--349}, + year = {1996}, + doi = {10.1007/BF01293593} +} + @InCollection{GJ00, author = {Gawrilow, Ewgenij and Joswig, Michael}, title = {polymake: a Framework for Analyzing Convex Polytopes}, diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index b570855983bc..2b44085d533f 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -472,6 +472,52 @@ end return A end +@doc raw""" + is_ad_nilpotent(x::LieAlgebraElem{C}) -> Bool + +Return whether `x` is ad-nilpotent, i.e. whether the linear operator $\mathrm{ad}(x)$ is nilpotent. +""" +function is_ad_nilpotent(x::LieAlgebraElem{C}) where {C<:FieldElem} + return is_nilpotent(adjoint_matrix(x)) +end + +@doc raw""" + any_non_ad_nilpotent_element(L::LieAlgebra{C}) -> LieAlgebraElem{C} + +Return an element of `L` that is not ad-nilpotent, or the zero element if all elements are ad-nilpotent. + +The used algorithm is described in [GIR96; Ch. 3](@cite). +""" +function any_non_ad_nilpotent_element(L::LieAlgebra{C}) where {C<:FieldElem} + if dim(L) <= 1 + # L is abelian and hence nilpotent + elseif characteristic(L) == 0 + for x in basis(L) + !is_ad_nilpotent(x) && return x + end + for (i, x) in enumerate(basis(L)) + for (j, y) in enumerate(basis(L)) + i > j && continue + xy = x * y + !is_ad_nilpotent(xy) && return xy + end + end + else # characteristic > 0 + x = basis(L, 1) + !is_ad_nilpotent(x) && return x + K = sub(L, [x]; is_basis=true) + while dim(K) < dim(L) + # find an element b in L \ K with [b,K]⊆K + N = normalizer(L, K) + b = basis(N, findfirst(b -> !(b in K), basis(N))) + !is_ad_nilpotent(b) && return b + K = sub(L, [basis(K); b]; is_basis=true) + end + end + set_attribute!(L, :is_nilpotent, true) + return zero(L) +end + ############################################################################### # # Root system getters diff --git a/experimental/LieAlgebras/src/LieSubalgebra.jl b/experimental/LieAlgebras/src/LieSubalgebra.jl index d9267e1403b0..574aae2de7e6 100644 --- a/experimental/LieAlgebras/src/LieSubalgebra.jl +++ b/experimental/LieAlgebras/src/LieSubalgebra.jl @@ -211,6 +211,18 @@ function is_self_normalizing(S::LieSubalgebra) return normalizer(base_lie_algebra(S), S) == S end +############################################################################### +# +# More misc stuff +# +############################################################################### + +function any_non_ad_nilpotent_element(S::LieSubalgebra) + LS, emb = lie_algebra(S) + x = any_non_ad_nilpotent_element(LS) + return emb(x) +end + ############################################################################### # # Conversion diff --git a/experimental/LieAlgebras/src/exports.jl b/experimental/LieAlgebras/src/exports.jl index cb07ee4db216..1c414f817eff 100644 --- a/experimental/LieAlgebras/src/exports.jl +++ b/experimental/LieAlgebras/src/exports.jl @@ -21,6 +21,7 @@ export WeylOrbitIterator export abelian_lie_algebra export abstract_module export adjoint_matrix +export any_non_ad_nilpotent_element export base_lie_algebra export bilinear_form export bracket @@ -46,6 +47,7 @@ export fundamental_weights export general_linear_lie_algebra export induced_map_on_symmetric_power export induced_map_on_tensor_power +export is_ad_nilpotent export is_cartan_matrix export is_cartan_type export is_coroot From 7af7e889808e7c9eb4ae0cf09d19325eb9348239 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Mon, 9 Sep 2024 11:24:12 +0200 Subject: [PATCH 04/17] Add `engel_subalgebra` and `cartan_subalgebra` --- docs/oscar_references.bib | 11 ++++ experimental/LieAlgebras/src/LieAlgebra.jl | 69 ++++++++++++++++++++++ experimental/LieAlgebras/src/exports.jl | 2 + 3 files changed, 82 insertions(+) diff --git a/docs/oscar_references.bib b/docs/oscar_references.bib index 068a9f9c2e78..1e9ada469139 100644 --- a/docs/oscar_references.bib +++ b/docs/oscar_references.bib @@ -1235,6 +1235,17 @@ @Article{Gat96 doi = {10.1007/BF01191379} } +@Book{Gra00, + author = {de Graaf, Willem A.}, + title = {Lie algebras: theory and algorithms}, + series = {North-Holland Mathematical Library}, + volume = {56}, + publisher = {North-Holland Publishing Co., Amsterdam}, + pages = {xii+393}, + year = {2000}, + url = {https://www.sciencedirect.com/bookseries/north-holland-mathematical-library/vol/56/} +} + @PhDThesis{Gro20, author = {Gromada, Daniel}, title = {Compact matrix quantum groups and their representation categories}, diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index 2b44085d533f..1c017c7cecc9 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -481,6 +481,12 @@ function is_ad_nilpotent(x::LieAlgebraElem{C}) where {C<:FieldElem} return is_nilpotent(adjoint_matrix(x)) end +############################################################################### +# +# Root system detection +# +############################################################################### + @doc raw""" any_non_ad_nilpotent_element(L::LieAlgebra{C}) -> LieAlgebraElem{C} @@ -518,6 +524,69 @@ function any_non_ad_nilpotent_element(L::LieAlgebra{C}) where {C<:FieldElem} return zero(L) end +@doc raw""" + engel_subalgebra(x::LieAlgebraElem{C}) -> LieSubalgebra{C,elem_type(parent(x))} + +Return the Engel subalgebra of `x`, i.e. the generalized eigenspace of the linear operator $\mathrm{ad}(x)$. +""" +function engel_subalgebra(x::LieAlgebraElem{C}) where {C<:FieldElem} + L = parent(x) + n = dim(L) + A = adjoint_matrix(x)^n + ker = kernel(A; side=:left) + basis = [L(ker[i, :]) for i in 1:nrows(ker)] + L0adx = sub(L, basis; is_basis=true) + return L0adx +end + +@doc raw""" + Oscar.LieAlgebras.cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} -> LieSubalgebra{C,elem_type(L)} + +Return a Cartan subalgebra of `L`. + +If `L` knows its root system, this function uses the Chevalley basis to construct a Cartan subalgebra. +Otherise, it uses the algorithm described in [Gra00; Ch. 3.2](@cite). +""" +function cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} + if has_root_system(L) + return sub(L, chevalley_basis(L)[3]; is_basis=true) + else + return _cartan_subalgebra(L) + end +end + +function _cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} + F = coefficient_ring(L) + n = dim(L) + @req is_infinite(F) || length(F) > dim(L) "The implemented algorithm requires a large field" + x = any_non_ad_nilpotent_element(L) + if is_zero(x) # L is nilpotent + return sub(L) + end + + L0adx = engel_subalgebra(x) + while true # decreasing variant is dim(L0adx) + y = any_non_ad_nilpotent_element(L0adx) + if is_zero(y) # L0adx is nilpotent + return L0adx + end + + c_itr = + characteristic(F) == 0 ? (F(i) for i in 1:(n + 1)) : Iterators.filter(!iszero, F) + z = x + L0adz = L0adx + for c in c_itr # at most n+1 iterations + z = x + c * (y - x) + L0adz = engel_subalgebra(z) + if dim(L0adz) < dim(L0adx) && is_subset(L0adz, L0adx) + break + end + end + x = z + L0adx = L0adz + end +end + ############################################################################### # # Root system getters diff --git a/experimental/LieAlgebras/src/exports.jl b/experimental/LieAlgebras/src/exports.jl index 1c414f817eff..8383f35fcc20 100644 --- a/experimental/LieAlgebras/src/exports.jl +++ b/experimental/LieAlgebras/src/exports.jl @@ -27,6 +27,7 @@ export bilinear_form export bracket export cartan_bilinear_form export cartan_matrix +export cartan_subalgebra export cartan_symmetrizer export cartan_type export cartan_type_with_ordering @@ -41,6 +42,7 @@ export derived_algebra export dim_of_simple_module export dominant_character export dominant_weights +export engel_subalgebra export exterior_power export fundamental_weight export fundamental_weights From d4d7611b41c911410de0c76788f6ff3ae73bf9b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Wed, 11 Sep 2024 15:03:40 +0200 Subject: [PATCH 05/17] More functionality for ideals and subalgebras --- .../LieAlgebras/src/LieAlgebraIdeal.jl | 69 ++++++++++++++++++- experimental/LieAlgebras/src/LieSubalgebra.jl | 67 +++++++++++++++++- 2 files changed, 132 insertions(+), 4 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraIdeal.jl b/experimental/LieAlgebras/src/LieAlgebraIdeal.jl index 0e622304d5c5..73d8db05890b 100644 --- a/experimental/LieAlgebras/src/LieAlgebraIdeal.jl +++ b/experimental/LieAlgebras/src/LieAlgebraIdeal.jl @@ -50,6 +50,8 @@ Return the dimension of the ideal `I`. """ dim(I::LieAlgebraIdeal) = length(basis(I)) +coefficient_ring(I::LieAlgebraIdeal) = coefficient_ring(base_lie_algebra(I)) + ############################################################################### # # String I/O @@ -81,6 +83,55 @@ function Base.show(io::IO, I::LieAlgebraIdeal) end end +############################################################################### +# +# Parent object call overload +# +############################################################################### + +@doc raw""" + (I::LieAlgebraIdeal{C})() -> LieAlgebraElem{C} + +Return the zero element of the Lie algebra ideal `I`. +""" +function (I::LieAlgebraIdeal)() + return zero(base_lie_algebra(I)) +end + +@doc raw""" + (I::LieAlgebraIdeal{C})(v::Vector{Int}) -> LieAlgebraElem{C} + +Return the element of `I` with coefficient vector `v`. +Fail, if `Int` cannot be coerced into the base ring of `I`. +""" +function (I::LieAlgebraIdeal)(v::Vector{Int}) + return I(coefficient_ring(I).(v)) +end + +@doc raw""" + (I::LieAlgebraIdeal{C})(v::Vector{C}) -> LieAlgebraElem{C} + +Return the element of `I` with coefficient vector `v`. +""" +function (I::LieAlgebraIdeal{C})(v::Vector{C}) where {C<:FieldElem} + @req length(v) == dim(I) "Length of vector does not match dimension." + mat = matrix(coefficient_ring(I), 1, length(v), v) + L = base_lie_algebra(I) + return elem_type(L)(L, mat * basis_matrix(I)) +end + +@doc raw""" + (I::LieAlgebraIdeal{C})(mat::MatElem{C}) -> LieAlgebraElem{C} + +Return the element of `I` with coefficient vector equivalent to +the $1 \times \dim(I)$ matrix `mat`. +""" +function (I::LieAlgebraIdeal{C})(mat::MatElem{C}) where {C<:FieldElem} + @req size(mat) == (1, dim(I)) "Invalid matrix dimensions." + L = base_lie_algebra(I) + return elem_type(L)(L, mat * basis_matrix(I)) +end + ############################################################################### # # Comparisons @@ -116,14 +167,28 @@ end ############################################################################### @doc raw""" - in(x::LieAlgebraElem, I::LieAlgebraIdeal) -> Bool + in(x::LieAlgebraElem{C}, I::LieAlgebraIdeal{C}) -> Bool Return `true` if `x` is in the ideal `I`, `false` otherwise. """ -function Base.in(x::LieAlgebraElem, I::LieAlgebraIdeal) +function Base.in(x::LieAlgebraElem{C}, I::LieAlgebraIdeal{C}) where {C<:FieldElem} + @req parent(x) === base_lie_algebra(I) "Incompatible Lie algebras" return can_solve(basis_matrix(I), _matrix(x); side=:left) end +@doc raw""" + Oscar.LieAlgebras.coefficient_vector(x::LieAlgebraElem{C}, I::LieAlgebraIdeal{C}) -> Vector{C} + +Return the coefficient vector of `x` in the basis of `I`. +This function will throw an error if `x` is not in `I`. +""" +function coefficient_vector( + x::LieAlgebraElem{C}, I::LieAlgebraIdeal{C} +) where {C<:FieldElem} + @req parent(x) === base_lie_algebra(I) "Incompatible Lie algebras" + return solve(basis_matrix(I), _matrix(x); side=:left) +end + ############################################################################### # # Constructions diff --git a/experimental/LieAlgebras/src/LieSubalgebra.jl b/experimental/LieAlgebras/src/LieSubalgebra.jl index 574aae2de7e6..f83919ff27cb 100644 --- a/experimental/LieAlgebras/src/LieSubalgebra.jl +++ b/experimental/LieAlgebras/src/LieSubalgebra.jl @@ -48,6 +48,8 @@ Return the dimension of the Lie subalgebra `S`. """ dim(S::LieSubalgebra) = length(basis(S)) +coefficient_ring(S::LieSubalgebra) = coefficient_ring(base_lie_algebra(S)) + ############################################################################### # # String I/O @@ -79,6 +81,55 @@ function Base.show(io::IO, S::LieSubalgebra) end end +############################################################################### +# +# Parent object call overload +# +############################################################################### + +@doc raw""" + (S::LieSubalgebra{C})() -> LieAlgebraElem{C} + +Return the zero element of the Lie subalgebra `S`. +""" +function (S::LieSubalgebra)() + return zero(base_lie_algebra(S)) +end + +@doc raw""" + (S::LieSubalgebra{C})(v::Vector{Int}) -> LieAlgebraElem{C} + +Return the element of `S` with coefficient vector `v`. +Fail, if `Int` cannot be coerced into the base ring of `S`. +""" +function (S::LieSubalgebra)(v::Vector{Int}) + return S(coefficient_ring(S).(v)) +end + +@doc raw""" + (S::LieSubalgebra{C})(v::Vector{C}) -> LieAlgebraElem{C} + +Return the element of `S` with coefficient vector `v`. +""" +function (S::LieSubalgebra{C})(v::Vector{C}) where {C<:FieldElem} + @req length(v) == dim(S) "Length of vector does not match dimension." + mat = matrix(coefficient_ring(S), 1, length(v), v) + L = base_lie_algebra(S) + return elem_type(L)(L, mat * basis_matrix(S)) +end + +@doc raw""" + (S::LieSubalgebra{C})(mat::MatElem{C}) -> LieAlgebraElem{C} + +Return the element of `S` with coefficient vector equivalent to +the $1 \times \dim(S)$ matrix `mat`. +""" +function (S::LieSubalgebra{C})(mat::MatElem{C}) where {C<:FieldElem} + @req size(mat) == (1, dim(S)) "Invalid matrix dimensions." + L = base_lie_algebra(S) + return elem_type(L)(L, mat * basis_matrix(S)) +end + ############################################################################### # # Comparisons @@ -114,14 +165,26 @@ end ############################################################################### @doc raw""" - in(x::LieAlgebraElem, S::LieSubalgebra) -> Bool + in(x::LieAlgebraElem{C}, S::LieSubalgebra{C}) -> Bool Return `true` if `x` is in the Lie subalgebra `S`, `false` otherwise. """ -function Base.in(x::LieAlgebraElem, S::LieSubalgebra) +function Base.in(x::LieAlgebraElem{C}, S::LieSubalgebra{C}) where {C<:FieldElem} + @req parent(x) === base_lie_algebra(S) "Incompatible Lie algebras" return can_solve(basis_matrix(S), _matrix(x); side=:left) end +@doc raw""" + Oscar.LieAlgebras.coefficient_vector(x::LieAlgebraElem{C}, S::LieSubalgebra{C}) -> Vector{C} + +Return the coefficient vector of `x` in the basis of `S`. +This function will throw an error if `x` is not in `S`. +""" +function coefficient_vector(x::LieAlgebraElem{C}, S::LieSubalgebra{C}) where {C<:FieldElem} + @req parent(x) === base_lie_algebra(S) "Incompatible Lie algebras" + return solve(basis_matrix(S), _matrix(x); side=:left) +end + ############################################################################### # # Constructions From 729308bbdec5bb39929fd56cb2029d3e5473c491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Wed, 11 Sep 2024 15:03:57 +0200 Subject: [PATCH 06/17] Fix some generator printing --- experimental/LieAlgebras/src/AbstractLieAlgebra.jl | 2 +- experimental/LieAlgebras/src/LinearLieAlgebra.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl index 367779d6d872..afa9bb6660be 100644 --- a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl +++ b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl @@ -234,7 +234,7 @@ function lie_algebra( @req fl "Not closed under the bracket." struct_consts[i, j] = sparse_row(row) end - s = map(AbstractAlgebra.obj_to_string, basis) + s = map(AbstractAlgebra.obj_to_string_wrt_times, basis) return lie_algebra(R, struct_consts, s; check) end diff --git a/experimental/LieAlgebras/src/LinearLieAlgebra.jl b/experimental/LieAlgebras/src/LinearLieAlgebra.jl index 39328a3e17de..8e5dddba355d 100644 --- a/experimental/LieAlgebras/src/LinearLieAlgebra.jl +++ b/experimental/LieAlgebras/src/LinearLieAlgebra.jl @@ -179,7 +179,7 @@ function lie_algebra( @req all(parent(x) === parent_L for x in basis) "Elements not compatible." R = coefficient_ring(parent_L) n = parent_L.n - s = map(AbstractAlgebra.obj_to_string, basis) + s = map(AbstractAlgebra.obj_to_string_wrt_times, basis) return lie_algebra(R, n, matrix_repr.(basis), s; check) end From 6b2d101881e5252e558d4f136ccb38aab099f7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Wed, 11 Sep 2024 15:04:29 +0200 Subject: [PATCH 07/17] Add internal `_root_system_and_chevalley_basis` --- experimental/LieAlgebras/src/LieAlgebra.jl | 169 +++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index 1c017c7cecc9..5d7801a5136b 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -459,6 +459,16 @@ function adjoint_matrix(x::LieAlgebraElem{C}) where {C<:FieldElem} ) end +function _adjoint_matrix(S::LieSubalgebra{C}, x::LieAlgebraElem{C}) where {C<:FieldElem} + L = parent(x) + @req base_lie_algebra(S) === L "Incompatible Lie algebras" + A = zero_matrix(coefficient_ring(L), dim(S), dim(S)) + for (i, bi) in enumerate(basis(S)) + A[i, :] = coefficient_vector(bracket(x, bi), S) + end + return A +end + @attr dense_matrix_type(C) function killing_matrix(L::LieAlgebra{C}) where {C<:FieldElem} R = coefficient_ring(L) A = zero_matrix(R, dim(L), dim(L)) @@ -546,6 +556,8 @@ Return a Cartan subalgebra of `L`. If `L` knows its root system, this function uses the Chevalley basis to construct a Cartan subalgebra. Otherise, it uses the algorithm described in [Gra00; Ch. 3.2](@cite). +The subalgebra object returned by this function may differ in the two cases, in particular, the bases may be different. +However, their spans are guaranteed to be equal. """ function cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} if has_root_system(L) @@ -587,6 +599,163 @@ function _cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} end end +function _root_system_and_chevalley_basis( + L::LieAlgebra{C}, H::LieSubalgebra{C}=_cartan_subalgebra(L) +) where {C<:FieldElem} + @req base_lie_algebra(H) === L "Incompatible Lie algebras." + # we just assume that H is indeed a Cartan subalgebra + + F = coefficient_ring(L) + + # compute the common eigenspaces of the adjoint action of H. + # B is a list of subspaces of L that gets refined in each iteration. + # to exploit existing functionality, we use the LieSubalgebra type even + # though the subspaces are in general not Lie subalgebras. With setting + # is_basis=true, we ensure that the subspace generators do not get + # extended to a subalgebra. + B = [sub(L, basis(L); is_basis=true)] + for h in basis(H) + B_new = empty(B) + for B_j in B + A = _adjoint_matrix(B_j, h) + facs = factor(minimal_polynomial(A)) + for (f, k) in facs + @assert k == 1 # TODO: is this always the case? + ker = kernel((f^k)(A); side=:left) + basis = [B_j(ker[i, :]) for i in 1:nrows(ker)] + push!(B_new, sub(L, basis; is_basis=true)) + end + end + B = B_new + end + filter!(!=(H), B) + @req all(B_j -> dim(B_j) == 1, B) "The Cartan subalgebra is not split" + + # compute the roots, i.e. the list of eigenvalues of basis(H) on each B_j + root_spaces = Dict( + begin + b = only(basis(B_j)) + root = [only(solve(_matrix(b), _matrix(bracket(h, b)); side=:left)) for h in basis(H)] + root => B_j + end for B_j in B + ) + roots = collect(keys(root_spaces)) + + # compute an R-basis of the root space, s.t. the corresponding co-roots are a basis of H + roots_basis = empty(roots) + basis_mat_H = zero_matrix(F, 0, dim(L)) + for root in roots + nrows(basis_mat_H) == dim(H) && break + x_j = only(basis(root_spaces[root])) + y_j = only(basis(root_spaces[-root])) + h_j = bracket(x_j, y_j) + if !can_solve(basis_mat_H, _matrix(h_j); side=:left) + basis_mat_H = vcat(basis_mat_H, _matrix(h_j)) + push!(roots_basis, root) + end + end + + function CartanInt( + roots::Vector{Vector{QQFieldElem}}, a::Vector{QQFieldElem}, b::Vector{QQFieldElem} + ) + # `a` and `b` are two roots in `roots`. + a == b && return 2 + a == -b && return -2 + # If a != ±b, the Cartan integer of `a` and `b` is `s-t`, where + # `s` and `t` are the largest integers such that `b-s*a` and `b+t*a` are still roots. + rt = b - a + s = 0 + while rt in roots + s += 1 + rt -= a + end + rt = b + a + t = 0 + while rt in roots + t += 1 + rt += a + end + return s - t + end + + # we define a root to be positive if the first of its non-zero Cartan integers with the R-basis is positive + roots_positive = empty(roots) + for root in roots + 2 * length(roots_positive) == length(roots) && break + root in roots_positive && continue + -root in roots_positive && continue + c = first( + Iterators.dropwhile( + iszero, Iterators.map(b_j -> CartanInt(roots, root, b_j), roots_basis) + ), + ) + if c > 0 + push!(roots_positive, root) + else + push!(roots_positive, -root) + end + end + + # a positive root is simple if it is not the sum of two positive roots + roots_simple = empty(roots) + roots_positive_sums = Set( + alpha_i + alpha_j for (i, alpha_i) in enumerate(roots_positive) for + (j, alpha_j) in enumerate(roots_positive) if i < j + ) + for root in roots_positive + if !(root in roots_positive_sums) + push!(roots_simple, root) + end + end + @assert length(roots_simple) == dim(H) + + # compute the Cartan matrix and abstract root system + cm = matrix( + ZZ, + [ + CartanInt(roots, alpha_i, alpha_j) for alpha_i in roots_simple, + alpha_j in roots_simple + ], + ) + R = root_system(cm; check=true) # TODO: disable check + + # reorder the simple roots according to the Dynkin diagram. + # users usually expect this ordering, but it is not necessary for the root system. + _, ordering = root_system_type_with_ordering(R) + permute!(roots_simple, ordering) + cm = matrix( + ZZ, + [ + CartanInt(roots, alpha_i, alpha_j) for alpha_i in roots_simple, + alpha_j in roots_simple + ], + ) + R = root_system(cm; check=true) # TODO: disable check + + # compute a Chevalley basis of L + root_vectors = [ + begin + concrete_root = sum( + c * root_simple for + (c, root_simple) in zip(coefficients(abstract_root), roots_simple) + ) + @assert concrete_root in roots + root_vector = only(basis(root_spaces[concrete_root])) + end for abstract_root in Oscar.roots(R) + ] + xs = root_vectors[1:n_positive_roots(R)] + ys = [ + begin + x = xs[i] + y = root_vectors[n_positive_roots(R) + i] + y *= 2//only(solve(_matrix(x), _matrix(bracket(bracket(x, y), x)); side=:left)) + end for i in 1:n_positive_roots(R) + ] + hs = [xs[i] * ys[i] for i in 1:n_simple_roots(R)] + + return R, (xs, ys, hs) +end + ############################################################################### # # Root system getters From bf6c60894b6589f3a30420227f769f4c8d64eb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 12 Sep 2024 14:37:35 +0200 Subject: [PATCH 08/17] Handle root system caching --- .../LieAlgebras/src/AbstractLieAlgebra.jl | 31 ++++++----- .../LieAlgebras/src/DirectSumLieAlgebra.jl | 23 ++++++-- experimental/LieAlgebras/src/LieAlgebra.jl | 53 ++++++++++--------- .../LieAlgebras/src/LinearLieAlgebra.jl | 25 +++++++++ experimental/LieAlgebras/src/Types.jl | 10 ++++ 5 files changed, 102 insertions(+), 40 deletions(-) diff --git a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl index afa9bb6660be..4f5f16ffcd98 100644 --- a/experimental/LieAlgebras/src/AbstractLieAlgebra.jl +++ b/experimental/LieAlgebras/src/AbstractLieAlgebra.jl @@ -95,22 +95,20 @@ end has_root_system(L::AbstractLieAlgebra) = isdefined(L, :root_system) function root_system(L::AbstractLieAlgebra) - @req has_root_system(L) "no root system known." + assure_root_system(L) return L.root_system end function chevalley_basis(L::AbstractLieAlgebra) - @req has_root_system(L) "no root system known." - # TODO: once there is root system detection, this function needs to be updated to indeed return the Chevalley basis - - npos = n_positive_roots(root_system(L)) - b = basis(L) - # root vectors - r_plus = b[1:npos] - r_minus = b[(npos + 1):(2 * npos)] - # basis for cartan algebra - h = b[(2 * npos + 1):dim(L)] - return (r_plus, r_minus, h) + assure_root_system(L) + return L.chevalley_basis::NTuple{3,Vector{elem_type(L)}} +end + +function set_root_system_and_chevalley_basis!( + L::AbstractLieAlgebra{C}, R::RootSystem, chev::NTuple{3,Vector{AbstractLieAlgebraElem{C}}} +) where {C<:FieldElem} + L.root_system = R + L.chevalley_basis = chev end ############################################################################### @@ -264,7 +262,14 @@ function lie_algebra( ] L = lie_algebra(R, struct_consts, s; check=false) - L.root_system = rs + + npos = n_positive_roots(rs) + # set Chevalley basis + r_plus = basis(L)[1:npos] + r_minus = basis(L)[(npos + 1):(2 * npos)] + h = basis(L)[(2 * npos + 1):end] + chev = (r_plus, r_minus, h) + set_root_system_and_chevalley_basis!(L, rs, chev) return L end diff --git a/experimental/LieAlgebras/src/DirectSumLieAlgebra.jl b/experimental/LieAlgebras/src/DirectSumLieAlgebra.jl index f03c10225694..88ccabac2793 100644 --- a/experimental/LieAlgebras/src/DirectSumLieAlgebra.jl +++ b/experimental/LieAlgebras/src/DirectSumLieAlgebra.jl @@ -135,9 +135,26 @@ end # ############################################################################### -# The following implementation needs direct sums of root systems, which -# is not yet implemented. -# has_root_system(D::DirectSumLieAlgebra) = all(has_root_system, D.summands) +has_root_system(D::DirectSumLieAlgebra) = isdefined(D, :root_system) + +function root_system(D::DirectSumLieAlgebra) + assure_root_system(D) + return D.root_system +end + +function chevalley_basis(D::DirectSumLieAlgebra) + assure_root_system(D) + return D.chevalley_basis::NTuple{3,Vector{elem_type(D)}} +end + +function set_root_system_and_chevalley_basis!( + D::DirectSumLieAlgebra{C}, + R::RootSystem, + chev::NTuple{3,Vector{DirectSumLieAlgebraElem{C}}}, +) where {C<:FieldElem} + D.root_system = R + D.chevalley_basis = chev +end ############################################################################### # diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index 5d7801a5136b..a511270b9f3d 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -8,10 +8,10 @@ # symbols(L::MyLieAlgebra) -> Vector{Symbol} # bracket(x::MyLieAlgebraElem{C}, y::MyLieAlgebraElem{C}) -> MyLieAlgebraElem{C} # Base.show(io::IO, x::MyLieAlgebra) -# If the subtype supports root systems: # has_root_system(::MyLieAlgebra) -> Bool # root_system(::MyLieAlgebra) -> RootSystem -# chevalley_basis(L::MyLieAlgebra) -> NTuple{3,Vector{elem_type(L)}} +# chevalley_basis(L::MyLieAlgebra{C}) -> NTuple{3,Vector{MyLieAlgebraElem{C}}} +# set_root_system_and_chevalley_basis!(L::MyLieAlgebra{C}, R::RootSystem, chev::NTuple{3,Vector{MyLieAlgebraElem{C}}}}}) ############################################################################### # @@ -549,24 +549,6 @@ function engel_subalgebra(x::LieAlgebraElem{C}) where {C<:FieldElem} return L0adx end -@doc raw""" - Oscar.LieAlgebras.cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} -> LieSubalgebra{C,elem_type(L)} - -Return a Cartan subalgebra of `L`. - -If `L` knows its root system, this function uses the Chevalley basis to construct a Cartan subalgebra. -Otherise, it uses the algorithm described in [Gra00; Ch. 3.2](@cite). -The subalgebra object returned by this function may differ in the two cases, in particular, the bases may be different. -However, their spans are guaranteed to be equal. -""" -function cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} - if has_root_system(L) - return sub(L, chevalley_basis(L)[3]; is_basis=true) - else - return _cartan_subalgebra(L) - end -end - function _cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} F = coefficient_ring(L) n = dim(L) @@ -756,6 +738,14 @@ function _root_system_and_chevalley_basis( return R, (xs, ys, hs) end +function assure_root_system(L::LieAlgebra{C}) where {C<:FieldElem} + if !has_root_system(L) + R, chev = _root_system_and_chevalley_basis(L) + set_root_system_and_chevalley_basis!(L, R, chev) + end + @assert has_root_system(L) +end + ############################################################################### # # Root system getters @@ -774,10 +764,9 @@ has_root_system(L::LieAlgebra) = false # to be implemented by subtypes Return the root system of `L`. -This function will error if no root system is known (see [`has_root_system(::LieAlgebra)`](@ref)). +This function will error if no root system is known and none can be computed. """ function root_system(L::LieAlgebra) # to be implemented by subtypes - @req has_root_system(L) "root system of `L` not known." throw(Hecke.NotImplemented()) end @@ -788,13 +777,29 @@ Return the Chevalley basis of the Lie algebra `L` in three vectors, stating firs 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 [`root_system(::LieAlgebra)`](@ref). -This function will error if no root system is known (see [`has_root_system(::LieAlgebra)`](@ref)). +This function will error if no root system is known and none can be computed. """ function chevalley_basis(L::LieAlgebra) # to be implemented by subtypes - @req has_root_system(L) "root system of `L` not known." throw(Hecke.NotImplemented()) end +@doc raw""" + cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} -> LieSubalgebra{C,elem_type(L)} + +Return a Cartan subalgebra of `L`. + +If `L` knows its root system, this function uses the Chevalley basis to construct a Cartan subalgebra. +Otherise, it uses the algorithm described in [Gra00; Ch. 3.2](@cite). +The return value of this function may change when the root system of `L` is first computed. +""" +function cartan_subalgebra(L::LieAlgebra{C}) where {C<:FieldElem} + if has_root_system(L) + return sub(L, chevalley_basis(L)[3]; is_basis=true) + else + return _cartan_subalgebra(L) + end +end + ############################################################################### # # Universal enveloping algebra diff --git a/experimental/LieAlgebras/src/LinearLieAlgebra.jl b/experimental/LieAlgebras/src/LinearLieAlgebra.jl index 8e5dddba355d..61d8183922ac 100644 --- a/experimental/LieAlgebras/src/LinearLieAlgebra.jl +++ b/experimental/LieAlgebras/src/LinearLieAlgebra.jl @@ -146,6 +146,31 @@ function bracket( return coerce_to_lie_algebra_elem(L, x_mat * y_mat - y_mat * x_mat) end +############################################################################### +# +# Root system getters +# +############################################################################### + +has_root_system(L::LinearLieAlgebra) = isdefined(L, :root_system) + +function root_system(L::LinearLieAlgebra) + assure_root_system(L) + return L.root_system +end + +function chevalley_basis(L::LinearLieAlgebra) + assure_root_system(L) + return L.chevalley_basis::NTuple{3,Vector{elem_type(L)}} +end + +function set_root_system_and_chevalley_basis!( + L::LinearLieAlgebra{C}, R::RootSystem, chev::NTuple{3,Vector{LinearLieAlgebraElem{C}}} +) where {C<:FieldElem} + L.root_system = R + L.chevalley_basis = chev +end + ############################################################################### # # Constructor diff --git a/experimental/LieAlgebras/src/Types.jl b/experimental/LieAlgebras/src/Types.jl index 7144089ed6f8..073a2c080081 100644 --- a/experimental/LieAlgebras/src/Types.jl +++ b/experimental/LieAlgebras/src/Types.jl @@ -157,6 +157,7 @@ abstract type LieAlgebraElem{C<:FieldElem} <: AbstractAlgebra.SetElem end # only set if known root_system::RootSystem + chevalley_basis::NTuple{3,Vector{<:LieAlgebraElem{C}}} function AbstractLieAlgebra{C}( R::Field, @@ -205,6 +206,10 @@ end basis::Vector{MatElem{C}} s::Vector{Symbol} + # only set if known + root_system::RootSystem + chevalley_basis::NTuple{3,Vector{<:LieAlgebraElem{C}}} + function LinearLieAlgebra{C}( R::Field, n::Int, @@ -240,6 +245,10 @@ end summands::Vector{<:LieAlgebra{C}} s::Vector{Symbol} + # only set if known + root_system::RootSystem + chevalley_basis::NTuple{3,Vector{<:LieAlgebraElem{C}}} + function DirectSumLieAlgebra{C}( R::Field, summands::Vector{<:LieAlgebra{C}}, @@ -248,6 +257,7 @@ end totaldim = sum(dim, summands; init=0) s = [Symbol("$(x)^($(i))") for (i, S) in enumerate(summands) for x in symbols(S)] L = new{C}(R, totaldim, summands, s) + # TODO: glue root systems if all summands have one return L end end From b0cf75f3dfb6ae087a42c31a9269ae6c43a9bc99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 12 Sep 2024 14:41:39 +0200 Subject: [PATCH 09/17] Misc fixes and some tests --- experimental/LieAlgebras/src/LieAlgebra.jl | 15 +++++----- experimental/LieAlgebras/src/serialization.jl | 13 ++++---- experimental/LieAlgebras/test/setup_tests.jl | 30 ++++++++++++++----- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index a511270b9f3d..eaa85f48bdeb 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -587,6 +587,8 @@ function _root_system_and_chevalley_basis( @req base_lie_algebra(H) === L "Incompatible Lie algebras." # we just assume that H is indeed a Cartan subalgebra + @req is_invertible(killing_matrix(L)) "The Killing form is degenerate" + F = coefficient_ring(L) # compute the common eigenspaces of the adjoint action of H. @@ -637,9 +639,7 @@ function _root_system_and_chevalley_basis( end end - function CartanInt( - roots::Vector{Vector{QQFieldElem}}, a::Vector{QQFieldElem}, b::Vector{QQFieldElem} - ) + function CartanInt(roots::Vector{RootType}, a::RootType, b::RootType) where {RootType} # `a` and `b` are two roots in `roots`. a == b && return 2 a == -b && return -2 @@ -718,7 +718,7 @@ function _root_system_and_chevalley_basis( root_vectors = [ begin concrete_root = sum( - c * root_simple for + c .* root_simple for (c, root_simple) in zip(coefficients(abstract_root), roots_simple) ) @assert concrete_root in roots @@ -726,14 +726,15 @@ function _root_system_and_chevalley_basis( end for abstract_root in Oscar.roots(R) ] xs = root_vectors[1:n_positive_roots(R)] - ys = [ + ys = Vector{elem_type(L)}([ begin x = xs[i] y = root_vectors[n_positive_roots(R) + i] y *= 2//only(solve(_matrix(x), _matrix(bracket(bracket(x, y), x)); side=:left)) + y end for i in 1:n_positive_roots(R) - ] - hs = [xs[i] * ys[i] for i in 1:n_simple_roots(R)] + ]) + hs = elem_type(L)[xs[i] * ys[i] for i in 1:n_simple_roots(R)] return R, (xs, ys, hs) end diff --git a/experimental/LieAlgebras/src/serialization.jl b/experimental/LieAlgebras/src/serialization.jl index 9d74c7a26357..c5178178c538 100644 --- a/experimental/LieAlgebras/src/serialization.jl +++ b/experimental/LieAlgebras/src/serialization.jl @@ -96,12 +96,13 @@ end function load_root_system_data(s::DeserializerState, L::LieAlgebra) if haskey(s, :root_system) - @assert L isa AbstractLieAlgebra # TODO: adapt once we have a proper interface for this - L.root_system = load_typed_object(s, :root_system) - chevalley_basis = load_object( - s, Tuple, [(Vector, (AbstractLieAlgebraElem, L)) for _ in 1:3], :chevalley_basis - ) - # chevalley basis will become an attribute in the near future + rs = load_typed_object(s, :root_system) + chev = NTuple{3,Vector{elem_type(L)}}( + load_object( + s, Tuple, [(Vector, (AbstractLieAlgebraElem, L)) for _ in 1:3], :chevalley_basis + ), + ) # coercion needed due to https://github.com/oscar-system/Oscar.jl/issues/3983 + set_root_system_and_chevalley_basis!(L, rs, chev) end end diff --git a/experimental/LieAlgebras/test/setup_tests.jl b/experimental/LieAlgebras/test/setup_tests.jl index 2c55ed3ed13d..baaa7ee27ce9 100644 --- a/experimental/LieAlgebras/test/setup_tests.jl +++ b/experimental/LieAlgebras/test/setup_tests.jl @@ -110,14 +110,28 @@ if !isdefined(Main, :lie_algebra_conformance_test) || isinteractive() end @testset "Root systems" begin - if has_root_system(L) - rs = root_system(L) - @test rs isa RootSystem - @test dim(L) == n_roots(rs) + n_simple_roots(rs) - chev = @inferred chevalley_basis(L) - @test length(chev) == 3 - @test length(chev[1]) == length(chev[2]) - @test dim(L) == sum(length, chev; init=0) + if !(L isa DirectSumLieAlgebra) # TODO: make root_system work for DirectSumLieAlgebra + try + root_system(L) + catch e + e isa ArgumentError || rethrow() + @test any( + msg -> contains(e.msg, msg), + ["Killing form is degenerate", "Cartan subalgebra is not split"], + ) + end + if has_root_system(L) + rs = root_system(L) + @test rs isa RootSystem + @test dim(L) == n_roots(rs) + n_simple_roots(rs) + chev = @inferred chevalley_basis(L) + @test length(chev) == 3 + @test length(chev[1]) == length(chev[2]) + @test dim(L) == sum(length, chev; init=0) + H = cartan_subalgebra(L) + @test all(h -> h in H, chev[3]) + @test all(xs -> bracket(xs...) in H, zip(chev[1], chev[2])) + end end end From 77925d7577926616263eec838c0a97a223380b3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 12 Sep 2024 14:44:13 +0200 Subject: [PATCH 10/17] Simplify some code using root system detection --- .../LieAlgebras/src/LieAlgebraModule.jl | 84 +++---------------- 1 file changed, 10 insertions(+), 74 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebraModule.jl b/experimental/LieAlgebras/src/LieAlgebraModule.jl index 562c86122eb6..08ddecd9f3b1 100644 --- a/experimental/LieAlgebras/src/LieAlgebraModule.jl +++ b/experimental/LieAlgebras/src/LieAlgebraModule.jl @@ -1413,17 +1413,8 @@ julia> dim_of_simple_module(L, [1, 1, 1]) ``` """ function dim_of_simple_module(T::Type, L::LieAlgebra, hw::Vector{<:IntegerUnion}) - if has_root_system(L) - R = root_system(L) - return dim_of_simple_module(T, R, hw) - else # TODO: remove branch once root system detection is implemented - @req is_dominant_weight(hw) "Not a dominant weight." - return T( - GAPWrap.DimensionOfHighestWeightModule( - codomain(Oscar.iso_oscar_gap(L)), GAP.Obj(hw; recursive=true) - ), - ) - end + R = root_system(L) + return dim_of_simple_module(T, R, hw) end function dim_of_simple_module(L::LieAlgebra, hw::Vector{<:IntegerUnion}) @@ -1456,24 +1447,8 @@ julia> dominant_weights(L, [1, 0, 3]) ``` """ function dominant_weights(T::Type, L::LieAlgebra, hw::Vector{<:IntegerUnion}) - if has_root_system(L) - R = root_system(L) - return dominant_weights(T, R, hw) - else # TODO: remove branch once root system detection is implemented - @req is_dominant_weight(hw) "Not a dominant weight." - return first.( - sort!( - collect( - T(w) => d for (w, d) in - zip( - GAP.Globals.DominantWeights( - GAP.Globals.RootSystem(codomain(Oscar.iso_oscar_gap(L))), - GAP.Obj(hw; recursive=true), - )..., - ) - ); by=last) - ) - end + R = root_system(L) + return dominant_weights(T, R, hw) end function dominant_weights(L::LieAlgebra, hw::Vector{<:IntegerUnion}) @@ -1503,20 +1478,8 @@ Dict{Vector{Int64}, Int64} with 4 entries: ``` """ function dominant_character(L::LieAlgebra, hw::Vector{<:IntegerUnion}) - if has_root_system(L) - R = root_system(L) - return dominant_character(R, hw) - else # TODO: remove branch once root system detection is implemented - @req is_dominant_weight(hw) "Not a dominant weight." - return Dict{Vector{Int},Int}( - Vector{Int}(w) => d for (w, d) in - zip( - GAPWrap.DominantCharacter( - codomain(Oscar.iso_oscar_gap(L)), GAP.Obj(hw; recursive=true) - )..., - ) - ) - end + R = root_system(L) + return dominant_character(R, hw) end @doc raw""" @@ -1547,22 +1510,8 @@ Dict{Vector{Int64}, Int64} with 10 entries: ``` """ function character(L::LieAlgebra, hw::Vector{<:IntegerUnion}) - if has_root_system(L) - R = root_system(L) - return character(R, hw) - else # TODO: remove branch once root system detection is implemented - @req is_dominant_weight(hw) "Not a dominant weight." - dc = dominant_character(L, hw) - c = Dict{Vector{Int},Int}() - W = GAPWrap.WeylGroup(GAPWrap.RootSystem(codomain(Oscar.iso_oscar_gap(L)))) - for (w, d) in dc - it = GAPWrap.WeylOrbitIterator(W, GAP.Obj(w)) - while !GAPWrap.IsDoneIterator(it) - push!(c, Vector{Int}(GAPWrap.NextIterator(it)) => d) - end - end - return c - end + R = root_system(L) + return character(R, hw) end @doc raw""" @@ -1595,19 +1544,6 @@ MSet{Vector{Int64}} with 6 elements: function tensor_product_decomposition( L::LieAlgebra, hw1::Vector{<:IntegerUnion}, hw2::Vector{<:IntegerUnion} ) - if has_root_system(L) - R = root_system(L) - return tensor_product_decomposition(R, hw1, hw2) - else # TODO: remove branch once root system detection is implemented - @req is_dominant_weight(hw1) && is_dominant_weight(hw2) "Both weights must be dominant." - return multiset( - Tuple{Vector{Vector{Int}},Vector{Int}}( - GAPWrap.DecomposeTensorProduct( - codomain(Oscar.iso_oscar_gap(L)), - GAP.Obj(hw1; recursive=true), - GAP.Obj(hw2; recursive=true), - ), - )..., - ) - end + R = root_system(L) + return tensor_product_decomposition(R, hw1, hw2) end From 11ceb45bb588cc201c57afd49ac3e0e5e41abb92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Thu, 12 Sep 2024 15:18:22 +0200 Subject: [PATCH 11/17] Update some flaky B2/C2 test --- .../LieAlgebras/test/LieAlgebraModule-test.jl | 52 ++++++++++++------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index 28abe42dee2f..0f7a031634ad 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -772,10 +772,10 @@ @test dim == 16 end - let L = special_orthogonal_lie_algebra(QQ, 5) # type B_2 but without known root system - dim = @inferred dim_of_simple_module(L, ZZ.([1, 2])) + let L = special_orthogonal_lie_algebra(QQ, 7) # type B_3 but without known root system + dim = @inferred dim_of_simple_module(L, ZZ.([1, 2, 1])) @test dim isa Int - @test dim == 35 + @test dim == 2800 end end @@ -896,14 +896,22 @@ ) end - let L = special_orthogonal_lie_algebra(QQ, 5), hw = ZZ.([1, 2]) # type B_2 but without known root system + let L = special_orthogonal_lie_algebra(QQ, 7), hw = ZZ.([1, 2, 0]) # type B_3 but without known root system domchar = check_dominant_character(L, hw) @test domchar == Dict( - [1, 2] => 1, - [2, 0] => 1, - [0, 2] => 2, - [1, 0] => 3, - [0, 0] => 3, + [1, 2, 0] => 1, + [2, 0, 2] => 1, + [2, 1, 0] => 1, + [0, 1, 2] => 2, + [0, 2, 0] => 2, + [3, 0, 0] => 2, + [1, 0, 2] => 3, + [1, 1, 0] => 6, + [2, 0, 0] => 6, + [0, 0, 2] => 9, + [0, 1, 0] => 9, + [1, 0, 0] => 15, + [0, 0, 0] => 15, ) end end @@ -984,7 +992,7 @@ ) end - let L = special_orthogonal_lie_algebra(QQ, 5), hw = ZZ.([1, 2]) # type B_2 but without known root system + let L = special_orthogonal_lie_algebra(QQ, 7), hw = ZZ.([1, 2, 1]) # type B_3 but without known root system char = check_character(L, hw) end end @@ -1090,18 +1098,24 @@ )) end - let L = special_orthogonal_lie_algebra(QQ, 5), hw1 = ZZ.([1, 2]), hw2 = ZZ.([1, 1]) # type B_2 but without known root system + let L = special_orthogonal_lie_algebra(QQ, 7), hw1 = ZZ.([1, 1, 0]), + hw2 = ZZ.([0, 1, 1]) # type B_3 but without known root system dec = test_tensor_product_decomposition(L, hw1, hw2) @test dec == multiset( Dict( - [2, 3] => 1, - [3, 1] => 1, - [0, 5] => 1, - [1, 3] => 2, - [2, 1] => 2, - [0, 3] => 2, - [1, 1] => 2, - [0, 1] => 1, + [1, 2, 1] => 1, + [2, 0, 3] => 1, + [2, 1, 1] => 1, + [0, 1, 3] => 1, + [0, 2, 1] => 1, + [3, 0, 1] => 1, + [1, 0, 3] => 2, + [1, 1, 1] => 3, + [2, 0, 1] => 2, + [0, 0, 3] => 2, + [0, 1, 1] => 2, + [1, 0, 1] => 2, + [0, 0, 1] => 1, ), ) end From 2626dad1a2bbf5aa5a62f6c4f573b8848d89ca9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 17 Sep 2024 09:52:44 +0200 Subject: [PATCH 12/17] Make the formatter happy --- experimental/LieAlgebras/test/LieAlgebraModule-test.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index 0f7a031634ad..c077a1862e11 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -1098,8 +1098,10 @@ )) end - let L = special_orthogonal_lie_algebra(QQ, 7), hw1 = ZZ.([1, 1, 0]), - hw2 = ZZ.([0, 1, 1]) # type B_3 but without known root system + let L = special_orthogonal_lie_algebra(QQ, 7) # type B_3 but without known root system + hw1 = ZZ.([1, 1, 0]) + hw2 = ZZ.([0, 1, 1]) + dec = test_tensor_product_decomposition(L, hw1, hw2) @test dec == multiset( Dict( From 8118bbeb50281e6fc86b604b7685ccdadbe736b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Tue, 17 Sep 2024 09:54:47 +0200 Subject: [PATCH 13/17] Try to fix docs --- experimental/LieAlgebras/docs/src/ideals_and_subalgebras.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/experimental/LieAlgebras/docs/src/ideals_and_subalgebras.md b/experimental/LieAlgebras/docs/src/ideals_and_subalgebras.md index e0c1c2bcad63..be741d34084d 100644 --- a/experimental/LieAlgebras/docs/src/ideals_and_subalgebras.md +++ b/experimental/LieAlgebras/docs/src/ideals_and_subalgebras.md @@ -17,7 +17,7 @@ They are used similarly in most cases. dim(::LieAlgebraIdeal) basis(::LieAlgebraIdeal) basis(::LieAlgebraIdeal, ::Int) -Base.in(::LieAlgebraElem, ::LieAlgebraIdeal) +Base.in(::LieAlgebraElem{C}, ::LieAlgebraIdeal{C}) where {C<:FieldElem} bracket(::LieAlgebraIdeal{C,LieT}, ::LieAlgebraIdeal{C,LieT}) where {C<:FieldElem,LieT<:LieAlgebraElem{C}} normalizer(::LieAlgebra, ::LieAlgebraIdeal) centralizer(::LieAlgebra, ::LieAlgebraIdeal) @@ -29,7 +29,7 @@ centralizer(::LieAlgebra, ::LieAlgebraIdeal) dim(::LieSubalgebra) basis(::LieSubalgebra) basis(::LieSubalgebra, ::Int) -Base.in(::LieAlgebraElem, ::LieSubalgebra) +Base.in(::LieAlgebraElem{C}, ::LieSubalgebra{C}) where {C<:FieldElem} bracket(::LieSubalgebra{C,LieT}, ::LieSubalgebra{C,LieT}) where {C<:FieldElem,LieT<:LieAlgebraElem{C}} normalizer(::LieAlgebra, ::LieSubalgebra) centralizer(::LieAlgebra, ::LieSubalgebra) From 8e0f354a9876a4c1d24984e8e4c830564f007456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Wed, 18 Sep 2024 11:47:22 +0200 Subject: [PATCH 14/17] Update experimental/LieAlgebras/src/LieAlgebra.jl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Felix Röhrich <47457568+felix-roehrich@users.noreply.github.com> --- experimental/LieAlgebras/src/LieAlgebra.jl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index eaa85f48bdeb..e7bdda177b31 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -699,11 +699,7 @@ function _root_system_and_chevalley_basis( alpha_j in roots_simple ], ) - R = root_system(cm; check=true) # TODO: disable check - - # reorder the simple roots according to the Dynkin diagram. - # users usually expect this ordering, but it is not necessary for the root system. - _, ordering = root_system_type_with_ordering(R) + _, ordering = cartan_type_with_ordering(cm) permute!(roots_simple, ordering) cm = matrix( ZZ, From b46fb11860ae28df8cc31d82e7a1510aaac1e5af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Wed, 18 Sep 2024 18:23:39 +0200 Subject: [PATCH 15/17] Skip second cartan matrix computation --- experimental/LieAlgebras/src/LieAlgebra.jl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index e7bdda177b31..a7a08882788c 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -701,14 +701,10 @@ function _root_system_and_chevalley_basis( ) _, ordering = cartan_type_with_ordering(cm) permute!(roots_simple, ordering) - cm = matrix( - ZZ, - [ - CartanInt(roots, alpha_i, alpha_j) for alpha_i in roots_simple, - alpha_j in roots_simple - ], - ) - R = root_system(cm; check=true) # TODO: disable check + p = inv(Perm(ordering)) + cm = p * cm * p + R = root_system(cm; check=false) + @assert root_system_type_with_ordering(R)[2] == 1:rank(R) # compute a Chevalley basis of L root_vectors = [ From 5491bc32fc93ef751a898af242167b0e1d84fabd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Wed, 18 Sep 2024 18:50:13 +0200 Subject: [PATCH 16/17] Update experimental/LieAlgebras/src/LieAlgebra.jl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Felix Röhrich <47457568+felix-roehrich@users.noreply.github.com> --- experimental/LieAlgebras/src/LieAlgebra.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index a7a08882788c..c38b4430ac0b 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -699,11 +699,9 @@ function _root_system_and_chevalley_basis( alpha_j in roots_simple ], ) - _, ordering = cartan_type_with_ordering(cm) + type, ordering = cartan_type_with_ordering(cm; check=false) permute!(roots_simple, ordering) - p = inv(Perm(ordering)) - cm = p * cm * p - R = root_system(cm; check=false) + R = root_system(type) @assert root_system_type_with_ordering(R)[2] == 1:rank(R) # compute a Chevalley basis of L From 5c289adc1cb9dd99722ed7c9175067c878da1f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20G=C3=B6ttgens?= Date: Wed, 18 Sep 2024 18:58:47 +0200 Subject: [PATCH 17/17] Update experimental/LieAlgebras/src/LieAlgebra.jl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Felix Röhrich <47457568+felix-roehrich@users.noreply.github.com> --- experimental/LieAlgebras/src/LieAlgebra.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/experimental/LieAlgebras/src/LieAlgebra.jl b/experimental/LieAlgebras/src/LieAlgebra.jl index c38b4430ac0b..04a487f5d124 100644 --- a/experimental/LieAlgebras/src/LieAlgebra.jl +++ b/experimental/LieAlgebras/src/LieAlgebra.jl @@ -702,7 +702,6 @@ function _root_system_and_chevalley_basis( type, ordering = cartan_type_with_ordering(cm; check=false) permute!(roots_simple, ordering) R = root_system(type) - @assert root_system_type_with_ordering(R)[2] == 1:rank(R) # compute a Chevalley basis of L root_vectors = [