Skip to content
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

Is_unit and is_nilpotent (with tests) #1933

Merged
merged 44 commits into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
27ad97f
Added generic/default defn of is_nilpotent
JohnAAbbott Dec 16, 2024
37ca35c
Merge branch 'master' of github.com:JohnAAbbott/AbstractAlgebra.jl
JohnAAbbott Dec 17, 2024
bceb3ad
Corrected several typos (all on one line)
JohnAAbbott Dec 17, 2024
a859a05
New impls of is_unit & is_nilpotent
JohnAAbbott Dec 17, 2024
6dcb4a3
Tests for new is_unit & is_nilpotent
JohnAAbbott Dec 17, 2024
bcdb3c3
Add impls of is_unit & is_nilpotent
JohnAAbbott Dec 18, 2024
c9e711b
Copied defns of is_unit & is_nilpotent from Nemo
JohnAAbbott Dec 18, 2024
68afc5b
Merge branch 'Nemocas:master' into master
JohnAAbbott Dec 18, 2024
d4343d1
Added is_nilpotent
JohnAAbbott Dec 18, 2024
e8c8c3e
Merge branch 'master' of github.com:JohnAAbbott/AbstractAlgebra.jl
JohnAAbbott Dec 18, 2024
3ecd278
Commented out old broken impl of is_unit
JohnAAbbott Dec 18, 2024
81ec784
is_unit for PolyRingElem & for NCPolyRing elem merged; ditto for is_n…
JohnAAbbott Dec 18, 2024
1a0cd07
Added is_nilpotent(::Integer)
JohnAAbbott Dec 18, 2024
2ad7109
Moved is_unit & is_nilpotent to NCPoly.jl; deleted cruft
JohnAAbbott Dec 18, 2024
8875511
Moved is_unit & is_nilpotent here from Poly.jl; improvements
JohnAAbbott Dec 18, 2024
af88767
Moved default is_nilpotent from Rings.jl to NCRings.jl
JohnAAbbott Dec 18, 2024
ff9a4d9
Improved is_unit & is_nilpotent (from feedback)
JohnAAbbott Dec 18, 2024
3cafa67
Now using neater test for zero ring
JohnAAbbott Dec 18, 2024
5def6e7
Inserted missing test is_unit(constant_coeff...)
JohnAAbbott Dec 19, 2024
20b2fac
Added is_nilpotent for Rational
JohnAAbbott Dec 19, 2024
19ac0ea
Removed old is_unit test; fixed typo
JohnAAbbott Dec 19, 2024
ff41535
Fixed typo
JohnAAbbott Dec 19, 2024
fd49a42
Removed outdated is_unit test
JohnAAbbott Dec 19, 2024
d2978c2
Removed outdated test for is_unit
JohnAAbbott Dec 19, 2024
76bbaa6
Moved new defns of is_unit, is_nilpotent from src/LaurentMPoly to src…
JohnAAbbott Dec 19, 2024
23ad37d
Removed old, buggy impl of is_unit
JohnAAbbott Dec 19, 2024
3228e1d
Removed old, buggy impl of is_unit
JohnAAbbott Dec 19, 2024
ed26e8c
Better layout in a comment
JohnAAbbott Dec 19, 2024
e664624
Minor improvements/tidying
JohnAAbbott Dec 19, 2024
4707518
Neater calls to is_domain_type
JohnAAbbott Dec 19, 2024
f135f15
Neater calls to is_domain_type
JohnAAbbott Dec 19, 2024
2cfde89
Deleted blank line
JohnAAbbott Dec 19, 2024
c10f880
Moved is_nilpotent up to src/Residue.jl
JohnAAbbott Dec 19, 2024
27747c3
More tests: now using coeffs which are non-nilpotent zero-divisors
JohnAAbbott Dec 20, 2024
62ce85f
generic is_nilpotent now takes a NCRingElement; changed is_unit to us…
JohnAAbbott Dec 20, 2024
3abc986
Added doc for is_nilpotent
JohnAAbbott Dec 20, 2024
4a59d4e
Replaced mod! by mod, since mod! did not work as expected
JohnAAbbott Dec 20, 2024
3ac6d1b
Apply suggestions from code review
fingolfin Dec 20, 2024
c2f096e
Apply suggestions from code review
fingolfin Dec 20, 2024
be2e968
Apply suggestions from code review
fingolfin Dec 20, 2024
c594a7c
Apply suggestions from code review
fingolfin Dec 20, 2024
1bce4d7
Apply suggestions from code review
fingolfin Dec 20, 2024
78197c9
Apply suggestions from code review
fingolfin Dec 21, 2024
625eb2b
fix
fingolfin Dec 21, 2024
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
36 changes: 25 additions & 11 deletions src/MPoly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -425,16 +425,22 @@ end

