diff --git a/src/HeckeMiscMatrix.jl b/src/HeckeMiscMatrix.jl index bb80599cbe..a23232c0d5 100644 --- a/src/HeckeMiscMatrix.jl +++ b/src/HeckeMiscMatrix.jl @@ -614,98 +614,3 @@ function map_entries(R::ZZModRing, M::ZZMatrix) end return N end - -################################################################################ -# -# Eigenvalues and eigenspaces -# -################################################################################ - -@doc raw""" - eigenvalues(M::MatElem{T}) where T <: FieldElem - -Return the eigenvalues of `M`. -""" -function eigenvalues(M::MatElem{T}) where T <: FieldElem - @assert is_square(M) - K = base_ring(M) - f = charpoly(M) - return roots(f) -end - -@doc raw""" - eigenvalues_with_multiplicities(M::MatElem{T}) where T <: FieldElem - -Return the eigenvalues of `M` together with their algebraic multiplicities as a -vector of tuples. -""" -function eigenvalues_with_multiplicities(M::MatElem{T}) where T <: FieldElem - @assert is_square(M) - K = base_ring(M) - Kx, x = polynomial_ring(K, "x", cached = false) - f = charpoly(Kx, M) - r = roots(f) - return [ (a, valuation(f, x - a)) for a in r ] -end - -@doc raw""" - eigenvalues(L::Field, M::MatElem{T}) where T <: RingElem - -Return the eigenvalues of `M` over the field `L`. -""" -function eigenvalues(L::Field, M::MatElem{T}) where T <: RingElem - @assert is_square(M) - M1 = change_base_ring(L, M) - return eigenvalues(M1) -end - -@doc raw""" - eigenvalues_with_multiplicities(L::Field, M::MatElem{T}) where T <: RingElem - -Return the eigenvalues of `M` over the field `L` together with their algebraic -multiplicities as a vector of tuples. -""" -function eigenvalues_with_multiplicities(L::Field, M::MatElem{T}) where T <: RingElem - @assert is_square(M) - M1 = change_base_ring(L, M) - return eigenvalues_with_multiplicities(M1) -end - -@doc raw""" - eigenspace(M::MatElem{T}, lambda::T; side::Symbol = :left) - where T <: FieldElem -> Vector{MatElem{T}} - -Return a basis of the eigenspace of $M$ with respect to the eigenvalue $\lambda$. -If side is `:right`, the right eigenspace is computed, i.e. vectors $v$ such that -$Mv = \lambda v$. If side is `:left`, the left eigenspace is computed, i.e. vectors -$v$ such that $vM = \lambda v$. -""" -function eigenspace(M::MatElem{T}, lambda::T; side::Symbol = :left) where T <: FieldElem - @assert is_square(M) - N = deepcopy(M) - for i = 1:ncols(N) - N[i, i] -= lambda - end - return kernel(N, side = side) -end - -@doc raw""" - eigenspaces(M::MatElem{T}; side::Symbol = :left) - where T <: FieldElem -> Dict{T, MatElem{T}} - -Return a dictionary containing the eigenvalues of $M$ as keys and bases for the -corresponding eigenspaces as values. -If side is `:right`, the right eigenspaces are computed, if it is `:left` then the -left eigenspaces are computed. - -See also `eigenspace`. -""" -function eigenspaces(M::MatElem{T}; side::Symbol = :left) where T<:FieldElem - - S = eigenvalues(M) - L = Dict{elem_type(base_ring(M)), typeof(M)}() - for k in S - push!(L, k => vcat(eigenspace(M, k, side = side))) - end - return L -end diff --git a/src/matrix.jl b/src/matrix.jl index 44294be16e..5f85c010f9 100644 --- a/src/matrix.jl +++ b/src/matrix.jl @@ -1,4 +1,8 @@ -# Support for view(A, :, i) and view(A, i, :) +################################################################################ +# +# Support for view(A, :, i) and view(A, i, :) +# +################################################################################ struct MatrixView{S, T} <: AbstractVector{T} A::S @@ -26,7 +30,12 @@ function Base.view(x::_MatTypes, r::UnitRange{Int}, c::Int) return MatrixView{typeof(x), typeof(base_ring(x))}(A) end -# Generic kernel (calling nullspace in flint) +################################################################################ +# +# Generic kernel (calling nullspace in flint) +# +################################################################################ + const _FieldMatTypes = Union{QQMatrix, fpMatrix, FpMatrix, FqMatrix, fqPolyRepMatrix, FqPolyRepMatrix} function kernel(A::_FieldMatTypes; side::Symbol = :left) @@ -40,6 +49,12 @@ function kernel(A::_FieldMatTypes; side::Symbol = :left) return nullspace(A)[2] end +################################################################################ +# +# Solve context functionality +# +################################################################################ + # Overwrite some solve context functionality so that it uses `transpose` and not # `lazy_transpose` @@ -83,3 +98,98 @@ function Solve.kernel(C::Solve.SolveCtx{S, T}; side::Symbol = :left) where {S <: return transpose(X) end end + +################################################################################ +# +# Eigenvalues and eigenspaces +# +################################################################################ + +@doc raw""" + eigenvalues(M::MatElem{T}) where T <: FieldElem + +Return the eigenvalues of `M`. +""" +function eigenvalues(M::MatElem{T}) where T <: FieldElem + @assert is_square(M) + K = base_ring(M) + f = charpoly(M) + return roots(f) +end + +@doc raw""" + eigenvalues_with_multiplicities(M::MatElem{T}) where T <: FieldElem + +Return the eigenvalues of `M` together with their algebraic multiplicities as a +vector of tuples. +""" +function eigenvalues_with_multiplicities(M::MatElem{T}) where T <: FieldElem + @assert is_square(M) + K = base_ring(M) + Kx, x = polynomial_ring(K, "x", cached = false) + f = charpoly(Kx, M) + r = roots(f) + return [ (a, valuation(f, x - a)) for a in r ] +end + +@doc raw""" + eigenvalues(L::Field, M::MatElem{T}) where T <: RingElem + +Return the eigenvalues of `M` over the field `L`. +""" +function eigenvalues(L::Field, M::MatElem{T}) where T <: RingElem + @assert is_square(M) + M1 = change_base_ring(L, M) + return eigenvalues(M1) +end + +@doc raw""" + eigenvalues_with_multiplicities(L::Field, M::MatElem{T}) where T <: RingElem + +Return the eigenvalues of `M` over the field `L` together with their algebraic +multiplicities as a vector of tuples. +""" +function eigenvalues_with_multiplicities(L::Field, M::MatElem{T}) where T <: RingElem + @assert is_square(M) + M1 = change_base_ring(L, M) + return eigenvalues_with_multiplicities(M1) +end + +@doc raw""" + eigenspace(M::MatElem{T}, lambda::T; side::Symbol = :left) + where T <: FieldElem -> Vector{MatElem{T}} + +Return a basis of the eigenspace of $M$ with respect to the eigenvalue $\lambda$. +If side is `:right`, the right eigenspace is computed, i.e. vectors $v$ such that +$Mv = \lambda v$. If side is `:left`, the left eigenspace is computed, i.e. vectors +$v$ such that $vM = \lambda v$. +""" +function eigenspace(M::MatElem{T}, lambda::T; side::Symbol = :left) where T <: FieldElem + @assert is_square(M) + N = deepcopy(M) + for i = 1:ncols(N) + N[i, i] -= lambda + end + return kernel(N, side = side) +end + +@doc raw""" + eigenspaces(M::MatElem{T}; side::Symbol = :left) + where T <: FieldElem -> Dict{T, MatElem{T}} + +Return a dictionary containing the eigenvalues of $M$ as keys and bases for the +corresponding eigenspaces as values. +If side is `:right`, the right eigenspaces are computed, if it is `:left` then the +left eigenspaces are computed. + +See also `eigenspace`. +""" +function eigenspaces(M::MatElem{T}; side::Symbol = :left) where T<:FieldElem + + S = eigenvalues(M) + L = Dict{elem_type(base_ring(M)), typeof(M)}() + for k in S + push!(L, k => vcat(eigenspace(M, k, side = side))) + end + return L +end diff --git a/test/HeckeMiscMatrix-test.jl b/test/matrix-test.jl similarity index 100% rename from test/HeckeMiscMatrix-test.jl rename to test/matrix-test.jl