Skip to content

Commit

Permalink
gh-37761: rings/infinite polynomial ring fixes
Browse files Browse the repository at this point in the history
    
Fix #37756
    
URL: #37761
Reported by: Martin Rubey
Reviewer(s): Martin Rubey, Travis Scrimshaw
  • Loading branch information
Release Manager committed Aug 27, 2024
2 parents 5ee375c + 7999167 commit 0ec42d1
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 35 deletions.
157 changes: 126 additions & 31 deletions src/sage/rings/polynomial/infinite_polynomial_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,41 @@ def is_nilpotent(self):
"""
return self._p.is_nilpotent()

def numerator(self):
r"""
Return a numerator of ``self``, computed as ``self * self.denominator()``.
.. WARNING::
This is not the numerator of the rational function
defined by ``self``, which would always be ``self`` since it is a
polynomial.
EXAMPLES::
sage: X.<x> = InfinitePolynomialRing(QQ)
sage: p = 2/3*x[1] + 4/9*x[2] - 2*x[1]*x[3]
sage: num = p.numerator(); num
-18*x_3*x_1 + 4*x_2 + 6*x_1
TESTS::
sage: num.parent()
Infinite polynomial ring in x over Rational Field
Check that :issue:`37756` is fixed::
sage: R.<a> = InfinitePolynomialRing(QQ)
sage: P.<x,y> = QQ[]
sage: FF = P.fraction_field()
sage: FF(a[0])
Traceback (most recent call last):
...
TypeError: Could not find a mapping of the passed element to this ring.
"""
P = self.parent()
return InfinitePolynomial(P, self._p.numerator())

@cached_method
def variables(self):
"""
Expand All @@ -569,9 +604,69 @@ def variables(self):
()
"""
if hasattr(self._p, 'variables'):
return tuple(self._p.variables())
P = self.parent()
return tuple(InfinitePolynomial(P, v) for v in self._p.variables())
return ()

def monomials(self):
"""
Return the list of monomials in ``self``.
The returned list is decreasingly ordered by the term ordering of
``self.parent()``.
EXAMPLES::
sage: X.<x> = InfinitePolynomialRing(QQ)
sage: p = x[1]^3 + x[2] - 2*x[1]*x[3]
sage: p.monomials()
[x_3*x_1, x_2, x_1^3]
sage: X.<x> = InfinitePolynomialRing(QQ, order='deglex')
sage: p = x[1]^3 + x[2] - 2*x[1]*x[3]
sage: p.monomials()
[x_1^3, x_3*x_1, x_2]
"""
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.<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"""
Expand Down Expand Up @@ -997,42 +1092,42 @@ def coefficient(self, monomial):
sage: a.coefficient({x[0]:1, x[1]:1})
2
"""
P = self.parent()
if self._p == 0:
res = 0
elif isinstance(monomial, self.__class__):
if not (self.parent().has_coerce_map_from(monomial.parent())):
res = 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:
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=self.parent().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=self.parent()._order)

res = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order)(R(self._p).coefficient(R(monomial._p)))
else:
R = PolynomialRing(self._p.base_ring(), VarList, order=self.parent()._order)
res = R(self._p).coefficient(R(monomial._p))
elif isinstance(monomial, dict):
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]
res = self.coefficient(K).coefficient(monomial)
else:
return self
else:
raise TypeError("Objects of type %s have no coefficients in InfinitePolynomials" % (type(monomial)))
return self.parent()(res)
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):
Expand Down
19 changes: 15 additions & 4 deletions src/sage/rings/polynomial/infinite_polynomial_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@
class InfinitePolynomialRingFactory(UniqueFactory):
"""
A factory for creating infinite polynomial ring elements. It
handles making sure that they are unique as well as handling
pickling. For more details, see
makes sure that they are unique as well as handling pickling.
For more details, see
:class:`~sage.structure.factory.UniqueFactory` and
:mod:`~sage.rings.polynomial.infinite_polynomial_ring`.
Expand Down Expand Up @@ -564,7 +564,7 @@ def __init__(self, parent, start):

def __next__(self):
"""
Return a dictionary that can be used to interprete strings in the base ring of ``self``.
Return a dictionary that can be used to interpret strings in the base ring of ``self``.
EXAMPLES::
Expand Down Expand Up @@ -701,7 +701,7 @@ def __init__(self, R, names, order):
names = ['x']
for n in names:
if not (isinstance(n, str) and n.isalnum() and (not n[0].isdigit())):
raise ValueError("generator names must be alpha-numeric strings not starting with a digit, but %s is not" % n)
raise ValueError("generator names must be alphanumeric strings not starting with a digit, but %s is not" % n)
if len(names) != len(set(names)):
raise ValueError("generator names must be pairwise different")
self._names = tuple(names)
Expand Down Expand Up @@ -884,6 +884,17 @@ def _element_constructor_(self, x):
Traceback (most recent call last):
...
ValueError: cannot convert 1/3 into an element of Infinite polynomial ring in x over Integer Ring
.. WARNING::
The :issue:`37756` is not yet fixed::
sage: L.<x, y> = QQ[]
sage: R.<a> = InfinitePolynomialRing(QQ)
sage: M = InfinitePolynomialRing(L, names=["a"])
sage: c = a[0]
sage: M(c) # known bug
a_0
"""
from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial
# In many cases, the easiest solution is to "simply" evaluate
Expand Down

0 comments on commit 0ec42d1

Please sign in to comment.