iszero(x::MPolyRingElem{T}) where T <: RingElement = length(x) == 0

function is_unit(a::MPolyRingElem{T}) where T <: RingElement
if is_constant(a)
return is_unit(leading_coefficient(a))
elseif is_domain_type(elem_type(coefficient_ring(a)))
return false
elseif length(a) == 1
return false
else
throw(NotImplementedError(:is_unit, a))
end
function is_unit(f::T) where {T <: MPolyRingElem}
# for constant polynomials we delegate to the coefficient ring:
is_constant(f) && return is_unit(constant_coefficient(f))
# Here deg(f) > 0; over an integral domain, non-constant polynomials are never units:
is_domain_type(T) && return false
# 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.
for (c, expv) in zip(coefficients(f), exponent_vectors(f))
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
if is_zero(expv)
is_unit(c) || return false
else
is_nilpotent(c) || return false
end
end
return true
end

function content(a::MPolyRingElem{T}) where T <: RingElement
Expand All @@ -445,8 +451,16 @@ function content(a::MPolyRingElem{T}) where T <: RingElement
return z
end


function is_nilpotent(f::T) where {T <: MPolyRingElem}
is_domain_type(T) && return is_zero(f)
return all(is_nilpotent, coefficients(f))
end


function is_zero_divisor(x::MPolyRingElem{T}) where T <: RingElement
JohnAAbbott marked this conversation as resolved.
Show resolved Hide resolved
return is_zero_divisor(content(x))
is_domain_type(T) && return is_zero(x)
return is_zero_divisor(content(x))
end

function is_zero_divisor_with_annihilator(a::MPolyRingElem{T}) where T <: RingElement
Expand Down
31 changes: 31 additions & 0 deletions src/NCPoly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -783,3 +783,34 @@ polynomial_ring_only(R::T, s::Symbol; cached::Bool=true) where T<:NCRing =
# Simplified constructor

PolyRing(R::NCRing) = polynomial_ring_only(R, :x; cached=false)



###############################################################################
#
# is_unit & is_nilpotent
#
###############################################################################

# ASSUMES structural interface is analogous to that for univariate polynomials

# This function handles both PolyRingElem & NCPolyRingElem
function is_unit(f::T) where {T <: PolynomialElem}
# constant coeff must itself be a unit
is_unit(constant_coefficient(f)) || return false
is_constant(f) && return true
# Here deg(f) > 0; over an integral domain, non-constant polynomials are never units:
is_domain_type(T) && return false
for i in 1:degree(f) # we have already checked coeff(f,0)
if !is_nilpotent(coeff(f, i))
return false
end
end
return true
end

# This function handles both PolyRingElem & NCPolyRingElem
function is_nilpotent(f::T) where {T <: PolynomialElem}
is_domain_type(T) && return is_zero(f)
return all(is_nilpotent, coefficients(f))
end
23 changes: 22 additions & 1 deletion src/NCRings.jl
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
###############################################################################

@doc raw"""
is_unit(a::T) where {T <: NCRingElem}
is_unit(a::T) where {T <: NCRingElement}

Return true if $a$ is invertible, else return false.

Expand All @@ -155,6 +155,27 @@
"""
function is_unit end

