From 1d4db522441f694a5282b853a95bb8881851f5a1 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 29 Jan 2025 23:49:07 +0100 Subject: [PATCH 1/2] Import more ConformanceTests stuff into TestExt --- ext/TestExt/Groups-conformance-tests.jl | 4 +- ext/TestExt/Mutating-ops.jl | 8 +-- ext/TestExt/Rings-conformance-tests.jl | 78 ++++++++++++------------- ext/TestExt/TestExt.jl | 19 ++++++ 4 files changed, 64 insertions(+), 45 deletions(-) diff --git a/ext/TestExt/Groups-conformance-tests.jl b/ext/TestExt/Groups-conformance-tests.jl index 5596a91f31..93948971a2 100644 --- a/ext/TestExt/Groups-conformance-tests.jl +++ b/ext/TestExt/Groups-conformance-tests.jl @@ -16,7 +16,7 @@ ############################################################################### -function ConformanceTests.test_Group_interface(G::Group) +function test_Group_interface(G::Group) @testset "Group interface" begin # @testset "Iteration protocol" begin # IS = Base.IteratorSize(typeof(G)) @@ -103,7 +103,7 @@ function ConformanceTests.test_Group_interface(G::Group) end end -function ConformanceTests.test_GroupElem_interface(g::GEl, h::GEl) where {GEl<:GroupElem} +function test_GroupElem_interface(g::GEl, h::GEl) where {GEl<:GroupElem} @testset "GroupElem interface" begin diff --git a/ext/TestExt/Mutating-ops.jl b/ext/TestExt/Mutating-ops.jl index b70da0f728..2d7a4ef02b 100644 --- a/ext/TestExt/Mutating-ops.jl +++ b/ext/TestExt/Mutating-ops.jl @@ -1,13 +1,13 @@ # The following functions should not expect that their input is a `NCRingElem` or similar. # They should be usable in more general types, that don't even have a `parent/elem` correspondence -function ConformanceTests.test_mutating_op_like_zero(f::Function, f!::Function, A) +function test_mutating_op_like_zero(f::Function, f!::Function, A) a = deepcopy(A) a = f!(a) @test equality(a, f(A)) end -function ConformanceTests.test_mutating_op_like_neg(f::Function, f!::Function, A) +function test_mutating_op_like_neg(f::Function, f!::Function, A) # initialize storage var with different values to check that its value is not used for z in [zero(A), deepcopy(A)] a = deepcopy(A) @@ -21,7 +21,7 @@ function ConformanceTests.test_mutating_op_like_neg(f::Function, f!::Function, A @test equality(a, f(A)) end -function ConformanceTests.test_mutating_op_like_add(f::Function, f!::Function, A, B, T = Any) +function test_mutating_op_like_add(f::Function, f!::Function, A, B, T = Any) @req A isa T || B isa T "Invalid argument types" # initialize storage var with different values to check that its value is not used @@ -83,7 +83,7 @@ function ConformanceTests.test_mutating_op_like_add(f::Function, f!::Function, A end end -function ConformanceTests.test_mutating_op_like_addmul(f::Function, f!_::Function, Z, A, B, T = Any) +function test_mutating_op_like_addmul(f::Function, f!_::Function, Z, A, B, T = Any) @req Z isa T "Invalid argument types" @req A isa T || B isa T "Invalid argument types" diff --git a/ext/TestExt/Rings-conformance-tests.jl b/ext/TestExt/Rings-conformance-tests.jl index 9f1140433a..f4ad629d79 100644 --- a/ext/TestExt/Rings-conformance-tests.jl +++ b/ext/TestExt/Rings-conformance-tests.jl @@ -15,7 +15,7 @@ # - test_MatAlgebra_interface(R) -function ConformanceTests.test_NCRing_interface(R::AbstractAlgebra.NCRing; reps = 50) +function test_NCRing_interface(R::AbstractAlgebra.NCRing; reps = 50) T = elem_type(R) @@ -200,17 +200,17 @@ function ConformanceTests.test_NCRing_interface(R::AbstractAlgebra.NCRing; reps b = generate_element(R)::T c = generate_element(R)::T - ConformanceTests.test_mutating_op_like_zero(zero, zero!, a) - ConformanceTests.test_mutating_op_like_zero(one, one!, a) + test_mutating_op_like_zero(zero, zero!, a) + test_mutating_op_like_zero(one, one!, a) - ConformanceTests.test_mutating_op_like_neg(-, neg!, a) + test_mutating_op_like_neg(-, neg!, a) - ConformanceTests.test_mutating_op_like_add(+, add!, a, b) - ConformanceTests.test_mutating_op_like_add(-, sub!, a, b) - ConformanceTests.test_mutating_op_like_add(*, mul!, a, b) + test_mutating_op_like_add(+, add!, a, b) + test_mutating_op_like_add(-, sub!, a, b) + test_mutating_op_like_add(*, mul!, a, b) - ConformanceTests.test_mutating_op_like_addmul((a, b, c) -> a + b*c, addmul!, a, b, c) - ConformanceTests.test_mutating_op_like_addmul((a, b, c) -> a - b*c, submul!, a, b, c) + test_mutating_op_like_addmul((a, b, c) -> a + b*c, addmul!, a, b, c) + test_mutating_op_like_addmul((a, b, c) -> a - b*c, submul!, a, b, c) end end end @@ -219,7 +219,7 @@ function ConformanceTests.test_NCRing_interface(R::AbstractAlgebra.NCRing; reps end -function ConformanceTests.test_Ring_interface(R::AbstractAlgebra.Ring; reps = 50) +function test_Ring_interface(R::AbstractAlgebra.Ring; reps = 50) T = elem_type(R) @@ -227,12 +227,12 @@ function ConformanceTests.test_Ring_interface(R::AbstractAlgebra.Ring; reps = 50 @test T <: RingElement - ConformanceTests.test_NCRing_interface(R; reps = reps) + test_NCRing_interface(R; reps = reps) @testset "Basic functionality for commutative rings only" begin @test isone(AbstractAlgebra.inv(one(R))) - ConformanceTests.test_mutating_op_like_neg(AbstractAlgebra.inv, inv!, one(R)) - ConformanceTests.test_mutating_op_like_neg(AbstractAlgebra.inv, inv!, -one(R)) + test_mutating_op_like_neg(AbstractAlgebra.inv, inv!, one(R)) + test_mutating_op_like_neg(AbstractAlgebra.inv, inv!, -one(R)) for i in 1:reps a = generate_element(R)::T b = generate_element(R)::T @@ -247,7 +247,7 @@ function ConformanceTests.test_Ring_interface(R::AbstractAlgebra.Ring; reps = 50 if T isa RingElem @test iszero(b) || equality((b*a) / b, a) end - iszero(b) || ConformanceTests.test_mutating_op_like_add(divexact, divexact!, b*a, b) + iszero(b) || test_mutating_op_like_add(divexact, divexact!, b*a, b) else try t = divexact(b*a, b) @@ -282,13 +282,13 @@ function ConformanceTests.test_Ring_interface(R::AbstractAlgebra.Ring; reps = 50 return nothing end -function ConformanceTests.test_Field_interface(R::AbstractAlgebra.Field; reps = 50) +function test_Field_interface(R::AbstractAlgebra.Field; reps = 50) T = elem_type(R) @testset "Field interface for $(R) of type $(typeof(R))" begin - ConformanceTests.test_Ring_interface(R, reps = reps) + test_Ring_interface(R, reps = reps) @test iszero(R(characteristic(R))) @test iszero(characteristic(R) * one(R)) @@ -301,7 +301,7 @@ function ConformanceTests.test_Field_interface(R::AbstractAlgebra.Field; reps = if !is_zero(a) @test is_one(a * inv(a)) @test is_one(inv(a) * a) - ConformanceTests.test_mutating_op_like_neg(inv, inv!, a) + test_mutating_op_like_neg(inv, inv!, a) end @test A == a end @@ -310,7 +310,7 @@ function ConformanceTests.test_Field_interface(R::AbstractAlgebra.Field; reps = return nothing end -function ConformanceTests.test_EuclideanRing_interface(R::AbstractAlgebra.Ring; reps = 20) +function test_EuclideanRing_interface(R::AbstractAlgebra.Ring; reps = 20) T = elem_type(R) @@ -386,10 +386,10 @@ function ConformanceTests.test_EuclideanRing_interface(R::AbstractAlgebra.Ring; @test d == s*f + t*g @test gcdinv(f, g) == (d, s) - ConformanceTests.test_mutating_op_like_add(AbstractAlgebra.div, div!, f, m) - ConformanceTests.test_mutating_op_like_add(mod, mod!, f, m) - ConformanceTests.test_mutating_op_like_add(gcd, gcd!, f, m) - ConformanceTests.test_mutating_op_like_add(lcm, lcm!, f, m) + test_mutating_op_like_add(AbstractAlgebra.div, div!, f, m) + test_mutating_op_like_add(mod, mod!, f, m) + test_mutating_op_like_add(gcd, gcd!, f, m) + test_mutating_op_like_add(lcm, lcm!, f, m) end end @@ -398,13 +398,13 @@ function ConformanceTests.test_EuclideanRing_interface(R::AbstractAlgebra.Ring; end -function ConformanceTests.test_Poly_interface(Rx::AbstractAlgebra.PolyRing; reps = 30) +function test_Poly_interface(Rx::AbstractAlgebra.PolyRing; reps = 30) T = elem_type(Rx) @testset "Poly interface for $(Rx) of type $(typeof(Rx))" begin - ConformanceTests.test_Ring_interface(Rx; reps = reps) + test_Ring_interface(Rx; reps = reps) x = gen(Rx) R = base_ring(Rx) @@ -430,7 +430,7 @@ function ConformanceTests.test_Poly_interface(Rx::AbstractAlgebra.PolyRing; reps end if R isa AbstractAlgebra.Field - ConformanceTests.test_EuclideanRing_interface(Rx, reps = 2 + fld(reps, 2)) + test_EuclideanRing_interface(Rx, reps = 2 + fld(reps, 2)) @testset "Half-GCD" begin for i in 1:reps a = generate_element(Rx) @@ -480,7 +480,7 @@ function ConformanceTests.test_Poly_interface(Rx::AbstractAlgebra.PolyRing; reps end -function ConformanceTests.test_MPoly_interface(Rxy::AbstractAlgebra.MPolyRing; reps = 30) +function test_MPoly_interface(Rxy::AbstractAlgebra.MPolyRing; reps = 30) # for simplicity, these tests for now assume exactly two generators @assert ngens(Rxy) == 2 @@ -489,7 +489,7 @@ function ConformanceTests.test_MPoly_interface(Rxy::AbstractAlgebra.MPolyRing; r @testset "MPoly interface for $(Rxy) of type $(typeof(Rxy))" begin - ConformanceTests.test_Ring_interface(Rxy; reps = reps) + test_Ring_interface(Rxy; reps = reps) @testset "Basic functionality" begin @test symbols(Rxy) isa Vector{Symbol} @@ -614,7 +614,7 @@ function ConformanceTests.test_MPoly_interface(Rxy::AbstractAlgebra.MPolyRing; r end -function ConformanceTests.test_MatSpace_interface(S::MatSpace; reps = 20) +function test_MatSpace_interface(S::MatSpace; reps = 20) ST = elem_type(S) R = base_ring(S) @@ -714,7 +714,7 @@ function ConformanceTests.test_MatSpace_interface(S::MatSpace; reps = 20) return nothing end -function ConformanceTests.test_MatAlgebra_interface(S::MatRing; reps = 20) +function test_MatAlgebra_interface(S::MatRing; reps = 20) ST = elem_type(S) R = base_ring(S) @@ -724,7 +724,7 @@ function ConformanceTests.test_MatAlgebra_interface(S::MatRing; reps = 20) @testset "MatRing interface for $(S) of type $(typeof(S))" begin - ConformanceTests.test_NCRing_interface(S, reps = reps) + test_NCRing_interface(S, reps = reps) @testset "Constructors" begin for k in 1:reps @@ -766,19 +766,19 @@ function ConformanceTests.test_MatAlgebra_interface(S::MatRing; reps = 20) return nothing end -function ConformanceTests.test_Ring_interface_recursive(R::AbstractAlgebra.Ring; reps = 50) - ConformanceTests.test_Ring_interface(R; reps = reps) +function test_Ring_interface_recursive(R::AbstractAlgebra.Ring; reps = 50) + test_Ring_interface(R; reps = reps) Rx, _ = polynomial_ring(R, :x) - ConformanceTests.test_Poly_interface(Rx, reps = 2 + fld(reps, 2)) + test_Poly_interface(Rx, reps = 2 + fld(reps, 2)) Rxy, _ = polynomial_ring(R, [:x, :y]) - ConformanceTests.test_MPoly_interface(Rxy, reps = 2 + fld(reps, 2)) + test_MPoly_interface(Rxy, reps = 2 + fld(reps, 2)) S = matrix_ring(R, rand(0:3)) - ConformanceTests.test_MatAlgebra_interface(S, reps = 2 + fld(reps, 2)) + test_MatAlgebra_interface(S, reps = 2 + fld(reps, 2)) S = matrix_space(R, rand(0:3), rand(0:3)) - ConformanceTests.test_MatSpace_interface(S, reps = 2 + fld(reps, 2)) + test_MatSpace_interface(S, reps = 2 + fld(reps, 2)) end -function ConformanceTests.test_Field_interface_recursive(R::AbstractAlgebra.Field; reps = 50) - ConformanceTests.test_Ring_interface_recursive(R, reps = reps) - ConformanceTests.test_Field_interface(R, reps = reps) +function test_Field_interface_recursive(R::AbstractAlgebra.Field; reps = 50) + test_Ring_interface_recursive(R, reps = reps) + test_Field_interface(R, reps = reps) end diff --git a/ext/TestExt/TestExt.jl b/ext/TestExt/TestExt.jl index 789de5c4a6..a1957c3de8 100644 --- a/ext/TestExt/TestExt.jl +++ b/ext/TestExt/TestExt.jl @@ -9,6 +9,25 @@ using .ConformanceTests: adhoc_partner_rings, generate_element +import .ConformanceTests: test_mutating_op_like_zero +import .ConformanceTests: test_mutating_op_like_neg +import .ConformanceTests: test_mutating_op_like_add +import .ConformanceTests: test_mutating_op_like_addmul + +import .ConformanceTests: test_Group_interface +import .ConformanceTests: test_GroupElem_interface + +import .ConformanceTests: test_NCRing_interface +import .ConformanceTests: test_Ring_interface +import .ConformanceTests: test_Field_interface +import .ConformanceTests: test_EuclideanRing_interface +import .ConformanceTests: test_Poly_interface +import .ConformanceTests: test_MPoly_interface +import .ConformanceTests: test_MatSpace_interface +import .ConformanceTests: test_MatAlgebra_interface +import .ConformanceTests: test_Ring_interface_recursive +import .ConformanceTests: test_Field_interface_recursive + include("Groups-conformance-tests.jl") include("Mutating-ops.jl") include("Rings-conformance-tests.jl") From ae4928921b88ec5456203853b1ee9aec47255774 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 22 Dec 2024 18:01:03 +0100 Subject: [PATCH 2/2] Add conformance tests for is_unit & is_nilpotent Also improve is_nilpotent for matrices / matrix ring elements to work if the base ring is trivial. --- ext/TestExt/Rings-conformance-tests.jl | 43 ++++++++++++++++++++++++-- src/Matrix.jl | 4 +-- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/ext/TestExt/Rings-conformance-tests.jl b/ext/TestExt/Rings-conformance-tests.jl index f4ad629d79..101e7525e4 100644 --- a/ext/TestExt/Rings-conformance-tests.jl +++ b/ext/TestExt/Rings-conformance-tests.jl @@ -67,14 +67,49 @@ function test_NCRing_interface(R::AbstractAlgebra.NCRing; reps = 50) end end - @testset "Basic functions" begin + @testset "Basic properties" begin @test iszero(R()) # R() is supposed to construct 0 ? @test iszero(zero(R)) @test isone(one(R)) @test iszero(R(0)) @test isone(R(1)) - @test isone(R(0)) || !is_unit(R(0)) + @test is_unit(R(1)) + if is_trivial(R) + @test isone(R(0)) + @test iszero(R(1)) + @test R(0) == R(1) + else + @test !is_unit(R(0)) + for i in 1:reps + a = generate_element(R)::T + @test is_unit(a) == is_unit(a^2) + end + end + + # test is_nilpotent if it is supported + try + f = is_nilpotent(R(1)) + @test is_nilpotent(R(0)) + if is_trivial(R) + @test is_nilpotent(R(1)) + else + @test !is_unit(R(0)) + @test !is_nilpotent(R(1)) + for i in 1:reps + a = generate_element(R)::T + @test !(is_unit(a) && is_nilpotent(a)) + @test is_nilpotent(a) == is_nilpotent(a^2) + if is_domain_type(typeof(a)) + @test is_nilpotent(a) == is_zero(a) + end + end + end + catch + end + end + + @testset "hash, deepcopy, equality, printing, parent" begin for i in 1:reps a = generate_element(R)::T @test hash(a) isa UInt @@ -82,11 +117,13 @@ function test_NCRing_interface(R::AbstractAlgebra.NCRing; reps = 50) @test !ismutable(a) || a !== A @test equality(a, A) @test hash(a) == hash(A) - @test parent(a) === parent(A) + @test parent(a) === R @test sprint(show, "text/plain", a) isa String end @test sprint(show, "text/plain", R) isa String + end + @testset "Basic arithmetic" begin for i in 1:reps a = generate_element(R)::T b = generate_element(R)::T diff --git a/src/Matrix.jl b/src/Matrix.jl index 5c03d141ab..71b4e227ca 100644 --- a/src/Matrix.jl +++ b/src/Matrix.jl @@ -3875,13 +3875,13 @@ Return if `A` is nilpotent, i.e. if there exists a natural number $k$ such that $A^k = 0$. If `A` is not square an exception is raised. """ function is_nilpotent(A::MatrixElem{T}) where {T <: RingElement} - is_domain_type(T) || error("Only supported over integral domains") !is_square(A) && error("Dimensions don't match in is_nilpotent") + is_zero(A) && return true + is_domain_type(T) || error("Only supported over integral domains") is_zero(tr(A)) || return false n = nrows(A) A = deepcopy(A) i = 1 - is_zero(A) && return true while i < n i *= 2 A = mul!(A, A, A)