Skip to content

Add conformance tests for is_unit & is_nilpotent #1947

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ext/TestExt/Groups-conformance-tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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

Expand Down
8 changes: 4 additions & 4 deletions ext/TestExt/Mutating-ops.jl
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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"

Expand Down
121 changes: 79 additions & 42 deletions ext/TestExt/Rings-conformance-tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -67,26 +67,63 @@
end
end

@testset "Basic functions" begin
@testset "Basic properties" begin

Check warning on line 70 in ext/TestExt/Rings-conformance-tests.jl

View check run for this annotation

Codecov / codecov/patch

ext/TestExt/Rings-conformance-tests.jl#L70

Added line #L70 was not covered by tests
@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

Check warning on line 112 in ext/TestExt/Rings-conformance-tests.jl

View check run for this annotation

Codecov / codecov/patch

ext/TestExt/Rings-conformance-tests.jl#L112

Added line #L112 was not covered by tests
for i in 1:reps
a = generate_element(R)::T
@test hash(a) isa UInt
A = deepcopy(a)
@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

Check warning on line 126 in ext/TestExt/Rings-conformance-tests.jl

View check run for this annotation

Codecov / codecov/patch

ext/TestExt/Rings-conformance-tests.jl#L126

Added line #L126 was not covered by tests
for i in 1:reps
a = generate_element(R)::T
b = generate_element(R)::T
Expand Down Expand Up @@ -200,17 +237,17 @@
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
Expand All @@ -219,20 +256,20 @@
end


function ConformanceTests.test_Ring_interface(R::AbstractAlgebra.Ring; reps = 50)
function test_Ring_interface(R::AbstractAlgebra.Ring; reps = 50)

T = elem_type(R)

@testset "Ring interface for $(R) of type $(typeof(R))" begin

@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
Expand All @@ -247,7 +284,7 @@
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)
Expand Down Expand Up @@ -282,13 +319,13 @@
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))
Expand All @@ -301,7 +338,7 @@
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
Expand All @@ -310,7 +347,7 @@
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)

Expand Down Expand Up @@ -386,10 +423,10 @@
@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
Expand All @@ -398,13 +435,13 @@
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)
Expand All @@ -430,7 +467,7 @@
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)
Expand Down Expand Up @@ -480,7 +517,7 @@
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
Expand All @@ -489,7 +526,7 @@

@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}
Expand Down Expand Up @@ -614,7 +651,7 @@
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)
Expand Down Expand Up @@ -714,7 +751,7 @@
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)
Expand All @@ -724,7 +761,7 @@

@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
Expand Down Expand Up @@ -766,19 +803,19 @@
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
19 changes: 19 additions & 0 deletions ext/TestExt/TestExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
4 changes: 2 additions & 2 deletions src/Matrix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading