Skip to content

Commit

Permalink
Add conformance tests for MPolyRing, and fix bugs in ^ and is_unit fo…
Browse files Browse the repository at this point in the history
…r MPoly (#1950)
  • Loading branch information
fingolfin authored Jan 10, 2025
1 parent 6be03b7 commit 1c605fb
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 6 deletions.
4 changes: 3 additions & 1 deletion src/MPoly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -433,14 +433,16 @@ function is_unit(f::T) where {T <: MPolyRingElem}
# A polynomial over a commutative ring is a unit iff its
# constant term is a unit and all other coefficients are nilpotent:
# see e.g. <https://kconrad.math.uconn.edu/blurbs/ringtheory/polynomial-properties.pdf> for a proof.
constant_term_is_unit = false
for (c, expv) in zip(coefficients(f), exponent_vectors(f))
if is_zero(expv)
is_unit(c) || return false
constant_term_is_unit = true
else
is_nilpotent(c) || return false
end
end
return true
return constant_term_is_unit
end

function content(a::MPolyRingElem{T}) where T <: RingElement
Expand Down
4 changes: 3 additions & 1 deletion src/generic/MPoly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2316,6 +2316,8 @@ function ^(a::MPoly{T}, b::Int) where {T <: RingElement}
return zero(a)
end
elseif length(a) == 1
c = coeff(a, 1)^b
is_zero(c) && return zero(a)
N = size(a.exps, 1)
exps = zeros(UInt, N, 1)
monomial_mul!(exps, 1, a.exps, 1, b, N)
Expand All @@ -2324,7 +2326,7 @@ function ^(a::MPoly{T}, b::Int) where {T <: RingElement}
error("Exponent overflow in powering")
end
end
return parent(a)([coeff(a, 1)^b], exps)
return parent(a)([c], exps)
elseif b == 0
return one(a)
elseif b == 1
Expand Down
142 changes: 139 additions & 3 deletions test/Rings-conformance-tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -719,15 +719,15 @@ function test_Poly_interface(Rx::AbstractAlgebra.PolyRing; reps = 30)
@test symbols(Rx) isa Vector{Symbol}
@test length(symbols(Rx)) == 1
@test is_gen(gen(Rx))
@test is_gen(x)
@test is_monic(x)
@test is_trivial(Rx) || !is_gen(x^2)
for i in 1:reps
a = test_elem(Rx)
@test iszero(a) || degree(a) >= 0
@test equality(a, leading_coefficient(a)*x^max(0, degree(a)) + tail(a))
@test constant_coefficient(a) isa elem_type(R)
@test trailing_coefficient(a) isa elem_type(R)
@test is_gen(x)
@test iszero(one(Rx)) || !is_gen(x^2)
@test is_monic(a) == isone(leading_coefficient(a))
end
end
Expand All @@ -737,6 +737,140 @@ function test_Poly_interface(Rx::AbstractAlgebra.PolyRing; reps = 30)
end


function test_MPoly_interface(Rxy::AbstractAlgebra.MPolyRing; reps = 30)

# for simplicity, these tests for now assume exactly two generators
@assert ngens(Rxy) == 2

T = elem_type(Rxy)

@testset "MPoly interface for $(Rxy) of type $(typeof(Rxy))" begin

test_Ring_interface(Rxy; reps = reps)

@testset "Basic functionality" begin
@test symbols(Rxy) isa Vector{Symbol}
@test length(symbols(Rxy)) == ngens(Rxy)
@test length(gens(Rxy)) == ngens(Rxy)
@test gens(Rxy) == [gen(Rxy, i) for i in 1:ngens(Rxy)]
@test all(is_gen, gens(Rxy)) || is_trivial(Rxy)
end

@testset "Polynomial Constructors" begin
for i in 1:reps
a = test_elem(Rxy)::T
for b in coefficients(a)
@assert Rxy(b) isa T
end

# test MPolyBuildCtx
B = MPolyBuildCtx(Rxy)
for (c, e) in zip(AbstractAlgebra.coefficients(a), AbstractAlgebra.exponent_vectors(a))
push_term!(B, c, e)
end
@test finish(B) == a
end
x, y = gens(Rxy)
f = 13*x^3*y^4 + 2*x - 7
#@test Rxy([2,-7,13], [[1,0],[0,0],[3,4]]) == f # FIXME: interface spec does not say this is required?

R = base_ring(Rxy)
@test Rxy(R.([2,-7,13]), [[1,0],[0,0],[3,4]]) == f
end

# skip trivial rings after this, it is not worth the bother
is_trivial(Rxy) && return

@testset "Element properties" begin
R = base_ring(Rxy)
x, y = gens(Rxy)

a = zero(Rxy)
@test !is_monomial(a)
@test !is_term(a)
@test is_constant(a)
@test !is_gen(a)
@test !is_unit(a)
@test is_nilpotent(a)
@test length(a) == 0
@test total_degree(a) < 0
@test all(is_negative, degrees(a))

a = one(Rxy)
@test is_monomial(a)
@test is_term(a)
@test is_constant(a)
@test !is_gen(a)
@test is_unit(a)
@test !is_nilpotent(a)
@test length(a) == 1
@test total_degree(a) == 0
@test degrees(a) == [0, 0]

a = x
@test is_monomial(a)
@test is_term(a)
@test !is_constant(a)
@test is_gen(a)
@test !is_unit(a)
@test !is_nilpotent(a)
@test length(a) == 1
@test total_degree(a) == 1
@test degrees(a) == [1, 0]

a = x^2
@test is_monomial(a)
@test is_term(a)
@test !is_constant(a)
@test !is_gen(a)
@test !is_unit(a)
@test !is_nilpotent(a)
@test length(a) == 1
@test total_degree(a) == 2
@test degrees(a) == [2, 0]

if !is_zero(R(2))
a = 2*x
@test !is_monomial(a)
@test is_term(a)
@test !is_constant(a)
@test !is_gen(a)
@test !is_unit(a)
@test is_nilpotent(a) == is_nilpotent(R(2))
@test length(a) == 1
@test total_degree(a) == 1
@test degrees(a) == [1, 0]
end

a = x^3 + y^4
@test !is_monomial(a)
@test !is_term(a)
@test !is_constant(a)
@test !is_gen(a)
@test !is_unit(a)
@test !is_nilpotent(a)
@test length(a) == 2
@test total_degree(a) == 4
@test degrees(a) == [3, 4]

for i in 1:reps
a = test_elem(Rxy)
iszero(a) && continue
@test length(a) >= 0
@test sum(degrees(a)) >= total_degree(a)
end

end

# TODO: add more tests, covering everything described in the manual, see
# https://nemocas.github.io/AbstractAlgebra.jl/dev/mpoly_interface/
# https://nemocas.github.io/AbstractAlgebra.jl/dev/mpolynomial/
end

return nothing
end


function test_MatSpace_interface(S::MatSpace; reps = 20)

ST = elem_type(S)
Expand Down Expand Up @@ -891,8 +1025,10 @@ end

function test_Ring_interface_recursive(R::AbstractAlgebra.Ring; reps = 50)
test_Ring_interface(R; reps = reps)
Rx, _ = polynomial_ring(R, "x")
Rx, _ = polynomial_ring(R, :x)
test_Poly_interface(Rx, reps = 2 + fld(reps, 2))
Rxy, _ = polynomial_ring(R, [:x, :y])
test_MPoly_interface(Rxy, reps = 2 + fld(reps, 2))
S = matrix_ring(R, rand(0:3))
test_MatAlgebra_interface(S, reps = 2 + fld(reps, 2))
S = matrix_space(R, rand(0:3), rand(0:3))
Expand Down
2 changes: 1 addition & 1 deletion test/generic/Residue-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
test_Ring_interface_recursive(T)

#
S, x = polynomial_ring(ZZ, "x")
S, x = polynomial_ring(QQ, "x")
T, = residue_ring(S, x^2 + 1)
test_Ring_interface_recursive(T)

Expand Down

0 comments on commit 1c605fb

Please sign in to comment.