PropertyT.jl/src/roots.jl

180 lines
5.2 KiB
Julia
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module Roots
using StaticArrays
using LinearAlgebra
export Root, isproportional, isorthogonal, ~,
abstract type AbstractRoot{N,T} end
struct Root{N,T} <: AbstractRoot{N,T}
coord::SVector{N,T}
end
Root(a) = Root(SVector(a...))
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))
Base.:+(r::Root{N,T}, s::Root{N,T}) where {N,T} = Root{N,T}(r.coord + s.coord)
Base.:-(r::Root{N,T}, s::Root{N,T}) where {N,T} = Root{N,T}(r.coord - s.coord)
Base.:-(r::Root{N}) where {N} = Root(-r.coord)
Base.:*(a::Number, r::Root) = Root(a * r.coord)
Base.:*(r::Root, a::Number) = a * r
Base.length(r::AbstractRoot) = norm(r, 2)
LinearAlgebra.norm(r::Root, p::Real=2) = norm(r.coord, p)
LinearAlgebra.dot(r::Root, s::Root) = dot(r.coord, s.coord)
cos_angle(a, b) = dot(a, b) / (norm(a) * norm(b))
function isproportional(α::AbstractRoot{N}, β::AbstractRoot{M}) where {N,M}
N == M || return false
val = abs(cos_angle(α, β))
return isapprox(val, one(val), atol=eps(one(val)))
end
function isorthogonal(α::AbstractRoot{N}, β::AbstractRoot{M}) where {N,M}
N == M || return false
val = cos_angle(α, β)
return isapprox(val, zero(val), atol=eps(one(val)))
end
function _positive_direction(α::Root{N}) where {N}
last = -1 / 2^(N - 1)
return Root{N,Float64}(
SVector(ntuple(i -> ifelse(i == N, last, (2)^-i), N)),
)
end
function positive(roots::AbstractVector{<:Root{N}}) where {N}
# return those roots for which dot(α, Root([½, ¼, …])) > 0.0
pd = _positive_direction(first(roots))
return filter(α -> dot(α, pd) > 0.0, roots)
end
function Base.show(io::IO, r::Root)
print(io, "Root$(r.coord)")
end
function Base.show(io::IO, ::MIME"text/plain", r::Root{N}) where {N}
lngth² = sum(x -> x^2, r.coord)
l = isinteger(sqrt(lngth²)) ? "$(sqrt(lngth²))" : "$(lngth²)"
print(io, "Root in ^$N of length $l\n", r.coord)
end
𝕖(N, i) = Root(ntuple(k -> k == i ? 1 : 0, N))
𝕆(N, ::Type{T}) where {T} = Root(ntuple(_ -> zero(T), N))
"""
classify_root_system(α, β)
Return the symbol of smallest system generated by roots `α` and `β`.
The classification is based only on roots length and
proportionality/orthogonality.
"""
function classify_root_system(α::AbstractRoot, β::AbstractRoot)
lα, = length(α), length(β)
if isproportional(α, β)
if lα 2
return :A₁
elseif lα 2.0
return :C₁
else
error("Unknown root system ⟨α, β⟩:\n α = $α\n β = ")
end
elseif isorthogonal(α, β)
if lα 2
return Symbol("A₁×A₁")
elseif lα 2.0
return Symbol("C₁×C₁")
elseif (lα 2.0 && 2) || (lα 2 && 2)
return Symbol("A₁×C₁")
else
error("Unknown root system ⟨α, β⟩:\n α = $α\n β = ")
end
else # ⟨α, β⟩ is 2-dimensional, but they're not orthogonal
if lα 2
return :A₂
elseif (lα 2.0 && 2) || (lα 2 && 2)
return :C₂
else
error("Unknown root system ⟨α, β⟩:\n α = $α\n β = ")
end
end
end
function proportional_root_from_system(Ω::AbstractVector{<:Root}, α::Root)
k = findfirst(v -> isproportional(α, v), Ω)
if isnothing(k)
error("Line L_α not contained in root system Ω:\n α = $α\n Ω = ")
end
return Ω[k]
end
struct Plane{R<:Root}
v1::R
v2::R
vectors::Vector{R}
end
Plane(α::R, β::R) where {R<:Root} =
Plane(α, β, [a * α + b * β for a in -3:3 for b in -3:3])
function Base.in(r::R, plane::Plane{R}) where {R}
return any(isproportional(r, v) for v in plane.vectors)
end
function classify_sub_root_system(
Ω::AbstractVector{<:Root{N}},
α::Root{N},
β::Root{N},
) where {N}
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)
return classify_root_system(x, x)
elseif l == 2
return classify_root_system(subsystem...)
elseif l == 3
a = classify_root_system(subsystem[1], subsystem[2])
b = classify_root_system(subsystem[2], subsystem[3])
c = classify_root_system(subsystem[1], subsystem[3])
if a == b == c # it's only A₂
return a
end
C = (:C₂, Symbol("C₁×C₁"))
if (a C && b C && c C) && (:C₂ (a, b, c))
return :C₂
end
elseif l == 4
for i = 1:l
for j = (i+1):l
T = classify_root_system(subsystem[i], subsystem[j])
T == :C₂ && return :C₂
end
end
end
@error "Unknown root subsystem generated by" α β
throw("Unknown root system: $subsystem")
end
end # of module Roots