@doc raw"""
is_nilpotent(a::T) where {T <: NCRingElement}

Return true iff $a$ is nilpotent, i.e. a^k == 0 for some k.

# Examples
```jldoctest

Check failure on line 164 in src/NCRings.jl

View workflow job for this annotation

GitHub Actions / Documentation

doctest failure in ~/work/AbstractAlgebra.jl/AbstractAlgebra.jl/src/NCRings.jl:164-171 ```jldoctest julia> ZZ720,_ = residue_ring(ZZ,720); julia> S, x = polynomial_ring(QQ, :x); julia> is_nilpotent(30*x), is_nilpotent(30+90*x), is_nilpotent(S(15)) (true, true, false) ``` Subexpression: S, x = polynomial_ring(QQ, :x); Evaluated output: Expected output: ZZ720,_ = residue_ring(ZZ,720); diff = Warning: Diff output requires color. ZZ720,_ = residue_ring(ZZ,720);

Check failure on line 164 in src/NCRings.jl

View workflow job for this annotation

GitHub Actions / Documentation

doctest failure in ~/work/AbstractAlgebra.jl/AbstractAlgebra.jl/src/NCRings.jl:164-171 ```jldoctest julia> ZZ720,_ = residue_ring(ZZ,720); julia> S, x = polynomial_ring(QQ, :x); julia> is_nilpotent(30*x), is_nilpotent(30+90*x), is_nilpotent(S(15)) (true, true, false) ``` Subexpression: is_nilpotent(30*x), is_nilpotent(30+90*x), is_nilpotent(S(15)) Evaluated output: (false, false, false) Expected output: diff = Warning: Diff output requires color. (false, false, false)
julia> ZZ720,_ = residue_ring(ZZ,720);
julia> S, x = polynomial_ring(QQ, :x);

julia> is_nilpotent(30*x), is_nilpotent(30+90*x), is_nilpotent(S(15))

(true, true, false)
fingolfin marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JohnAAbbott seems you and the code disagree as to what the correct output is?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thepolynomial_ring should be over the ring created before, not over QQ. Fixed now

```
"""
function is_nilpotent(a::T) where {T <: NCRingElement}
is_domain_type(T) && return is_zero(a)
throw(NotImplementedError(:is_nilpotent, a))
end


###############################################################################
#
# Characteristic
Expand Down
14 changes: 3 additions & 11 deletions src/Poly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -222,17 +222,9 @@ function is_monic(a::PolynomialElem)
return isone(leading_coefficient(a))
end

function is_unit(a::PolynomialElem)
if length(a) <= 1
return is_unit(coeff(a, 0))
elseif is_domain_type(elem_type(coefficient_ring(a)))
return false
elseif !is_unit(coeff(a, 0)) || is_unit(coeff(a, length(a) - 1))
return false
else
throw(NotImplementedError(:is_unit, a))
end
end
# function is_unit(...) see NCPoly.jl
# function is_nilpotent(...) see NCPoly.jl


JohnAAbbott marked this conversation as resolved.
Show resolved Hide resolved
is_zero_divisor(a::PolynomialElem) = is_zero_divisor(content(a))

Expand Down
13 changes: 13 additions & 0 deletions src/Residue.jl
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ function is_unit(a::ResElem)
return isone(g)
end

function is_nilpotent(res::ResElem)
m = modulus(res)
r = data(res)
while true
g = gcd(r, m)
(g == m) && return true
is_one(g) && return false
m = divexact(m, g)
g = mod(g, m); r = g^2 # if computation domain is limited precision integer then g = mod(g,m) guarantees that g^2 will not overflow!
end
end


# currently residue rings are only allowed over domains
# otherwise this function would be more complicated
is_zero_divisor(a::ResElem) = !is_unit(a)
Expand Down
24 changes: 20 additions & 4 deletions src/algorithms/LaurentPoly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,28 @@ function is_monomial_recursive(p::LaurentPolyRingElem)
is_monomial_recursive(coeff(p, dr[]))
end

