Skip to content

Commit 17ca141

Browse files
Add braid moves for words in Weyl groups (#4687)
Co-authored-by: Lars Göttgens <[email protected]>
1 parent f856f67 commit 17ca141

File tree

4 files changed

+230
-0
lines changed

4 files changed

+230
-0
lines changed

experimental/LieAlgebras/docs/src/weyl_groups_experimental.md

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ DocTestSetup = Oscar.doctestsetup()
77

88
This page is an addition to the documentation of [Weyl groups](@ref) with the additional experimental features.
99

10+
# Exchange lemma and Braid moves
11+
12+
```@docs
13+
braid_moves(::WeylGroup, ::Vector{UInt8}, ::Vector{UInt8})
14+
apply_braid_move!(::Vector{UInt8}, ::Tuple{Int, Int, Int})
15+
```
1016

1117
## Conversion to other group types
1218

experimental/LieAlgebras/src/WeylGroup.jl

+186
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,189 @@
1+
@doc raw"""
2+
exchange_left!(W::WeylGroup, w::AbstractVector{UInt8}, i::UInt8) -> AbstractVector{UInt8}
3+
4+
Given a word `w` which is reduced w.r.t. `W`, modify `w` in-place into an equivalent word
5+
starting with `i` if the exchange condition can be applied, otherwise leave `w` unchanged.
6+
Finally, return `w`.
7+
8+
!!! warning
9+
If `w` is not a reduced expression, the behaviour is arbitrary.
10+
11+
See also [`exchange_right!(::WeylGroup, ::AbstractVector{UInt8}, ::UInt8)`](@ref).
12+
"""
13+
function exchange_left!(W::WeylGroup, w::AbstractVector{UInt8}, i::UInt8)
14+
if w[1] == i
15+
return w
16+
end
17+
18+
root = i
19+
for s in 1:length(w)
20+
if w[s] == root
21+
for n in s:-1:2
22+
w[n] = w[n - 1]
23+
end
24+
w[1] = i
25+
return w
26+
end
27+
root = W.refl[Int(w[s]), Int(root)]
28+
end
29+
30+
return w
31+
end
32+
33+
@doc raw"""
34+
exchange_right!(W::WeylGroup, w::AbstractVector{UInt8}, i::UInt8) -> AbstractVector{UInt8}
35+
36+
Given a word `w` which is reduced w.r.t. `W`, modify `w` in-place into an equivalent word
37+
ending with `i` if the exchange condition can be applied, otherwise leave `w` unchanged.
38+
Finally, return `w`.
39+
40+
!!! warning
41+
If `w` is not a reduced expression, the behaviour is arbitrary.
42+
43+
See also [`exchange_left!(::WeylGroup, ::AbstractVector{UInt8}, ::UInt8)`](@ref).
44+
"""
45+
function exchange_right!(W::WeylGroup, w::AbstractVector{UInt8}, i::UInt8)
46+
# The algorithm is almost the same as explain_rmul
47+
# But we don't need a normal form, so this is faster
48+
if w[end] == i
49+
return w
50+
end
51+
52+
root = i
53+
for s in length(w):-1:1
54+
if w[s] == root
55+
for n in s:(length(w) - 1)
56+
w[n] = w[s + 1]
57+
end
58+
w[end] = i
59+
return w
60+
end
61+
root = W.refl[Int(w[s]), Int(root)]
62+
end
63+
64+
return w
65+
end
66+
67+
@doc raw"""
68+
braid_moves(W::WeylGroup, w1::Vector{UInt8}, w2::Vector{UInt8}) -> Vector{Tuple{Int,Int,Int}}
69+
70+
Return the braid moves required to transform the reduced expression `w2`
71+
into the reduced expression `w1` with respect to the Weyl group `W`.
72+
A braid move `(n, len, dir)` should be understood as follows:
73+
- `n` is the position where the braid move starts
74+
- `len` is the length of the braid move
75+
- `dir` is the direction of the braid move. If `len=2` or `len=3`, `dir` is `0` or `-1`.
76+
If `len=4` or `len=6`, `dir` is `-2` or `-3` if the root at `n` is short, otherwise `dir` is `-1`.
77+
This information can be used, when computing the tropical Plücker relations.
78+
79+
!!! warning
80+
If `w1` and `w2` do not define the same element in `W`, the behaviour is arbitrary.
81+
82+
# Examples
83+
```jldoctest
84+
julia> W = weyl_group(:A, 3);
85+
86+
julia> braid_moves(W, UInt8[1,2,1,3,2,1], UInt8[1,3,2,1,3,2])
87+
4-element Vector{Tuple{Int64, Int64, Int64}}:
88+
(4, 2, 0)
89+
(2, 3, -1)
90+
(4, 3, -1)
91+
(3, 2, 0)
92+
93+
julia> W = weyl_group(:B, 2);
94+
95+
julia> braid_moves(W, UInt8[2,1,2,1], UInt8[1,2,1,2])
96+
1-element Vector{Tuple{Int64, Int64, Int64}}:
97+
(1, 4, -2)
98+
```
99+
"""
100+
function braid_moves(W::WeylGroup, w1::Vector{UInt8}, w2::Vector{UInt8})
101+
return _braid_moves(W, w1, w2, 0)
102+
end
103+
104+
function _braid_moves(
105+
W::WeylGroup, w1::AbstractVector{UInt8}, w2::AbstractVector{UInt8}, offset::Int
106+
)
107+
mvs = Tuple{Int,Int,Int}[]
108+
if w1 == w2
109+
return mvs
110+
end
111+
112+
C = cartan_matrix(W)
113+
jo, jn = copy(w2), copy(w2)
114+
for i in 1:length(w1)
115+
if w1[i] == jo[i]
116+
continue
117+
end
118+
119+
cij = Int(C[Int(w1[i]), Int(jo[i])])
120+
cji = Int(C[Int(jo[i]), Int(w1[i])])
121+
122+
# in all cases we need to move w1[i] to the right of jn[i]
123+
# so that we can later apply the appropriate braid move
124+
@views exchange_left!(W, jn[(i + 1):end], w1[i])
125+
126+
if cij == 0
127+
# compute how to get jn[i:end] into [jn[i], w1[i], ...]
128+
@views append!(mvs, _braid_moves(W, jn[(i + 1):end], jo[(i + 1):end], i + offset))
129+
push!(mvs, (i + offset, 2, cij))
130+
reverse!(jn, i, i + 1)
131+
elseif cij == -1 && cji == -1
132+
@views exchange_left!(W, jn[(i + 2):end], jn[i]) # move jn[i] to the right of jn[i+1]
133+
134+
# compute how to get jn[i:end] into [jn[i], w1[i], jn[i], ...]
135+
@views append!(mvs, _braid_moves(W, jn[(i + 1):end], jo[(i + 1):end], i + offset))
136+
push!(mvs, (i + offset, 3, cij))
137+
jn[i], jn[i + 1], jn[i + 2] = jn[i + 1], jn[i], jn[i + 1]
138+
elseif cij == -2 || cji == -2
139+
@views exchange_left!(W, jn[(i + 2):end], jn[i]) # move jn[i] to the right of jn[i+1]
140+
@views exchange_left!(W, jn[(i + 3):end], w1[i]) # move w1[i] to the right of jn[i+2]
141+
142+
# compute how to get jn[i:end] into [jn[i], w1[i], jn[i], w1[i], ...]
143+
@views append!(mvs, _braid_moves(W, jn[(i + 1):end], jo[(i + 1):end], i + offset))
144+
push!(mvs, (i + offset, 4, cij))
145+
reverse!(jn, i, i + 3)
146+
elseif cij == -3 || cji == -3
147+
@views exchange_left!(W, jn[(i + 2):end], jn[i]) # move jn[i] to the right of jn[i+1]
148+
@views exchange_left!(W, jn[(i + 3):end], w1[i]) # move w1[i] to the right of jn[i+2]
149+
@views exchange_left!(W, jn[(i + 4):end], jn[i]) # move jn[i] to the right of jn[i+3]
150+
@views exchange_left!(W, jn[(i + 5):end], w1[i]) # move w1[i] to the right of jn[i+4]
151+
152+
# compute how to get jn[i:end] into [jn[i], w1[i], jn[i], w1[i], jn[i], w1[i], ...]
153+
@views append!(mvs, _braid_moves(W, jn[(i + 1):end], jo[(i + 1):end], i + offset))
154+
push!(mvs, (i + offset, 6, cij))
155+
reverse!(jn, i, i + 5)
156+
end
157+
158+
copyto!(jo, i, jn, i, length(jo) - i + 1)
159+
end
160+
161+
return mvs
162+
end
163+
164+
@doc raw"""
165+
apply_braid_move!(w::Vector{UInt8}, mv::Tuple{Int,Int,Int}) -> Vector{UInt8}
166+
167+
Apply the braid move `mv` to the word `w` and return the result.
168+
If `mv` is not a valid braid move for `w`, the behaviour is arbitrary.
169+
See also [`braid_moves`](@ref).
170+
"""
171+
function apply_braid_move!(w::Vector{UInt8}, mv::Tuple{Int,Int,Int})
172+
i, len, _ = mv
173+
if len == 2
174+
w[i], w[i + 1] = w[i + 1], w[i]
175+
elseif len == 3
176+
w[i], w[i + 1], w[i + 2] = w[i + 1], w[i], w[i + 1]
177+
elseif len == 4
178+
w[i], w[i + 1], w[i + 2], w[i + 3] = w[i + 1], w[i], w[i + 1], w[i]
179+
elseif len == 6
180+
w[i], w[i + 1], w[i + 2], w[i + 3], w[i + 4], w[i + 5] = w[i + 1],
181+
w[i], w[i + 1], w[i], w[i + 1],
182+
w[i]
183+
end
184+
return w
185+
end
186+
1187
@doc raw"""
2188
FPGroup(W::WeylGroup) -> FPGroup
3189
fp_group(W::WeylGroup) -> FPGroup

experimental/LieAlgebras/src/exports.jl

+2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ export abelian_lie_algebra
1616
export abstract_module
1717
export adjoint_matrix
1818
export any_non_ad_nilpotent_element
19+
export apply_braid_move!
1920
export base_lie_algebra
2021
export bracket
22+
export braid_moves
2123
export cartan_subalgebra
2224
export chevalley_basis
2325
export coerce_to_lie_algebra_elem

experimental/LieAlgebras/test/WeylGroup-test.jl

+36
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,42 @@
55

66
_isomorphic_group_on_gens = Oscar.LieAlgebras._isomorphic_group_on_gens
77

8+
@testset "apply_braid_move!" begin
9+
@test apply_braid_move!(UInt8[1, 3, 2, 1], (1, 2, 0)) == [3, 1, 2, 1]
10+
@test apply_braid_move!(UInt8[2, 3, 1, 4], (2, 2, 0)) == [2, 1, 3, 4]
11+
@test apply_braid_move!(UInt8[2, 3, 1, 2, 1, 4], (3, 3, -1)) == [2, 3, 2, 1, 2, 4]
12+
@test apply_braid_move!(UInt8[2, 3, 4, 3, 4, 2], (2, 4, -2)) == [2, 4, 3, 4, 3, 2]
13+
@test apply_braid_move!(UInt8[1, 2, 1, 2, 1, 2], (1, 6, -3)) == [2, 1, 2, 1, 2, 1]
14+
end
15+
16+
@testset "braid_moves for $fam$rk" for (fam, rk) in [
17+
(:A, 3), (:B, 3), (:C, 3), (:D, 4), (:F, 4), (:G, 2)
18+
]
19+
W = weyl_group(fam, rk)
20+
for _ in 1:10
21+
w = rand(W)
22+
w1 = word(w)
23+
w2 = copy(w1)
24+
25+
i = 0
26+
n = rand(1:(10 * order(W)))
27+
for r in reduced_expressions(w)
28+
i += 1
29+
copy!(w2, r)
30+
if i == n
31+
break
32+
end
33+
end
34+
35+
mvs = braid_moves(W, w1, w2)
36+
@test !isnothing(mvs)
37+
for mv in mvs
38+
apply_braid_move!(w2, mv)
39+
end
40+
@test w1 == w2
41+
end
42+
end
43+
844
# TODO: merge with conformance tests in test/LieTheory/WeylGroup.jl once this is moved to src
945
@testset "WeylGroup Group isomorphism test for $(Wname)" for (Wname, W) in [
1046
("A1", weyl_group(:A, 1)),

0 commit comments

Comments
 (0)