From 46b614990617ae872f54f757f9ed4d3d8b15dabb Mon Sep 17 00:00:00 2001 From: Hetarth Jodha Date: Fri, 28 Nov 2025 21:49:10 +0530 Subject: [PATCH 1/5] Fix #41221: Implement is_square for LaurentSeries --- .../rings/laurent_series_ring_element.pyx | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index e98ecdbb4f3..8d66f7f9f44 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -1547,6 +1547,72 @@ cdef class LaurentSeries(AlgebraElement): P = self._parent.change_ring(rev.parent().base_ring()) return P(rev) + def is_square(self, root=False): + r""" + Return whether this Laurent series is a square. + + INPUT: + + - ``root`` -- boolean (default: ``False``); if ``True``, return a pair + ``(True, sqrt)`` if this element is a square, and ``(False, None)`` + otherwise. + + EXAMPLES:: + + sage: R. = LaurentSeriesRing(QQ) + sage: (x^2).is_square() + True + sage: (x^3).is_square() + False + sage: (4/x^2 + 4/x + 1).is_square(root=True) + (True, 2*x^-1 + 1) + sage: R. = LaurentSeriesRing(ZZ) + sage: (t^-4).is_square() + True + sage: (2*t^-4).is_square() + False + """ + # Case 1: Handle Zero + if self.is_zero(): + if root: + return True, self + return True + + # Case 2: Valuation must be even + v = self.valuation() + if v % 2: + if root: + return False, None + return False + + # Case 3: The unit part must be a square + unit_part = (self >> v).power_series() + + # We use a try-except block to handle inconsistent API in base rings + try: + # Check is_square without keyword args first (safest) + is_sq = unit_part.is_square() + except (TypeError, ValueError, ArithmeticError, NotImplementedError): + if root: + return False, None + return False + + if not root: + return is_sq + + if is_sq: + # If we need the root, calculate it + # We try .sqrt() which is standard across most elements + try: + sqrt_unit = unit_part.sqrt() + except (ValueError, ArithmeticError): + return False, None + + # Reconstruct: t^(v/2) * sqrt(unit) + return True, self.parent()(sqrt_unit) << (v // 2) + else: + return False, None + def derivative(self, *args): """ The formal derivative of this Laurent series, with respect to From 257a1749fd48aa66d3ea2137b27d31c5b0afc91f Mon Sep 17 00:00:00 2001 From: Hetarth Jodha Date: Mon, 1 Dec 2025 17:12:30 +0530 Subject: [PATCH 2/5] Implement is_square for LazyLaurentSeries --- src/sage/rings/lazy_series.py | 58 +++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 9ffb120eb38..fc9f6738e11 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -3925,6 +3925,64 @@ class LazyLaurentSeries(LazyCauchyProductSeries): sage: f = 1 / (1 - z - z^2) sage: TestSuite(f).run() """ + def is_square(self, root=False): + r""" + Return whether this lazy series is a square. + + INPUT: + + - ``root`` -- boolean (default: ``False``); if ``True``, return a pair + ``(True, sqrt)`` if this element is a square, and ``(False, None)`` + otherwise. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: (z^2).is_square() + True + sage: (z^3).is_square() + False + sage: (1 + z).is_square() + True + """ + if self.is_zero(): + if root: + return True, self + return True + + v = self.valuation() + if v % 2 != 0: + if root: + return False, None + return False + + if v == 0: + unit_part = self + else: + P = self.parent() + z = P.gen() + unit_part = self * z**(-v) + + if not unit_part.coefficient(0).is_square(): + if root: + return False, None + return False + + try: + sqrt_unit = unit_part.sqrt() + + if root: + if v == 0: + return True, sqrt_unit + else: + return True, sqrt_unit * z**(v // 2) + return True + + except (ValueError, ArithmeticError): + if root: + return False, None + return False + def is_unit(self): """ Return whether this element is a unit in the ring. From bdcaec58254dade7fbf8d7049ecf969aeaf4125a Mon Sep 17 00:00:00 2001 From: Hetarth Jodha Date: Mon, 1 Dec 2025 17:15:54 +0530 Subject: [PATCH 3/5] Lint correction --- src/sage/rings/lazy_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index fc9f6738e11..2949dd574a9 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -3970,7 +3970,7 @@ def is_square(self, root=False): try: sqrt_unit = unit_part.sqrt() - + if root: if v == 0: return True, sqrt_unit From d8144b5a29c98c5c33a92bfdf47835836e9eac27 Mon Sep 17 00:00:00 2001 From: Hetarth Jodha <148987754+Hapsa21@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:24:53 +0530 Subject: [PATCH 4/5] Update src/sage/rings/lazy_series.py Co-authored-by: Travis Scrimshaw --- src/sage/rings/lazy_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 01ea26b6366..618ca89f3fa 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -3933,7 +3933,7 @@ def is_square(self, root=False): - ``root`` -- boolean (default: ``False``); if ``True``, return a pair ``(True, sqrt)`` if this element is a square, and ``(False, None)`` - otherwise. + otherwise EXAMPLES:: From f775ce72f65a1ba524314c930ed6119afb302486 Mon Sep 17 00:00:00 2001 From: Hetarth Jodha <148987754+Hapsa21@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:25:02 +0530 Subject: [PATCH 5/5] Update src/sage/rings/laurent_series_ring_element.pyx Co-authored-by: Travis Scrimshaw --- src/sage/rings/laurent_series_ring_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 8d66f7f9f44..22b52b002fa 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -1555,7 +1555,7 @@ cdef class LaurentSeries(AlgebraElement): - ``root`` -- boolean (default: ``False``); if ``True``, return a pair ``(True, sqrt)`` if this element is a square, and ``(False, None)`` - otherwise. + otherwise EXAMPLES::