function is_unit(p::LaurentPolyRingElem)
dr = degrees_range(p)
length(dr) == 1 || return false
is_unit(coeff(p, dr[]))
function is_unit(f::T) where {T <: LaurentPolyRingElem}
# **NOTE** f.poly is not normalized so that the degree 0 coeff is non-zero
is_trivial(parent(f)) && return true # coeffs in zero ring
unit_seen = false
for i in 0:degree(f.poly)
if is_nilpotent(coeff(f.poly, i))
continue
end
if unit_seen || !is_unit(coeff(f.poly, i))
return false
end
unit_seen = true
end
return unit_seen
end


function is_nilpotent(f::T) where {T <: LaurentPolyRingElem}
return is_nilpotent(f.poly);
end


###############################################################################
#
# Comparisons
Expand Down
25 changes: 19 additions & 6 deletions src/generic/LaurentMPoly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -109,15 +109,28 @@ function Base.inv(a::LaurentMPolyWrap)
return LaurentMPolyWrap(parent(a), inv(ap), neg!(ad, ad))
end

function is_unit(a::LaurentMPolyWrap)
(ap, ad) = _normalize(a)
if is_domain_type(elem_type(coefficient_ring(a))) || length(ap) <= 1
return is_unit(ap)
else
throw(NotImplementedError(:is_unit, a))
function is_unit(f::T) where {T <: LaurentMPolyRingElem}
# **NOTE** f.mpoly is not normalized in any way
is_trivial(parent(f)) && return true # coeffs in zero ring
unit_seen = false
for i in 1:length(f.mpoly)
if is_nilpotent(coeff(f.mpoly, i))
continue
end
if unit_seen || !is_unit(coeff(f.mpoly, i))
return false
end
unit_seen = true
end
return unit_seen
end


function is_nilpotent(f::T) where {T <: LaurentMPolyRingElem}
return is_nilpotent(f.mpoly);
end


is_zero_divisor(p::LaurentMPolyWrap) = is_zero_divisor(p.mpoly)

function is_zero_divisor_with_annihilator(p::LaurentMPolyWrap)
Expand Down
9 changes: 0 additions & 9 deletions src/generic/LaurentPoly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -201,15 +201,6 @@ function Base.inv(p::LaurentPolyWrap)
return LaurentPolyWrap(parent(p), inv(g), -p.mindeg-v)
end

function is_unit(p::LaurentPolyWrap)
v, g = _remove_gen(p)
if is_domain_type(elem_type(coefficient_ring(p))) || length(g) <= 1
return is_unit(g)
else
throw(NotImplementedError(:is_unit, p))
end
end

is_zero_divisor(p::LaurentPolyWrap) = is_zero_divisor(p.poly)

function is_zero_divisor_with_annihilator(p::LaurentPolyWrap)
Expand Down
1 change: 1 addition & 0 deletions src/generic/imports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ import ..AbstractAlgebra: is_exact_type
import ..AbstractAlgebra: is_finite
import ..AbstractAlgebra: is_gen
import ..AbstractAlgebra: is_monomial
import ..AbstractAlgebra: is_nilpotent
import ..AbstractAlgebra: is_power
import ..AbstractAlgebra: is_square
import ..AbstractAlgebra: is_square_with_sqrt
Expand Down
2 changes: 1 addition & 1 deletion src/julia/Float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ zero(::Floats{T}) where T <: AbstractFloat = T(0)

one(::Floats{T}) where T <: AbstractFloat = T(1)

is_unit(a::AbstractFloat) = a != 0
is_unit(a::AbstractFloat) = !is_zero(a)

canonical_unit(a::AbstractFloat) = a

Expand Down
2 changes: 1 addition & 1 deletion src/julia/Rational.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ zero(::Rationals{T}) where T <: Integer = Rational{T}(0)

one(::Rationals{T}) where T <: Integer = Rational{T}(1)

is_unit(a::Rational) = a != 0
is_unit(a::Rational) = !is_zero(a)

is_zero_divisor(a::Rational) = is_zero(a)

Expand Down
Loading
Loading