PropertyT.jl/src/roots.jl

222 lines
6.3 KiB
Julia
Raw Permalink Normal View History

2022-11-07 16:13:39 +01:00
module Roots
using StaticArrays
using LinearAlgebra
export Root, isproportional, isorthogonal, ~,
2023-05-09 01:02:39 +02:00
abstract type AbstractRoot{N,T} end # <: AbstractVector{T} ?
2022-11-07 16:13:39 +01:00
2023-04-06 11:39:54 +02:00
₂length(r::AbstractRoot) = norm(r, 2)
ambient_dim(r::AbstractRoot) = length(r)
Base.:*(r::AbstractRoot, a::Number) = a * r
2022-11-07 16:13:39 +01:00
cos_angle(a, b) = dot(a, b) / (norm(a) * norm(b))
2023-04-06 11:39:54 +02:00
function isproportional(α::AbstractRoot, β::AbstractRoot)
ambient_dim(α) == ambient_dim(β) || return false
2022-11-07 16:13:39 +01:00
val = abs(cos_angle(α, β))
2023-03-19 20:33:28 +01:00
return isapprox(val, one(val); atol = eps(one(val)))
2022-11-07 16:13:39 +01:00
end
2023-04-06 11:39:54 +02:00
function isorthogonal(α::AbstractRoot, β::AbstractRoot)
ambient_dim(α) == ambient_dim(β) || return false
2022-11-07 16:13:39 +01:00
val = cos_angle(α, β)
2023-03-19 20:33:28 +01:00
return isapprox(val, zero(val); atol = eps(one(val)))
2022-11-07 16:13:39 +01:00
end
2023-04-06 11:39:54 +02:00
function positive(roots::AbstractVector{<:AbstractRoot})
isempty(roots) && return empty(roots)
2022-11-07 16:13:39 +01:00
pd = _positive_direction(first(roots))
return filter(α -> dot(α, pd) > 0.0, roots)
end
2023-04-06 11:39:54 +02:00
function Base.show(io::IO, r::AbstractRoot)
return print(io, "Root $(r.coord)")
2022-11-07 16:13:39 +01:00
end
2023-04-06 11:39:54 +02:00
function Base.show(io::IO, ::MIME"text/plain", r::AbstractRoot)
l₂l = ₂length(r)
2023-05-09 01:02:39 +02:00
l = round(Int, l₂l) l₂l ? "$(round(Int, l₂l))" : "$(round(Int, l₂l^2))"
return print(io, "Root in ^$(length(r)) of length $l\n", r.coord)
2022-11-07 16:13:39 +01:00
end
2023-04-06 11:39:54 +02:00
function reflection(α::AbstractRoot, β::AbstractRoot)
return β - Int(2dot(α, β) // dot(α, α)) * α
end
function cartan(α::AbstractRoot, β::AbstractRoot)
ambient_dim(α) == ambient_dim(β) || throw("incompatible ambient dimensions")
return [
2023-04-06 11:39:54 +02:00
₂length(reflection(a, b) - b) / ₂length(a) for a in (α, β),
b in (α, β)
]
end
2022-11-07 16:13:39 +01:00
"""
classify_root_system(α, β)
Return the symbol of smallest system generated by roots `α` and `β`.
The classification is based only on roots length,
proportionality/orthogonality and Cartan matrix.
2022-11-07 16:13:39 +01:00
"""
function classify_root_system(
α::AbstractRoot,
β::AbstractRoot,
long::Tuple{Bool,Bool},
)
2022-11-07 16:13:39 +01:00
if isproportional(α, β)
if all(long)
2022-11-07 16:13:39 +01:00
return :C₁
elseif all(.!long) # both short
return :A₁
2022-11-07 16:13:39 +01:00
else
@error "Proportional roots of different length"
2022-11-07 16:13:39 +01:00
error("Unknown root system ⟨α, β⟩:\n α = $α\n β = ")
end
elseif isorthogonal(α, β)
if all(long)
2022-11-07 16:13:39 +01:00
return Symbol("C₁×C₁")
elseif all(.!long) # both short
return Symbol("A₁×A₁")
elseif any(long)
2022-11-07 16:13:39 +01:00
return Symbol("A₁×C₁")
end
else # ⟨α, β⟩ is 2-dimensional, but they're not orthogonal
a, b, c, d = abs.(cartan(α, β))
@assert a == d == 2
b, c = b < c ? (b, c) : (c, b)
if b == c == 1
2022-11-07 16:13:39 +01:00
return :A₂
elseif b == 1 && c == 2
2022-11-07 16:13:39 +01:00
return :C₂
elseif b == 1 && c == 3
return :G₂
2022-11-07 16:13:39 +01:00
else
@error a, b, c, d
2022-11-07 16:13:39 +01:00
error("Unknown root system ⟨α, β⟩:\n α = $α\n β = ")
end
end
end
2023-04-06 11:39:54 +02:00
function proportional_root_from_system(
Ω::AbstractVector{<:AbstractRoot},
α::AbstractRoot,
)
2022-11-07 16:13:39 +01:00
k = findfirst(v -> isproportional(α, v), Ω)
if isnothing(k)
error("Line L_α not contained in root system Ω:\n α = $α\n Ω = ")
end
return Ω[k]
end
2023-04-06 11:39:54 +02:00
struct Plane{R<:AbstractRoot}
2022-11-07 16:13:39 +01:00
v1::R
v2::R
vectors::Vector{R}
end
2023-04-06 11:39:54 +02:00
function Plane(α::AbstractRoot, β::AbstractRoot)
2023-03-19 20:33:28 +01:00
return Plane(α, β, [a * α + b * β for a in -3:3 for b in -3:3])
end
2022-11-07 16:13:39 +01:00
2023-04-06 11:39:54 +02:00
function Base.in(r::AbstractRoot, plane::Plane)
2022-11-07 16:13:39 +01:00
return any(isproportional(r, v) for v in plane.vectors)
end
2023-04-06 11:39:54 +02:00
function _islong(α::AbstractRoot, Ω)
lα = ₂length(α)
return any(r -> lα - ₂length(r) > eps(lα), Ω)
end
2022-11-07 16:13:39 +01:00
function classify_sub_root_system(
2023-04-06 11:39:54 +02:00
Ω::AbstractVector{<:AbstractRoot{N}},
α::AbstractRoot{N},
β::AbstractRoot{N},
2022-11-07 16:13:39 +01:00
) where {N}
2023-04-06 11:39:54 +02:00
@assert 1 length(unique(₂length, Ω)) 2
2022-11-07 16:13:39 +01:00
v = proportional_root_from_system(Ω, α)
w = proportional_root_from_system(Ω, β)
subsystem = filter(ω -> ω in Plane(v, w), Ω)
@assert length(subsystem) > 0
subsystem = positive(union(subsystem, -1 .* subsystem))
l = length(subsystem)
if l == 1
x = first(subsystem)
long = _islong(x, Ω)
return classify_root_system(x, -x, (long, long))
2022-11-07 16:13:39 +01:00
elseif l == 2
x, y = subsystem
return classify_root_system(x, y, (_islong(x, Ω), _islong(y, Ω)))
2022-11-07 16:13:39 +01:00
elseif l == 3
x, y, z = subsystem
l1, l2, l3 = _islong(x, Ω), _islong(y, Ω), _islong(z, Ω)
a = classify_root_system(x, y, (l1, l2))
b = classify_root_system(y, z, (l2, l3))
c = classify_root_system(x, z, (l1, l3))
2022-11-07 16:13:39 +01:00
if :A₂ == a == b == c # it's only A₂
2022-11-07 16:13:39 +01:00
return a
end
throw("Unknown subroot system! $((x,y,z))")
2022-11-07 16:13:39 +01:00
elseif l == 4
subtypes = [
classify_root_system(x, y, (_islong(x, Ω), _islong(y, Ω))) for
x in subsystem for y in subsystem if x y
]
if :C₂ in subtypes
return :C₂
2022-11-07 16:13:39 +01:00
end
2023-05-10 15:20:07 +02:00
@warn subtypes
elseif l == 6
return :G₂
2022-11-07 16:13:39 +01:00
end
2023-05-10 15:20:07 +02:00
@error "Unknown root subsystem generated by" α β l
2022-11-07 16:13:39 +01:00
throw("Unknown root system: $subsystem")
end
2023-04-06 11:39:54 +02:00
## concrete implementation:
struct Root{N,T} <: AbstractRoot{N,T}
coord::SVector{N,T}
end
Root(a) = Root(SVector(a...))
# convienience constructors
𝕖(N, i) = Root(ntuple(k -> k == i ? 1 : 0, N))
𝕆(N, ::Type{T}) where {T} = Root(ntuple(_ -> zero(T), N))
function Base.:(==)(r::Root{N}, s::Root{M}) where {M,N}
M == N || return false
r.coord == s.coord || return false
return true
end
Base.hash(r::Root, h::UInt) = hash(r.coord, hash(Root, h))
function Base.:+(r::Root, s::Root)
ambient_dim(r) == ambient_dim(s) || throw("incompatible ambient dimensions")
return Root(r.coord + s.coord)
end
function Base.:-(r::Root, s::Root)
ambient_dim(r) == ambient_dim(s) || throw("incompatible ambient dimensions")
return Root(r.coord - s.coord)
end
Base.:-(r::Root) = Root(-r.coord)
Base.:*(a::Number, r::Root) = Root(a * r.coord)
Base.length(r::Root) = length(r.coord)
LinearAlgebra.norm(r::Root, p::Real = 2) = norm(r.coord, p)
LinearAlgebra.dot(r::Root, s::Root) = dot(r.coord, s.coord)
function _positive_direction(α::Root{N}) where {N}
v = α.coord + 1 / (N * 100) * rand(N)
return Root{N,Float64}(v / norm(v, 2))
end
2022-11-07 16:13:39 +01:00
end # of module Roots