Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 19 additions & 20 deletions src/WeightLattice.jl
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,11 @@ end
# ─── Conjugation to dominant chamber ────────────────────────────────────────

# Reject reflection nodes outside the diagram, so that a mistyped node is an
# error rather than a silently skipped reflection. `nothing` means every node.
# Called from the fold kernels themselves rather than from their callers, so no
# future entry point can reach a fold without passing through it.
# error rather than a silently skipped reflection. Called from the fold kernels
# themselves rather than from their callers, so no future entry point can reach
# a fold without passing through it. Free on the default `1:R`, where the
# compiler drops the loop: the induction variable is provably within bounds.
function _check_nodes(nodes, R::Integer)
nodes === nothing && return nothing
for s in nodes
1 <= s <= R ||
throw(ArgumentError("Reflection node $s is out of range for rank $R"))
Expand All @@ -366,23 +366,22 @@ end

# Core dominant-chamber fold: fold `v` into the dominant chamber of the root
# subsystem spanned by the simple roots `nodes`, in place, and return the number
# of simple reflections applied. `nodes === nothing` means every node, and the
# membership test then const-folds away, so the full fold stays exactly as fast
# as it was before the subsystem case existed. `@inline` so each caller
# const-folds its compile-time-constant (sparse) Cartan matrix into the loop —
# this is a hot path and the method is small enough to re-specialize cheaply.
# of simple reflections applied. The default `1:R` is every node, so there is no
# sentinel and one code path. `@inline` so each caller const-folds its
# compile-time-constant (sparse) Cartan matrix into the loop — this is a hot path
# and the method is small enough to re-specialize cheaply.
#
# The scan runs over `1:R` and *tests* membership rather than iterating over
# `nodes`: that keeps every index into `v` and `C` provably in range under
# `@inbounds`, and accepts any container supporting `in`.
@inline function _fold_dominant!(
v::MVector{R,Int}, C::SMatrix{R,R,Int}, nodes=nothing
v::MVector{R,Int}, C::SMatrix{R,R,Int}, nodes=1:R
) where {R}
_check_nodes(nodes, R)
len = 0
s = 1
@inbounds while s <= R
if v[s] < 0 && (nodes === nothing || s in nodes)
if v[s] < 0 && s in nodes
pairing = v[s]
for j in 1:R
v[j] -= pairing * C[j, s]
Expand All @@ -397,7 +396,7 @@ end
end

"""
conjugate_dominant_weight(w::WeightLatticeElem{DT,R}, nodes=nothing) -> WeightLatticeElem{DT,R}
conjugate_dominant_weight(w::WeightLatticeElem{DT,R}, nodes=1:R) -> WeightLatticeElem{DT,R}

Return the unique dominant weight in the Weyl orbit of `w`.

Expand All @@ -407,7 +406,7 @@ spanned by ``S`` = `nodes`, i.e. dominant for the corresponding Levi subgroup.
The coordinates outside `nodes` are still moved along.

`nodes` may be any iterable of node indices — a tuple, vector, range or `Set` —
and `nothing` means every simple root. A node outside `1:R` throws an
and defaults to every simple root. A node outside `1:R` throws an
`ArgumentError`.

# Examples
Expand All @@ -432,7 +431,7 @@ julia> conjugate_dominant_weight(WeightLatticeElem(TypeA{2}, [-1, -1]), (2,))
```
"""
@inline function conjugate_dominant_weight(
w::WeightLatticeElem{DT,R}, nodes=nothing
w::WeightLatticeElem{DT,R}, nodes=1:R
) where {DT,R}
v = MVector{R,Int}(w.vec)
_fold_dominant!(v, cartan_matrix(DT), nodes)
Expand All @@ -441,13 +440,13 @@ end

