diff --git a/docs/src/matrix.md b/docs/src/matrix.md index f628057f6e..4a408d4337 100644 --- a/docs/src/matrix.md +++ b/docs/src/matrix.md @@ -230,10 +230,10 @@ julia> multiply_row(M, 2, 3) ## Swapping rows and columns ```@docs -swap_rows(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement -swap_rows!(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement -swap_cols(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement -swap_cols!(a::MatrixElem{T}, i::Int, j::Int) where T <: RingElement +swap_rows(a::MatElem{T}, i::Int, j::Int) where T <: RingElement +swap_rows!(a::MatElem{T}, i::Int, j::Int) where T <: RingElement +swap_cols(a::MatElem{T}, i::Int, j::Int) where T <: RingElement +swap_cols!(a::MatElem{T}, i::Int, j::Int) where T <: RingElement ``` Swap the rows of `M` in place. The function returns the mutated matrix (since @@ -634,7 +634,7 @@ julia> U*A ### Smith normal form ```@docs -is_snf(::MatrixElem{T}) where T <: RingElement +is_snf(::MatElem{T}) where T <: RingElement snf(::MatElem{T}) where T <: RingElem snf_with_transform(::MatElem{T}) where T <: RingElem diff --git a/src/Matrix.jl b/src/Matrix.jl index 8adafc9e6d..efb8d020ea 100644 --- a/src/Matrix.jl +++ b/src/Matrix.jl @@ -5189,23 +5189,23 @@ end # Kannan-Bachem algorithm @doc raw""" - hnf_kb(A::MatrixElem{T}) where {T <: RingElement} + hnf_kb(A::MatElem{T}) where {T <: RingElement} Compute the upper right row Hermite normal form of $A$ using a modification of the algorithm of Kannan-Bachem. """ -function hnf_kb(A::MatrixElem{T}) where {T <: RingElement} +function hnf_kb(A::MatElem{T}) where {T <: RingElement} return _hnf_kb(A, Val(false)) end @doc raw""" - hnf_kb_with_transform(A::MatrixElem{T}) where {T <: RingElement} + hnf_kb_with_transform(A::MatElem{T}) where {T <: RingElement} Compute the upper right row Hermite normal form $H$ of $A$ and an invertible matrix $U$ with $UA = H$ using a modification of the algorithm of Kannan-Bachem. """ -function hnf_kb_with_transform(A::MatrixElem{T}) where {T <: RingElement} +function hnf_kb_with_transform(A::MatElem{T}) where {T <: RingElement} return _hnf_kb(A, Val(true)) end @@ -5235,7 +5235,7 @@ function kb_search_first_pivot(H, start_element::Int = 1) end # Reduces the entries above H[pivot[c], c] -function kb_reduce_column!(H::MatrixElem{T}, U::MatrixElem{T}, pivot::Vector{Int}, c::Int, with_trafo::Bool, start_element::Int = 1) where {T <: RingElement} +function kb_reduce_column!(H::MatElem{T}, U::MatElem{T}, pivot::Vector{Int}, c::Int, with_trafo::Bool, start_element::Int = 1) where {T <: RingElement} # Let c = 4 and pivot[c] = 4. H could look like this: # ( 0 . * # * ) @@ -5289,7 +5289,7 @@ function kb_canonical_row!(H, U, r::Int, c::Int, with_trafo::Bool) return nothing end -function kb_sort_rows!(H::MatrixElem{T}, U::MatrixElem{T}, pivot::Vector{Int}, with_trafo::Bool, start_element::Int = 1) where {T <:RingElement} +function kb_sort_rows!(H::MatElem{T}, U::MatElem{T}, pivot::Vector{Int}, with_trafo::Bool, start_element::Int = 1) where {T <:RingElement} m = nrows(H) n = ncols(H) pivot2 = zeros(Int, m) @@ -5325,7 +5325,7 @@ function kb_sort_rows!(H::MatrixElem{T}, U::MatrixElem{T}, pivot::Vector{Int}, w return nothing end -function hnf_kb!(H::MatrixElem{T}, U::MatrixElem{T}, with_trafo::Bool = false, start_element::Int = 1) where {T <: RingElement} +function hnf_kb!(H::MatElem{T}, U::MatElem{T}, with_trafo::Bool = false, start_element::Int = 1) where {T <: RingElement} m = nrows(H) n = ncols(H) pivot = zeros(Int, n) # pivot[j] == i if the pivot of column j is in row i @@ -5407,30 +5407,30 @@ function hnf_kb!(H::MatrixElem{T}, U::MatrixElem{T}, with_trafo::Bool = false, s end @doc raw""" - hnf(A::MatrixElem{T}) where {T <: RingElement} + hnf(A::MatElem{T}) where {T <: RingElement} Return the upper right row Hermite normal form of $A$. """ -function hnf(A::MatrixElem{T}) where {T <: RingElement} +function hnf(A::MatElem{T}) where {T <: RingElement} return hnf_kb(A) end @doc raw""" - hnf_with_transform(A) + hnf_with_transform(A::MatElem{T}) where {T <: RingElement} Return the tuple $H, U$ consisting of the upper right row Hermite normal form $H$ of $A$ together with invertible matrix $U$ such that $UA = H$. """ -function hnf_with_transform(A) +function hnf_with_transform(A::MatElem{T}) where {T <: RingElement} return hnf_kb_with_transform(A) end @doc raw""" - is_hnf(M::MatrixElem{T}) where T <: RingElement + is_hnf(M::MatElem{T}) where T <: RingElement Return `true` if the matrix is in Hermite normal form. """ -function is_hnf(M::MatrixElem{T}) where T <: RingElement +function is_hnf(M::MatElem{T}) where T <: RingElement r = nrows(M) c = ncols(M) row = 1 @@ -5480,11 +5480,11 @@ end ############################################################################### @doc raw""" - is_snf(A::MatrixElem{T}) where T <: RingElement + is_snf(A::MatElem{T}) where T <: RingElement Return `true` if $A$ is in Smith Normal Form. """ -function is_snf(A::MatrixElem{T}) where T <: RingElement +function is_snf(A::MatElem{T}) where T <: RingElement m = nrows(A) n = ncols(A) a = A[1, 1] @@ -5508,15 +5508,15 @@ function is_snf(A::MatrixElem{T}) where T <: RingElement return true end -function snf_kb(A::MatrixElem{T}) where {T <: RingElement} +function snf_kb(A::MatElem{T}) where {T <: RingElement} return _snf_kb(A, Val(false)) end -function snf_kb_with_transform(A::MatrixElem{T}) where {T <: RingElement} +function snf_kb_with_transform(A::MatElem{T}) where {T <: RingElement} return _snf_kb(A, Val(true)) end -function _snf_kb(A::MatrixElem{T}, ::Val{with_transform} = Val(false)) where {T <: RingElement, with_transform} +function _snf_kb(A::MatElem{T}, ::Val{with_transform} = Val(false)) where {T <: RingElement, with_transform} S = deepcopy(A) m = nrows(S) n = ncols(S) @@ -5533,7 +5533,7 @@ function _snf_kb(A::MatrixElem{T}, ::Val{with_transform} = Val(false)) where {T end end -function kb_clear_row!(S::MatrixElem{T}, K::MatrixElem{T}, i::Int, with_trafo::Bool) where {T <: RingElement} +function kb_clear_row!(S::MatElem{T}, K::MatElem{T}, i::Int, with_trafo::Bool) where {T <: RingElement} m = nrows(S) n = ncols(S) t = base_ring(S)() @@ -5570,7 +5570,7 @@ function kb_clear_row!(S::MatrixElem{T}, K::MatrixElem{T}, i::Int, with_trafo::B return nothing end -function snf_kb!(S::MatrixElem{T}, U::MatrixElem{T}, K::MatrixElem{T}, with_trafo::Bool = false) where {T <: RingElement} +function snf_kb!(S::MatElem{T}, U::MatElem{T}, K::MatElem{T}, with_trafo::Bool = false) where {T <: RingElement} m = nrows(S) n = ncols(S) l = min(m, n) @@ -5630,21 +5630,21 @@ function snf_kb!(S::MatrixElem{T}, U::MatrixElem{T}, K::MatrixElem{T}, with_traf end @doc raw""" - snf(A::MatrixElem{T}) where {T <: RingElement} + snf(A::MatElem{T}) where {T <: RingElement} Return the Smith normal form of $A$. """ -function snf(a::MatrixElem{T}) where {T <: RingElement} - return snf_kb(a) +function snf(A::MatElem{T}) where {T <: RingElement} + return snf_kb(A) end @doc raw""" - snf_with_transform(A) + snf_with_transform(A::MatElem{T}) where {T <: RingElement} Return the tuple $S, T, U$ consisting of the Smith normal form $S$ of $A$ together with invertible matrices $T$ and $U$ such that $TAU = S$. """ -function snf_with_transform(a::MatrixElem{T}) where {T <: RingElement} +function snf_with_transform(a::MatElem{T}) where {T <: RingElement} return snf_kb_with_transform(a) end @@ -6352,10 +6352,10 @@ end ############################################################################### @doc raw""" - swap_rows(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement + swap_rows(a::MatElem{T}, i::Int, j::Int) where T <: NCRingElement -Return a matrix $b$ with the entries of $a$, where the $i$th and $j$th -row are swapped. +Return a matrix $b$ with the entries of $a$, where the $i$-th and $j$-th +rows are swapped. # Examples ```jldoctest @@ -6375,7 +6375,7 @@ julia> M # was not modified [0 0 1] ``` """ -function swap_rows(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement +function swap_rows(a::MatElem{T}, i::Int, j::Int) where T <: NCRingElement (1 <= i <= nrows(a) && 1 <= j <= nrows(a)) || throw(BoundsError()) b = deepcopy(a) swap_rows!(b, i, j) @@ -6383,10 +6383,11 @@ function swap_rows(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement end @doc raw""" - swap_rows!(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement + swap_rows!(a::MatElem{T}, i::Int, j::Int) where T <: NCRingElement -Swap the $i$th and $j$th row of $a$ in place. The function returns the mutated -matrix (since matrices are assumed to be mutable in AbstractAlgebra.jl). +Swap the $i$-th and $j$-th rows of $a$ in place. The function returns the mutated +matrix (since matrices are assumed to be mutable in AbstractAlgebra.jl). It is +the caller's responsibility to ensure that the indices $i$ and $j$ are in range. # Examples ```jldoctest @@ -6406,7 +6407,7 @@ julia> M # was modified [0 0 1] ``` """ -function swap_rows!(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement +function swap_rows!(a::MatElem{T}, i::Int, j::Int) where T <: NCRingElement if i != j for k = 1:ncols(a) a[i, k], a[j, k] = a[j, k], a[i, k] @@ -6416,12 +6417,12 @@ function swap_rows!(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement end @doc raw""" - swap_cols(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement + swap_cols(a::MatElem{T}, i::Int, j::Int) where T <: NCRingElement -Return a matrix $b$ with the entries of $a$, where the $i$th and $j$th -row are swapped. +Return a matrix $b$ with the entries of $a$, where the $i$-th and $j$-th +columns are swapped. """ -function swap_cols(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement +function swap_cols(a::MatElem{T}, i::Int, j::Int) where T <: NCRingElement (1 <= i <= ncols(a) && 1 <= j <= ncols(a)) || throw(BoundsError()) b = deepcopy(a) swap_cols!(b, i, j) @@ -6429,12 +6430,13 @@ function swap_cols(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement end @doc raw""" - swap_cols!(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement + swap_cols!(a::MatElem{T}, i::Int, j::Int) where T <: NCRingElement -Swap the $i$th and $j$th column of $a$ in place. The function returns the mutated -matrix (since matrices are assumed to be mutable in AbstractAlgebra.jl). +Swap the $i$-th and $j$-th columns of $a$ in place. The function returns the mutated +matrix (since matrices are assumed to be mutable in AbstractAlgebra.jl). It is +the caller's responsibility to ensure that the indices $i$ and $j$ are in range. """ -function swap_cols!(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement +function swap_cols!(a::MatElem{T}, i::Int, j::Int) where T <: NCRingElement if i != j for k = 1:nrows(a) a[k, i], a[k, j] = a[k, j], a[k, i] @@ -6444,12 +6446,13 @@ function swap_cols!(a::MatrixElem{T}, i::Int, j::Int) where T <: NCRingElement end @doc raw""" - reverse_rows!(a::MatrixElem{T}) where T <: NCRingElement + reverse_rows!(a::MatElem{T}) where T <: NCRingElement -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$. +Swap the $i$-th and $(r - i)$-th rows of $a$ for each $1 \leq i \leq r/2$, +where $r$ is the number of rows of $a$. The swaps are performed in place. +The return value is the modified matrix. """ -function reverse_rows!(a::MatrixElem{T}) where T <: NCRingElement +function reverse_rows!(a::MatElem{T}) where T <: NCRingElement k = div(nrows(a), 2) for i in 1:k swap_rows!(a, i, nrows(a) - i + 1) @@ -6458,24 +6461,25 @@ function reverse_rows!(a::MatrixElem{T}) where T <: NCRingElement end @doc raw""" - reverse_rows(a::MatrixElem{T}) where T <: NCRingElement + reverse_rows(a::MatElem{T}) where T <: NCRingElement -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 +Return a matrix $b$ with the entries of $a$, where the $i$-th and $(r - i)$-th +rows are swapped for each $1 \leq i \leq r/2$, where $r$ is the number of rows of $a$. """ -function reverse_rows(a::MatrixElem{T}) where T <: NCRingElement +function reverse_rows(a::MatElem{T}) where T <: NCRingElement b = deepcopy(a) return reverse_rows!(b) end @doc raw""" - reverse_cols!(a::MatrixElem{T}) where T <: NCRingElement + reverse_cols!(a::MatElem{T}) where T <: NCRingElement -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$. +Swap the $i$-th and $(r - i)$-th columns of $a$ for each $1 \leq i \leq c/2$, +where $c$ is the number of columns of $a$. The swaps are performed in place. +The return value is the modified matrix. """ -function reverse_cols!(a::MatrixElem{T}) where T <: NCRingElement +function reverse_cols!(a::MatElem{T}) where T <: NCRingElement k = div(ncols(a), 2) for i in 1:k swap_cols!(a, i, ncols(a) - i + 1) @@ -6484,13 +6488,12 @@ function reverse_cols!(a::MatrixElem{T}) where T <: NCRingElement end @doc raw""" - reverse_cols(a::MatrixElem{T}) where T <: NCRingElement + reverse_cols(a::MatElem{T}) where T <: NCRingElement -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$. +Return a matrix $b$ with the entries of $a$, where the $i$-th and $(r - i)$-th +columns are swapped for each $1 \leq i \leq c/2$, where $c$ is the number of columns of $a$. """ -function reverse_cols(a::MatrixElem{T}) where T <: NCRingElement +function reverse_cols(a::MatElem{T}) where T <: NCRingElement b = deepcopy(a) return reverse_cols!(b) end @@ -6575,7 +6578,7 @@ end @doc raw""" 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$. +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. @@ -6594,7 +6597,7 @@ end @doc raw""" 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$. +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. @@ -6609,7 +6612,7 @@ end @doc raw""" 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$. +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. @@ -6628,7 +6631,7 @@ end @doc raw""" 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$. +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. diff --git a/test/generic/MatRing-test.jl b/test/generic/MatRing-test.jl index 8cc8aaa8b4..b8c25cfb44 100644 --- a/test/generic/MatRing-test.jl +++ b/test/generic/MatRing-test.jl @@ -1328,18 +1328,6 @@ end @test p1 == evaluate(p3, gen(U)) end -@testset "Generic.MatRing.row_swapping" begin - R, x = polynomial_ring(ZZ, "x") - M = matrix_ring(R, 3) - - a = M(map(R, [1 2 3; 4 5 6; 7 8 9])) - - @test swap_rows(a, 1, 3) == M(map(R, [7 8 9; 4 5 6; 1 2 3])) - - swap_rows!(a, 2, 3) - - @test a == M(map(R, [1 2 3; 7 8 9; 4 5 6])) -end if false # see bug 160 @testset "Generic.MatRing.hnf_minors" begin @@ -1378,185 +1366,6 @@ if false # see bug 160 end end -@testset "Generic.MatRing.hnf_kb" begin - R, x = polynomial_ring(QQ, "x") - - M = matrix_ring(R, 3) - - A = M(map(R, Any[0 0 0; x^3+1 x^2 0; 0 x^2 x^5])) - - H = @inferred AbstractAlgebra.hnf_kb(A) - @test is_upper_triangular(H) - - H, U = @inferred AbstractAlgebra.hnf_kb_with_transform(A) - @test is_upper_triangular(H) - @test is_unit(det(U)) - @test U*A == H - - # Fake up finite field of char 7, degree 2 - R, x = polynomial_ring(GF(7), "x") - F, = residue_field(R, x^2 + 6x + 3) - a = F(x) - - S, y = polynomial_ring(F, "y") - - N = matrix_ring(S, 3) - - B = N(map(S, Any[1 0 a; a*y^3 0 3*a^2; y^4+a 0 y^2+y])) - - H = @inferred AbstractAlgebra.hnf_kb(B) - @test is_upper_triangular(H) - - H, U = @inferred AbstractAlgebra.hnf_kb_with_transform(B) - @test is_upper_triangular(H) - @test is_unit(det(U)) - @test U*B == H -end - -@testset "Generic.MatRing.hnf_cohen" begin - R, x = polynomial_ring(QQ, "x") - - M = matrix_ring(R, 3) - - A = M(map(R, Any[0 0 0; x^3+1 x^2 0; 0 x^2 x^5])) - - H = AbstractAlgebra.hnf_cohen(A) - @test is_upper_triangular(H) - - H, U = AbstractAlgebra.hnf_cohen_with_transform(A) - @test is_upper_triangular(H) - @test is_unit(det(U)) - @test U*A == H - - # Fake up finite field of char 7, degree 2 - R, x = polynomial_ring(GF(7), "x") - F, = residue_field(R, x^2 + 6x + 3) - a = F(x) - - S, y = polynomial_ring(F, "y") - - N = matrix_ring(S, 3) - - B = N(map(S, Any[1 0 a; a*y^3 0 3*a^2; y^4+a 0 y^2+y])) - - H = AbstractAlgebra.hnf_cohen(B) - @test is_upper_triangular(H) - - H, U = AbstractAlgebra.hnf_cohen_with_transform(B) - @test is_upper_triangular(H) - @test is_unit(det(U)) - @test U*B == H -end - -@testset "Generic.MatRing.hnf" begin - R, x = polynomial_ring(QQ, "x") - - M = matrix_ring(R, 3) - - A = M(map(R, Any[0 0 0; x^3+1 x^2 0; 0 x^2 x^5])) - - H = hnf(A) - @test is_upper_triangular(H) - - H, U = hnf_with_transform(A) - @test is_upper_triangular(H) - @test is_unit(det(U)) - @test U*A == H - - # Fake up finite field of char 7, degree 2 - R, x = polynomial_ring(GF(7), "x") - F, = residue_field(R, x^2 + 6x + 3) - a = F(x) - - S, y = polynomial_ring(F, "y") - - N = matrix_ring(S, 3) - - B = N(map(S, Any[1 0 a; a*y^3 0 3*a^2; y^4+a 0 y^2+y])) - - H = hnf(B) - @test is_upper_triangular(H) - - H, U = hnf_with_transform(B) - @test is_upper_triangular(H) - @test is_unit(det(U)) - @test U*B == H -end - -@testset "Generic.MatRing.snf_kb" begin - R, x = polynomial_ring(QQ, "x") - - M = matrix_ring(R, 3) - - A = M(map(R, Any[0 0 0; x^3+1 x^2 0; 0 x^2 x^5])) - - T = @inferred AbstractAlgebra.snf_kb(A) - @test is_snf(T) - - T, U, K = @inferred AbstractAlgebra.snf_kb_with_transform(A) - @test is_snf(T) - @test is_unit(det(U)) - @test is_unit(det(K)) - @test U*A*K == T - - # Fake up finite field of char 7, degree 2 - R, x = polynomial_ring(GF(7), "x") - F, = residue_field(R, x^2 + 6x + 3) - a = F(x) - - S, y = polynomial_ring(F, "y") - - N = matrix_ring(S, 3) - - B = N(map(S, Any[1 0 a; a*y^3 0 3*a^2; y^4+a 0 y^2+y])) - - T = @inferred AbstractAlgebra.snf_kb(B) - @test is_snf(T) - - T, U, K = @inferred AbstractAlgebra.snf_kb_with_transform(B) - @test is_snf(T) - @test is_unit(det(U)) - @test is_unit(det(K)) - @test U*B*K == T -end - -@testset "Generic.MatRing.snf" begin - R, x = polynomial_ring(QQ, "x") - - M = matrix_ring(R, 3) - - A = M(map(R, Any[0 0 0; x^3+1 x^2 0; 0 x^2 x^5])) - - T = snf(A) - @test is_snf(T) - - T, U, K = snf_with_transform(A) - @test is_snf(T) - @test is_unit(det(U)) - @test is_unit(det(K)) - @test U*A*K == T - - # Fake up finite field of char 7, degree 2 - R, x = polynomial_ring(GF(7), "x") - F, = residue_field(R, x^2 + 6x + 3) - a = F(x) - - S, y = polynomial_ring(F, "y") - - N = matrix_ring(S, 3) - - B = N(map(S, Any[1 0 a; a*y^3 0 3*a^2; y^4+a 0 y^2+y])) - - T = snf(B) - @test is_snf(T) - - T, U, K = snf_with_transform(B) - @test is_snf(T) - @test is_unit(det(U)) - @test is_unit(det(K)) - @test U*B*K == T -end - @testset "Generic.MatRing.$sim_zero" for sim_zero in (similar, zero) test_zero = sim_zero === zero for R = (ZZ, GF(11))