From 9f288ac3cff1007e9afb0cb873e53b426207bb20 Mon Sep 17 00:00:00 2001 From: wbhart Date: Wed, 4 May 2022 15:35:40 +0200 Subject: [PATCH] Enable matrix algebras over noncommutative rings (#1127) --- docs/src/matrix.md | 52 ++-- docs/src/matrix_introduction.md | 2 +- src/Matrix.jl | 440 ++++++++++++++++------------- src/MatrixAlgebra.jl | 68 ++--- src/NCPoly.jl | 38 +++ src/NCRings.jl | 92 +++++- src/Rings.jl | 111 -------- src/generic/GenericTypes.jl | 40 +-- src/generic/Matrix.jl | 44 +-- src/generic/MatrixAlgebra.jl | 46 +-- test/generic/Matrix-test.jl | 272 ++++++++++++++++++ test/generic/MatrixAlgebra-test.jl | 232 +++++++++++++++ 12 files changed, 984 insertions(+), 453 deletions(-) diff --git a/docs/src/matrix.md b/docs/src/matrix.md index 699b582210..0bfee6cd15 100644 --- a/docs/src/matrix.md +++ b/docs/src/matrix.md @@ -8,17 +8,17 @@ end # Matrix functionality AbstractAlgebra.jl provides a module, implemented in `src/Matrix.jl` for -matrices over any commutative ring belonging to the AbstractAlgebra abstract type +matrices over any ring belonging to the AbstractAlgebra abstract type hierarchy. This functionality will work for any matrix type which follows the Matrix interface. Similarly, AbstractAlgebra.jl provides a module in `src/MatrixAlgebra.jl` for -matrix algebras over a commutative ring. +matrix algebras over a ring. ## Generic matrix types -AbstractAlgebra.jl allows the creation of dense matrices over any computable commutative -ring $R$. Generic matrices over a commutative ring are implemented in +AbstractAlgebra.jl allows the creation of dense matrices over any computable +ring $R$. Generic matrices over a ring are implemented in `src/generic/Matrix.jl`. Generic matrix algebras of $m\times m$ matrices are implemented in @@ -291,8 +291,8 @@ and an `Array` constructors taking an `AbstractAlgebra` matrix as input are provided: ```@docs -Matrix(::MatrixElem) -Array(::MatrixElem) +Matrix(::MatrixElem{T}) where T <: RingElement +Array(::MatrixElem{T}) where T <: RingElement ``` Matrices also support iteration, and therefore functions accepting an iterator @@ -374,16 +374,16 @@ ncols(::MatSpace) ``` ```@docs -nrows(::MatrixElem) -ncols(::MatrixElem) +nrows(::MatrixElem{T}) where T <: RingElement +ncols(::MatrixElem{T}) where T <: RingElement ``` ```@docs -length(::MatElem) +length(::MatrixElem{T}) where T <: RingElement ``` ```@docs -isempty(::MatElem) +isempty(::MatrixElem{T}) where T <: RingElement ``` ```@docs @@ -400,12 +400,12 @@ diagonal_matrix(::RingElement, ::Int, ::Int) ```@docs zero(::MatSpace) -zero(::MatrixElem, ::Ring) +zero(::MatrixElem{T}, ::Ring) where T <: RingElement ``` ```@docs one(::MatSpace) -one(::MatElem) +one(::MatElem{T}) where T <: RingElement ``` ```@docs @@ -413,15 +413,15 @@ istriu(::MatrixElem{T}) where T <: RingElement ``` ```@docs -change_base_ring(::Ring, ::MatElem) +change_base_ring(::Ring, ::MatElem{T}) where T <: RingElement ``` ```@docs -Base.map(f, ::MatrixElem) +Base.map(f, ::MatrixElem{T}) where T <: RingElement ``` ```@docs -Base.map!(f, ::MatrixElem, ::MatrixElem) +Base.map!(f, ::MatrixElem{S}, ::MatrixElem{T}) where {S <: RingElement, T <: RingElement} ``` **Examples** @@ -570,14 +570,14 @@ julia> N3 = M[2:3, 2:3] ### Elementary row and column operations ```@docs -add_column(::MatElem, ::Int, ::Int, ::Int) -add_column!(::MatElem, ::Int, ::Int, ::Int) -add_row(::MatElem, ::Int, ::Int, ::Int) -add_row!(::MatElem, ::Int, ::Int, ::Int) -multiply_column(::MatElem, ::Int, ::Int) -multiply_column!(::MatElem, ::Int, ::Int) -multiply_row(::MatElem, ::Int, ::Int) -multiply_row!(::MatElem, ::Int, ::Int) +add_column(::MatElem{T}, ::Int, ::Int, ::Int) where T <: RingElement +add_column!(::MatElem{T}, ::Int, ::Int, ::Int) where T <: RingElement +add_row(::MatElem{T}, ::Int, ::Int, ::Int) where T <: RingElement +add_row!(::MatElem{T}, ::Int, ::Int, ::Int) where T <: RingElement +multiply_column(::MatElem{T}, ::Int, ::Int) where T <: RingElement +multiply_column!(::MatElem{T}, ::Int, ::Int) where T <: RingElement +multiply_row(::MatElem{T}, ::Int, ::Int) where T <: RingElement +multiply_row!(::MatElem{T}, ::Int, ::Int) where T <: RingElement ``` @@ -834,7 +834,7 @@ julia> B = gram(A) ### Trace ```@docs -tr(::MatElem) +tr(::MatElem{T}) where T <: RingElement ``` **Examples** @@ -859,7 +859,7 @@ t^2 + 3*t + 2 ### Content ```@docs -content(::MatElem) +content(::MatElem{T}) where T <: RingElement ``` **Examples** @@ -884,7 +884,7 @@ julia> b = content(A) ### Permutation ```@docs -*(::Perm, ::MatElem) +*(::Perm, ::MatElem{T}) where T <: RingElement ``` **Examples** diff --git a/docs/src/matrix_introduction.md b/docs/src/matrix_introduction.md index 670d1fca62..3bb47551cc 100644 --- a/docs/src/matrix_introduction.md +++ b/docs/src/matrix_introduction.md @@ -1,7 +1,7 @@ # Introduction AbstractAlgebra provides matrix spaces (mxn matrices) and matrix algebras -(nxn matrices) over a commutative ring. Whilst both types of matrix provide +(nxn matrices) over a ring. Whilst both types of matrix provide matrix multiplication for matrices whose dimensions are compatible for multiplication, only the latter kind of matrices form rings in the system. diff --git a/src/Matrix.jl b/src/Matrix.jl index c961a58b54..70140d4c9a 100644 --- a/src/Matrix.jl +++ b/src/Matrix.jl @@ -35,9 +35,9 @@ export MatrixSpace, add_column, add_column!, add_row, add_row!, # ############################################################################### -base_ring(a::MatSpace{T}) where {T <: RingElement} = a.base_ring::parent_type(T) +base_ring(a::MatSpace{T}) where {T <: NCRingElement} = a.base_ring::parent_type(T) -base_ring(a::MatrixElem{T}) where {T <: RingElement} = a.base_ring::parent_type(T) +base_ring(a::MatrixElem{T}) where {T <: NCRingElement} = a.base_ring::parent_type(T) function check_parent(a::MatElem, b::MatElem, throw::Bool = true) fl = (base_ring(a) != base_ring(b) || nrows(a) != nrows(b) || ncols(a) != ncols(b)) @@ -74,7 +74,7 @@ function _checkbounds(A, rows::AbstractArray{Int}, cols::AbstractArray{Int}) throw(BoundsError(A, cols)) end -function check_square(A::MatrixElem) +function check_square(A::MatrixElem{T}) where T <: NCRingElement issquare(A) || throw(DomainError(A, "matrix must be square")) A end @@ -118,36 +118,36 @@ function Base.hash(a::MatElem, h::UInt) end @doc Markdown.doc""" - nrows(a::MatrixElem) + nrows(a::MatrixElem{T}) where T <: NCRingElement Return the number of rows of the given matrix. """ -nrows(::MatrixElem) +nrows(::MatrixElem{T}) where T <: NCRingElement @doc Markdown.doc""" - ncols(a::MatrixElem) + ncols(a::MatrixElem{T}) where T <: NCRingElement Return the number of columns of the given matrix. """ -ncols(::MatrixElem) +ncols(::MatrixElem{T}) where T <: NCRingElement @doc Markdown.doc""" - length(a::MatrixElem) + length(a::MatrixElem{T}) where T <: NCRingElement Return the number of entries in the given matrix. """ -length(a::MatrixElem) = nrows(a) * ncols(a) +length(a::MatrixElem{T}) where T <: NCRingElement = nrows(a) * ncols(a) @doc Markdown.doc""" - isempty(a::MatrixElem) + isempty(a::MatrixElem{T}) where T <: NCRingElement Return `true` if `a` does not contain any entry (i.e. `length(a) == 0`), and `false` otherwise. """ -isempty(a::MatrixElem) = (nrows(a) == 0) | (ncols(a) == 0) +isempty(a::MatrixElem{T}) where T <: NCRingElement = (nrows(a) == 0) | (ncols(a) == 0) -Base.eltype(::Type{<:MatrixElem{T}}) where {T} = T +Base.eltype(::Type{<:MatrixElem{T}}) where {T <: NCRingElement} = T -function Base.isassigned(a::MatrixElem, i, j) +function Base.isassigned(a::MatrixElem{T}, i, j) where T <: NCRingElement try a[i, j] true @@ -168,18 +168,18 @@ Return the zero matrix in the given matrix space. zero(a::MatSpace) = a() @doc Markdown.doc""" - zero(x::MatrixElem, R::Ring, r::Int, c::Int) - zero(x::MatrixElem, R::Ring=base_ring(x)) - zero(x::MatrixElem, r::Int, c::Int) + zero(x::MatrixElem{T}, R::NCRing, r::Int, c::Int) where T <: NCRingElement + zero(x::MatrixElem{T}, R::NCRing=base_ring(x)) where T <: NCRingElement + zero(x::MatrixElem{T}, r::Int, c::Int) where T <: NCRingElement Return a zero matrix similar to the given matrix, with optionally different base ring or dimensions. """ -zero(x::MatrixElem, R::Ring=base_ring(x)) = zero(x, R, nrows(x), ncols(x)) -zero(x::MatrixElem, R::Ring, r::Int, c::Int) = zero!(similar(x, R, r, c)) -zero(x::MatrixElem, r::Int, c::Int) = zero(x, base_ring(x), r, c) +zero(x::MatrixElem{T}, R::NCRing=base_ring(x)) where T <: NCRingElement = zero(x, R, nrows(x), ncols(x)) +zero(x::MatrixElem{T}, R::NCRing, r::Int, c::Int) where T <: NCRingElement = zero!(similar(x, R, r, c)) +zero(x::MatrixElem{T}, r::Int, c::Int) where T <: NCRingElement = zero(x, base_ring(x), r, c) -function zero!(x::MatrixElem) +function zero!(x::MatrixElem{T}) where T <: NCRingElement R = base_ring(x) for i = 1:nrows(x), j = 1:ncols(x) x[i, j] = zero(R) @@ -196,14 +196,14 @@ square matrices or else an error is thrown. one(a::MatSpace) = check_square(a)(1) @doc Markdown.doc""" - one(a::MatrixElem) + one(a::MatrixElem{T}) where T <: NCRingElement Return the identity matrix in the same matrix space as $a$. If the space does not contain square matrices, an error is thrown. """ -one(a::MatrixElem) = identity_matrix(a) +one(a::MatrixElem{T}) where T <: NCRingElement = identity_matrix(a) -function iszero(a::MatrixElem) +function iszero(a::MatrixElem{T}) where T <: NCRingElement for i = 1:nrows(a) for j = 1:ncols(a) if !iszero(a[i, j]) @@ -214,7 +214,7 @@ function iszero(a::MatrixElem) return true end -function isone(a::MatrixElem) +function isone(a::MatrixElem{T}) where T <: NCRingElement issquare(a) || return false for i = 1:nrows(a) for j = 1:ncols(a) @@ -233,11 +233,11 @@ function isone(a::MatrixElem) end @doc Markdown.doc""" - iszero_row(M::MatrixElem{T}, i::Int) where T <: RingElement + iszero_row(M::MatrixElem{T}, i::Int) where T <: NCRingElement Return `true` if the $i$-th row of the matrix $M$ is zero. """ -function iszero_row(M::MatrixElem{T}, i::Int) where T <: RingElement +function iszero_row(M::MatrixElem{T}, i::Int) where T <: NCRingElement for j in 1:ncols(M) if !iszero(M[i, j]) return false @@ -247,11 +247,11 @@ function iszero_row(M::MatrixElem{T}, i::Int) where T <: RingElement end @doc Markdown.doc""" - iszero_column(M::MatrixElem{T}, i::Int) where T <: RingElement + iszero_column(M::MatrixElem{T}, i::Int) where T <: NCRingElement Return `true` if the $i$-th column of the matrix $M$ is zero. """ -function iszero_column(M::MatrixElem{T}, i::Int) where T <: RingElement +function iszero_column(M::MatrixElem{T}, i::Int) where T <: NCRingElement for j in 1:nrows(M) if !iszero(M[j, i]) return false @@ -267,12 +267,12 @@ end ############################################################################### @doc Markdown.doc""" - block_diagonal_matrix(V::Vector{<:MatElem{T}}) where T <: RingElement + block_diagonal_matrix(V::Vector{<:MatElem{T}}) where T <: NCRingElement Create the block diagonal matrix whose blocks are given by the matrices in `V`. There must be at least one matrix in V. """ -function block_diagonal_matrix(V::Vector{<:MatElem{T}}) where T <: RingElement +function block_diagonal_matrix(V::Vector{<:MatElem{T}}) where T <: NCRingElement length(V) > 0 || error("At least one matrix is required") rows = sum(nrows(N) for N in V) cols = sum(ncols(N) for N in V) @@ -301,12 +301,12 @@ function block_diagonal_matrix(V::Vector{<:MatElem{T}}) where T <: RingElement end @doc Markdown.doc""" - block_diagonal_matrix(R::Ring, V::Vector{<:Matrix{T}}) where T <: RingElement + block_diagonal_matrix(R::NCRing, V::Vector{<:Matrix{T}}) where T <: NCRingElement Create the block diagonal matrix over the ring `R` whose blocks are given by the matrices in `V`. Entries are coerced into `R` upon creation. """ -function block_diagonal_matrix(R::Ring, V::Vector{<:Matrix{T}}) where T <: RingElement +function block_diagonal_matrix(R::NCRing, V::Vector{<:Matrix{T}}) where T <: NCRingElement if length(V) == 0 return zero_matrix(R, 0, 0) end @@ -335,7 +335,7 @@ end # ############################################################################### -function _similar(x::MatrixElem{T}, R::Ring, r::Int, c::Int) where T <: RingElement +function _similar(x::MatrixElem{T}, R::NCRing, r::Int, c::Int) where T <: NCRingElement TT = elem_type(R) M = Matrix{TT}(undef, (r, c)) z = x isa MatElem ? Generic.MatSpaceElem{TT}(M) : Generic.MatAlgElem{TT}(M) @@ -343,9 +343,9 @@ function _similar(x::MatrixElem{T}, R::Ring, r::Int, c::Int) where T <: RingElem return z end -similar(x::MatElem, R::Ring, r::Int, c::Int) = _similar(x, R, r, c) +similar(x::MatElem, R::NCRing, r::Int, c::Int) = _similar(x, R, r, c) -similar(x::MatElem, R::Ring=base_ring(x)) = similar(x, R, nrows(x), ncols(x)) +similar(x::MatElem, R::NCRing=base_ring(x)) = similar(x, R, nrows(x), ncols(x)) similar(x::MatElem, r::Int, c::Int) = similar(x, base_ring(x), r, c) @@ -355,7 +355,7 @@ similar(x::MatElem, r::Int, c::Int) = similar(x, base_ring(x), r, c) # ############################################################################### -canonical_unit(a::MatrixElem) = canonical_unit(a[1, 1]) +canonical_unit(a::MatrixElem{T}) where T <: NCRingElement = canonical_unit(a[1, 1]) ############################################################################### # @@ -402,21 +402,21 @@ function _to_indices(x, rows, cols) (rows, cols) end -function Base.view(M::MatElem{T}, rows::Colon, cols::UnitRange{Int}) where T <: RingElement +function Base.view(M::MatElem{T}, rows::Colon, cols::UnitRange{Int}) where T <: NCRingElement return view(M, 1:nrows(M), cols) end -function Base.view(M::MatElem{T}, rows::UnitRange{Int}, cols::Colon) where T <: RingElement +function Base.view(M::MatElem{T}, rows::UnitRange{Int}, cols::Colon) where T <: NCRingElement return view(M, rows, 1:ncols(M)) end -function Base.view(M::MatElem{T}, rows::Colon, cols::Colon) where T <: RingElement +function Base.view(M::MatElem{T}, rows::Colon, cols::Colon) where T <: NCRingElement return view(M, 1:nrows(M), 1:ncols(M)) end -Base.firstindex(M::MatrixElem, i::Int) = 1 +Base.firstindex(M::MatrixElem{T}, i::Int) where T <: NCRingElement = 1 -function Base.lastindex(M::MatrixElem, i::Int) +function Base.lastindex(M::MatrixElem{T}, i::Int) where T <: NCRingElement if i == 1 return nrows(M) elseif i == 2 @@ -432,23 +432,23 @@ end # ############################################################################### -Base.ndims(::MatrixElem) = 2 +Base.ndims(::MatrixElem{T}) where T <: RingElement = 2 # Cartesian indexing -Base.eachindex(a::MatrixElem) = CartesianIndices((nrows(a), ncols(a))) +Base.eachindex(a::MatrixElem{T}) where T <: RingElement = CartesianIndices((nrows(a), ncols(a))) -Base.@propagate_inbounds Base.getindex(a::MatrixElem, I::CartesianIndex) = +Base.@propagate_inbounds Base.getindex(a::MatrixElem{T}, I::CartesianIndex) where T <: RingElement = a[I[1], I[2]] -Base.@propagate_inbounds function Base.setindex!(a::MatrixElem, x, I::CartesianIndex) +Base.@propagate_inbounds function Base.setindex!(a::MatrixElem{T}, x, I::CartesianIndex) where T <: RingElement a[I[1], I[2]] = x a end # iteration -function Base.iterate(a::MatrixElem, ij=(0, 1)) +function Base.iterate(a::MatrixElem{T}, ij=(0, 1)) where T <: NCRingElement i, j = ij i += 1 if i > nrows(a) @@ -469,7 +469,7 @@ Base.IteratorEltype(::Type{<:MatrixElem}) = Base.HasEltype() # default # ############################################################################### -function setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix}, r::UnitRange{Int}, c::UnitRange{Int}) where T +function setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix}, r::UnitRange{Int}, c::UnitRange{Int}) where T <: RingElement _checkbounds(a, r, c) size(b) == (length(r), length(c)) || throw(DimensionMismatch("tried to assign a $(size(b, 1))x$(size(b, 2)) matrix to a $(length(r))x$(length(c)) destination")) startr = first(r) @@ -481,7 +481,7 @@ function setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix}, r::UnitRange{ end end -function setindex!(a::MatrixElem{T}, b::Vector, r::UnitRange{Int}, c::UnitRange{Int}) where T +function setindex!(a::MatrixElem{T}, b::Vector, r::UnitRange{Int}, c::UnitRange{Int}) where T <: RingElement _checkbounds(a, r, c) if !((length(r) == 1 && length(c) == length(b)) || length(c) == 1 && length(r) == length(b)) throw(DimensionMismatch("tried to assign vector of length $(length(b)) to a $(length(r))x$(length(c)) destination")) @@ -496,27 +496,27 @@ function setindex!(a::MatrixElem{T}, b::Vector, r::UnitRange{Int}, c::UnitRange{ end # UnitRange{Int}, Colon -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::UnitRange{Int}, ::Colon) where T = setindex!(a, b, r, 1:ncols(a)) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::UnitRange{Int}, ::Colon) where T <: RingElement = setindex!(a, b, r, 1:ncols(a)) # Colon, UnitRange{Int} -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, ::Colon, c::UnitRange{Int}) where T = setindex!(a, b, 1:nrows(a), c) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, ::Colon, c::UnitRange{Int}) where T <: RingElement = setindex!(a, b, 1:nrows(a), c) # Colon, Colon -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, ::Colon, ::Colon) where T = setindex!(a, b, 1:nrows(a), 1:ncols(a)) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, ::Colon, ::Colon) where T <: RingElement = setindex!(a, b, 1:nrows(a), 1:ncols(a)) # Int, UnitRange{Int} -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Int, c::UnitRange{Int}) where T = setindex!(a, b, r:r, c) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Int, c::UnitRange{Int}) where T <: RingElement = setindex!(a, b, r:r, c) # UnitRange{Int}, Int -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::UnitRange{Int}, c::Int) where T = setindex!(a, b, r, c:c) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::UnitRange{Int}, c::Int) where T <: RingElement = setindex!(a, b, r, c:c) # Int, Colon -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Int, ::Colon) where T = setindex!(a, b, r:r, 1:ncols(a)) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Int, ::Colon) where T <: RingElement = setindex!(a, b, r:r, 1:ncols(a)) # Colon, Int -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, ::Colon, c::Int) where T = setindex!(a, b, 1:nrows(a), c:c) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, ::Colon, c::Int) where T <: RingElement = setindex!(a, b, 1:nrows(a), c:c) -function _setindex!(a::MatrixElem{T}, b, r, c) where T +function _setindex!(a::MatrixElem{T}, b, r, c) where T <: RingElement for (i, i2) in enumerate(r) for (j, j2) in enumerate(c) a[i2, j2] = b[i, j] @@ -524,7 +524,7 @@ function _setindex!(a::MatrixElem{T}, b, r, c) where T end end -function _setindex!(a::MatrixElem{T}, b::Vector, r, c) where T +function _setindex!(a::MatrixElem{T}, b::Vector, r, c) where T <: RingElement for (i, i2) in enumerate(r) for (j, j2) in enumerate(c) a[i2, j2] = b[i + j - 1] @@ -533,25 +533,25 @@ function _setindex!(a::MatrixElem{T}, b::Vector, r, c) where T end # Vector{Int}, Vector{Int} -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Vector{Int}, c::Vector{Int}) where T = _setindex!(a, b, r, c) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Vector{Int}, c::Vector{Int}) where T <: RingElement = _setindex!(a, b, r, c) # Vector{Int}, UnitRange{Int} -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Vector{Int}, c::UnitRange{Int}) where T = _setindex!(a, b, r, c) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Vector{Int}, c::UnitRange{Int}) where T <: RingElement = _setindex!(a, b, r, c) # UnitRange{Int}, Vector{Int} -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::UnitRange{Int}, c::Vector{Int}) where T = _setindex!(a, b, r, c) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::UnitRange{Int}, c::Vector{Int}) where T <: RingElement = _setindex!(a, b, r, c) # Vector{Int}, Colon -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Vector{Int}, ::Colon) where T = _setindex!(a, b, r, 1:ncols(a)) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Vector{Int}, ::Colon) where T <: RingElement = _setindex!(a, b, r, 1:ncols(a)) # Colon, Vector{Int} -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, ::Colon, c::Vector{Int}) where T = _setindex!(a, b, 1:nrows(a), c) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, ::Colon, c::Vector{Int}) where T <: RingElement = _setindex!(a, b, 1:nrows(a), c) # Int, Vector{Int} -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Int, c::Vector{Int}) where T = setindex!(a, b, r:r, c) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Int, c::Vector{Int}) where T <: RingElement = setindex!(a, b, r:r, c) # Vector{Int}, Int -setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Vector{Int}, c::Int) where T = setindex!(a, b, r, c:c) +setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Vector{Int}, c::Int) where T <: RingElement = setindex!(a, b, r, c:c) ################################################################################ # @@ -559,13 +559,13 @@ setindex!(a::MatrixElem{T}, b::Union{MatrixElem, Matrix, Vector}, r::Vector{Int} # ################################################################################ -size(x::MatrixElem) = (nrows(x), ncols(x)) +size(x::MatrixElem{T}) where T <: NCRingElement = (nrows(x), ncols(x)) -size(t::MatrixElem, d::Integer) = d <= 2 ? size(t)[d] : 1 +size(t::MatrixElem{T}, d::Integer) where T <: NCRingElement = d <= 2 ? size(t)[d] : 1 -axes(t::MatrixElem) = Base.OneTo.(size(t)) +axes(t::MatrixElem{T}) where T <: NCRingElement = Base.OneTo.(size(t)) -axes(t::MatrixElem, d::Integer) = Base.OneTo(size(t, d)) +axes(t::MatrixElem{T}, d::Integer) where T <: NCRingElement = Base.OneTo(size(t, d)) ############################################################################### # @@ -595,7 +595,7 @@ Base.length(M::MatSpace) = BigInt(length(base_ring(M)))^(nrows(M)*ncols(M)) # ############################################################################### -function expressify(a::MatrixElem; context = nothing) +function expressify(a::MatrixElem{T}; context = nothing) where T <: NCRingElement r = nrows(a) c = ncols(a) isempty(a) && return "$r by $c empty matrix" @@ -614,15 +614,15 @@ function expressify(a::MatrixElem; context = nothing) return mat end -function Base.show(io::IO, a::MatrixElem) +function Base.show(io::IO, a::MatrixElem{T}) where T <: NCRingElement show_via_expressify(io, a) end -function Base.show(io::IO, mi::MIME"text/html", a::MatrixElem) +function Base.show(io::IO, mi::MIME"text/html", a::MatrixElem{T}) where T <: NCRingElement show_via_expressify(io, mi, a) end -function Base.show(io::IO, ::MIME"text/plain", a::MatrixElem) +function Base.show(io::IO, ::MIME"text/plain", a::MatrixElem{T}) where T <: NCRingElement r = nrows(a) c = ncols(a) @@ -665,7 +665,7 @@ end # ############################################################################### -function -(x::MatrixElem) +function -(x::MatrixElem{T}) where T <: NCRingElement z = similar(x) for i in 1:nrows(x) for j in 1:ncols(x) @@ -681,7 +681,7 @@ end # ############################################################################### -function +(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: RingElement} +function +(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: NCRingElement} check_parent(x, y) r = similar(x) for i = 1:nrows(x) @@ -692,7 +692,7 @@ function +(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: RingElement} return r end -function -(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: RingElement} +function -(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: NCRingElement} check_parent(x, y) r = similar(x) for i = 1:nrows(x) @@ -703,7 +703,7 @@ function -(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: RingElement} return r end -function *(x::MatElem{T}, y::MatElem{T}) where {T <: RingElement} +function *(x::MatElem{T}, y::MatElem{T}) where {T <: NCRingElement} ncols(x) != nrows(y) && error("Incompatible matrix dimensions") A = similar(x, nrows(x), ncols(y)) C = base_ring(x)() @@ -725,7 +725,7 @@ end # ############################################################################### -function *(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem) +function *(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem{T}) where T <: NCRingElement z = similar(y) for i = 1:nrows(y) for j = 1:ncols(y) @@ -735,7 +735,7 @@ function *(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem) return z end -function *(x::T, y::MatrixElem{T}) where {T <: RingElem} +function *(x::T, y::MatrixElem{T}) where {T <: NCRingElem} z = similar(y) for i = 1:nrows(y) for j = 1:ncols(y) @@ -745,16 +745,32 @@ function *(x::T, y::MatrixElem{T}) where {T <: RingElem} return z end -*(x::MatrixElem, y::Union{Integer, Rational, AbstractFloat}) = y*x +function *(x::MatrixElem{T}, y::Union{Integer, Rational, AbstractFloat}) where T <: NCRingElement + z = similar(x) + for i = 1:nrows(x) + for j = 1:ncols(x) + z[i, j] = x[i, j]*y + end + end + return z +end -*(x::MatrixElem{T}, y::T) where {T <: RingElem} = y*x +function *(x::MatrixElem{T}, y::T) where {T <: NCRingElem} + z = similar(x) + for i = 1:nrows(x) + for j = 1:ncols(x) + z[i, j] = x[i, j]*y + end + end + return z +end @doc Markdown.doc""" +(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem) Return $S(x) + y$ where $S$ is the parent of $y$. """ -function +(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem) +function +(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem{T}) where T <: NCRingElement z = similar(y) R = base_ring(y) for i = 1:nrows(y) @@ -770,18 +786,18 @@ function +(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem) end @doc Markdown.doc""" - +(x::MatrixElem, y::Union{Integer, Rational, AbstractFloat}) + +(x::MatrixElem{T}, y::Union{Integer, Rational, AbstractFloat}) where T <: NCRingElement Return $x + S(y)$ where $S$ is the parent of $x$. """ -+(x::MatrixElem, y::Union{Integer, Rational, AbstractFloat}) = y + x ++(x::MatrixElem{T}, y::Union{Integer, Rational, AbstractFloat}) where T <: NCRingElement = y + x @doc Markdown.doc""" - +(x::T, y::MatrixElem{T}) where {T <: RingElem} + +(x::T, y::MatrixElem{T}) where {T <: NCRingElem} Return $S(x) + y$ where $S$ is the parent of $y$. """ -function +(x::T, y::MatrixElem{T}) where {T <: RingElem} +function +(x::T, y::MatrixElem{T}) where {T <: NCRingElem} z = similar(y) for i = 1:nrows(y) for j = 1:ncols(y) @@ -800,14 +816,14 @@ end Return $x + S(y)$ where $S$ is the parent of $x$. """ -+(x::MatrixElem{T}, y::T) where {T <: RingElem} = y + x ++(x::MatrixElem{T}, y::T) where {T <: NCRingElem} = y + x @doc Markdown.doc""" - -(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem) + -(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem{T}) where T <: NCRingElement Return $S(x) - y$ where $S$ is the parent of $y$. """ -function -(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem) +function -(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem{T}) where T <: NCRingElement z = similar(y) R = base_ring(y) for i = 1:nrows(y) @@ -823,11 +839,11 @@ function -(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem) end @doc Markdown.doc""" - -(x::MatrixElem, y::Union{Integer, Rational, AbstractFloat}) + -(x::MatrixElem{T}, y::Union{Integer, Rational, AbstractFloat}) where T <: NCRingElement Return $x - S(y)$, where $S$ is the parent of $x$. """ -function -(x::MatrixElem, y::Union{Integer, Rational, AbstractFloat}) +function -(x::MatrixElem{T}, y::Union{Integer, Rational, AbstractFloat}) where T <: NCRingElement z = similar(x) R = base_ring(x) for i = 1:nrows(x) @@ -843,11 +859,11 @@ function -(x::MatrixElem, y::Union{Integer, Rational, AbstractFloat}) end @doc Markdown.doc""" - -(x::T, y::MatrixElem{T}) where {T <: RingElem} + -(x::T, y::MatrixElem{T}) where {T <: NCRingElem} Return $S(x) - y$ where $S$ is the parent of $y$. """ -function -(x::T, y::MatrixElem{T}) where {T <: RingElem} +function -(x::T, y::MatrixElem{T}) where {T <: NCRingElem} z = similar(y) R = base_ring(y) for i = 1:nrows(y) @@ -863,11 +879,11 @@ function -(x::T, y::MatrixElem{T}) where {T <: RingElem} end @doc Markdown.doc""" - -(x::MatrixElem{T}, y::T) where {T <: RingElem} + -(x::MatrixElem{T}, y::T) where {T <: NCRingElem} Return $x - S(y)$, where $S$ is the parent of $a$. """ -function -(x::MatrixElem{T}, y::T) where {T <: RingElem} +function -(x::MatrixElem{T}, y::T) where {T <: NCRingElem} z = similar(x) R = base_ring(x) for i = 1:nrows(x) @@ -882,7 +898,7 @@ function -(x::MatrixElem{T}, y::T) where {T <: RingElem} return z end -function mul!(z::Vector{T}, x::MatrixElem{T}, y::Vector{T}) where T <: RingElement +function mul!(z::Vector{T}, x::MatrixElem{T}, y::Vector{T}) where T <: NCRingElement n = min(ncols(x), length(y)) tmp = base_ring(x)() for i in 1:nrows(x) @@ -899,12 +915,12 @@ function mul!(z::Vector{T}, x::MatrixElem{T}, y::Vector{T}) where T <: RingEleme return z end -function *(x::MatrixElem{T}, y::Vector{T}) where T <: RingElement +function *(x::MatrixElem{T}, y::Vector{T}) where T <: NCRingElement ncols(x) == length(y) || error("Incompatible dimensions") return mul!(T[base_ring(x)() for i in 1:nrows(x)], x, y) end -function mul!(z::Vector{T}, x::Vector{T}, y::MatrixElem{T}) where T <: RingElement +function mul!(z::Vector{T}, x::Vector{T}, y::MatrixElem{T}) where T <: NCRingElement m = min(length(x), nrows(y)) tmp = base_ring(y)() for j in 1:ncols(y) @@ -921,7 +937,7 @@ function mul!(z::Vector{T}, x::Vector{T}, y::MatrixElem{T}) where T <: RingEleme return z end -function *(x::Vector{T}, y::MatrixElem{T}) where T <: RingElement +function *(x::Vector{T}, y::MatrixElem{T}) where T <: NCRingElement length(x) == nrows(y) || error("Incompatible dimensions") return mul!(T[base_ring(y)() for j in 1:ncols(y)], x, y) end @@ -933,8 +949,8 @@ end ################################################################################ function Base.promote(x::MatrixElem{S}, - y::MatrixElem{T}) where {S <: RingElement, - T <: RingElement} + y::MatrixElem{T}) where {S <: NCRingElement, + T <: NCRingElement} U = promote_rule_sym(S, T) if U === S return x, change_base_ring(base_ring(x), y) @@ -945,15 +961,15 @@ function Base.promote(x::MatrixElem{S}, end end -*(x::MatrixElem, y::MatrixElem) = *(promote(x, y)...) +*(x::MatElem, y::MatElem) = *(promote(x, y)...) -+(x::MatrixElem, y::MatrixElem) = +(promote(x, y)...) ++(x::MatElem, y::MatElem) = +(promote(x, y)...) --(x::MatrixElem, y::MatrixElem) = -(promote(x, y)...) +-(x::MatElem, y::MatElem) = -(promote(x, y)...) -==(x::MatrixElem, y::MatrixElem) = ==(promote(x, y)...) +==(x::MatElem, y::MatElem) = ==(promote(x, y)...) -function Base.promote(x::MatrixElem{S}, y::T) where {S <: RingElement, T <: RingElement} +function Base.promote(x::MatElem{S}, y::T) where {S <: NCRingElement, T <: NCRingElement} U = promote_rule_sym(S, T) if U === S return x, base_ring(x)(y) @@ -962,22 +978,22 @@ function Base.promote(x::MatrixElem{S}, y::T) where {S <: RingElement, T <: Ring end end -function Base.promote(x::S, y::MatrixElem{T}) where {S <: RingElement, T <: RingElement} +function Base.promote(x::S, y::MatElem{T}) where {S <: NCRingElement, T <: NCRingElement} u, v = Base.promote(y, x) return v, u end -*(x::MatrixElem, y::RingElem) = *(promote(x, y)...) +*(x::MatElem, y::NCRingElem) = *(promote(x, y)...) -*(x::RingElem, y::MatrixElem) = *(promote(x, y)...) +*(x::NCRingElem, y::MatElem) = *(promote(x, y)...) -+(x::MatrixElem, y::RingElem) = +(promote(x, y)...) ++(x::MatElem, y::NCRingElem) = +(promote(x, y)...) -+(x::RingElem, y::MatrixElem) = +(promote(x, y)...) ++(x::NCRingElem, y::MatElem) = +(promote(x, y)...) -==(x::MatrixElem, y::RingElem) = ==(promote(x, y)...) +==(x::MatElem, y::NCRingElem) = ==(promote(x, y)...) -==(x::RingElem, y::MatrixElem) = ==(promote(x, y)...) +==(x::NCRingElem, y::MatElem) = ==(promote(x, y)...) ############################################################################### # @@ -985,14 +1001,14 @@ end # ############################################################################### -Base.literal_pow(::typeof(^), x::T, ::Val{p}) where {p, T <: MatrixElem} = x^p +Base.literal_pow(::typeof(^), x::T, ::Val{p}) where {p, U <: NCRingElement, T <: MatrixElem{U}} = x^p @doc Markdown.doc""" - ^(a::MatrixElem, b::Int) + ^(a::MatrixElem{T}, b::Int) where T <: NCRingElement Return $a^b$. We require that the matrix $a$ is square. """ -function ^(a::MatrixElem, b::Int) +function ^(a::MatrixElem{T}, b::Int) where T <: NCRingElement !issquare(a) && error("Incompatible matrix dimensions in power") if b < 0 return inv(a)^(-b) @@ -1027,13 +1043,13 @@ end ############################################################################### @doc Markdown.doc""" - ==(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: RingElement} + ==(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: NCRingElement} Return `true` if $x == y$ arithmetically, otherwise return `false`. Recall that power series to different precisions may still be arithmetically equal to the minimum of the two precisions. """ -function ==(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: RingElement} +function ==(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: NCRingElement} b = check_parent(x, y, false) !b && return false for i = 1:nrows(x) @@ -1047,14 +1063,14 @@ function ==(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: RingElement} end @doc Markdown.doc""" - isequal(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: RingElement} + isequal(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: NCRingElement} Return `true` if $x == y$ exactly, otherwise return `false`. This function is useful in cases where the entries of the matrices are inexact, e.g. power series. Only if the power series are precisely the same, to the same precision, are they declared equal by this function. """ -function isequal(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: RingElement} +function isequal(x::MatrixElem{T}, y::MatrixElem{T}) where {T <: NCRingElement} b = check_parent(x, y, false) !b && return false for i = 1:nrows(x) @@ -1074,12 +1090,12 @@ end ############################################################################### @doc Markdown.doc""" - ==(x::MatrixElem, y::Union{Integer, Rational, AbstractFloat}) + ==(x::MatrixElem{T}, y::Union{Integer, Rational, AbstractFloat}) where T <: RingElement Return `true` if $x == S(y)$ arithmetically, where $S$ is the parent of $x$, otherwise return `false`. """ -function ==(x::MatrixElem, y::Union{Integer, Rational, AbstractFloat}) +function ==(x::MatrixElem{T}, y::Union{Integer, Rational, AbstractFloat}) where T <: NCRingElement for i = 1:min(nrows(x), ncols(x)) if x[i, i] != y return false @@ -1096,20 +1112,20 @@ function ==(x::MatrixElem, y::Union{Integer, Rational, AbstractFloat}) end @doc Markdown.doc""" - ==(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem) + ==(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem{T}) where T <: RingElement Return `true` if $S(x) == y$ arithmetically, where $S$ is the parent of $y$, otherwise return `false`. """ -==(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem) = y == x +==(x::Union{Integer, Rational, AbstractFloat}, y::MatrixElem{T}) where T <: NCRingElement = y == x @doc Markdown.doc""" - ==(x::MatrixElem{T}, y::T) where {T <: RingElem} + ==(x::MatrixElem{T}, y::T) where {T <: NCRingElem} Return `true` if $x == S(y)$ arithmetically, where $S$ is the parent of $x$, otherwise return `false`. """ -function ==(x::MatrixElem{T}, y::T) where {T <: RingElem} +function ==(x::MatrixElem{T}, y::T) where {T <: NCRingElem} for i = 1:min(nrows(x), ncols(x)) if x[i, i] != y return false @@ -1126,12 +1142,12 @@ function ==(x::MatrixElem{T}, y::T) where {T <: RingElem} end @doc Markdown.doc""" - ==(x::T, y::MatrixElem{T}) where {T <: RingElem} + ==(x::T, y::MatrixElem{T}) where {T <: NCRingElem} Return `true` if $S(x) == y$ arithmetically, where $S$ is the parent of $y$, otherwise return `false`. """ -==(x::T, y::MatrixElem{T}) where {T <: RingElem} = y == x +==(x::T, y::MatrixElem{T}) where {T <: NCRingElem} = y == x ############################################################################### # @@ -1139,7 +1155,7 @@ otherwise return `false`. # ############################################################################### -function divexact(x::MatrixElem, y::Union{Integer, Rational, AbstractFloat}; check::Bool=true) +function divexact(x::MatrixElem{T}, y::Union{Integer, Rational, AbstractFloat}; check::Bool=true) where T <: NCRingElement z = similar(x) for i = 1:nrows(x) for j = 1:ncols(x) @@ -1159,6 +1175,26 @@ function divexact(x::MatrixElem{T}, y::T; check::Bool=true) where {T <: RingElem return z end +function divexact_left(x::MatrixElem{T}, y::T; check::Bool=true) where {T <: NCRingElem} + z = similar(x) + for i = 1:nrows(x) + for j = 1:ncols(x) + z[i, j] = divexact_left(x[i, j], y; check=check) + end + end + return z +end + +function divexact_right(x::MatrixElem{T}, y::T; check::Bool=true) where {T <: NCRingElem} + z = similar(x) + for i = 1:nrows(x) + for j = 1:ncols(x) + z[i, j] = divexact_right(x[i, j], y; check=check) + end + end + return z +end + ############################################################################### # # Symmetry @@ -1166,12 +1202,12 @@ end ############################################################################### @doc Markdown.doc""" - issymmetric(a::MatrixElem) + issymmetric(a::MatrixElem{T}) where T <: RingElement Return `true` if the given matrix is symmetric with respect to its main diagonal, otherwise return `false`. """ -function issymmetric(a::MatrixElem) +function issymmetric(a::MatrixElem{T}) where T <: NCRingElement if !issquare(a) return false end @@ -1241,12 +1277,12 @@ end ############################################################################### @doc Markdown.doc""" - tr(x::MatrixElem) + tr(x::MatrixElem{T}) where T <: RingElement Return the trace of the matrix $a$, i.e. the sum of the diagonal elements. We require the matrix to be square. """ -function tr(x::MatrixElem) +function tr(x::MatrixElem{T}) where T <: RingElement !issquare(x) && error("Not a square matrix in trace") d = zero(base_ring(x)) for i = 1:nrows(x) @@ -1262,12 +1298,12 @@ end ############################################################################### @doc Markdown.doc""" - content(x::MatrixElem) + content(x::MatrixElem{T}) where T <: RingElement Return the content of the matrix $a$, i.e. the greatest common divisor of all its entries, assuming it exists. """ -function content(x::MatrixElem) +function content(x::MatrixElem{T}) where T <: RingElement d = zero(base_ring(x)) for i = 1:nrows(x) for j = 1:ncols(x) @@ -1287,11 +1323,11 @@ end ############################################################################### @doc Markdown.doc""" - *(P::perm, x::MatrixElem) + *(P::perm, x::MatrixElem{T}) where T <: RingElement Apply the pemutation $P$ to the rows of the matrix $x$ and return the result. """ -function *(P::Perm, x::MatrixElem) +function *(P::Perm, x::MatrixElem{T}) where T <: RingElement z = similar(x) m = nrows(x) n = ncols(x) @@ -5663,12 +5699,12 @@ end ############################################################################### @doc Markdown.doc""" - swap_rows(a::MatrixElem, i::Int, j::Int) + swap_rows(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement Return a matrix $b$ with the entries of $a$, where the $i$th and $j$th row are swapped. """ -function swap_rows(a::MatrixElem, i::Int, j::Int) +function swap_rows(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement (1 <= i <= nrows(a) && 1 <= j <= nrows(a)) || throw(BoundsError()) b = deepcopy(a) swap_rows!(b, i, j) @@ -5676,11 +5712,11 @@ function swap_rows(a::MatrixElem, i::Int, j::Int) end @doc Markdown.doc""" - swap_rows!(a::MatrixElem, i::Int, j::Int) + swap_rows!(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement Swap the $i$th and $j$th row of $a$. """ -function swap_rows!(a::MatrixElem, i::Int, j::Int) +function swap_rows!(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement (1 <= i <= nrows(a) && 1 <= j <= nrows(a)) || throw(BoundsError()) if i != j for k = 1:ncols(a) @@ -5693,12 +5729,12 @@ function swap_rows!(a::MatrixElem, i::Int, j::Int) end @doc Markdown.doc""" - swap_cols(a::MatrixElem, i::Int, j::Int) + swap_cols(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement Return a matrix $b$ with the entries of $a$, where the $i$th and $j$th row are swapped. """ -function swap_cols(a::MatrixElem, i::Int, j::Int) +function swap_cols(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement (1 <= i <= ncols(a) && 1 <= j <= ncols(a)) || throw(BoundsError()) b = deepcopy(a) swap_cols!(b, i, j) @@ -5706,11 +5742,11 @@ function swap_cols(a::MatrixElem, i::Int, j::Int) end @doc Markdown.doc""" - swap_cols!(a::MatrixElem, i::Int, j::Int) + swap_cols!(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement Swap the $i$th and $j$th column of $a$. """ -function swap_cols!(a::MatrixElem, i::Int, j::Int) +function swap_cols!(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement if i != j for k = 1:nrows(a) x = a[k, i] @@ -5722,12 +5758,12 @@ function swap_cols!(a::MatrixElem, i::Int, j::Int) end @doc Markdown.doc""" - reverse_rows!(a::MatrixElem) + reverse_rows!(a::MatrixElem{T}) where T <: RingElement Swap the $i$th and $r - i$th row of $a$ for $1 \leq i \leq r/2$, where $r$ is the number of rows of $a$. """ -function reverse_rows!(a::MatrixElem) +function reverse_rows!(a::MatrixElem{T}) where T <: RingElement k = div(nrows(a), 2) for i in 1:k swap_rows!(a, i, nrows(a) - i + 1) @@ -5736,24 +5772,24 @@ function reverse_rows!(a::MatrixElem) end @doc Markdown.doc""" - reverse_rows(a::MatrixElem) + reverse_rows(a::MatrixElem{T}) where T <: RingElement Return a matrix $b$ with the entries of $a$, where the $i$th and $r - i$th row is swapped for $1 \leq i \leq r/2$. Here $r$ is the number of rows of $a$. """ -function reverse_rows(a::MatrixElem) +function reverse_rows(a::MatrixElem{T}) where T <: RingElement b = deepcopy(a) return reverse_rows!(b) end @doc Markdown.doc""" - reverse_cols!(a::MatrixElem) + reverse_cols!(a::MatrixElem{T}) where T <: RingElement Swap the $i$th and $r - i$th column of $a$ for $1 \leq i \leq c/2$, where $c$ is the number of columns of $a$. """ -function reverse_cols!(a::MatrixElem) +function reverse_cols!(a::MatrixElem{T}) where T <: RingElement k = div(ncols(a), 2) for i in 1:k swap_cols!(a, i, ncols(a) - i + 1) @@ -5762,13 +5798,13 @@ function reverse_cols!(a::MatrixElem) end @doc Markdown.doc""" - reverse_cols(a::MatrixElem) + reverse_cols(a::MatrixElem{T}) where T <: RingElement Return a matrix $b$ with the entries of $a$, where the $i$th and $r - i$th column is swapped for $1 \leq i \leq c/2$. Here $c$ is the number of columns of$a$. """ -function reverse_cols(a::MatrixElem) +function reverse_cols(a::MatrixElem{T}) where T <: RingElement b = deepcopy(a) return reverse_cols!(b) end @@ -5780,14 +5816,14 @@ end ################################################################################ @doc Markdown.doc""" - add_column!(a::MatrixElem, s::RingElement, i::Int, j::Int, rows = 1:nrows(a)) + add_column!(a::MatrixElem{T}, s::RingElement, i::Int, j::Int, rows = 1:nrows(a)) where T <: RingElement Add $s$ times the $i$-th row to the $j$-th row of $a$. By default, the transformation is applied to all rows of $a$. This can be changed using the optional `rows` argument. """ -function add_column!(a::MatrixElem, s::RingElement, i::Int, j::Int, rows = 1:nrows(a)) +function add_column!(a::MatrixElem{T}, s::RingElement, i::Int, j::Int, rows = 1:nrows(a)) where T <: RingElement v = base_ring(a)(s) nc = ncols(a) !_checkbounds(nc, i) && error("Column index ($i) must be between 1 and $nc") @@ -5801,7 +5837,7 @@ function add_column!(a::MatrixElem, s::RingElement, i::Int, j::Int, rows = 1:nro end @doc Markdown.doc""" - add_column(a::MatrixElem, s::RingElement, i::Int, j::Int, rows = 1:nrows(a)) + add_column(a::MatrixElem{T}, s::RingElement, i::Int, j::Int, rows = 1:nrows(a)) where T <: RingElement Create a copy of $a$ and add $s$ times the $i$-th row to the $j$-th row of $a$. @@ -5809,20 +5845,20 @@ By default, the transformation is applied to all rows of $a$. This can be changed using the optional `rows` argument. """ -function add_column(a::MatrixElem, s::RingElement, i::Int, j::Int, rows = 1:nrows(a)) +function add_column(a::MatrixElem{T}, s::RingElement, i::Int, j::Int, rows = 1:nrows(a)) where T <: RingElement b = deepcopy(a) return add_column!(b, s, i, j, rows) end @doc Markdown.doc""" - add_row!(a::MatrixElem, s::RingElement, i::Int, j::Int, cols = 1:ncols(a)) + add_row!(a::MatrixElem{T}, s::RingElement, i::Int, j::Int, cols = 1:ncols(a)) where T <: RingElement Add $s$ times the $i$-th row to the $j$-th row of $a$. By default, the transformation is applied to all columns of $a$. This can be changed using the optional `cols` argument. """ -function add_row!(a::MatrixElem, s::RingElement, i::Int, j::Int, cols = 1:ncols(a)) +function add_row!(a::MatrixElem{T}, s::RingElement, i::Int, j::Int, cols = 1:ncols(a)) where T <: RingElement v = base_ring(a)(s) nr = nrows(a) !_checkbounds(nr, i) && error("Row index ($i) must be between 1 and $nr") @@ -5836,14 +5872,14 @@ function add_row!(a::MatrixElem, s::RingElement, i::Int, j::Int, cols = 1:ncols( end @doc Markdown.doc""" - add_row(a::MatrixElem, s::RingElement, i::Int, j::Int, cols = 1:ncols(a)) + add_row(a::MatrixElem{T}, s::RingElement, i::Int, j::Int, cols = 1:ncols(a)) where T <: RingElement Create a copy of $a$ and add $s$ times the $i$-th row to the $j$-th row of $a$. By default, the transformation is applied to all columns of $a$. This can be changed using the optional `cols` argument. """ -function add_row(a::MatrixElem, s::RingElement, i::Int, j::Int, cols = 1:ncols(a)) +function add_row(a::MatrixElem{T}, s::RingElement, i::Int, j::Int, cols = 1:ncols(a)) where T <: RingElement b = deepcopy(a) return add_row!(b, s, i, j, cols) end @@ -5851,14 +5887,14 @@ end # Multiply column @doc Markdown.doc""" - multiply_column!(a::MatrixElem, s::RingElement, i::Int, rows = 1:nrows(a)) + multiply_column!(a::MatrixElem{T}, s::RingElement, i::Int, rows = 1:nrows(a)) where T <: RingElement Multiply the $i$th column of $a$ with $s$. By default, the transformation is applied to all rows of $a$. This can be changed using the optional `rows` argument. """ -function multiply_column!(a::MatrixElem, s::RingElement, i::Int, rows = 1:nrows(a)) +function multiply_column!(a::MatrixElem{T}, s::RingElement, i::Int, rows = 1:nrows(a)) where T <: RingElement c = base_ring(a)(s) nc = ncols(a) !_checkbounds(nc, i) && error("Row index ($i) must be between 1 and $nc") @@ -5870,14 +5906,14 @@ function multiply_column!(a::MatrixElem, s::RingElement, i::Int, rows = 1:nrows( end @doc Markdown.doc""" - multiply_column(a::MatrixElem, s::RingElement, i::Int, rows = 1:nrows(a)) + multiply_column(a::MatrixElem{T}, s::RingElement, i::Int, rows = 1:nrows(a)) where T <: RingElement Create a copy of $a$ and multiply the $i$th column of $a$ with $s$. By default, the transformation is applied to all rows of $a$. This can be changed using the optional `rows` argument. """ -function multiply_column(a::MatrixElem, s::RingElement, i::Int, rows = 1:nrows(a)) +function multiply_column(a::MatrixElem{T}, s::RingElement, i::Int, rows = 1:nrows(a)) where T <: RingElement b = deepcopy(a) return multiply_column!(b, s, i, rows) end @@ -5885,14 +5921,14 @@ end # Multiply row @doc Markdown.doc""" - multiply_row!(a::MatrixElem, s::RingElement, i::Int, cols = 1:ncols(a)) + multiply_row!(a::MatrixElem{T}, s::RingElement, i::Int, cols = 1:ncols(a)) where T <: RingElement Multiply the $i$th row of $a$ with $s$. By default, the transformation is applied to all columns of $a$. This can be changed using the optional `cols` argument. """ -function multiply_row!(a::MatrixElem, s::RingElement, i::Int, cols = 1:ncols(a)) +function multiply_row!(a::MatrixElem{T}, s::RingElement, i::Int, cols = 1:ncols(a)) where T <: RingElement c = base_ring(a)(s) nr = nrows(a) !_checkbounds(nr, i) && error("Row index ($i) must be between 1 and $nr") @@ -5904,14 +5940,14 @@ function multiply_row!(a::MatrixElem, s::RingElement, i::Int, cols = 1:ncols(a)) end @doc Markdown.doc""" - multiply_row(a::MatrixElem, s::RingElement, i::Int, cols = 1:ncols(a)) + multiply_row(a::MatrixElem{T}, s::RingElement, i::Int, cols = 1:ncols(a)) where T <: RingElement Create a copy of $a$ and multiply the $i$th row of $a$ with $s$. By default, the transformation is applied to all columns of $a$. This can be changed using the optional `cols` argument. """ -function multiply_row(a::MatrixElem, s::RingElement, i::Int, cols = 1:ncols(a)) +function multiply_row(a::MatrixElem{T}, s::RingElement, i::Int, cols = 1:ncols(a)) where T <: RingElement b = deepcopy(a) return multiply_row!(b, s, i, cols) end @@ -5967,12 +6003,12 @@ function vcat(a::MatElem, b::MatElem) end @doc Markdown.doc""" - vcat(A::MatrixElem...) -> MatrixElem + vcat(A::MatrixElem{T}...) where T <: NCRingElement -> MatrixElem Return the horizontal concatenation of the matrices $A$. All component matrices need to have the same base ring and number of columns. """ -function Base.vcat(A::MatrixElem...) +function Base.vcat(A::MatrixElem{T}...) where T <: NCRingElement return _vcat(A) end @@ -6005,12 +6041,12 @@ function _vcat(A) end @doc Markdown.doc""" - hcat(A::MatrixElem...) -> MatrixElem + hcat(A::MatrixElem{T}...) where T <: NCRingElement -> MatrixElem Return the horizontal concatenating of the matrices $A$. All component matrices need to have the same base ring and number of rows. """ -function Base.hcat(A::MatrixElem...) +function Base.hcat(A::MatrixElem{T}...) where T <: NCRingElement return _hcat(A) end @@ -6042,7 +6078,7 @@ function _hcat(A) return M end -function Base.cat(A::MatrixElem...;dims) +function Base.cat(A::MatrixElem{T}...;dims) where T <: NCRingElement @assert dims == (1,2) || isa(dims, Int) if isa(dims, Int) @@ -6066,7 +6102,7 @@ function Base.cat(A::MatrixElem...;dims) return X end -function Base.hvcat(rows::Tuple{Vararg{Int}}, A::MatrixElem...) +function Base.hvcat(rows::Tuple{Vararg{Int}}, A::MatrixElem{T}...) where T <: NCRingElement nr = 0 k = 1 for i in 1:length(rows) @@ -6105,15 +6141,15 @@ end # like change_base_ring, but without initializing the entries # this function exists until a better API is implemented -_change_base_ring(R::Ring, a::MatElem) = zero_matrix(R, nrows(a), ncols(a)) -_change_base_ring(R::Ring, a::MatAlgElem) = MatrixAlgebra(R, nrows(a))() +_change_base_ring(R::NCRing, a::MatElem) = zero_matrix(R, nrows(a), ncols(a)) +_change_base_ring(R::NCRing, a::MatAlgElem) = MatrixAlgebra(R, nrows(a))() @doc Markdown.doc""" - change_base_ring(R::Ring, M::MatrixElem) + change_base_ring(R::NCRing, M::MatrixElem{T}) where T <: NCRingElement Return the matrix obtained by coercing each entry into `R`. """ -function change_base_ring(R::Ring, M::MatrixElem) +function change_base_ring(R::NCRing, M::MatrixElem{T}) where T <: NCRingElement N = _change_base_ring(R, M) for i = 1:nrows(M), j = 1:ncols(M) N[i,j] = R(M[i,j]) @@ -6128,11 +6164,11 @@ end ############################################################################### @doc Markdown.doc""" - map_entries!(f, dst::MatrixElem, src::MatrixElem) + map_entries!(f, dst::MatrixElem{T}, src::MatrixElem{U}) where {T <: NCRingElement, U <: NCRingElement} Like `map_entries`, but stores the result in `dst` rather than a new matrix. """ -function map_entries!(f, dst::MatrixElem, src::MatrixElem) +function map_entries!(f, dst::MatrixElem{T}, src::MatrixElem{U}) where {T <: NCRingElement, U <: NCRingElement} for i = 1:nrows(src), j = 1:ncols(src) dst[i, j] = f(src[i, j]) end @@ -6140,19 +6176,19 @@ function map_entries!(f, dst::MatrixElem, src::MatrixElem) end @doc Markdown.doc""" - map!(f, dst::MatrixElem, src::MatrixElem) + map!(f, dst::MatrixElem{T}, src::MatrixElem{U}) where {T <: NCRingElement, U <: NCRingElement} Like `map`, but stores the result in `dst` rather than a new matrix. This is equivalent to `map_entries!(f, dst, src)`. """ -Base.map!(f, dst::MatrixElem, src::MatrixElem) = map_entries!(f, dst, src) +Base.map!(f, dst::MatrixElem{T}, src::MatrixElem{U}) where {T <: NCRingElement, U <: NCRingElement} = map_entries!(f, dst, src) @doc Markdown.doc""" - map_entries(f, a::MatrixElem) + map_entries(f, a::MatrixElem{T}) where T <: NCRingElement Transform matrix `a` by applying `f` on each element. """ -function map_entries(f, a::MatrixElem) +function map_entries(f, a::MatrixElem{T}) where T <: NCRingElement isempty(a) && return _change_base_ring(parent(f(zero(base_ring(a)))), a) b11 = f(a[1, 1]) b = _change_base_ring(parent(b11), a) @@ -6165,12 +6201,12 @@ function map_entries(f, a::MatrixElem) end @doc Markdown.doc""" - map(f, a::MatrixElem) + map(f, a::MatrixElem{T}) where T <: NCRingElement Transform matrix `a` by applying `f` on each element. This is equivalent to `map_entries(f, a)`. """ -Base.map(f, a::MatrixElem) = map_entries(f, a) +Base.map(f, a::MatrixElem{T}) where T <: NCRingElement = map_entries(f, a) ############################################################################### # @@ -6291,7 +6327,7 @@ randmat_with_rank(S::MatSpace{T}, rank::Int, v...) where {T <: RingElement} = Constructs the matrix over $R$ with entries as in `arr`. """ -function matrix(R::Ring, arr::AbstractMatrix{T}) where {T} +function matrix(R::NCRing, arr::AbstractMatrix{T}) where {T} if elem_type(R) === T z = Generic.MatSpaceElem{elem_type(R)}(arr) z.base_ring = R @@ -6308,7 +6344,7 @@ end Constructs the $r \times c$ matrix over $R$, where the entries are taken row-wise from `arr`. """ -function matrix(R::Ring, r::Int, c::Int, arr::AbstractVecOrMat{T}) where T +function matrix(R::NCRing, r::Int, c::Int, arr::AbstractVecOrMat{T}) where T _check_dim(r, c, arr) ndims(arr) == 2 && return matrix(R, arr) if elem_type(R) === T @@ -6332,7 +6368,7 @@ end Return the $r \times c$ zero matrix over $R$. """ -function zero_matrix(R::Ring, r::Int, c::Int) +function zero_matrix(R::NCRing, r::Int, c::Int) arr = Array{elem_type(R)}(undef, r, c) for i in 1:r for j in 1:c @@ -6351,11 +6387,11 @@ end ################################################################################ @doc Markdown.doc""" - identity_matrix(R::Ring, n::Int) + identity_matrix(R::NCRing, n::Int) Return the $n \times n$ identity matrix over $R$. """ -identity_matrix(R::Ring, n::Int) = diagonal_matrix(one(R), n) +identity_matrix(R::NCRing, n::Int) = diagonal_matrix(one(R), n) ################################################################################ # @@ -6364,17 +6400,17 @@ identity_matrix(R::Ring, n::Int) = diagonal_matrix(one(R), n) ################################################################################ @doc Markdown.doc""" - identity_matrix(M::MatElem{T}) where T <: RingElement + identity_matrix(M::MatElem{T}) where T <: NCRingElement Construct the identity matrix in the same matrix space as `M`, i.e. with ones down the diagonal and zeroes elsewhere. `M` must be square. This is an alias for `one(M)`. """ -function identity_matrix(M::MatElem{T}) where T <: RingElement +function identity_matrix(M::MatElem{T}) where T <: NCRingElement identity_matrix(check_square(M), nrows(M)) end -function identity_matrix(M::MatElem{T}, n::Int) where T <: RingElement +function identity_matrix(M::MatElem{T}, n::Int) where T <: NCRingElement z = zero(M, n, n) R = base_ring(M) for i = 1:n @@ -6407,7 +6443,7 @@ julia> diagonal_matrix(QQ(-1), 3) [ 0//1 0//1 -1//1] ``` """ -function diagonal_matrix(x::RingElement, m::Int, n::Int) +function diagonal_matrix(x::NCRingElement, m::Int, n::Int) z = zero_matrix(parent(x), m, n) for i in 1:min(m, n) z[i, i] = x @@ -6415,7 +6451,7 @@ function diagonal_matrix(x::RingElement, m::Int, n::Int) return z end -diagonal_matrix(x::RingElement, m::Int) = diagonal_matrix(x, m, m) +diagonal_matrix(x::NCRingElement, m::Int) = diagonal_matrix(x, m, m) ############################################################################### # @@ -6424,14 +6460,14 @@ diagonal_matrix(x::RingElement, m::Int) = diagonal_matrix(x, m, m) ############################################################################### @doc Markdown.doc""" - MatrixSpace(R::Ring, r::Int, c::Int; cached::Bool = true) + MatrixSpace(R::NCRing, r::Int, c::Int; cached::Bool = true) Return parent object corresponding to the space of $r\times c$ matrices over the ring $R$. If `cached == true` (the default), the returned parent object is cached so that it can returned by future calls to the constructor with the same dimensions and base ring. """ -function MatrixSpace(R::Ring, r::Int, c::Int; cached::Bool = true) +function MatrixSpace(R::NCRing, r::Int, c::Int; cached::Bool = true) return Generic.MatrixSpace(R, r, c, cached=cached) end @@ -6442,7 +6478,7 @@ end ############################################################################### """ - Matrix(A::MatrixElem) + Matrix(A::MatrixElem{T}) where T <: RingElement Convert `A` to a Julia `Matrix` of the same dimensions with the same elements. @@ -6458,10 +6494,10 @@ julia> Matrix(A) 4 5 6 ``` """ -Matrix(M::MatrixElem) = eltype(M)[M[i, j] for i = 1:nrows(M), j = 1:ncols(M)] +Matrix(M::MatrixElem{T}) where T <: RingElement = eltype(M)[M[i, j] for i = 1:nrows(M), j = 1:ncols(M)] """ - Array(A::MatrixElem) + Array(A::MatrixElem{T}) where T <: RingElement Convert `A` to a Julia `Matrix` of the same dimensions with the same elements. @@ -6477,4 +6513,4 @@ julia> Array(A) x^2 x^3 ``` """ -Array(M::MatrixElem) = Matrix(M) +Array(M::MatrixElem{T}) where T <: RingElement = Matrix(M) diff --git a/src/MatrixAlgebra.jl b/src/MatrixAlgebra.jl index dae1928dde..a6f44acb18 100644 --- a/src/MatrixAlgebra.jl +++ b/src/MatrixAlgebra.jl @@ -12,11 +12,11 @@ export MatrixAlgebra, divexact_left, divexact_right # ############################################################################### -function base_ring(a::MatAlgebra{T}) where {T <: RingElement} +function base_ring(a::MatAlgebra{T}) where {T <: NCRingElement} a.base_ring::parent_type(T) end -function check_parent(a::MatAlgElem{T}, b::MatAlgElem{T}, throw::Bool = true) where T <: RingElement +function check_parent(a::MatAlgElem{T}, b::MatAlgElem{T}, throw::Bool = true) where T <: NCRingElement fl = (base_ring(a) != base_ring(b) || degree(a) != degree(b)) fl && throw && error("Incompatible matrix spaces in matrix operation") return !fl @@ -50,11 +50,11 @@ Return the degree $n$ of the given matrix algebra. degree(a::MatAlgebra) = nrows(a) @doc Markdown.doc""" - degree(a::MatAlgElem) + degree(a::MatAlgElem{T}) where T <: RingElement Return the degree $n$ of the given matrix algebra. """ -degree(a::MatAlgElem) = degree(parent(a)) +degree(a::MatAlgElem{T}) where T <: NCRingElement = degree(parent(a)) zero(a::MatAlgebra) = a() @@ -64,7 +64,7 @@ isunit(a::MatAlgElem{T}) where T <: RingElement = isunit(det(a)) isunit(a::MatAlgElem{T}) where T <: FieldElement = rank(a) == degree(a) -function characteristic(a::MatAlgebra{T}) where T <: RingElement +function characteristic(a::MatAlgebra) return characteristic(base_ring(a)) end @@ -75,22 +75,22 @@ end ############################################################################### @doc Markdown.doc""" - similar(x::Generic.MatrixElem, R::Ring=base_ring(x)) - similar(x::Generic.MatrixElem, R::Ring, r::Int, c::Int) + similar(x::Generic.MatrixElem, R::NCRing=base_ring(x)) + similar(x::Generic.MatrixElem, R::NCRing, r::Int, c::Int) similar(x::Generic.MatrixElem, r::Int, c::Int) - similar(x::MatAlgElem, R::Ring, n::Int) + similar(x::MatAlgElem, R::NCRing, n::Int) similar(x::MatAlgElem, n::Int) Create an uninitialized matrix over the given ring and dimensions, with defaults based upon the given source matrix `x`. """ -similar(x::MatAlgElem, R::Ring, n::Int) = _similar(x, R, n, n) +similar(x::MatAlgElem, R::NCRing, n::Int) = _similar(x, R, n, n) -similar(x::MatAlgElem, R::Ring=base_ring(x)) = similar(x, R, degree(x)) +similar(x::MatAlgElem, R::NCRing=base_ring(x)) = similar(x, R, degree(x)) similar(x::MatAlgElem, n::Int) = similar(x, base_ring(x), n) -function similar(x::MatAlgElem{T}, R::Ring, m::Int, n::Int) where T <: RingElement +function similar(x::MatAlgElem{T}, R::NCRing, m::Int, n::Int) where T <: NCRingElement m != n && error("Dimensions don't match in similar") return similar(x, R, n) end @@ -98,16 +98,16 @@ end similar(x::MatAlgElem, m::Int, n::Int) = similar(x, base_ring(x), m, n) @doc Markdown.doc""" - zero(x::MatrixElem, R::Ring=base_ring(x)) - zero(x::MatrixElem, R::Ring, r::Int, c::Int) + zero(x::MatrixElem, R::NCRing=base_ring(x)) + zero(x::MatrixElem, R::NCRing, r::Int, c::Int) zero(x::MatrixElem, r::Int, c::Int) - zero(x::MatAlgElem, R::Ring, n::Int) + zero(x::MatAlgElem, R::NCRing, n::Int) zero(x::MatAlgElem, n::Int) Create a zero matrix over the given ring and dimensions, with defaults based upon the given source matrix `x`. """ -zero(x::MatAlgElem, R::Ring, n::Int) = zero!(similar(x, R, n)) +zero(x::MatAlgElem, R::NCRing, n::Int) = zero!(similar(x, R, n)) zero(x::MatAlgElem, n::Int) = zero!(similar(x, n)) ################################################################################ @@ -116,7 +116,7 @@ zero(x::MatAlgElem, n::Int) = zero!(similar(x, n)) # ################################################################################ -function copy(d::MatAlgElem{T}) where T <: RingElement +function copy(d::MatAlgElem{T}) where T <: NCRingElement z = similar(d) for i = 1:nrows(d) for j = 1:ncols(d) @@ -126,7 +126,7 @@ function copy(d::MatAlgElem{T}) where T <: RingElement return z end -function deepcopy_internal(d::MatAlgElem{T}, dict::IdDict) where T <: RingElement +function deepcopy_internal(d::MatAlgElem{T}, dict::IdDict) where T <: NCRingElement z = similar(d) for i = 1:nrows(d) for j = 1:ncols(d) @@ -162,7 +162,7 @@ end # ############################################################################### -function *(x::MatAlgElem{T}, y::MatAlgElem{T}) where {T <: RingElement} +function *(x::MatAlgElem{T}, y::MatAlgElem{T}) where {T <: NCRingElement} degree(x) != degree(y) && error("Incompatible matrix degrees") A = similar(x) C = base_ring(x)() @@ -203,7 +203,7 @@ end ==(x::Union{Integer, Rational, AbstractFloat}, y::MatAlgElem) = y == x -function ==(x::MatAlgElem{T}, y::T) where {T <: RingElem} +function ==(x::MatAlgElem{T}, y::T) where T <: NCRingElem n = degree(x) for i = 1:n if x[i, i] != y @@ -220,6 +220,8 @@ function ==(x::MatAlgElem{T}, y::T) where {T <: RingElem} return true end +==(x::T, y::MatAlgElem{T}) where T <: NCRingElem = y == x + ############################################################################### # # Exact division @@ -248,28 +250,6 @@ function divexact_right(f::MatAlgElem{T}, return f*inv(g) end -############################################################################### -# -# Ad hoc exact division -# -############################################################################### - -function divexact_left(x::MatAlgElem{T}, y::T; check::Bool=true) where {T <: RingElem} - return divexact(x, y; check=check) -end - -function divexact_right(x::MatAlgElem{T}, y::T; check::Bool=true) where {T <: RingElem} - return divexact(x, y; check=check) -end - -function divexact_left(x::MatAlgElem, y::Union{Integer, Rational, AbstractFloat}; check::Bool=true) - return divexact(x, y; check=check) -end - -function divexact_right(x::MatAlgElem, y::Union{Integer, Rational, AbstractFloat}; check::Bool=true) - return divexact(x, y; check=check) -end - ############################################################################### # # Gram @@ -408,7 +388,7 @@ randmat_with_rank(S::MatAlgebra{T}, rank::Int, v...) where {T <: RingElement} = # ############################################################################### -function identity_matrix(M::MatAlgElem{T}, n::Int) where T <: RingElement +function identity_matrix(M::MatAlgElem{T}, n::Int) where T <: NCRingElement R = base_ring(M) arr = Array{T}(undef, n, n) for i in 1:n @@ -427,7 +407,7 @@ end Return the identity matrix over the same base ring as $M$ and with the same dimensions. """ -function identity_matrix(M::MatAlgElem{T}) where T <: RingElement +function identity_matrix(M::MatAlgElem{T}) where T <: NCRingElement return identity_matrix(M, nrows(M)) end @@ -445,6 +425,6 @@ the ring $R$. If `cached == true` (the default), the returned parent object is cached so that it can returned by future calls to the constructor with the same degree and base ring. """ -function MatrixAlgebra(R::Ring, n::Int; cached::Bool = true) +function MatrixAlgebra(R::NCRing, n::Int; cached::Bool = true) Generic.MatrixAlgebra(R, n, cached = cached) end diff --git a/src/NCPoly.jl b/src/NCPoly.jl index 6b217daf8e..e705fb6767 100644 --- a/src/NCPoly.jl +++ b/src/NCPoly.jl @@ -565,6 +565,44 @@ end # Note: composition is not associative, e.g. consider fo(goh) vs (fog)oh # for f and g of degree 2 and h of degree 1 -- and recall coeffs don't commute +################################################################################ +# +# Change base ring +# +################################################################################ + +function change_base_ring(R::NCRing, p::NCPolyElem{T}; cached::Bool = true, parent::PolyRing = _change_poly_ring(R, parent(p), cached)) where T <: NCRingElement + return _map(R, p, parent) +end + +function change_coefficient_ring(R::NCRing, p::NCPolyElem{T}; cached::Bool = true, parent::PolyRing = _change_poly_ring(R, parent(p), cached)) where T <: NCRingElement + return change_base_ring(R, p; cached = cached, parent = parent) +end + +################################################################################ +# +# Map +# +################################################################################ + +_make_parent(g, p::NCPolyElem, cached::Bool) = + _change_poly_ring(parent(g(zero(base_ring(p)))), + parent(p), cached) + +function map_coefficients(g, p::NCPolyElem{<:NCRingElement}; + cached::Bool = true, + parent::NCPolyRing = _make_parent(g, p, cached)) + return _map(g, p, parent) +end + +function _map(g, p::NCPolyElem, Rx) + R = base_ring(Rx) + new_coefficients = elem_type(R)[let c = coeff(p, i) + iszero(c) ? zero(R) : R(g(c)) + end for i in 0:degree(p)] + return Rx(new_coefficients) +end + ############################################################################### # # Unsafe functions diff --git a/src/NCRings.jl b/src/NCRings.jl index 3eb31646e6..6622914875 100644 --- a/src/NCRings.jl +++ b/src/NCRings.jl @@ -6,13 +6,56 @@ include("Rings.jl") -############################################################################### +################################################################################ # # Promotion system # -############################################################################### +# The promote_rule functions are not extending Base.promote_rule. The +# AbstractAlgebra promotion system is orthogonal to the built-in julia promotion +# system. The julia system assumes that whenever you have a method signature of +# the form Base.promote_rule(::Type{T}, ::Type{S}) = R, then there is also a +# corresponding Base.convert(::Type{R}, ::T) and similar for S. Since we +# cannot use the julia convert system (we need an instance of the type and not +# the type), we cannot use the julia promotion system. +# +# The AbstractAlgebra promotion system is used to define catch all functions for +# arithmetic between arbitrary ring elements. +# +# TODO: move this to NCRing.jl +# +################################################################################ + +promote_rule(::Type{T}, ::Type{T}) where T <: NCRingElement = T + +function promote_rule_sym(::Type{T}, ::Type{S}) where {T, S} + U = promote_rule(T, S) + if U !== Union{} + return U + else + UU = promote_rule(S, T) + return UU + end +end -promote_rule(::Type{T}, ::Type{T}) where T <: NCRingElem = T +@inline function try_promote(x::S, y::T) where {S <: NCRingElem, T <: NCRingElem} + U = promote_rule_sym(S, T) + if S === U + return true, x, parent(x)(y) + elseif T === U + return true, parent(y)(x), y + else + return false, x, y + end +end + +function Base.promote(x::S, y::T) where {S <: NCRingElem, T <: NCRingElem} + fl, u, v = try_promote(x, y) + if fl + return u, v + else + error("Cannot promote to common type") + end +end ############################################################################### # @@ -38,6 +81,19 @@ promote_rule(::Type{T}, ::Type{T}) where T <: NCRingElem = T *(x::NCRingElement, y::NCRingElem) = parent(y)(x)*y +function ==(x::NCRingElem, y::NCRingElem) + fl, u, v = try_promote(x, y) + if fl + return u == v + else + return false + end + end + + ==(x::NCRingElem, y::NCRingElement) = x == parent(x)(y) + + ==(x::NCRingElement, y::NCRingElem) = parent(y)(x) == y + function divexact_left(x::NCRingElem, y::NCRingElem; check::Bool = true) return divexact_left(promote(x, y)...) end @@ -62,14 +118,6 @@ function divexact_right( return divexact_right(x, parent(x)(y); check = check) end -function ==(x::S, y::T) where {S <: NCRingElem, T <: NCRingElem} - if S == promote_rule(S, T) - ==(x, parent(x)(y)) - else - ==(parent(y)(x), y) - end -end - Base.literal_pow(::typeof(^), x::NCRingElem, ::Val{p}) where {p} = x^p function check_parent(a::NCRingElement, b::NCRingElement, throw::Bool=true) @@ -145,6 +193,28 @@ one(x::NCRingElem) = one(parent(x)) zero(x::NCRingElem) = zero(parent(x)) +############################################################################### +# +# Delayed reduction +# +############################################################################### + +# Fall back to ordinary multiplication +function mul_red!(a::T, b::T, c::T, flag::Bool) where T <: NCRingElement + return mul!(a, b, c) +end + +# Define addmul_delayed_reduction! for all ring elem types +function addmul_delayed_reduction!(a::T, b::T, c::T, d::T) where T <: NCRingElement + d = mul_red!(d, b, c, false) + return addeq!(a, d) +end + +# Fall back to nop +function reduce!(a::NCRingElement) + return a +end + ############################################################################### # # Baby-steps giant-steps powering diff --git a/src/Rings.jl b/src/Rings.jl index 9bfce6ebfa..8b7c474a80 100644 --- a/src/Rings.jl +++ b/src/Rings.jl @@ -8,81 +8,6 @@ function isequal(a::RingElem, b::RingElem) return parent(a) == parent(b) && a == b end -################################################################################ -# -# Promotion system -# -# The promote_rule functions are not extending Base.promote_rule. The -# AbstractAlgebra promotion system is orthogonal to the built-in julia promotion -# system. The julia system assumes that whenever you have a method signature of -# the form Base.promote_rule(::Type{T}, ::Type{S}) = R, then there is also a -# corresponding Base.convert(::Type{R}, ::T) and similar for S. Since we -# cannot use the julia convert system (we need an instance of the type and not -# the type), we cannot use the julia promotion system. -# -# The AbstractAlgebra promotion system is used to define catch all functions for -# arithmetic between arbitrary ring elements. -# -# TODO: move this to NCRing.jl -# -################################################################################ - -promote_rule(::Type{T}, ::Type{T}) where T <: NCRingElement = T - -function promote_rule_sym(::Type{T}, ::Type{S}) where {T, S} - U = promote_rule(T, S) - if U !== Union{} - return U - else - UU = promote_rule(S, T) - return UU - end -end - -@inline function try_promote(x::S, y::T) where {S <: NCRingElem, T <: NCRingElem} - U = promote_rule_sym(S, T) - if S === U - return true, x, parent(x)(y) - elseif T === U - return true, parent(y)(x), y - else - return false, x, y - end -end - -function Base.promote(x::S, y::T) where {S <: NCRingElem, T <: NCRingElem} - fl, u, v = try_promote(x, y) - if fl - return u, v - else - error("Cannot promote to common type") - end -end - -############################################################################### -# -# Generic catchall functions -# -############################################################################### - -+(x::RingElem, y::RingElem) = +(promote(x, y)...) - -+(x::RingElem, y::RingElement) = x + parent(x)(y) - -+(x::RingElement, y::RingElem) = parent(y)(x) + y - --(x::RingElem, y::RingElem) = -(promote(x, y)...) - --(x::RingElem, y::RingElement) = x - parent(x)(y) - --(x::RingElement, y::RingElem) = parent(y)(x) - y - -*(x::RingElem, y::RingElem) = *(promote(x, y)...) - -*(x::RingElem, y::RingElement) = x*parent(x)(y) - -*(x::RingElement, y::RingElem) = parent(y)(x)*y - """ divexact(x, y; check::Bool=true) @@ -115,20 +40,6 @@ function isdivisible_by(x::T, y::T) where T <: RingElem return iszero(r) end -function ==(x::RingElem, y::RingElem) - fl, u, v = try_promote(x, y) - if fl - return u == v - else - return false - end -end - -==(x::RingElem, y::RingElement) = x == parent(x)(y) - -==(x::RingElement, y::RingElem) = parent(y)(x) == y - - ############################################################################### # # Evaluation @@ -151,28 +62,6 @@ end Base.broadcastable(m::RingElem) = Ref(m) -############################################################################### -# -# Delayed reduction -# -############################################################################### - -# Fall back to ordinary multiplication -function mul_red!(a::T, b::T, c::T, flag::Bool) where T <: RingElement - return mul!(a, b, c) -end - -# Define addmul_delayed_reduction! for all ring elem types -function addmul_delayed_reduction!(a::T, b::T, c::T, d::T) where T <: RingElement - d = mul_red!(d, b, c, false) - return addeq!(a, d) -end - -# Fall back to nop -function reduce!(a::RingElement) - return a -end - ############################################################################### # # Type traits diff --git a/src/generic/GenericTypes.jl b/src/generic/GenericTypes.jl index c596b3cc6c..aff79b42ca 100644 --- a/src/generic/GenericTypes.jl +++ b/src/generic/GenericTypes.jl @@ -988,37 +988,39 @@ end # ############################################################################### +const NCRingElement = Union{NCRingElem, RingElement} + # All MatSpaceElem's and views thereof abstract type Mat{T} <: MatElem{T} end # not really a mathematical ring -mutable struct MatSpace{T <: RingElement} <: AbstractAlgebra.MatSpace{T} +mutable struct MatSpace{T <: NCRingElement} <: AbstractAlgebra.MatSpace{T} nrows::Int ncols::Int - base_ring::Ring + base_ring::NCRing - function MatSpace{T}(R::Ring, r::Int, c::Int, cached::Bool = true) where T <: RingElement + function MatSpace{T}(R::NCRing, r::Int, c::Int, cached::Bool = true) where T <: NCRingElement return get_cached!(MatDict, (R, r, c), cached) do new{T}(r, c, R) end::MatSpace{T} end end -const MatDict = CacheDictType{Tuple{Ring, Int, Int}, MatSpace}() +const MatDict = CacheDictType{Tuple{NCRing, Int, Int}, MatSpace}() -mutable struct MatSpaceElem{T <: RingElement} <: Mat{T} +mutable struct MatSpaceElem{T <: NCRingElement} <: Mat{T} entries::Matrix{T} - base_ring::Ring + base_ring::NCRing - function MatSpaceElem{T}(A::Matrix{T}) where T <: RingElement + function MatSpaceElem{T}(A::Matrix{T}) where T <: NCRingElement return new{T}(A) end - function MatSpaceElem{T}(A::AbstractMatrix{T}) where T <: RingElement + function MatSpaceElem{T}(A::AbstractMatrix{T}) where T <: NCRingElement return new{T}(Array(A)) end - function MatSpaceElem{T}(r::Int, c::Int, A::Vector{T}) where T <: RingElement + function MatSpaceElem{T}(r::Int, c::Int, A::Vector{T}) where T <: NCRingElement t = Array{T}(undef, r, c) for i = 1:r for j = 1:c @@ -1029,9 +1031,9 @@ mutable struct MatSpaceElem{T <: RingElement} <: Mat{T} end end -mutable struct MatSpaceView{T <: RingElement, V, W} <: Mat{T} +mutable struct MatSpaceView{T <: NCRingElement, V, W} <: Mat{T} entries::SubArray{T, 2, Matrix{T}, V, W} - base_ring::Ring + base_ring::NCRing end ############################################################################### @@ -1040,28 +1042,28 @@ end # ############################################################################### -mutable struct MatAlgebra{T <: RingElement} <: AbstractAlgebra.MatAlgebra{T} +mutable struct MatAlgebra{T <: NCRingElement} <: AbstractAlgebra.MatAlgebra{T} n::Int - base_ring::Ring + base_ring::NCRing - function MatAlgebra{T}(R::Ring, n::Int, cached::Bool = true) where T <: RingElement + function MatAlgebra{T}(R::NCRing, n::Int, cached::Bool = true) where T <: NCRingElement return get_cached!(MatAlgDict, (R, n), cached) do new{T}(n, R) end::MatAlgebra{T} end end -const MatAlgDict = CacheDictType{Tuple{Ring, Int}, NCRing}() +const MatAlgDict = CacheDictType{Tuple{NCRing, Int}, NCRing}() -mutable struct MatAlgElem{T <: RingElement} <: AbstractAlgebra.MatAlgElem{T} +mutable struct MatAlgElem{T <: NCRingElement} <: AbstractAlgebra.MatAlgElem{T} entries::Matrix{T} - base_ring::Ring + base_ring::NCRing - function MatAlgElem{T}(A::Matrix{T}) where T <: RingElement + function MatAlgElem{T}(A::Matrix{T}) where T <: NCRingElement return new{T}(A) end - function MatAlgElem{T}(n::Int, A::Vector{T}) where T <: RingElement + function MatAlgElem{T}(n::Int, A::Vector{T}) where T <: NCRingElement t = Array{T}(undef, n, n) for i = 1:n for j = 1:n diff --git a/src/generic/Matrix.jl b/src/generic/Matrix.jl index 1e3e80a342..0daa5c9d42 100644 --- a/src/generic/Matrix.jl +++ b/src/generic/Matrix.jl @@ -10,26 +10,26 @@ # ############################################################################### -parent_type(::Type{S}) where {T <: RingElement, S <: Mat{T}} = MatSpace{T} +parent_type(::Type{S}) where {T <: NCRingElement, S <: Mat{T}} = MatSpace{T} -elem_type(::Type{MatSpace{T}}) where {T <: RingElement} = MatSpaceElem{T} +elem_type(::Type{MatSpace{T}}) where {T <: NCRingElement} = MatSpaceElem{T} @doc Markdown.doc""" - parent(a::AbstractAlgebra.MatElem{T}, cached::Bool = true) where T <: RingElement + parent(a::AbstractAlgebra.MatElem{T}, cached::Bool = true) where T <: NCRingElement Return the parent object of the given matrix. """ -parent(a::Mat{T}, cached::Bool = true) where T <: RingElement = +parent(a::Mat{T}, cached::Bool = true) where T <: NCRingElement = MatSpace{T}(a.base_ring, size(a.entries)..., cached) -dense_matrix_type(::Type{T}) where T <: RingElement = MatSpaceElem{T} +dense_matrix_type(::Type{T}) where T <: NCRingElement = MatSpaceElem{T} @doc Markdown.doc""" dense_matrix_type(R::Ring) Return the type of matrices over the given ring. """ -dense_matrix_type(R::Ring) = dense_matrix_type(elem_type(R)) +dense_matrix_type(R::NCRing) = dense_matrix_type(elem_type(R)) ############################################################################### # @@ -57,7 +57,7 @@ ncols(a::Union{Mat, MatAlgElem}) = size(a.entries, 2) Base.@propagate_inbounds getindex(a::Union{Mat, MatAlgElem}, r::Int, c::Int) = a.entries[r, c] -Base.@propagate_inbounds function setindex!(a::Union{Mat, MatAlgElem}, d::RingElement, +Base.@propagate_inbounds function setindex!(a::Union{Mat, MatAlgElem}, d::NCRingElement, r::Int, c::Int) a.entries[r, c] = base_ring(a)(d) end @@ -70,7 +70,7 @@ Base.isassigned(a::Union{Mat,MatAlgElem}, i, j) = isassigned(a.entries, i, j) # ################################################################################ -function copy(d::MatSpaceElem{T}) where T <: RingElement +function copy(d::MatSpaceElem{T}) where T <: NCRingElement z = similar(d) for i = 1:nrows(d) for j = 1:ncols(d) @@ -80,7 +80,7 @@ function copy(d::MatSpaceElem{T}) where T <: RingElement return z end -function deepcopy_internal(d::MatSpaceElem{T}, dict::IdDict) where T <: RingElement +function deepcopy_internal(d::MatSpaceElem{T}, dict::IdDict) where T <: NCRingElement z = similar(d) for i = 1:nrows(d) for j = 1:ncols(d) @@ -90,7 +90,7 @@ function deepcopy_internal(d::MatSpaceElem{T}, dict::IdDict) where T <: RingElem return z end -function deepcopy_internal(d::MatSpaceView{T}, dict::IdDict) where T <: RingElement +function deepcopy_internal(d::MatSpaceView{T}, dict::IdDict) where T <: NCRingElement return MatSpaceView(deepcopy(d.entries), d.base_ring) end @@ -111,7 +111,7 @@ Base.@propagate_inbounds function getindex(M::MatElem, x::Integer) end end -function Base.view(M::Mat{T}, rows::UnitRange{Int}, cols::UnitRange{Int}) where T <: RingElement +function Base.view(M::Mat{T}, rows::UnitRange{Int}, cols::UnitRange{Int}) where T <: NCRingElement return MatSpaceView(view(M.entries, rows, cols), M.base_ring) end @@ -130,11 +130,11 @@ issquare(a::MatElem) = (nrows(a) == ncols(a)) ############################################################################### @doc Markdown.doc""" - transpose(x::Mat) + transpose(x::Mat{T}) where T <: NCRingElement Return the transpose of the given matrix. """ -function transpose(x::Mat) +function transpose(x::Mat{T}) where T <: NCRingElement y = MatSpaceElem{eltype(x)}(permutedims(x.entries)) y.base_ring = x.base_ring y @@ -146,9 +146,9 @@ end # ############################################################################### -promote_rule(::Type{S}, ::Type{S}) where {T <: RingElement, S <: Mat{T}} = MatSpaceElem{T} +promote_rule(::Type{S}, ::Type{S}) where {T <: NCRingElement, S <: Mat{T}} = MatSpaceElem{T} -function promote_rule(::Type{S}, ::Type{U}) where {T <: RingElement, S <: Mat{T}, U <: RingElement} +function promote_rule(::Type{S}, ::Type{U}) where {T <: NCRingElement, S <: Mat{T}, U <: NCRingElement} promote_rule(T, U) == T ? MatSpaceElem{T} : Union{} end @@ -158,7 +158,7 @@ end # ############################################################################### -function (a::MatSpace{T})() where {T <: RingElement} +function (a::MatSpace{T})() where {T <: NCRingElement} R = base_ring(a) entries = Array{T}(undef, a.nrows, a.ncols) for i = 1:a.nrows @@ -171,7 +171,7 @@ function (a::MatSpace{T})() where {T <: RingElement} return z end -function (a::MatSpace{T})(b::S) where {S <: RingElement, T <: RingElement} +function (a::MatSpace{T})(b::S) where {S <: NCRingElement, T <: NCRingElement} R = base_ring(a) entries = Array{T}(undef, a.nrows, a.ncols) rb = R(b) @@ -189,12 +189,12 @@ function (a::MatSpace{T})(b::S) where {S <: RingElement, T <: RingElement} return z end -function (a::MatSpace{T})(b::Mat{T}) where {T <: RingElement} +function (a::MatSpace{T})(b::Mat{T}) where {T <: NCRingElement} parent(b) != a && error("Unable to coerce matrix") return b end -function (a::MatSpace{T})(b::Matrix{T}) where T <: RingElement +function (a::MatSpace{T})(b::Matrix{T}) where T <: NCRingElement R = base_ring(a) _check_dim(a.nrows, a.ncols, b) if !isempty(b) @@ -205,7 +205,7 @@ function (a::MatSpace{T})(b::Matrix{T}) where T <: RingElement return z end -function (a::MatSpace{T})(b::AbstractMatrix{S}) where {S <: RingElement, T <: RingElement} +function (a::MatSpace{T})(b::AbstractMatrix{S}) where {S <: NCRingElement, T <: NCRingElement} R = base_ring(a) _check_dim(a.nrows, a.ncols, b) entries = Array{T}(undef, a.nrows, a.ncols) @@ -219,7 +219,7 @@ function (a::MatSpace{T})(b::AbstractMatrix{S}) where {S <: RingElement, T <: Ri return z end -function (a::MatSpace{T})(b::AbstractVector{S}) where {S <: RingElement, T <: RingElement} +function (a::MatSpace{T})(b::AbstractVector{S}) where {S <: NCRingElement, T <: NCRingElement} _check_dim(a.nrows, a.ncols, b) b = Matrix{S}(transpose(reshape(b, a.ncols, a.nrows))) z = a(b) @@ -232,7 +232,7 @@ end # ############################################################################### -function MatrixSpace(R::AbstractAlgebra.Ring, r::Int, c::Int; cached::Bool = true) +function MatrixSpace(R::AbstractAlgebra.NCRing, r::Int, c::Int; cached::Bool = true) T = elem_type(R) return MatSpace{T}(R, r, c, cached) end diff --git a/src/generic/MatrixAlgebra.jl b/src/generic/MatrixAlgebra.jl index 927feb64e6..2dc0b395a7 100644 --- a/src/generic/MatrixAlgebra.jl +++ b/src/generic/MatrixAlgebra.jl @@ -10,21 +10,21 @@ # ############################################################################### -parent_type(::Type{MatAlgElem{T}}) where T <: RingElement = MatAlgebra{T} +parent_type(::Type{MatAlgElem{T}}) where T <: NCRingElement = MatAlgebra{T} -elem_type(::Type{MatAlgebra{T}}) where {T <: RingElement} = MatAlgElem{T} +elem_type(::Type{MatAlgebra{T}}) where {T <: NCRingElement} = MatAlgElem{T} @doc Markdown.doc""" - parent(a::MatAlgElem{T}, cached::Bool = true) where T <: RingElement + parent(a::MatAlgElem{T}, cached::Bool = true) where T <: NCRingElement Return the parent object of the given matrix. """ -parent(a::MatAlgElem{T}, cached::Bool = true) where T <: RingElement = +parent(a::MatAlgElem{T}, cached::Bool = true) where T <: NCRingElement = MatAlgebra{T}(a.base_ring, size(a.entries)[1], cached) -isexact_type(::Type{MatAlgElem{T}}) where T <: RingElement = isexact_type(T) +isexact_type(::Type{MatAlgElem{T}}) where T <: NCRingElement = isexact_type(T) -isdomain_type(::Type{MatAlgElem{T}}) where T <: RingElement = false +isdomain_type(::Type{MatAlgElem{T}}) where T <: NCRingElement = false ############################################################################### # @@ -37,7 +37,7 @@ isdomain_type(::Type{MatAlgElem{T}}) where T <: RingElement = false Return the transpose of the given matrix. """ -function transpose(x::MatAlgElem{T}) where T <: RingElement +function transpose(x::MatAlgElem{T}) where T <: NCRingElement arr = permutedims(x.entries, [2, 1]) z = MatAlgElem{T}(arr) z.base_ring = base_ring(x) @@ -100,7 +100,7 @@ end # ############################################################################### -function zero!(M::MatAlgElem{T}) where T <: RingElement +function zero!(M::MatAlgElem{T}) where T <: NCRingElement n = degree(M) R = base_ring(M) for i = 1:n @@ -112,12 +112,12 @@ function zero!(M::MatAlgElem{T}) where T <: RingElement end function mul!(A::MatAlgElem{T}, B::MatAlgElem{T}, - C::MatAlgElem{T}) where T <: RingElement + C::MatAlgElem{T}) where T <: NCRingElement return B*C end function add!(A::MatAlgElem{T}, B::MatAlgElem{T}, - C::MatAlgElem{T}) where T <: RingElement + C::MatAlgElem{T}) where T <: NCRingElement n = degree(A) for i = 1:n for j = 1:n @@ -127,7 +127,7 @@ function add!(A::MatAlgElem{T}, B::MatAlgElem{T}, return A end -function addeq!(A::MatAlgElem{T}, B::MatAlgElem{T}) where T <: RingElement +function addeq!(A::MatAlgElem{T}, B::MatAlgElem{T}) where T <: NCRingElement n = degree(A) for i = 1:n for j = 1:n @@ -137,13 +137,25 @@ function addeq!(A::MatAlgElem{T}, B::MatAlgElem{T}) where T <: RingElement return A end +############################################################################### +# +# Promotion rules +# +############################################################################### + +promote_rule(::Type{S}, ::Type{S}) where {T <: NCRingElement, S <: MatAlgElem{T}} = MatAlgElem{T} + +function promote_rule(::Type{S}, ::Type{U}) where {T <: NCRingElement, S <: MatAlgElem{T}, U <: NCRingElement} + promote_rule(T, U) == T ? MatAlgElem{T} : Union{} +end + ############################################################################### # # Parent object call overload # ############################################################################### -function (a::MatAlgebra{T})() where {T <: RingElement} +function (a::MatAlgebra{T})() where {T <: NCRingElement} R = base_ring(a) entries = Array{T}(undef, a.n, a.n) for i = 1:a.n @@ -156,7 +168,7 @@ function (a::MatAlgebra{T})() where {T <: RingElement} return z end -function (a::MatAlgebra{T})(b::S) where {S <: RingElement, T <: RingElement} +function (a::MatAlgebra{T})(b::S) where {S <: NCRingElement, T <: NCRingElement} R = base_ring(a) entries = Array{T}(undef, a.n, a.n) rb = R(b) @@ -174,12 +186,12 @@ function (a::MatAlgebra{T})(b::S) where {S <: RingElement, T <: RingElement} return z end -function (a::MatAlgebra{T})(b::MatAlgElem{T}) where {T <: RingElement} +function (a::MatAlgebra{T})(b::MatAlgElem{T}) where {T <: NCRingElement} parent(b) != a && error("Unable to coerce matrix") return b end -function (a::MatAlgebra{T})(b::Matrix{S}) where {S <: RingElement, T <: RingElement} +function (a::MatAlgebra{T})(b::Matrix{S}) where {S <: NCRingElement, T <: NCRingElement} R = base_ring(a) _check_dim(a.n, a.n, b) entries = Array{T}(undef, a.n, a.n) @@ -193,7 +205,7 @@ function (a::MatAlgebra{T})(b::Matrix{S}) where {S <: RingElement, T <: RingElem return z end -function (a::MatAlgebra{T})(b::Vector{S}) where {S <: RingElement, T <: RingElement} +function (a::MatAlgebra{T})(b::Vector{S}) where {S <: NCRingElement, T <: NCRingElement} _check_dim(a.n, a.n, b) b = Matrix{S}(transpose(reshape(b, a.n, a.n))) z = a(b) @@ -206,7 +218,7 @@ end # ############################################################################### -function MatrixAlgebra(R::AbstractAlgebra.Ring, n::Int; cached::Bool = true) +function MatrixAlgebra(R::AbstractAlgebra.NCRing, n::Int; cached::Bool = true) T = elem_type(R) return MatAlgebra{T}(R, n, cached) end diff --git a/test/generic/Matrix-test.jl b/test/generic/Matrix-test.jl index 20901ee22f..2ecdf05c9b 100644 --- a/test/generic/Matrix-test.jl +++ b/test/generic/Matrix-test.jl @@ -303,6 +303,57 @@ end @test block_diagonal_matrix(ZZ, Matrix{Int}[]) == matrix(ZZ, 0, 0, []) @test block_diagonal_matrix(ZZ, [M1, N1, K1]) == block_diagonal_matrix([M, N, K]) @test block_diagonal_matrix(ZZ, [K1]) == block_diagonal_matrix([K]) + + # Test constructors over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + @test isa(S, MatSpace) + + @test base_ring(S) == R + + @test elem_type(S) == Generic.MatSpaceElem{elem_type(R)} + @test elem_type(Generic.MatSpace{elem_type(R)}) == Generic.MatSpaceElem{elem_type(R)} + @test parent_type(Generic.MatSpaceElem{elem_type(R)}) == Generic.MatSpace{elem_type(R)} + + @test dense_matrix_type(R) == elem_type(S) + + @test isa(S(), MatElem) + @test isa(S(ZZ(1)), MatElem) + @test isa(S(one(R)), MatElem) + @test isa(S([1 2; 3 4]), MatElem) + @test isa(S([1, 2, 3, 4]), MatElem) + + @test parent(S()) == S + + @test isa(zero_matrix(R, 2, 2), MatElem) + @test isa(identity_matrix(R, 2), MatElem) + + @test isa(matrix(R, 2, 2, [1 2; 3 4]), MatElem) + + # these are not supported + # @test isa(matrix(R, 2, 2, [[1 2; 3 4] [2 3; 4 5]; [3 4; 5 6] [4 5; 6 7]]), MatElem) + # @test isa(matrix(R, 2, 2, [matrix(R, [1 2; 3 4]) matrix(R, [2 3; 3 4]); matrix(R, [3 4; 5 6]) matrix(R, [4 5; 6 7])]), MatElem) + # @test isa(matrix(R, matrix(R, [R([1 2; 3 4]) R([2 3; 3 4]); R([3 4; 5 6]) R([4 5; 6 7])]), MatElem) + + A = [1 2; 3 4] + B = [2 3; 4 5] + C = [3 4; 5 6] + D = [4 5; 6 7] + + RA = R(A) + RB = R(B) + RC = R(C) + RD = R(D) + + @test isa(matrix(R, 2, 2, [A, B, C, D]), MatElem) + @test isa(matrix(R, 2, 2, [RA, RB, RC, RD]), MatElem) + + @test isa(block_diagonal_matrix(R, [A, B]), MatElem) + + # the following is not supported + # @test isa(block_diagonal_matrix([RA, RB]), MatElem) end @testset "Generic.Mat.size/axes" begin @@ -352,6 +403,21 @@ end @test_throws ErrorException lastindex(B, 3) @test !issquare(B) + + # test over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + M = rand(S, -10:10) + + @test firstindex(M, 1) == 1 + @test lastindex(M, 1) == 2 + @test size(M) == (2, 2) + @test size(M, 1) == 2 + @test axes(M) == (1:2, 1:2) + @test axes(M, 1) == 1:2 + @test issquare(M) end @testset "Generic.Mat.manipulation" begin @@ -469,6 +535,32 @@ end @test eltype(m) == BigInt @test eltype(typeof(m)) == BigInt end + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + M = rand(S, -10:10) + + @test isa(hash(M), UInt) + @test nrows(M) == 2 + @test ncols(M) == 2 + @test length(M) == 4 + @test isempty(M) == false + @test isassigned(M, 1, 1) == true + + @test iszero(zero(M, 3, 3)) + @test iszero(zero(M, QQ, 3, 3)) + @test iszero(zero(M, QQ)) + + zero!(M) + @test iszero(M) + + @test isone(one(R)) + + @test iszero_row(M, 1) + @test iszero_column(M, 1) end @testset "Generic.Mat.unary_ops" begin @@ -523,6 +615,15 @@ end z = zero_matrix(F2(), 2, 3) @test -z isa F2Matrix @test -z.m isa Generic.MatSpaceElem{F2Elem} + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + M = rand(S, -10:10) + + @test -(-M) == M end @testset "Generic.Mat.getindex" begin @@ -981,6 +1082,19 @@ end @test A * B == S(A.entries * B.entries) end end + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + M = rand(S, -10:10) + N = rand(S, -10:10) + P = rand(S, -10:10) + + @test M + N == N + M + @test M - N == M + (-N) + @test M*(N + P) == M*N + M*P end # add x to all the elements of the main diagonal of a copy of M @@ -1132,6 +1246,39 @@ add_diag(M::Matrix, x) = [i != j ? M[i, j] : M[i, j] + x for (i, j) in Tuple.(Ca end _test_matrix_vector_prod(R, -1000:1000) + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + M = rand(S, -10:10) + N = rand(S, -10:10) + + t1 = rand(ZZ, -10:10) + t2 = rand(R, -10:10) + + @test t1*(M + N) == t1*M + t1*N + @test t1*(M - N) == t1*M - t1*N + @test (M + N)*t1 == M*t1 + N*t1 + @test (M - N)*t1 == M*t1 - N*t1 + + @test t2*(M + N) == t2*M + t2*N + @test t2*(M - N) == t2*M - t2*N + @test (M + N)*t2 == M*t2 + N*t2 + @test (M - N)*t2 == M*t2 - N*t2 + + @test M + t1 == M - (-t1) + @test M + t2 == M - (-t2) + + @test t1 + M == t1 - (-M) + @test t2 + M == t2 - (-M) + + r1 = rand(R, -10:10) + r2 = rand(R, -10:10) + + @test (M + N)*[r1, r2] == M*[r1, r2] + N*[r1, r2] + @test [r1, r2]*(M + N) == [r1, r2]*M + [r1, r2]*N end @testset "Generic.Mat.promotion" begin @@ -1231,6 +1378,21 @@ end @test matrix(R, copy(A.entries)) == A end + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + M = rand(S, -10:10) + N = deepcopy(M) + + @test M == M + @test M == N + @test M == copy(M) + @test isequal(M, M) + @test isequal(M, N) + @test isequal(M, copy(M)) end @testset "Generic.Mat.adhoc_comparison" begin @@ -1249,6 +1411,21 @@ end @test t + 1 == S(t + 1) @test A != one(S) @test one(S) == one(S) + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + @test S(5) == 5 + @test 5 == S(5) + @test S(BigInt(5)) == 5 + @test 5 == S(BigInt(5)) + + m = rand(R, -10:10) + + @test S(m) == m + @test m == S(m) end @testset "Generic.Mat.powering" begin @@ -1266,6 +1443,18 @@ end A = S(Rational{BigInt}[2 3; 7 -4]) @test A^-1 == inv(A) + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + M = rand(S, -10:10) + + @test M^0 == one(S) + @test M^1 == M + @test M^2 == M*M + @test M^3 == M*M*M end @testset "Generic.Mat.adhoc_exact_division" begin @@ -1278,6 +1467,28 @@ end @test divexact(12*A, BigInt(12)) == A @test divexact(12*A, Rational{BigInt}(12)) == A @test divexact((1 + t)*A, 1 + t) == A + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + U, x = PolynomialRing(R, "x") + + S = MatrixSpace(R, 2, 2) + T = MatrixSpace(U, 2, 2) + + M = rand(S, -10:10) + + @test divexact(5*M, 5) == M + + c = rand(R, -10:10) + + @test divexact_left(c*M, c) == M + @test divexact_right(M*c, c) == M + + N = rand(T, 0:5, -10:10) + d = rand(U, 0:5, -10:10) + + @test divexact_left(d*N, d) == N + @test divexact_right(N*d, d) == N end @testset "Generic.Mat.issymmetric" begin @@ -1288,6 +1499,15 @@ end S = MatrixAlgebra(R, 3) @test issymmetric(S([t + 1 t R(1); t t^2 t; R(1) t R(5)])) @test !issymmetric(S([t + 1 t R(1); t + 1 t^2 t; R(1) t R(5)])) + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + M = rand(S, -10:10) + + @test issymmetric(M + transpose(M)) end @testset "Generic.Mat.transpose" begin @@ -3202,6 +3422,18 @@ end 3 4 3 4 1; 0 1 0 1 0; 0 1 0 1 2;]) + + # Test constructors over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 2, 2) + + M = rand(S, -10:10) + N = rand(S, -10:10) + + @test isa(hcat(M, N), MatElem) + @test isa(vcat(M, N), MatElem) + @test isa([M N; M N], MatElem) end @testset "Generic.Mat.hnf_minors" begin @@ -3653,6 +3885,21 @@ end @test fflu(N3) == fflu(M) # tests that deepcopy is correct @test M2 == M + + # Test views over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixSpace(R, 4, 4) + + M = rand(S, -10:10) + + N1 = @view M[:,1:2] + N2 = @view M[1:2, :] + N3 = @view M[:,:] + + @test isa(N1, Generic.MatSpaceView) + @test isa(N2, Generic.MatSpaceView) + @test isa(N3, Generic.MatSpaceView) end @testset "Generic.Mat.change_base_ring" begin @@ -3677,6 +3924,17 @@ end z = zero_matrix(F2(), 2, 3) @test change_base_ring(F2(), z) isa F2Matrix @test change_base_ring(F2(), z.m) isa F2Matrix + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + U, x = PolynomialRing(R, "x") + S = MatrixSpace(R, 2, 2) + + M = rand(S, -10:10) + + N = change_base_ring(U, M) + + @test isa(N, MatElem) end @testset "Generic.Mat.map" begin @@ -3721,6 +3979,20 @@ end z = zero_matrix(F2(), 2, 3) @test map(identity, z) isa F2Matrix @test map(identity, z.m) isa F2Matrix + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + U, x = PolynomialRing(R, "x") + S = MatrixSpace(R, 2, 2) + T = MatrixSpace(U, 2, 2) + + M = rand(S, -10:10) + N = rand(T, 0:5, -10:10) + P = map(x->x^2, M) + Q = map(x->x^2, N) + + @test isa(P, MatElem) + @test isa(Q, MatElem) end @testset "Generic.Mat.similar/zero" begin diff --git a/test/generic/MatrixAlgebra-test.jl b/test/generic/MatrixAlgebra-test.jl index eee8da27c7..27fd47c2ef 100644 --- a/test/generic/MatrixAlgebra-test.jl +++ b/test/generic/MatrixAlgebra-test.jl @@ -61,6 +61,30 @@ end @test_throws ErrorConstrDimMismatch S([t t^2 t^3 ; t^4 t^5 t^6 ; t^7 t^8 t^9 ; t t^2 t^3]) @test_throws ErrorConstrDimMismatch S([t, t^2]) @test_throws ErrorConstrDimMismatch S([t, t^2, t^3, t^4, t^5, t^6, t^7, t^8, t^9, t^10]) + + # Test constructors over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixAlgebra(R, 2) + + @test isa(S, MatAlgebra) + + @test base_ring(S) == R + + @test elem_type(S) == Generic.MatAlgElem{elem_type(R)} + @test elem_type(Generic.MatAlgebra{elem_type(R)}) == Generic.MatAlgElem{elem_type(R)} + @test parent_type(Generic.MatAlgElem{elem_type(R)}) == Generic.MatAlgebra{elem_type(R)} + + @test isexact_type(elem_type(S)) == true + @test isdomain_type(elem_type(S)) == false + + @test isa(S(), MatAlgElem) + @test isa(S(ZZ(1)), MatAlgElem) + @test isa(S(one(R)), MatAlgElem) + @test isa(S([1 2; 3 4]), MatAlgElem) + @test isa(S([1, 2, 3, 4]), MatAlgElem) + + @test parent(S()) == S end @testset "Generic.MatAlg.manipulation" begin @@ -119,6 +143,41 @@ end @test nrows(A) == ncols(A) == 3 @test degree(A) == 3 + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixAlgebra(R, 2) + + M = rand(S, -10:10) + + @test isa(hash(M), UInt) + @test nrows(M) == 2 + @test ncols(M) == 2 + @test length(M) == 4 + @test isempty(M) == false + @test isassigned(M, 1, 1) == true + + @test iszero(zero(M, 3, 3)) + @test iszero(zero(M, QQ, 3, 3)) + @test iszero(zero(M, QQ)) + + zero!(M) + @test iszero(M) + + @test isone(one(R)) + + @test iszero_row(M, 1) + @test iszero_column(M, 1) + + @test degree(M) == 2 +end + +@testset "Generic.MatAlg.size/axes" begin + R, t = PolynomialRing(QQ, "t") + S = MatrixAlgebra(R, 3) + + A = S([t + 1 t R(1); t^2 t t; R(-2) t + 2 t^2 + t + 1]) + @test issquare(A) @test size(A) == (3, 3) @test size(A, 1) == 3 @@ -131,6 +190,21 @@ end @test axes(A, rand(3:99)) == 1:1 @test_throws BoundsError axes(A, 0) @test_throws BoundsError axes(A, -rand(1:99)) + + # test over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixAlgebra(R, 2) + + M = rand(S, -10:10) + + @test firstindex(M, 1) == 1 + @test lastindex(M, 1) == 2 + @test size(M) == (2, 2) + @test size(M, 1) == 2 + @test axes(M) == (1:2, 1:2) + @test axes(M, 1) == 1:2 + @test issquare(M) end @testset "Generic.MatAlg.unary_ops" begin @@ -141,6 +215,15 @@ end B = S([-t - 1 (-t) -R(1); -t^2 (-t) (-t); -R(-2) (-t - 2) (-t^2 - t - 1)]) @test -A == B + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixAlgebra(R, 2) + + M = rand(S, -10:10) + + @test -(-M) == M end @testset "Generic.MatAlg.binary_ops" begin @@ -155,6 +238,19 @@ end @test A - B == S([t-1 t-3 R(0); t^2 - t R(-1) R(-2); R(-1) (-t^2 + t + 2) (-t^3 + t^2 + t + 1)]) @test A*B == S([t^2 + 2*t + 1 2*t^2 + 4*t + 3 t^3 + t^2 + 3*t + 1; 3*t^2 - t (t^3 + 4*t^2 + t) t^4 + 2*t^2 + 2*t; t-5 t^4 + t^3 + 2*t^2 + 3*t - 4 t^5 + 1*t^4 + t^3 + t^2 + 4*t + 2]) + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixAlgebra(R, 2) + + M = rand(S, -10:10) + N = rand(S, -10:10) + P = rand(S, -10:10) + + @test M + N == N + M + @test M - N == M + (-N) + @test M*(N + P) == M*N + M*P end @testset "Generic.MatAlg.adhoc_binary" begin @@ -175,6 +271,39 @@ end @test BigInt(3)*A == A*BigInt(3) @test Rational{BigInt}(3)*A == A*Rational{BigInt}(3) @test (t - 1)*A == A*(t - 1) + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixAlgebra(R, 2) + + M = rand(S, -10:10) + N = rand(S, -10:10) + + t1 = rand(ZZ, -10:10) + t2 = rand(R, -10:10) + + @test t1*(M + N) == t1*M + t1*N + @test t1*(M - N) == t1*M - t1*N + @test (M + N)*t1 == M*t1 + N*t1 + @test (M - N)*t1 == M*t1 - N*t1 + + @test t2*(M + N) == t2*M + t2*N + @test t2*(M - N) == t2*M - t2*N + @test (M + N)*t2 == M*t2 + N*t2 + @test (M - N)*t2 == M*t2 - N*t2 + + @test M + t1 == M - (-t1) + @test M + t2 == M - (-t2) + + @test t1 + M == t1 - (-M) + @test t2 + M == t2 - (-M) + + r1 = rand(R, -10:10) + r2 = rand(R, -10:10) + + @test (M + N)*[r1, r2] == M*[r1, r2] + N*[r1, r2] + @test [r1, r2]*(M + N) == [r1, r2]*M + [r1, r2]*N end @testset "Generic.MatAlg.promotion" begin @@ -230,6 +359,22 @@ end @test A == B @test A != one(S) + + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixAlgebra(R, 2) + + M = rand(S, -10:10) + N = deepcopy(M) + + @test M == M + @test M == N + @test M == copy(M) + @test isequal(M, M) + @test isequal(M, N) + @test isequal(M, copy(M)) end @testset "Generic.MatAlg.adhoc_comparison" begin @@ -248,6 +393,21 @@ end @test t + 1 == S(t + 1) @test A != one(S) @test one(S) == one(S) + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixAlgebra(R, 2) + + @test S(5) == 5 + @test 5 == S(5) + @test S(BigInt(5)) == 5 + @test 5 == S(BigInt(5)) + + m = rand(R, -10:10) + + @test S(m) == m + @test m == S(m) end @testset "Generic.MatAlg.powering" begin @@ -259,6 +419,18 @@ end @test A^5 == A^2*A^3 @test A^0 == one(S) + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixAlgebra(R, 2) + + M = rand(S, -10:10) + + @test M^0 == one(S) + @test M^1 == M + @test M^2 == M*M + @test M^3 == M*M*M end @testset "Generic.MatAlg.exact_division" begin @@ -281,6 +453,28 @@ end @test divexact(12*A, BigInt(12)) == A @test divexact(12*A, Rational{BigInt}(12)) == A @test divexact((1 + t)*A, 1 + t) == A + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + U, x = PolynomialRing(R, "x") + + S = MatrixAlgebra(R, 2) + T = MatrixAlgebra(U, 2) + + M = rand(S, -10:10) + + @test divexact(5*M, 5) == M + + c = rand(R, 1:10) + + @test divexact_left(c*M, c) == M + @test divexact_right(M*c, c) == M + + N = rand(T, 0:5, -10:10) + d = rand(U, 0:5, -10:10) + + @test divexact_left(d*N, d) == N + @test divexact_right(N*d, d) == N end @testset "Generic.MatAlg.transpose" begin @@ -290,6 +484,15 @@ end A = S(arr) B = S(permutedims(arr, [2, 1])) @test transpose(A) == B + + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + + S = MatrixAlgebra(R, 2) + + M = rand(S, -10:10) + + @test issymmetric(M + transpose(M)) end @testset "Generic.MatAlg.gram" begin @@ -1324,6 +1527,35 @@ end end end +@testset "Generic.MatAlg.change_base_ring" begin + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + U, x = PolynomialRing(R, "x") + S = MatrixAlgebra(R, 2) + + M = rand(S, -10:10) + + N = change_base_ring(U, M) + + @test isa(N, MatAlgElem) +end + +@testset "Generic.MatAlg.map" begin + # Tests over noncommutative ring + R = MatrixAlgebra(ZZ, 2) + U, x = PolynomialRing(R, "x") + S = MatrixAlgebra(U, 2) + + M = rand(R, -10:10) + N = map(U, M) + P = map(x->x^2, M) + Q = map(S, M) + + @test isa(N, MatAlgElem) + @test isa(P, MatAlgElem) + @test isa(Q, MatAlgElem) +end + @testset "Generic.MatAlg.rand" begin M = MatrixAlgebra(ZZ, 3)