# Like `_fold_dominant!` but records the word of simple reflections applied.
function _fold_dominant_with_word!(
v::MVector{R,Int}, C::SMatrix{R,R,Int}, nodes=nothing
v::MVector{R,Int}, C::SMatrix{R,R,Int}, nodes=1:R
) where {R}
_check_nodes(nodes, R)
word = Int[]
s = 1
while s <= R
if v[s] < 0 && (nodes === nothing || s in nodes)
if v[s] < 0 && s in nodes
pairing = v[s]
for j in 1:R
v[j] -= pairing * C[j, s]
Expand All @@ -462,7 +461,7 @@ function _fold_dominant_with_word!(
end

"""
conjugate_dominant_weight_with_elem(w::WeightLatticeElem{DT,R}, nodes=nothing) -> (WeightLatticeElem, Vector{Int})
conjugate_dominant_weight_with_elem(w::WeightLatticeElem{DT,R}, nodes=1:R) -> (WeightLatticeElem, Vector{Int})

Return the dominant weight and the sequence of simple reflections applied.

Expand All @@ -482,15 +481,15 @@ julia> conjugate_dominant_weight_with_elem(WeightLatticeElem(TypeA{2}, [-1, 1]),
```
"""
function conjugate_dominant_weight_with_elem(
w::WeightLatticeElem{DT,R}, nodes=nothing
w::WeightLatticeElem{DT,R}, nodes=1:R
) where {DT,R}
v = MVector{R,Int}(w.vec)
word = _fold_dominant_with_word!(v, cartan_matrix(DT), nodes)
return WeightLatticeElem{DT,R}(SVector{R,Int}(v)), word
end

"""
conjugate_dominant_weight_with_length(w::WeightLatticeElem{DT,R}, nodes=nothing) -> (WeightLatticeElem, Int)
conjugate_dominant_weight_with_length(w::WeightLatticeElem{DT,R}, nodes=1:R) -> (WeightLatticeElem, Int)

Return the dominant weight in the Weyl orbit of `w` together with the number
of simple reflections applied (i.e. the length of the Weyl group element
Expand Down Expand Up @@ -518,7 +517,7 @@ julia> conjugate_dominant_weight_with_length(WeightLatticeElem(TypeA{3}, [-1, 2,
```
"""
@inline function conjugate_dominant_weight_with_length(
w::WeightLatticeElem{DT,R}, nodes=nothing
w::WeightLatticeElem{DT,R}, nodes=1:R
) where {DT,R}
v = MVector{R,Int}(w.vec)
len = _fold_dominant!(v, cartan_matrix(DT), nodes)
Expand Down
12 changes: 6 additions & 6 deletions src/WeylGroup.jl
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,7 @@ weyl_dimension(dt::DynkinType, v) = degree(typeof(dt), v)
# ─── Singularity ─────────────────────────────────────────────────────────────

"""
is_singular(w::WeightLatticeElem{DT,R}, nodes=nothing) -> Bool
is_singular(w::WeightLatticeElem{DT,R}, nodes=1:R) -> Bool

Check whether the weight `w` is singular, i.e. lies on some wall of a Weyl
chamber. Equivalently, `w` is singular iff `⟨α∨, w⟩ = 0` for some positive
Expand Down Expand Up @@ -970,15 +970,15 @@ julia> is_singular(fundamental_weight(TypeA{2}, 2), (2,))
false
```
"""
function is_singular(w::WeightLatticeElem{DT,R}, nodes=nothing) where {DT,R}
function is_singular(w::WeightLatticeElem{DT,R}, nodes=1:R) where {DT,R}
dom = conjugate_dominant_weight(w, nodes)
return any(s -> dom.vec[s] == 0, something(nodes, 1:R))
return any(s -> dom.vec[s] == 0, nodes)
end

# ─── Borel–Weil–Bott ────────────────────────────────────────────────────────

"""
_borel_weil_bott(λ::WeightLatticeElem{DT,R}, nodes=nothing) -> Union{Nothing, Tuple{Int, WeightLatticeElem{DT,R}}}
_borel_weil_bott(λ::WeightLatticeElem{DT,R}, nodes=1:R) -> Union{Nothing, Tuple{Int, WeightLatticeElem{DT,R}}}

Apply the Borel–Weil–Bott theorem to the weight `λ`.

Expand Down Expand Up @@ -1036,7 +1036,7 @@ julia> _borel_weil_bott(-weyl_vector(TypeA{2}), (2,)) === nothing # singular f
true
```
"""
function _borel_weil_bott(λ::WeightLatticeElem{DT,R}, nodes=nothing) where {DT,R}
function _borel_weil_bott(λ::WeightLatticeElem{DT,R}, nodes=1:R) where {DT,R}
ρ = weyl_vector(DT)
μ = λ + ρ

Expand All @@ -1045,7 +1045,7 @@ function _borel_weil_bott(λ::WeightLatticeElem{DT,R}, nodes=nothing) where {DT,

# If any coordinate of μ_dom is zero, λ + ρ lies on a wall of the chamber
# (including the case μ = 0 when λ = -ρ), so all cohomology vanishes.
any(s -> μ_dom.vec[s] == 0, something(nodes, 1:R)) && return nothing
any(s -> μ_dom.vec[s] == 0, nodes) && return nothing

return (d, μ_dom - ρ)
end
13 changes: 12 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1192,8 +1192,19 @@ end
@test_throws ArgumentError is_singular(λ, nodes)
end

# No nodes at all: nothing moves.
# A range is checked by its extremes rather than element by element, so it
# needs its own out-of-range cases.
@test_throws ArgumentError conjugate_dominant_weight(λ, 2:5)
@test_throws ArgumentError conjugate_dominant_weight(λ, 0:2)
@test_throws ArgumentError _borel_weil_bott(λ, 0:4)
@test conjugate_dominant_weight(λ, 1:3) == conjugate_dominant_weight(λ)
@test conjugate_dominant_weight(λ, Base.OneTo(3)) == conjugate_dominant_weight(λ)

# No nodes at all: nothing moves. An empty range is vacuously in range, even
# when its endpoints are not.
@test conjugate_dominant_weight(λ, ()) == λ
@test conjugate_dominant_weight(λ, 3:2) == λ
@test conjugate_dominant_weight(λ, 9:1) == λ
@test conjugate_dominant_weight_with_length(λ, ()) == (λ, 0)
@test !is_singular(λ, ())
end
Expand Down