Skip to content
Draft
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: 1 addition & 3 deletions src/sage/arith/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2775,9 +2775,7 @@ def radical(n, *args, **kwds):
sage: radical(2 * 3^2 * 5^5)
30
sage: radical(0)
Traceback (most recent call last):
...
ArithmeticError: radical of 0 is not defined
0
sage: K.<i> = QuadraticField(-1) # needs sage.rings.number_field
sage: radical(K(2)) # needs sage.rings.number_field
i - 1
Expand Down
6 changes: 2 additions & 4 deletions src/sage/categories/unique_factorization_domains.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,9 +262,7 @@ def radical(self, *args, **kwds):
sage: Integer(-100).radical()
10
sage: Integer(0).radical()
Traceback (most recent call last):
...
ArithmeticError: radical of 0 is not defined
0

The next example shows how to compute the radical of a number,
assuming no prime > 100000 has exponent > 1 in the factorization::
Expand All @@ -281,7 +279,7 @@ def radical(self, *args, **kwds):
10
"""
if self.is_zero():
raise ArithmeticError("radical of 0 is not defined")
return self
try:
decomp = self.squarefree_decomposition()
except AttributeError:
Expand Down
133 changes: 133 additions & 0 deletions src/sage/rings/power_series_ring_element.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1592,21 +1592,154 @@ cdef class PowerSeries(AlgebraElement):
sage: f = (1+t)^100
sage: f.is_square()
True

Over a ring with nilpotent elements, odd valuation is possible for squares::

sage: R.<x> = PowerSeriesRing(Zmod(4))
sage: (2*x).is_square() # (2*x)^2 has odd valuation but 2^2 = 0 mod 4
Traceback (most recent call last):
...
NotImplementedError: is_square() not implemented for power series over rings with nonzero nilradical

Power series over `\Zmod{n}` where `n` is squarefree::

sage: R.<x> = PowerSeriesRing(Zmod(6))
sage: ((x + 1)^2).is_square()
True
sage: (x^2).is_square()
True
sage: R(0).is_square()
True
sage: R(4).is_square()
True
sage: R.<x> = PowerSeriesRing(Zmod(15))
sage: ((x + 2)^2).is_square()
True
sage: (5 + x).is_square()
False

For prime power moduli with exponent > 1, a :exc:`NotImplementedError` is raised::

sage: R.<x> = PowerSeriesRing(Zmod(8))
sage: ((x + 1)^2).is_square()
Traceback (most recent call last):
...
NotImplementedError: is_square() not implemented for power series over rings with nonzero nilradical
"""
import sage.rings.abc

# Zero is always a square
if self.is_zero():
return True

val = self.valuation()
if val is not infinity and val % 2 == 1:
# Odd valuation can only happen for a square if the leading coefficient
# squares to zero, i.e., the base ring has nonzero nilradical.
# Check if the base ring has zero nilradical.
try:
nilrad = self.base_ring().nilradical()
if not nilrad.is_zero():
raise NotImplementedError(
"is_square() not implemented for power series over rings with nonzero nilradical"
)
except (AttributeError, NotImplementedError, ArithmeticError):
# If nilradical() is not available, try to check if leading coeff^2 = 0
if self[val]**2 == 0:
raise NotImplementedError(
"is_square() not implemented for power series over rings with nonzero nilradical"
)
return False
elif not self[val].is_square():
return False
elif self.base_ring() in _Fields:
return True
# Check if base ring is IntegerModRing - use CRT for squarefree moduli
elif isinstance(self.base_ring(), sage.rings.abc.IntegerModRing):
return self._is_square_crt()
else:
try:
self.parent()(self.sqrt())
return True
except TypeError:
return False

def _is_square_crt(self):
r"""
Check if this power series is a square over `\Zmod{n}` using CRT.

This method works for squarefree moduli (products of distinct primes).
For prime power moduli with exponent > 1, it raises NotImplementedError.

EXAMPLES::

sage: R.<x> = PowerSeriesRing(Zmod(6))
sage: ((x + 1)^2)._is_square_crt()
True
sage: (5 + x)._is_square_crt()
False
sage: R.<x> = PowerSeriesRing(Zmod(15))
sage: ((2*x + 3)^2)._is_square_crt()
True

Elements with even valuation::

sage: R.<x> = PowerSeriesRing(Zmod(6))
sage: (x^2)._is_square_crt()
True
sage: (4*x^2)._is_square_crt()
True

The zero element is always a square::

sage: R.<x> = PowerSeriesRing(Zmod(6))
sage: R(0)._is_square_crt()
True

Constant elements::

sage: R.<x> = PowerSeriesRing(Zmod(6))
sage: R(4)._is_square_crt()
True
sage: R(3)._is_square_crt()
True
sage: R(2)._is_square_crt()
False
"""
from sage.rings.finite_rings.integer_mod_ring import IntegerModRing

# Zero is always a square
if self.is_zero():
return True

base = self.base_ring()
n = base.order()

# Factor n
factorization = n.factor()

# Check if any prime power has exponent > 1
for p, e in factorization:
if e > 1:
raise NotImplementedError(
"is_square() not implemented for power series over rings with nonzero nilradical"
)

# n is squarefree - use CRT
# For each prime p, reduce mod p and check if square
primes = [p for p, e in factorization]

for p in primes:
Zp = IntegerModRing(p)
Rp = self.parent().change_ring(Zp)
# Map self to Rp
fp = Rp([Zp(c) for c in self.list()]).add_bigoh(self.prec())
# Check if square over the prime field
if not fp.is_square():
return False

return True

def sqrt(self, prec=None, extend=False, all=False, name=None):
r"""
Return a square root of ``self``.
Expand Down
Loading