From 8ab4265393c2ae0e86d86b0a5fb060d4a3a3d83e Mon Sep 17 00:00:00 2001 From: Janika Peters Date: Thu, 9 Jan 2025 15:34:53 +0100 Subject: [PATCH] LieAlgebras: Adapt Demazure operator (#4384) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lars Göttgens Co-authored-by: Max Horn --- docs/src/LieTheory/root_systems.md | 2 + docs/src/LieTheory/weight_lattices.md | 2 + docs/src/LieTheory/weyl_groups.md | 14 ++++- experimental/LieAlgebras/src/RootSystem.jl | 35 +++++++++-- .../LieAlgebras/test/LieAlgebraModule-test.jl | 31 +++++++++ src/Groups/spinor_norms.jl | 16 ++--- src/LieTheory/RootSystem.jl | 44 ++++++++++++- src/LieTheory/Types.jl | 4 +- src/LieTheory/WeightLattice.jl | 26 ++++++++ src/LieTheory/WeylGroup.jl | 37 +++++++++++ src/exports.jl | 1 + test/LieTheory/WeylGroup.jl | 63 +++++++++++++++++++ 12 files changed, 255 insertions(+), 20 deletions(-) diff --git a/docs/src/LieTheory/root_systems.md b/docs/src/LieTheory/root_systems.md index 0a858bdb5d8c..2ab0c74e6638 100644 --- a/docs/src/LieTheory/root_systems.md +++ b/docs/src/LieTheory/root_systems.md @@ -145,6 +145,8 @@ is_negative_root_with_index(::RootSpaceElem) ```@docs reflect(::RootSpaceElem, ::Int) reflect!(::RootSpaceElem, ::Int) +reflect(::RootSpaceElem, ::RootSpaceElem) +reflect!(::RootSpaceElem, ::RootSpaceElem) ``` diff --git a/docs/src/LieTheory/weight_lattices.md b/docs/src/LieTheory/weight_lattices.md index 6d9dc41663c9..09425ca16444 100644 --- a/docs/src/LieTheory/weight_lattices.md +++ b/docs/src/LieTheory/weight_lattices.md @@ -68,6 +68,8 @@ is_fundamental_weight_with_index(::WeightLatticeElem) ```@docs reflect(::WeightLatticeElem, ::Int) reflect!(::WeightLatticeElem, ::Int) +reflect(::WeightLatticeElem, ::RootSpaceElem) +reflect!(::WeightLatticeElem, ::RootSpaceElem) ``` ### Conjugate dominant weight diff --git a/docs/src/LieTheory/weyl_groups.md b/docs/src/LieTheory/weyl_groups.md index 70c05a2f3231..82a9b52ca159 100644 --- a/docs/src/LieTheory/weyl_groups.md +++ b/docs/src/LieTheory/weyl_groups.md @@ -33,7 +33,6 @@ weyl_group(::Vector{Tuple{Symbol,Int}}) ## Basic properties Basic group arithmetic like `*`, and `inv` are defined for `WeylGroupElem` objects. -Using `(W::WeylGroup)(word::Vector{<:Integer})`, one can construct group elements from a word in the generators. Finite Weyl groups support iteration over all group elements (in an arbitrary order). ```@docs @@ -50,6 +49,19 @@ order(::Type{T}, ::WeylGroup) where {T} root_system(::WeylGroup) ``` +### Element constructors + +Using `(W::WeylGroup)(word::Vector{<:Integer})`, one can construct group elements from a word in the generators. + +```@docs; canonical=false +gen(::WeylGroup, ::Int) +gens(::WeylGroup) +``` + +```@docs +reflection(::RootSpaceElem) +``` + ### Words and length ```@docs word(::WeylGroupElem) diff --git a/experimental/LieAlgebras/src/RootSystem.jl b/experimental/LieAlgebras/src/RootSystem.jl index c868ae014439..f3da46ae4856 100644 --- a/experimental/LieAlgebras/src/RootSystem.jl +++ b/experimental/LieAlgebras/src/RootSystem.jl @@ -335,13 +335,12 @@ end ############################################################################### # demazures character formula function _demazure_operator(r::RootSpaceElem, w::WeightLatticeElem) - fl, index_of_r = is_simple_root_with_index(r) - @req fl "not a simple root" + @req is_positive_root(r) "r is not a positive root" d = 2 * dot(w, r)//dot(r, r) list_of_occuring_weights = WeightLatticeElem[] - refl = reflect(w, index_of_r) + refl = reflect(w, r) wlelem_r = WeightLatticeElem(r) if d > -1 @@ -364,10 +363,30 @@ function _demazure_operator(r::RootSpaceElem, w::WeightLatticeElem) end end -function demazure_operator(r::RootSpaceElem, w::WeightLatticeElem) - return demazure_operator(r, Dict(w => 1)) -end +@doc raw""" + demazure_operator(r::RootSpaceElem, w::WeightLatticeElem) -> Dict{WeightLatticeElem,<:IntegerUnion} + demazure_operator(r::RootSpaceElem, groupringelem::Dict{WeightLatticeElem,<:IntegerUnion}) -> Dict{WeightLatticeElem,<:IntegerUnion} + +Computes the action of the Demazure operator associated to the positive root `r` on the given element of the group ring $\mathbb{Z}[P]$. + +If a single Weight lattice element `w` is supplied, this is interpreted as `Dict(w => 1)`. + +# Examples +```jldoctest +julia> R = root_system(:A, 3); +julia> pos_r = positive_root(R, 4) +a_1 + a_2 + +julia> w = fundamental_weight(R, 1) +w_1 + +julia> demazure_operator(pos_r, w) +Dict{WeightLatticeElem, Int64} with 2 entries: + -w_2 + w_3 => 1 + w_1 => 1 +``` +""" function demazure_operator( r::RootSpaceElem, groupringelem::Dict{WeightLatticeElem,<:IntegerUnion} ) @@ -386,6 +405,10 @@ function demazure_operator( return dict end +function demazure_operator(r::RootSpaceElem, w::WeightLatticeElem) + return demazure_operator(r, Dict(w => 1)) +end + @doc raw""" demazure_character([T = Int], R::RootSystem, w::WeightLatticeElem, x::WeylGroupElem) -> Dict{WeightLatticeElem, T} demazure_character([T = Int], R::RootSystem, w::Vector{<:IntegerUnion}, x::WeylGroupElem) -> Dict{WeightLatticeElem, T} diff --git a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl index 33a1c2161e31..3fe45041a4d6 100644 --- a/experimental/LieAlgebras/test/LieAlgebraModule-test.jl +++ b/experimental/LieAlgebras/test/LieAlgebraModule-test.jl @@ -1150,6 +1150,37 @@ end end + @testset "demazure_operator" begin + R = root_system(:B, 2) + rho = weyl_vector(R) + + @test demazure_operator(simple_root(R, 1), rho) == Dict( + WeightLatticeElem(R, [1, 1]) => 1, + WeightLatticeElem(R, [-1, 3]) => 1, + ) + + @test demazure_operator(simple_root(R, 2), rho) == Dict( + WeightLatticeElem(R, [1, 1]) => 1, + WeightLatticeElem(R, [2, -1]) => 1, + ) + + @test demazure_operator(root(R, 3), rho) == Dict( + WeightLatticeElem(R, [1, 1]) => 1, + WeightLatticeElem(R, [-2, 1]) => 1, + WeightLatticeElem(R, [0, 1]) => 1, + WeightLatticeElem(R, [-1, 1]) => 1, + ) + + @test demazure_operator(simple_root(R, 1), -rho) == Dict() + + @test demazure_operator(simple_root(R, 2), -rho) == Dict() + + @test demazure_operator(root(R, 3), -rho) == Dict( + WeightLatticeElem(R, [0, -1]) => -1, + WeightLatticeElem(R, [1, -1]) => -1, + ) + end + @testset "demazure_character" begin function demazure_character_trivial_tests(R::RootSystem, w::WeightLatticeElem) W = weyl_group(R) diff --git a/src/Groups/spinor_norms.jl b/src/Groups/spinor_norms.jl index 0487ad6d64f4..4c5c5478ab85 100644 --- a/src/Groups/spinor_norms.jl +++ b/src/Groups/spinor_norms.jl @@ -114,11 +114,11 @@ function _sigma_sharp(rkL, detL, q, p) end @doc raw""" - reflection(gram::QQMatrix, v::QQMatrix) -> QQMatrix + Oscar._reflection(gram::QQMatrix, v::QQMatrix) -> QQMatrix Return the matrix representation of the orthogonal reflection in the row vector `v`. """ -function reflection(gram::MatElem, v::MatElem) +function _reflection(gram::MatElem, v::MatElem) n = ncols(gram) E = identity_matrix(base_ring(gram), n) c = base_ring(gram)(2) * ((v * gram * transpose(v)))[1,1]^(-1) @@ -154,7 +154,7 @@ function spin(gram_diag::MatElem, isometry::MatElem, check::Bool=true) r = v - w s = r * G * transpose(r) if !iszero(s) - tau = reflection(G, r) + tau = _reflection(G, r) f = f * tau @assert w * f == w spinor_norm *= s @@ -164,8 +164,8 @@ function spin(gram_diag::MatElem, isometry::MatElem, check::Bool=true) r2 = v s2 = r2 * G * transpose(r2)/2 @assert !iszero(s1) && !iszero(s2) - tau1 = reflection(G, r1) - tau2 = reflection(G, r2) + tau1 = _reflection(G, r1) + tau2 = _reflection(G, r2) f = f * tau2 * tau1 @assert w * f == w spinor_norm *= s1 * s2 @@ -219,15 +219,15 @@ function det_spin(G::QQMatrix, T::QQMatrix, p, nu) bm = g - E[k:k,:] qm = bm * G * transpose(bm) if valuation(qm, p) <= gammaL[k] + 2*delta - tau1 = reflection(G, bm) + tau1 = _reflection(G, bm) push!(reflection_vectors, bm) tau2 = E else bp = g + E[k:k,:] qp = bp * G * transpose(bp) @assert valuation(qp, p) <= gammaL[k] + 2*delta - tau1 = reflection(G, bp) - tau2 = reflection(G, E[k:k,:]) + tau1 = _reflection(G, bp) + tau2 = _reflection(G, E[k:k,:]) push!(reflection_vectors,bp) push!(reflection_vectors,E[k:k,:]) end diff --git a/src/LieTheory/RootSystem.jl b/src/LieTheory/RootSystem.jl index 147646d0464d..c646c6d4c3ea 100644 --- a/src/LieTheory/RootSystem.jl +++ b/src/LieTheory/RootSystem.jl @@ -1102,6 +1102,32 @@ function reflect!(r::RootSpaceElem, s::Int) return r end +@doc raw""" + reflect(r::RootSpaceElem, beta::RootSpaceElem) -> RootSpaceElem + +Return the reflection of `r` in the hyperplane orthogonal to root `beta`. + +See also: [`reflect!(::RootSpaceElem, ::RootSpaceElem)`](@ref). +""" +function reflect(r::RootSpaceElem, beta::RootSpaceElem) + return reflect!(deepcopy(r), beta) +end + +@doc raw""" + reflect!(r::RootSpaceElem, beta::RootSpaceElem) -> RootSpaceElem + +Reflect `r` in the hyperplane orthogonal to the root `beta`, and return it. + +This is a mutating version of [`reflect(::RootSpaceElem, ::RootSpaceElem)`](@ref). +""" +function reflect!(r::RootSpaceElem, beta::RootSpaceElem) + @req root_system(r) === root_system(beta) "Incompatible root systems" + for s in word(reflection(beta)) + reflect!(r, Int(s)) + end + return r +end + @doc raw""" root_system(r::RootSpaceElem) -> RootSystem @@ -1452,8 +1478,20 @@ end ############################################################################### # internal helpers -# cartan matrix in the format -function positive_roots_and_reflections(cartan_matrix::ZZMatrix) +@doc raw""" + _positive_roots_and_reflections(cartan_matrix::ZZMatrix) -> Vector{Vector{ZZRingElem}}, Vector{Vector{ZZRingElem}}, Matrix{UInt64} + +Compute the positive roots, the positive coroots, and a matrix `refl` of size $m \times n$, +where $m$ is the rank of the root system and $n$ is the number of minimal roots. + +The minimal roots and coroots are given as coefficient vectors w.r.t. the simple roots and simple coroots, respectively. +The minimal roots are indexed by `1:n`, with the first `m` of them corresponding +to the simple roots, and the other roots sorted by height. + +If `beta = alpha_j * s_i` is a minimal root, then `refl_table[i, j]` stores the index of beta, and otherwise `0`. +Note that `refl_table[i, i] = 0` for every simple root `alpha_i`. +""" +function _positive_roots_and_reflections(cartan_matrix::ZZMatrix) rank, _ = size(cartan_matrix) roots = [[l == s ? one(ZZ) : zero(ZZ) for l in 1:rank] for s in 1:rank] @@ -1508,5 +1546,5 @@ function positive_roots_and_reflections(cartan_matrix::ZZMatrix) table[s, i] = iszero(refl[s, perm[i]]) ? 0 : invp[refl[s, perm[i]]] end - roots[perm], coroots[perm], table + return roots[perm], coroots[perm], table end diff --git a/src/LieTheory/Types.jl b/src/LieTheory/Types.jl index b3b256ef1438..1de1be7fd2d1 100644 --- a/src/LieTheory/Types.jl +++ b/src/LieTheory/Types.jl @@ -27,7 +27,7 @@ See [`root_system(::ZZMatrix)`](@ref) for the constructor. function RootSystem(mat::ZZMatrix; check::Bool=true, detect_type::Bool=true) check && @req is_cartan_matrix(mat) "Requires a generalized Cartan matrix" - pos_roots, pos_coroots, refl = positive_roots_and_reflections(mat) + pos_roots, pos_coroots, refl = _positive_roots_and_reflections(mat) finite = count(refl .== 0) == nrows(mat) R = new(mat) @@ -149,7 +149,7 @@ See [`weyl_group(::RootSystem)`](@ref) for the constructor. """ @attributes mutable struct WeylGroup <: AbstractAlgebra.Group finite::Bool # finite indicates whether the Weyl group is finite - refl::Matrix{UInt} # see positive_roots_and_reflections + refl::Matrix{UInt} # see _positive_roots_and_reflections root_system::RootSystem # root_system is the RootSystem from which the Weyl group was constructed function WeylGroup(finite::Bool, refl::Matrix{UInt}, root_system::RootSystem) diff --git a/src/LieTheory/WeightLattice.jl b/src/LieTheory/WeightLattice.jl index 2ca7f19c7d71..a96fc97305ee 100644 --- a/src/LieTheory/WeightLattice.jl +++ b/src/LieTheory/WeightLattice.jl @@ -462,3 +462,29 @@ function reflect!(w::WeightLatticeElem, s::Int) w.vec = addmul!(w.vec, view(cartan_matrix_tr(root_system(w)), s:s, :), -w.vec[s]) # change to submul! once available return w end + +@doc raw""" + reflect(w::WeightLatticeElem, beta::RootSpaceElem) -> RootSpaceElem + +Return the reflection of `w` in the hyperplane orthogonal to root `beta`. + +See also: [`reflect!(::WeightLatticeElem, ::RootSpaceElem)`](@ref). +""" +function reflect(w::WeightLatticeElem, beta::RootSpaceElem) + return reflect!(deepcopy(w), beta) +end + +@doc raw""" + reflect!(w::WeightLatticeElem, beta::RootSpaceElem) -> RootSpaceElem + +Reflect `w` in the hyperplane orthogonal to the root `beta`, and return it. + +This is a mutating version of [`reflect(::WeightLatticeElem, ::RootSpaceElem)`](@ref). +""" +function reflect!(w::WeightLatticeElem, beta::RootSpaceElem) + @req root_system(w) === root_system(beta) "Incompatible root systems" + for s in word(reflection(beta)) + reflect!(w, Int(s)) + end + return w +end diff --git a/src/LieTheory/WeylGroup.jl b/src/LieTheory/WeylGroup.jl index f545dd8442b8..6feaa1e0565f 100644 --- a/src/LieTheory/WeylGroup.jl +++ b/src/LieTheory/WeylGroup.jl @@ -516,6 +516,43 @@ function word(x::WeylGroupElem) return x.word end +@doc raw""" + reflection(beta::RootSpaceElem) -> WeylGroupElem + +Return the Weyl group element corresponding to the reflection at the hyperplane orthogonal to the root `beta`. +""" +function reflection(beta::RootSpaceElem) + R = root_system(beta) + W = weyl_group(R) + rk = number_of_simple_roots(R) + + b, index_of_beta = is_root_with_index(beta) + @req b "Not a root" + # for a negative root we want the index of the corresponding positive root + if index_of_beta > number_of_positive_roots(R) + index_of_beta -= number_of_positive_roots(R) + end + + found_simple_root = index_of_beta <= rk + current_index = index_of_beta + list_of_indices = Int[] + while !found_simple_root + for j in 1:rk + next_index = W.refl[j, current_index] + if !iszero(next_index) && + next_index < current_index + current_index = next_index + if current_index <= rk + found_simple_root = true + end + push!(list_of_indices, j) + break + end + end + end + return W([list_of_indices; current_index; reverse(list_of_indices)]) +end + ############################################################################### # ReducedExpressionIterator diff --git a/src/exports.jl b/src/exports.jl index 3f9540b53cec..3858b2071b00 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -1408,6 +1408,7 @@ export reduced_groebner_basis export reduced_scheme export reducible_fibers export reflect, reflect! +export reflection export register_morphism! export regular_120_cell export regular_24_cell diff --git a/test/LieTheory/WeylGroup.jl b/test/LieTheory/WeylGroup.jl index d6aad4be1fa5..fbf9120bd38a 100644 --- a/test/LieTheory/WeylGroup.jl +++ b/test/LieTheory/WeylGroup.jl @@ -348,7 +348,70 @@ include( @test parent(x) === x.parent @test parent(x) isa WeylGroup end + + @testset "reflection" begin + for i in 2:4 + R = root_system(:A, i) + for r in roots(R) + @test r * reflection(r) == -r + end + end + + for i in 2:4 + R = root_system(:B, i) + for r in roots(R) + @test r * reflection(r) == -r + end + end + + for i in 2:4 + R = root_system(:C, i) + for r in roots(R) + @test r * reflection(r) == -r + end + end + + R = root_system(:D, 4) + for r in roots(R) + @test r * reflection(r) == -r + end + + R = root_system(:E, 6) + for r in roots(R) + @test r * reflection(r) == -r + end + R = root_system(:E, 7) + for r in roots(R) + @test r * reflection(r) == -r + end + + R = root_system(:E, 8) + for r in roots(R) + @test r * reflection(r) == -r + end + + R = root_system(:F, 4) + for r in roots(R) + @test r * reflection(r) == -r + end + + R = root_system(:G, 2) + for r in roots(R) + @test r * reflection(r) == -r + end + + R = root_system([(:B, 2), (:A, 2)]) + for r in roots(R) + @test r * reflection(r) == -r + for r2 in roots(R) + if is_zero(dot(r, r2)) #roots are orthogonal + @test r2 * reflection(r) == r2 + end + end + end + end + @testset "ReducedExpressionIterator" begin W = weyl_group(:A, 3) s = gens(W)