From 963fd1973b87ac2dd07a20ff51b7b1e5a08e70ca Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 31 Jul 2025 13:47:30 +0200 Subject: [PATCH 1/8] fix gcd, coefficient and monomial_coefficient --- .../polynomial/infinite_polynomial_element.py | 370 +++++++++++------- 1 file changed, 237 insertions(+), 133 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 91ff5346b9b..dc52bb135bd 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -102,6 +102,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer +from sage.structure.element import parent from sage.structure.richcmp import richcmp from sage.misc.cachefunc import cached_method from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass @@ -630,43 +631,6 @@ def monomials(self): P = self.parent() return [InfinitePolynomial(P, m) for m in self._p.monomials()] - def monomial_coefficient(self, mon): - """ - Return the base ring element that is the coefficient of ``mon`` - in ``self``. - - This function contrasts with the function :meth:`coefficient`, - which returns the coefficient of a monomial viewing this - polynomial in a polynomial ring over a base ring having fewer - variables. - - INPUT: - - - ``mon`` -- a monomial in the parent of ``self`` - - OUTPUT: coefficient in base ring - - .. SEEALSO:: - - For coefficients in a base ring of fewer variables, - look at :meth:`coefficient`. - - EXAMPLES:: - - sage: X. = InfinitePolynomialRing(QQ) - sage: f = 2*x[0]*x[2] + 3*x[1]^2 - sage: c = f.monomial_coefficient(x[1]^2); c - 3 - sage: c.parent() - Rational Field - - sage: c = f.coefficient(x[2]); c - 2*x_0 - sage: c.parent() - Infinite polynomial ring in x over Rational Field - """ - return self._p.monomial_coefficient(mon._p) - @cached_method def max_index(self): r""" @@ -864,7 +828,7 @@ def squeezed(self): EXAMPLES:: - sage: X. = InfinitePolynomialRing(QQ,implementation='sparse') + sage: X. = InfinitePolynomialRing(QQ, implementation='sparse') sage: p = x[1]*y[100] + x[50]*y[1000] sage: p.squeezed() x_2*y_4 + x_1*y_3 @@ -1059,74 +1023,6 @@ def symmetric_cancellation_order(self, other): from sage.combinat.permutation import Permutation return (rawcmp, Permutation(P[1:]), OUT) - def coefficient(self, monomial): - """ - Return the coefficient of a monomial in this polynomial. - - INPUT: - - - A monomial (element of the parent of self) or - - a dictionary that describes a monomial (the keys - are variables of the parent of self, the values - are the corresponding exponents) - - EXAMPLES: - - We can get the coefficient in front of monomials:: - - sage: X. = InfinitePolynomialRing(QQ) - sage: a = 2*x[0]*x[1] + x[1] + x[2] - sage: a.coefficient(x[0]) - 2*x_1 - sage: a.coefficient(x[1]) - 2*x_0 + 1 - sage: a.coefficient(x[2]) - 1 - sage: a.coefficient(x[0]*x[1]) - 2 - - We can also pass in a dictionary:: - - sage: a.coefficient({x[0]:1, x[1]:1}) - 2 - """ - P = self.parent() - if self._p == 0: - return P.zero() - if isinstance(monomial, self.__class__): - if not P.has_coerce_map_from(monomial.parent()): - return P.zero() - if hasattr(self._p, 'variables'): - VarList = [str(X) for X in self._p.variables()] - else: - VarList = [] - if hasattr(monomial._p, 'variables'): - VarList.extend([str(X) for X in monomial._p.variables()]) - VarList = list(set(VarList)) - VarList.sort(key=P.varname_key, reverse=True) - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - if len(VarList) == 1: - # 'xx' is guaranteed to be no variable - # name of monomial, since coercions - # were tested before - R = PolynomialRing(self._p.base_ring(), VarList + ['xx'], order=P._order) - S = PolynomialRing(self._p.base_ring(), VarList, order=P._order) - res = S(R(self._p).coefficient(R(monomial._p))) - return InfinitePolynomial(P, res) - - R = PolynomialRing(self._p.base_ring(), VarList, order=P._order) - res = R(self._p).coefficient(R(monomial._p)) - return InfinitePolynomial(P, res) - - if isinstance(monomial, dict): - if monomial: - I = iter(monomial) - K = next(I) - del monomial[K] - return self.coefficient(K).coefficient(monomial) - return self - raise TypeError("Objects of type %s have no coefficients in InfinitePolynomials" % (type(monomial))) - # Essentials for Buchberger def reduce(self, I, tailreduce=False, report=None): r""" @@ -1263,25 +1159,6 @@ def __iter__(self): self.__class__(self.parent(), monomial)) for coefficient, monomial in self._p) - def gcd(self, x): - """ - Compute the greatest common divisor. - - EXAMPLES:: - - sage: R. = InfinitePolynomialRing(QQ) - sage: p1 = x[0] + x[1]^2 - sage: gcd(p1, p1 + 3) - 1 - sage: gcd(p1, p1) == p1 - True - """ - P = self.parent() - self._p = P._P(self._p) - x._p = P._P(x._p) - g = self._p.gcd(x._p) - return self.__class__.__base__(P, g) - class InfinitePolynomial_sparse(InfinitePolynomial): """ @@ -1298,7 +1175,7 @@ class InfinitePolynomial_sparse(InfinitePolynomial): EXAMPLES:: sage: A. = QQ[] - sage: B. = InfinitePolynomialRing(A,implementation='sparse') + sage: B. = InfinitePolynomialRing(A, implementation='sparse') sage: p = a*b[100] + 1/2*c[4] sage: p a*b_100 + 1/2*c_4 @@ -1309,12 +1186,11 @@ class InfinitePolynomial_sparse(InfinitePolynomial): Multivariate Polynomial Ring in b_100, b_0, c_4, c_0 over Univariate Polynomial Ring in a over Rational Field """ - def __call__(self, *args, **kwargs): """ EXAMPLES:: - sage: X. = InfinitePolynomialRing(QQ,implementation='sparse') + sage: X. = InfinitePolynomialRing(QQ, implementation='sparse') sage: a = x[0] + x[1] sage: a(x_0=2,x_1=x[1]) x_1 + 2 @@ -1369,7 +1245,7 @@ def _add_(self, x): """ EXAMPLES:: - sage: X. = InfinitePolynomialRing(QQ) + sage: X. = InfinitePolynomialRing(QQ, implementation='sparse') sage: x[1] + x[2] # indirect doctest x_2 + x_1 @@ -1379,7 +1255,7 @@ def _add_(self, x): sage: x[0] - x_0 0 """ - # One may need a new parent for self._p and x._p + # One may need a new parent for self._p and x._p try: return InfinitePolynomial_sparse(self.parent(), self._p + x._p) except Exception: @@ -1422,7 +1298,7 @@ def _sub_(self, x): """ EXAMPLES:: - sage: X. = InfinitePolynomialRing(QQ) + sage: X. = InfinitePolynomialRing(QQ, implementation='sparse') sage: x[2] - x[1] # indirect doctest x_2 - x_1 """ @@ -1532,7 +1408,7 @@ def _richcmp_(self, x, op): parent for the two rings can be constructed. This is why we have to have the order 'degrevlex':: - sage: X. = InfinitePolynomialRing(ZZ,order='degrevlex', implementation='sparse') + sage: X. = InfinitePolynomialRing(ZZ, order='degrevlex', implementation='sparse') sage: Y. = QQ[] sage: x[3] == x_3 # indirect doctest True @@ -1540,7 +1416,7 @@ def _richcmp_(self, x, op): Two infinite polynomial rings in different implementation and order:: - sage: Y = InfinitePolynomialRing(QQ,['x','y'],order='deglex', implementation='dense') + sage: Y = InfinitePolynomialRing(QQ,['x','y'], order='deglex', implementation='dense') sage: x[2] == Y(x[2]) # indirect doctest True @@ -1578,6 +1454,104 @@ def _richcmp_(self, x, op): fx = x._p.parent().hom(x._p.parent().variable_names(), R) return richcmp(fself(self._p), fx(x._p), op) + def gcd(self, x): + """ + Compute the greatest common divisor. + + EXAMPLES:: + + sage: R. = InfinitePolynomialRing(QQ, implementation="sparse") + sage: p1 = x[0] + x[1]^2 + sage: gcd(p1, p1 + 3) + 1 + sage: gcd(p1, p1) == p1 + True + """ + try: + return InfinitePolynomial_sparse(self.parent(), self._p.gcd(x._p)) + except Exception: + pass + # We can now assume that self._p and x._p actually are polynomials, + # hence, their parent is not simply the underlying ring. + VarList = list(set(self._p.parent().variable_names()).union(set(x._p.parent().variable_names()))) + VarList.sort(key=self.parent().varname_key, reverse=True) + if VarList: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) + else: + R = self._p.base_ring() + return InfinitePolynomial_sparse(self.parent(), R(self._p).gcd(R(x._p))) + + def coefficient(self, monomial): + """ + Return the coefficient of a monomial in this polynomial. + + INPUT: + + - A monomial (element of the parent of self) or + - a dictionary that describes a monomial (the keys + are variables of the parent of self, the values + are the corresponding exponents) + + EXAMPLES: + + We can get the coefficient in front of monomials:: + + sage: X. = InfinitePolynomialRing(QQ, implementation="sparse") + sage: a = 2*x[0]*x[1] + x[1] + x[2] + sage: a.coefficient(x[0]) + 2*x_1 + sage: a.coefficient(x[1]) + 2*x_0 + 1 + sage: a.coefficient(x[2]) + 1 + sage: a.coefficient(x[0]*x[1]) + 2 + + We can also pass in a dictionary:: + + sage: a.coefficient({x[0]:1, x[1]:1}) + 2 + + TESTS: + + Check that :issue:`40504` is fixed:: + + sage: R. = InfinitePolynomialRing(QQ, implementation="sparse") + sage: p = a[1]*a[2] + a[3]*a[4] + sage: p.coefficient(a[5]) + 0 + + sage: R. = InfinitePolynomialRing(QQ, implementation="sparse") + sage: P. = LazyPowerSeriesRing(R) + sage: f1 = P(lambda n: -a[n] if n else 1) + sage: diff(log(f1))[3] + -4*a_4 - 4*a_3*a_1 - 2*a_2^2 - 4*a_2*a_1^2 - a_1^4 + sage: diff(log(f1))[3].coefficient(a[3]*a[1]) + -4 + """ + if isinstance(monomial, dict): + if monomial: + I = iter(monomial) + K = next(I) + del monomial[K] + return self.coefficient(K).coefficient(monomial) + return self + + try: + c = self._p.coefficient(monomial._p) + except Exception: + VarList = list(set(self._p.parent().variable_names()).union(set(monomial._p.parent().variable_names()))) + VarList.sort(key=self.parent().varname_key, reverse=True) + if VarList: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) + else: + R = self._p.base_ring() + c = R(self._p).coefficient(R(monomial._p)) + + return InfinitePolynomial_sparse(self.parent(), c) + class InfinitePolynomial_dense(InfinitePolynomial): """ @@ -1765,3 +1739,133 @@ def p(m): # else, n is supposed to be an integer return InfinitePolynomial_dense(self.parent(), self._p**n) + + def gcd(self, x): + """ + Compute the greatest common divisor. + + EXAMPLES:: + + sage: R. = InfinitePolynomialRing(QQ) + sage: p1 = x[0] + x[1]^2 + sage: gcd(p1, p1 + 3) + 1 + sage: gcd(p1, p1) == p1 + True + """ + P = self.parent() + self._p = P._P(self._p) + x._p = P._P(x._p) + return InfinitePolynomial_dense(self.parent(), self._p.gcd(x._p)) + + def monomial_coefficient(self, mon): + """ + Return the base ring element that is the coefficient of ``mon`` + in ``self``. + + This function contrasts with the function :meth:`coefficient`, + which returns the coefficient of a monomial viewing this + polynomial in a polynomial ring over a base ring having fewer + variables. + + INPUT: + + - ``mon`` -- a monomial in the parent of ``self`` + + OUTPUT: coefficient in base ring + + .. SEEALSO:: + + For coefficients in a base ring of fewer variables, + look at :meth:`coefficient`. + + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(QQ) + sage: f = 2*x[0]*x[2] + 3*x[1]^2 + sage: c = f.monomial_coefficient(x[1]^2); c + 3 + sage: c.parent() + Rational Field + + sage: c = f.coefficient(x[2]); c + 2*x_0 + sage: c.parent() + Infinite polynomial ring in x over Rational Field + + TESTS:: + + sage: R. = InfinitePolynomialRing(QQ) + sage: m = a[1]*a[2] + sage: p = a[1]*a[2] + a[1]*a[2]*a[4] + sage: p.monomial_coefficient(m) + 1 + """ + P = self.parent() + if parent(mon) is not P: + raise TypeError("mon must be a monomial in the parent of self") + self._p = P._P(self._p) + mon._p = P._P(mon._p) + return self._p.monomial_coefficient(mon._p) + + def coefficient(self, monomial): + """ + Return the coefficient of a monomial in this polynomial. + + INPUT: + + - A monomial (element of the parent of self) or + - a dictionary that describes a monomial (the keys + are variables of the parent of self, the values + are the corresponding exponents) + + EXAMPLES: + + We can get the coefficient in front of monomials:: + + sage: X. = InfinitePolynomialRing(QQ) + sage: a = 2*x[0]*x[1] + x[1] + x[2] + sage: a.coefficient(x[0]) + 2*x_1 + sage: a.coefficient(x[1]) + 2*x_0 + 1 + sage: a.coefficient(x[2]) + 1 + sage: a.coefficient(x[0]*x[1]) + 2 + + We can also pass in a dictionary:: + + sage: a.coefficient({x[0]:1, x[1]:1}) + 2 + + TESTS: + + Check that :issue:`40504` is fixed:: + + sage: R. = InfinitePolynomialRing(QQ) + sage: p = a[1]*a[2] + a[3]*a[4] + sage: p.coefficient(a[5]) + 0 + + sage: R. = InfinitePolynomialRing(QQ) + sage: P. = LazyPowerSeriesRing(R) + sage: f1 = P(lambda n: -a[n] if n else 1) + sage: diff(log(f1))[3] + -4*a_4 - 4*a_3*a_1 - 2*a_2^2 - 4*a_2*a_1^2 - a_1^4 + sage: diff(log(f1))[3].coefficient(a[3]*a[1]) + -4 + """ + P = self.parent() + if self._p == 0: + return P.zero() + self._p = P._P(self._p) + if isinstance(monomial, dict): + x = {P._P(v): d for v, d in monomial.items()} + elif parent(monomial) is P: + monomial._p = P._P(monomial._p) + x = monomial._p + else: + raise TypeError(f"{monomial} must be a monomial in the parent of self") + + return InfinitePolynomial_dense(P, self._p.coefficient(x)) From e77877ef2ff3618e5c5b8e530bc9dba1d81624b5 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 31 Jul 2025 15:39:24 +0200 Subject: [PATCH 2/8] implement monomial_coefficient in the sparse case --- .../polynomial/infinite_polynomial_element.py | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index dc52bb135bd..52233548dd3 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -1482,6 +1482,71 @@ def gcd(self, x): R = self._p.base_ring() return InfinitePolynomial_sparse(self.parent(), R(self._p).gcd(R(x._p))) + def monomial_coefficient(self, mon): + """ + Return the base ring element that is the coefficient of ``mon`` + in ``self``. + + This function contrasts with the function :meth:`coefficient`, + which returns the coefficient of a monomial viewing this + polynomial in a polynomial ring over a base ring having fewer + variables. + + INPUT: + + - ``mon`` -- a monomial in the parent of ``self`` + + OUTPUT: coefficient in base ring + + .. SEEALSO:: + + For coefficients in a base ring of fewer variables, + look at :meth:`coefficient`. + + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(QQ, implementation="sparse") + sage: f = 2*x[0]*x[2] + 3*x[1]^2 + sage: c = f.monomial_coefficient(x[1]^2); c + 3 + sage: c.parent() + Rational Field + + sage: c = f.coefficient(x[2]); c + 2*x_0 + sage: c.parent() + Infinite polynomial ring in x over Rational Field + + TESTS:: + + sage: R. = InfinitePolynomialRing(QQ, implementation="sparse") + sage: m = a[1]*a[2] + sage: p = a[1]*a[2] + a[1]*a[2]*a[4] + sage: p.monomial_coefficient(m) + 1 + + sage: p = R(25) + sage: p.monomial_coefficient(R(1)) + 25 + """ + P = self.parent() + if parent(mon) is not P: + raise TypeError("mon must be a monomial in the parent of self") + + VarList = set(self._p.parent().variable_names()) + if any(str(v) not in VarList for v in mon._p.variables()): + return P.zero() + + if VarList: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + + VarList.update(mon._p.parent().variable_names()) + VarList = sorted(VarList, key=self.parent().varname_key, reverse=True) + R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) + return R(self._p).monomial_coefficient(R(mon._p)) + + return self._p.constant_coefficient() + def coefficient(self, monomial): """ Return the coefficient of a monomial in this polynomial. @@ -1800,6 +1865,10 @@ def monomial_coefficient(self, mon): sage: p = a[1]*a[2] + a[1]*a[2]*a[4] sage: p.monomial_coefficient(m) 1 + + sage: p = R(25) + sage: p.monomial_coefficient(R(1)) + 25 """ P = self.parent() if parent(mon) is not P: From 41155094e13fd283ac9280260768597cb4ca7832 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 31 Jul 2025 15:43:50 +0200 Subject: [PATCH 3/8] mild linting --- .../polynomial/infinite_polynomial_element.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 52233548dd3..6c7637294c4 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -75,9 +75,9 @@ We test whether coercion works, even in complicated cases in which finite polynomial rings are merged with infinite polynomial rings:: - sage: A. = InfinitePolynomialRing(ZZ,implementation='sparse',order='degrevlex') + sage: A. = InfinitePolynomialRing(ZZ, implementation='sparse', order='degrevlex') sage: B. = A[] - sage: C. = InfinitePolynomialRing(B,order='degrevlex') + sage: C. = InfinitePolynomialRing(B, order='degrevlex') sage: C Infinite polynomial ring in b, c over Infinite polynomial ring in a over Integer Ring sage: 1/2*b_1*a[4] + c[3] @@ -320,7 +320,7 @@ def _getAttributeNames(self): sage: X. = InfinitePolynomialRing(QQ) sage: import sage.interfaces.tab_completion as s sage: p = x[3]*x[2] - sage: s.completions('p.co',globals()) # indirect doctest + sage: s.completions('p.co', globals()) # indirect doctest ['p.coefficient', 'p.coefficients', 'p.constant_coefficient', @@ -338,7 +338,7 @@ def __dir__(self): sage: X. = InfinitePolynomialRing(QQ) sage: import sage.interfaces.tab_completion as s sage: p = x[3]*x[2] - sage: s.completions('p.co',globals()) # indirect doc test + sage: s.completions('p.co', globals()) # indirect doc test ['p.coefficient', 'p.coefficients', 'p.constant_coefficient', @@ -374,7 +374,7 @@ def __getattr__(self, s): sage: import sage.interfaces.tab_completion as s sage: p = alpha[3]*alpha[2]^2 - sage: s.completions('p.co',globals()) # indirect doc test + sage: s.completions('p.co', globals()) # indirect doc test ['p.coefficient', 'p.coefficients', 'p.constant_coefficient', @@ -483,7 +483,7 @@ def ring(self): EXAMPLES:: - sage: X. = InfinitePolynomialRing(ZZ,implementation='sparse') + sage: X. = InfinitePolynomialRing(ZZ, implementation='sparse') sage: p = x[100]*y[1]^3*x[1]^2 + 2*x[10]*y[30] sage: p.ring() Infinite polynomial ring in x, y over Integer Ring @@ -1416,7 +1416,7 @@ def _richcmp_(self, x, op): Two infinite polynomial rings in different implementation and order:: - sage: Y = InfinitePolynomialRing(QQ,['x','y'], order='deglex', implementation='dense') + sage: Y = InfinitePolynomialRing(QQ, ['x','y'], order='deglex', implementation='dense') sage: x[2] == Y(x[2]) # indirect doctest True From bb4269b24bce6b5b9f2174caba2cc786af10dc5e Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 1 Aug 2025 11:33:15 +0200 Subject: [PATCH 4/8] Apply suggestions from code review Co-authored-by: Travis Scrimshaw --- .../polynomial/infinite_polynomial_element.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 6c7637294c4..b722d941f29 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -1549,13 +1549,13 @@ def monomial_coefficient(self, mon): def coefficient(self, monomial): """ - Return the coefficient of a monomial in this polynomial. + Return the coefficient of a monomial in ``self``. INPUT: - - A monomial (element of the parent of self) or + - a monomial (element of the parent of ``self``) or - a dictionary that describes a monomial (the keys - are variables of the parent of self, the values + are variables of the parent of ``self``, the values are the corresponding exponents) EXAMPLES: @@ -1879,13 +1879,13 @@ def monomial_coefficient(self, mon): def coefficient(self, monomial): """ - Return the coefficient of a monomial in this polynomial. + Return the coefficient of a monomial in ``self``. INPUT: - - A monomial (element of the parent of self) or + - a monomial (element of the parent of ``self``) or - a dictionary that describes a monomial (the keys - are variables of the parent of self, the values + are variables of the parent of ``self``, the values are the corresponding exponents) EXAMPLES: @@ -1926,7 +1926,7 @@ def coefficient(self, monomial): -4 """ P = self.parent() - if self._p == 0: + if not self._p: return P.zero() self._p = P._P(self._p) if isinstance(monomial, dict): From 00984d103783be5ff32f9715db6f308a042e4784 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 1 Aug 2025 13:05:06 +0200 Subject: [PATCH 5/8] refactor common polynomial ring --- .../polynomial/infinite_polynomial_element.py | 194 +++++++++--------- 1 file changed, 94 insertions(+), 100 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index b722d941f29..cb8a06b6586 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -720,29 +720,6 @@ def _div_(self, x): # there remains a problem in reduction return FractionFieldElement(field, self, x, reduce=False) - def _floordiv_(self, x): - """ - EXAMPLES:: - - sage: X. = InfinitePolynomialRing(ZZ) - sage: x[2]//x[2] # indirect doctest - 1 - """ - try: - return InfinitePolynomial_sparse(self.parent(), self._p // x._p) - except Exception: - pass - # We can now assume that self._p and x._p actually are polynomials, - # hence, their parent is not just the underlying ring. - VarList = list(set(self._p.parent().variable_names()).union(set(x._p.parent().variable_names()))) - VarList.sort(key=self.parent().varname_key, reverse=True) - if VarList: - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) - else: - R = self._p.base_ring() - return InfinitePolynomial_sparse(self.parent(), R(self._p) // R(x._p)) - @cached_method def lm(self): """ @@ -1240,6 +1217,28 @@ def __call__(self, *args, **kwargs): except Exception: return res + def _common_polynomial_ring(self, x): + r""" + Return the polynomial ring that contains all variables of + ``self`` and of ``x``. + + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(QQ, implementation='sparse') + sage: x[10]._common_polynomial_ring(x[5]) + Multivariate Polynomial Ring in x_10, x_5, x_0 over Rational Field + """ + VarList = set(self._p.parent().variable_names()) + VarList.update(x._p.parent().variable_names()) + VarList = sorted(VarList, key=self.parent().varname_key, reverse=True) + if VarList: + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) + else: + # TODO: this is currently dead code + R = self._p.base_ring() + return R + # Basic arithmetics def _add_(self, x): """ @@ -1257,19 +1256,12 @@ def _add_(self, x): """ # One may need a new parent for self._p and x._p try: - return InfinitePolynomial_sparse(self.parent(), self._p + x._p) - except Exception: - pass - # We can now assume that self._p and x._p actually are polynomials, - # hence, their parent is not simply the underlying ring. - VarList = list(set(self._p.parent().variable_names()).union(set(x._p.parent().variable_names()))) - VarList.sort(key=self.parent().varname_key, reverse=True) - if VarList: - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) - else: - R = self._p.base_ring() - return InfinitePolynomial_sparse(self.parent(), R(self._p) + R(x._p)) + result = self._p + x._p + except TypeError: + R = self._common_polynomial_ring(x) + result = R(self._p) + R(x._p) + + return InfinitePolynomial_sparse(self.parent(), result) def _mul_(self, x): """ @@ -1280,19 +1272,12 @@ def _mul_(self, x): x_2*x_1 """ try: - return InfinitePolynomial_sparse(self.parent(), self._p * x._p) - except Exception: - pass - # We can now assume that self._p and x._p actually are polynomials, - # hence, their parent is not just the underlying ring. - VarList = list(set(self._p.parent().variable_names()).union(set(x._p.parent().variable_names()))) - VarList.sort(key=self.parent().varname_key, reverse=True) - if VarList: - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) - else: - R = self._p.base_ring() - return InfinitePolynomial_sparse(self.parent(), R(self._p) * R(x._p)) + result = self._p * x._p + except TypeError: + R = self._common_polynomial_ring(x) + result = R(self._p) * R(x._p) + + return InfinitePolynomial_sparse(self.parent(), result) def _sub_(self, x): """ @@ -1303,19 +1288,30 @@ def _sub_(self, x): x_2 - x_1 """ try: - return InfinitePolynomial_sparse(self.parent(), self._p - x._p) - except Exception: - pass - # We can now assume that self._p and x._p actually are polynomials, - # hence, their parent is not just the underlying ring. - VarList = list(set(self._p.parent().variable_names()).union(x._p.parent().variable_names())) - VarList.sort(key=self.parent().varname_key, reverse=True) - if VarList: - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) - else: - R = self._p.base_ring() - return InfinitePolynomial_sparse(self.parent(), R(self._p) - R(x._p)) + result = self._p - x._p + except TypeError: + R = self._common_polynomial_ring(x) + result = R(self._p) - R(x._p) + + return InfinitePolynomial_sparse(self.parent(), result) + + def _floordiv_(self, x): + """ + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(ZZ, implementation="sparse") + sage: x[2] // x[2] # indirect doctest + 1 + sage: (x[2]^2 - 1) // (x[2] + 1) + x_2 - 1 + """ + try: + result = self._p // x._p + except TypeError: + R = self._common_polynomial_ring(x) + result = R(self._p) // R(x._p) + + return InfinitePolynomial_sparse(self.parent(), result) def __pow__(self, n): """ @@ -1435,15 +1431,10 @@ def _richcmp_(self, x, op): from sage.structure.element import parent R1 = parent(self._p) R2 = parent(x._p) - if (hasattr(R1, 'has_coerce_map_from') and R1.has_coerce_map_from(R2)) or (hasattr(R2, 'has_coerce_map_from') and R2.has_coerce_map_from(R1)): + if ((hasattr(R1, 'has_coerce_map_from') and R1.has_coerce_map_from(R2)) + or (hasattr(R2, 'has_coerce_map_from') and R2.has_coerce_map_from(R1))): return richcmp(self._p, x._p, op) - VarList = list(set(self._p.parent().variable_names()).union(x._p.parent().variable_names())) - VarList.sort(key=self.parent().varname_key, reverse=True) - if VarList: - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) - else: - R = self._p.base_ring() + R = self._common_polynomial_ring(x) if (self._p.parent() is self._p.base_ring()) or not self._p.parent().gens(): fself = self._p.base_ring() else: @@ -1468,19 +1459,12 @@ def gcd(self, x): True """ try: - return InfinitePolynomial_sparse(self.parent(), self._p.gcd(x._p)) - except Exception: - pass - # We can now assume that self._p and x._p actually are polynomials, - # hence, their parent is not simply the underlying ring. - VarList = list(set(self._p.parent().variable_names()).union(set(x._p.parent().variable_names()))) - VarList.sort(key=self.parent().varname_key, reverse=True) - if VarList: - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) - else: - R = self._p.base_ring() - return InfinitePolynomial_sparse(self.parent(), R(self._p).gcd(R(x._p))) + result = self._p.gcd(x._p) + except (ValueError, TypeError): + R = self._common_polynomial_ring(x) + result = R(self._p).gcd(R(x._p)) + + return InfinitePolynomial_sparse(self.parent(), result) def monomial_coefficient(self, mon): """ @@ -1547,7 +1531,7 @@ def monomial_coefficient(self, mon): return self._p.constant_coefficient() - def coefficient(self, monomial): + def coefficient(self, x): """ Return the coefficient of a monomial in ``self``. @@ -1595,27 +1579,22 @@ def coefficient(self, monomial): sage: diff(log(f1))[3].coefficient(a[3]*a[1]) -4 """ - if isinstance(monomial, dict): - if monomial: - I = iter(monomial) + if isinstance(x, dict): + if x: + I = iter(x) K = next(I) - del monomial[K] - return self.coefficient(K).coefficient(monomial) + del x[K] + return self.coefficient(K).coefficient(x) + return self try: - c = self._p.coefficient(monomial._p) - except Exception: - VarList = list(set(self._p.parent().variable_names()).union(set(monomial._p.parent().variable_names()))) - VarList.sort(key=self.parent().varname_key, reverse=True) - if VarList: - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) - else: - R = self._p.base_ring() - c = R(self._p).coefficient(R(monomial._p)) + result = self._p.coefficient(x._p) + except TypeError: + R = self._common_polynomial_ring(x) + result = R(self._p).coefficient(R(x._p)) - return InfinitePolynomial_sparse(self.parent(), c) + return InfinitePolynomial_sparse(self.parent(), result) class InfinitePolynomial_dense(InfinitePolynomial): @@ -1746,6 +1725,21 @@ def _sub_(self, x): x._p = P._P(x._p) return InfinitePolynomial_dense(self.parent(), self._p - x._p) + def _floordiv_(self, x): + """ + EXAMPLES:: + + sage: X. = InfinitePolynomialRing(ZZ) + sage: x[2] // x[2] # indirect doctest + 1 + sage: (x[2]^2 - 1) // (x[2] + 1) + x_2 - 1 + """ + P = self.parent() + self._p = P._P(self._p) + x._p = P._P(x._p) + return InfinitePolynomial_dense(self.parent(), self._p // x._p) + def __pow__(self, n): """ Exponentiation by an integer, or action by a callable object. From 25d8c21e859dbabf61b0c4efbcd431b67d01dcf6 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 1 Aug 2025 13:06:48 +0200 Subject: [PATCH 6/8] use items --- src/sage/rings/polynomial/infinite_polynomial_element.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index cb8a06b6586..0dbd04900a2 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -1190,8 +1190,7 @@ def __call__(self, *args, **kwargs): V = [str(x) for x in self._p.variables()] else: V = [] - for kw in kwargs: - value = kwargs[kw] + for kw, value in kwargs.items(): if isinstance(value, InfinitePolynomial): kwargs[kw] = value._p V.append(kw) @@ -1629,8 +1628,7 @@ def __call__(self, *args, **kwargs): x_100 + x_0 """ # Replace any InfinitePolynomials by their underlying polynomials - for kw in kwargs: - value = kwargs[kw] + for kw, value in kwargs.items(): if isinstance(value, InfinitePolynomial): kwargs[kw] = value._p args = list(args) From 8d87a844889af2d5aa6e5d5b1d5be5a9ff9d1801 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 1 Aug 2025 14:53:24 +0200 Subject: [PATCH 7/8] minor simplification --- .../polynomial/infinite_polynomial_element.py | 42 +++++++------------ 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index 0dbd04900a2..cd837525309 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -853,8 +853,8 @@ def footprint(self): [(2, [7]), (4, [1]), (12, [3])] """ if not self._has_footprint: - PARENT = self.parent() - l = len(self.parent()._names) + P = self.parent() + l = len(P._names) # get the pairs (shift,exponent) of the leading monomial, indexed by the variable names Vars = self._p.parent().variable_names() from sage.rings.polynomial.multi_polynomial import MPolynomial_libsingular @@ -864,7 +864,7 @@ def footprint(self): # self._p is multivariate, but not libsingular, hence, # exponents is slow and does not accept the optional argument as_ETuples. # Thus, fall back to regular expressions - L = PARENT._find_varpowers.findall(repr(self.lm()._p)) + L = P._find_varpowers.findall(repr(self.lm()._p)) L = [((x[0:2]), int(x[2]) if x[2] else 1) for x in L] else: # it is a univariate polynomial -- this should never happen, but just in case... L = [(Vars[0].split('_'), self._p.degree())] @@ -873,7 +873,7 @@ def footprint(self): s = int(t[0][1]) # the variable *s*hift if s not in self._footprint: self._footprint[s] = [0]*l - self._footprint[s][self.parent()._name_dict[n]] = t[1] # the exponent + self._footprint[s][P._name_dict[n]] = t[1] # the exponent self._has_footprint = True return self._footprint @@ -1524,8 +1524,8 @@ def monomial_coefficient(self, mon): from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing VarList.update(mon._p.parent().variable_names()) - VarList = sorted(VarList, key=self.parent().varname_key, reverse=True) - R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order) + VarList = sorted(VarList, key=P.varname_key, reverse=True) + R = PolynomialRing(self._p.base_ring(), VarList, order=P._order) return R(self._p).monomial_coefficient(R(mon._p)) return self._p.constant_coefficient() @@ -1668,19 +1668,9 @@ def _richcmp_(self, x, op): sage: p < q False """ - # We can assume that self and x belong to the same ring. - # We can not assume yet that self._p and - # x._p are already defined over self.parent()._P - # It won't hurt to change self in place. - # But, to be on the safe side... - try: - self._p = self.parent()._P(self._p) - except Exception: - pass - try: - x._p = x.parent()._P(x._p) - except Exception: - pass + P = self.parent() + self._p = P._P(self._p) + x._p = P._P(x._p) return richcmp(self._p, x._p, op) # Basic arithmetics @@ -1695,7 +1685,7 @@ def _add_(self, x): P = self.parent() self._p = P._P(self._p) x._p = P._P(x._p) - return InfinitePolynomial_dense(self.parent(), self._p + x._p) + return InfinitePolynomial_dense(P, self._p + x._p) def _mul_(self, x): """ @@ -1708,7 +1698,7 @@ def _mul_(self, x): P = self.parent() self._p = P._P(self._p) x._p = P._P(x._p) - return InfinitePolynomial_dense(self.parent(), self._p * x._p) + return InfinitePolynomial_dense(P, self._p * x._p) def _sub_(self, x): """ @@ -1721,7 +1711,7 @@ def _sub_(self, x): P = self.parent() self._p = P._P(self._p) x._p = P._P(x._p) - return InfinitePolynomial_dense(self.parent(), self._p - x._p) + return InfinitePolynomial_dense(P, self._p - x._p) def _floordiv_(self, x): """ @@ -1736,7 +1726,7 @@ def _floordiv_(self, x): P = self.parent() self._p = P._P(self._p) x._p = P._P(x._p) - return InfinitePolynomial_dense(self.parent(), self._p // x._p) + return InfinitePolynomial_dense(P, self._p // x._p) def __pow__(self, n): """ @@ -1792,10 +1782,10 @@ def p(m): newVars.extend([PPgens[sh-p(j)] for j in range(blocklength, -1, -1)]) sh += nM mapR = PP.hom(newVars, PP) - return InfinitePolynomial_dense(self.parent(), mapR(self._p)) + return InfinitePolynomial_dense(P, mapR(self._p)) # else, n is supposed to be an integer - return InfinitePolynomial_dense(self.parent(), self._p**n) + return InfinitePolynomial_dense(P, self._p**n) def gcd(self, x): """ @@ -1813,7 +1803,7 @@ def gcd(self, x): P = self.parent() self._p = P._P(self._p) x._p = P._P(x._p) - return InfinitePolynomial_dense(self.parent(), self._p.gcd(x._p)) + return InfinitePolynomial_dense(P, self._p.gcd(x._p)) def monomial_coefficient(self, mon): """ From d9d7c5c658ca3e1e355afcc07b5ff482770d61a7 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 1 Aug 2025 16:28:16 +0200 Subject: [PATCH 8/8] test a non-libsingular case --- .../polynomial/infinite_polynomial_element.py | 120 +++++++++++++++++- 1 file changed, 115 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/polynomial/infinite_polynomial_element.py b/src/sage/rings/polynomial/infinite_polynomial_element.py index cd837525309..e5823442a9f 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_element.py +++ b/src/sage/rings/polynomial/infinite_polynomial_element.py @@ -1244,7 +1244,7 @@ def _add_(self, x): EXAMPLES:: sage: X. = InfinitePolynomialRing(QQ, implementation='sparse') - sage: x[1] + x[2] # indirect doctest + sage: x[1] + x[2] # indirect doctest x_2 + x_1 Check adding from a different parent:: @@ -1252,8 +1252,13 @@ def _add_(self, x): sage: Y. = PolynomialRing(QQ) sage: x[0] - x_0 0 + + TESTS:: + + sage: X. = InfinitePolynomialRing(QQbar, implementation='sparse') + sage: x[1] + x[3] + x_3 + x_1 """ - # One may need a new parent for self._p and x._p try: result = self._p + x._p except TypeError: @@ -1267,8 +1272,14 @@ def _mul_(self, x): EXAMPLES:: sage: X. = InfinitePolynomialRing(ZZ) - sage: x[2]*x[1] # indirect doctest + sage: x[2] * x[1] # indirect doctest x_2*x_1 + + TESTS:: + + sage: X. = InfinitePolynomialRing(QQbar, implementation='sparse') + sage: x[1] * x[3] + x_3*x_1 """ try: result = self._p * x._p @@ -1285,6 +1296,12 @@ def _sub_(self, x): sage: X. = InfinitePolynomialRing(QQ, implementation='sparse') sage: x[2] - x[1] # indirect doctest x_2 - x_1 + + TESTS:: + + sage: X. = InfinitePolynomialRing(QQbar, implementation='sparse') + sage: x[1] - x[3] + -x_3 + x_1 """ try: result = self._p - x._p @@ -1303,6 +1320,12 @@ def _floordiv_(self, x): 1 sage: (x[2]^2 - 1) // (x[2] + 1) x_2 - 1 + + TESTS:: + + sage: X. = InfinitePolynomialRing(QQbar, implementation='sparse') + sage: (x[2]^2 - 1) // (x[2] + 1) + x_2 - 1 """ try: result = self._p // x._p @@ -1330,6 +1353,12 @@ def __pow__(self, n): sage: P = Permutation(((1,2),(3,4,5))) sage: p^P # indirect doctest x_10*y_1 + 2*x_2*y_4 + + TESTS:: + + sage: X. = InfinitePolynomialRing(QQbar, implementation='sparse') + sage: (x[3] + x[1])^2 + x_3^2 + 2*x_3*x_1 + x_1^2 """ P = self.parent() if callable(n): @@ -1465,6 +1494,35 @@ def gcd(self, x): return InfinitePolynomial_sparse(self.parent(), result) + def quo_rem(self, x): + """ + Return quotient and remainder. + + EXAMPLES:: + + sage: R. = InfinitePolynomialRing(QQ, implementation="sparse") + sage: p = 1 + 3*x[0]*x[1] + 2*x[2] + sage: q = x[0] - 1 + sage: p.quo_rem(q) + (3*x_1, 2*x_2 + 3*x_1 + 1) + + TESTS:: + + sage: R. = InfinitePolynomialRing(QQbar, implementation="sparse") + sage: p = 1 + 3*x[0]*x[1] + 2*x[2] + sage: q = x[0] - 1 + sage: p.quo_rem(q) + (3*x_1, 2*x_2 + 3*x_1 + 1) + """ + try: + result = self._p.quo_rem(x._p) + except (ValueError, TypeError): + R = self._common_polynomial_ring(x) + result = R(self._p).quo_rem(R(x._p)) + + return (InfinitePolynomial_sparse(self.parent(), result[0]), + InfinitePolynomial_sparse(self.parent(), result[1])) + def monomial_coefficient(self, mon): """ Return the base ring element that is the coefficient of ``mon`` @@ -1679,8 +1737,14 @@ def _add_(self, x): EXAMPLES:: sage: X. = InfinitePolynomialRing(QQ) - sage: x[1] + x[2] # indirect doctest + sage: x[1] + x[2] # indirect doctest x_2 + x_1 + + TESTS:: + + sage: X. = InfinitePolynomialRing(QQbar) + sage: x[1] + x[3] + x_3 + x_1 """ P = self.parent() self._p = P._P(self._p) @@ -1692,8 +1756,14 @@ def _mul_(self, x): EXAMPLES:: sage: X. = InfinitePolynomialRing(QQ) - sage: x[2]*x[1] # indirect doctest + sage: x[2]*x[1] # indirect doctest x_2*x_1 + + TESTS:: + + sage: X. = InfinitePolynomialRing(QQbar) + sage: x[1]*x[3] + x_3*x_1 """ P = self.parent() self._p = P._P(self._p) @@ -1707,6 +1777,12 @@ def _sub_(self, x): sage: X. = InfinitePolynomialRing(QQ) sage: x[2] - x[1] # indirect doctest x_2 - x_1 + + TESTS:: + + sage: X. = InfinitePolynomialRing(QQbar) + sage: x[1] - x[3] + -x_3 + x_1 """ P = self.parent() self._p = P._P(self._p) @@ -1722,6 +1798,12 @@ def _floordiv_(self, x): 1 sage: (x[2]^2 - 1) // (x[2] + 1) x_2 - 1 + + TESTS:: + + sage: X. = InfinitePolynomialRing(QQbar) + sage: (x[2]^2 - 1) // (x[2] + 1) + x_2 - 1 """ P = self.parent() self._p = P._P(self._p) @@ -1805,6 +1887,34 @@ def gcd(self, x): x._p = P._P(x._p) return InfinitePolynomial_dense(P, self._p.gcd(x._p)) + def quo_rem(self, x): + """ + Return quotient and remainder. + + EXAMPLES:: + + sage: R. = InfinitePolynomialRing(QQ) + sage: p = 1 + 3*x[0]*x[1] + 2*x[2] + sage: q = x[0] - 1 + sage: p.quo_rem(q) + (3*x_1, 2*x_2 + 3*x_1 + 1) + + TESTS:: + + sage: R. = InfinitePolynomialRing(QQbar) + sage: p = 1 + 3*x[0]*x[1] + 2*x[2] + sage: q = x[0] - 1 + sage: p.quo_rem(q) + (3*x_1, 2*x_2 + 3*x_1 + 1) + """ + P = self.parent() + self._p = P._P(self._p) + x._p = P._P(x._p) + result = (self._p).quo_rem(x._p) + + return (InfinitePolynomial_dense(P, result[0]), + InfinitePolynomial_dense(P, result[1])) + def monomial_coefficient(self, mon): """ Return the base ring element that is the coefficient of ``mon``