Skip to content

Commit

Permalink
enhancements in infinite polynomial rings
Browse files Browse the repository at this point in the history
  • Loading branch information
fchapoton committed Nov 5, 2023
1 parent ebef87a commit 37d3c84
Showing 1 changed file with 120 additions and 45 deletions.
165 changes: 120 additions & 45 deletions src/sage/rings/polynomial/infinite_polynomial_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,11 +261,15 @@
import re
from functools import reduce

from sage.rings.ring import CommutativeRing
from sage.categories.cartesian_product import cartesian_product
from sage.categories.rings import Rings
from sage.misc.cachefunc import cached_method
from sage.rings.integer import Integer
from sage.rings.ring import CommutativeRing
from sage.sets.family import Family
from sage.sets.non_negative_integers import NonNegativeIntegers
from sage.structure.all import SageObject, parent
from sage.structure.factory import UniqueFactory
from sage.misc.cachefunc import cached_method

###################################################
# The Construction Functor
Expand Down Expand Up @@ -487,11 +491,13 @@ def __getitem__(self, k):
"""
if not isinstance(k, str):
raise KeyError("string expected")
if '_' not in k:
raise KeyError(f"{k} is not a variable name")
L = k.split('_')
try:
if len(L) == 2:
return self._D[L[0]][int(L[1])]
except Exception:
except (TypeError, ValueError):
pass
raise KeyError("%s is not a variable name" % k)

Expand Down Expand Up @@ -614,7 +620,7 @@ def __getitem__(self, k):
return D[k]
except KeyError:
pass
raise KeyError("%s is not a variable name of %s or its iterated base rings" % (k, self._P))
raise KeyError("{} is not a variable name of {} or its iterated base rings".format(k, self._P))


##############################################################
Expand Down Expand Up @@ -739,7 +745,7 @@ def __init__(self, R, names, order):

# some basic data
self._order = order
self._name_dict = dict([(names[i], i) for i in range(len(names))])
self._name_dict = {names[i]: i for i in range(len(names))}
from sage.categories.commutative_algebras import CommutativeAlgebras
CommutativeRing.__init__(self, R, category=CommutativeAlgebras(R))

Expand All @@ -755,7 +761,7 @@ def __repr__(self):
sage: X.<alpha,beta> = InfinitePolynomialRing(ZZ, order='deglex'); X
Infinite polynomial ring in alpha, beta over Integer Ring
"""
return "Infinite polynomial ring in %s over %s" % (", ".join(self._names), self._base)
return "Infinite polynomial ring in {} over {}".format(", ".join(self._names), self._base)

def _latex_(self):
r"""
Expand All @@ -767,8 +773,8 @@ def _latex_(self):
\Bold{Q}[x_{\ast}, y_{\ast}]
"""
from sage.misc.latex import latex
vars = ', '.join(latex(X) for X in self.gens())
return "%s[%s]" % (latex(self.base_ring()), vars)
vars = ', '.join(latex(self.gen(i)) for i in range(len(self._names)))
return f"{latex(self.base_ring())}[{vars}]"

@cached_method
def _an_element_(self):
Expand All @@ -794,7 +800,7 @@ def one(self):
1
"""
from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial
return InfinitePolynomial(self, self._base(1))
return InfinitePolynomial(self, self._base.one())

#####################
# coercion
Expand Down Expand Up @@ -856,7 +862,7 @@ def _coerce_map_from_(self, S):
# We do not care about the orders. But base ring and generators
# of the pushout should remain the same as in self.
return P._names == self._names and P._base == self._base
except Exception:
except (TypeError, ValueError):
return False

def _element_constructor_(self, x):
Expand Down Expand Up @@ -890,21 +896,21 @@ def _element_constructor_(self, x):
ValueError: cannot convert 1/3 into an element of Infinite polynomial ring in x over Integer Ring
"""
from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial

# In many cases, the easiest solution is to "simply" evaluate
# the string representation.
from sage.misc.sage_eval import sage_eval
if isinstance(x, str):
try:
x = sage_eval(x, self.gens_dict())
except Exception:
raise ValueError("cannot convert %s into an element of %s" % (x, self))
except (TypeError, ValueError, SyntaxError):
raise ValueError(f"cannot convert {x} into an element of {self}")

Check warning on line 907 in src/sage/rings/polynomial/infinite_polynomial_ring.py

View check run for this annotation

Codecov / codecov/patch

src/sage/rings/polynomial/infinite_polynomial_ring.py#L906-L907

Added lines #L906 - L907 were not covered by tests
P = parent(x)
if P is self:
return x
elif self._base.has_coerce_map_from(P):
if self._base.has_coerce_map_from(P):
return InfinitePolynomial(self, self._base(x))
else:
raise ValueError("cannot convert %s into an element of %s" % (x, self))
raise ValueError(f"cannot convert {x} into an element of {self}")

if isinstance(parent(x), InfinitePolynomialRing_sparse):
# the easy case - parent == self - is already past
Expand All @@ -926,7 +932,7 @@ def _element_constructor_(self, x):
# remark: Conversion to self._P (if applicable)
# is done in InfinitePolynomial()
return InfinitePolynomial(self, x)
except Exception:
except (TypeError, ValueError):
pass

# By now, we can assume that x has a parent, because
Expand All @@ -937,8 +943,8 @@ def _element_constructor_(self, x):
if not hasattr(x, 'variables'):
try:
return sage_eval(repr(x), self.gens_dict())
except Exception:
raise ValueError("cannot convert %s into an element of %s" % (x, self))
except (TypeError, ValueError, SyntaxError):
raise ValueError(f"cannot convert {x} into an element of {self}")

# direct conversion will only be used if the underlying polynomials are libsingular.
from sage.rings.polynomial.multi_polynomial import MPolynomial_libsingular
Expand All @@ -965,7 +971,7 @@ def _element_constructor_(self, x):
# This tests admissibility on the fly:
VarList.sort(key=self.varname_key, reverse=True)
except ValueError:
raise ValueError("cannot convert %s into an element of %s - variables are not admissible" % (x, self))
raise ValueError("cannot convert {} into an element of {} - variables are not admissible".format(x, self))

Check warning on line 974 in src/sage/rings/polynomial/infinite_polynomial_ring.py

View check run for this annotation

Codecov / codecov/patch

src/sage/rings/polynomial/infinite_polynomial_ring.py#L974

Added line #L974 was not covered by tests
xmaxind = max([int(v.split('_')[1]) for v in VarList])
try:
# Apparently, in libsingular, the polynomial conversion is not done by
Expand All @@ -981,13 +987,13 @@ def _element_constructor_(self, x):
# conversion to self._P will be done in InfinitePolynomial.__init__
return InfinitePolynomial(self, x)
except (ValueError, TypeError, NameError):
raise ValueError("cannot convert %s (from %s, but variables %s) into an element of %s - no conversion into underlying polynomial ring %s" % (x, x.parent(), x.variables(), self, self._P))
raise ValueError("cannot convert {} (from {}, but variables {}) into an element of {} - no conversion into underlying polynomial ring {}".format(x, x.parent(), x.variables(), self, self._P))

Check warning on line 990 in src/sage/rings/polynomial/infinite_polynomial_ring.py

View check run for this annotation

Codecov / codecov/patch

src/sage/rings/polynomial/infinite_polynomial_ring.py#L990

Added line #L990 was not covered by tests
# By now, x or self._P are not libsingular. Since MPolynomialRing_polydict
# is too buggy, we use string evaluation
try:
return sage_eval(repr(x), self.gens_dict())
except (ValueError, TypeError, NameError):
raise ValueError("cannot convert %s into an element of %s - no conversion into underlying polynomial ring" % (x, self))
except (ValueError, TypeError, NameError, SyntaxError):
raise ValueError("cannot convert {} into an element of {} - no conversion into underlying polynomial ring".format(x, self))

# By now, we are in the sparse case.
try:
Expand All @@ -998,7 +1004,7 @@ def _element_constructor_(self, x):
# This tests admissibility on the fly:
VarList.sort(key=self.varname_key, reverse=True)
except ValueError:
raise ValueError("cannot convert %s into an element of %s - variables are not admissible" % (x, self))
raise ValueError("cannot convert {} into an element of {} - variables are not admissible".format(x, self))

if len(VarList) == 1:
# univariate polynomial rings are crab. So, make up another variable.
Expand All @@ -1017,7 +1023,7 @@ def _element_constructor_(self, x):
try:
VarList.sort(key=self.varname_key, reverse=True)
except ValueError:
raise ValueError("cannot convert %s into an element of %s; the variables are not admissible" % (x, self))
raise ValueError("cannot convert {} into an element of {}; the variables are not admissible".format(x, self))

Check warning on line 1026 in src/sage/rings/polynomial/infinite_polynomial_ring.py

View check run for this annotation

Codecov / codecov/patch

src/sage/rings/polynomial/infinite_polynomial_ring.py#L1026

Added line #L1026 was not covered by tests

from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
R = PolynomialRing(self._base, VarList, order=self._order)
Expand All @@ -1029,18 +1035,18 @@ def _element_constructor_(self, x):
# of x.parent() and R, then R(x) raises an error (which, I think,
# is a bug, since we talk here about conversion, not coercion).
# Hence, for being on the safe side, we coerce into a pushout ring:
x = R(1) * x
x = R.one() * x
return InfinitePolynomial(self, x)
except Exception:
except (TypeError, ValueError):
# OK, last resort, to be on the safe side
try:
return sage_eval(repr(x), self.gens_dict())
except (ValueError, TypeError, NameError):
raise ValueError("cannot convert %s into an element of %s; conversion of the underlying polynomial failed" % (x, self))
except (ValueError, TypeError, NameError, SyntaxError):
raise ValueError("cannot convert {} into an element of {}; conversion of the underlying polynomial failed".format(x, self))

Check warning on line 1045 in src/sage/rings/polynomial/infinite_polynomial_ring.py

View check run for this annotation

Codecov / codecov/patch

src/sage/rings/polynomial/infinite_polynomial_ring.py#L1044-L1045

Added lines #L1044 - L1045 were not covered by tests
try:
return sage_eval(repr(x), self.gens_dict())
except (ValueError, TypeError, NameError):
raise ValueError("cannot convert %s into an element of %s" % (x, self))
except (ValueError, TypeError, NameError, SyntaxError):
raise ValueError(f"cannot convert {x} into an element of {self}")

Check warning on line 1049 in src/sage/rings/polynomial/infinite_polynomial_ring.py

View check run for this annotation

Codecov / codecov/patch

src/sage/rings/polynomial/infinite_polynomial_ring.py#L1048-L1049

Added lines #L1048 - L1049 were not covered by tests

def tensor_with_ring(self, R):
"""
Expand Down Expand Up @@ -1085,7 +1091,7 @@ def tensor_with_ring(self, R):
# try to find the correct base ring in other ways:
try:
o = B.one() * R.one()
except Exception:
except (TypeError, ValueError):

Check warning on line 1094 in src/sage/rings/polynomial/infinite_polynomial_ring.py

View check run for this annotation

Codecov / codecov/patch

src/sage/rings/polynomial/infinite_polynomial_ring.py#L1094

Added line #L1094 was not covered by tests
raise TypeError("we cannot tensor with " + repr(R))
return InfinitePolynomialRing(o.parent(), self._names, self._order, implementation='sparse')

Expand Down Expand Up @@ -1212,39 +1218,92 @@ def ngens(self):
sage: X.<x1,x2> = InfinitePolynomialRing(QQ)
sage: X.ngens()
2
"""
return len(self._names)

@cached_method
def gen(self, i=None):
def gen(self, *args):
"""
Return the `i^{th}` 'generator' (see the description in :meth:`.ngens`)
of this infinite polynomial ring.
Return a 'generator' of this infinite polynomial ring.
EXAMPLES::
See the description in :meth:`.ngens`.
EXAMPLES:
When called with no argument or an integer,
this returns an indexed generator::
sage: X = InfinitePolynomialRing(QQ)
sage: x = X.gen()
sage: x = X.gen(); x
x_*
sage: x[1]
x_1
sage: X.gen() is X.gen(0)
True
sage: XX = InfinitePolynomialRing(GF(5))
sage: XX.gen(0) is XX.gen()
True
When called with a string, a tuple or 2 arguments,
this returns one specific generator::
sage: X = InfinitePolynomialRing(QQ, ('a','b'))
sage: X.gen('a_0')
a_0
sage: X.gen(('a', 2))
a_2
sage: X.gen('b', 1)
b_1
TESTS::
sage: X = InfinitePolynomialRing(QQ, ('a','b'))
sage: X.gen('c_0')
Traceback (most recent call last):
...
ValueError: invalid input for gen
sage: X.gen(('a', -1))
Traceback (most recent call last):
...
ValueError: invalid input for gen
sage: X.gen(4)
Traceback (most recent call last):
...
ValueError: index is too large
sage: X.gen(1,2,3)
Traceback (most recent call last):
...
ValueError: too many arguments
"""
if i is not None and i > len(self._names):
raise ValueError
j = i if i is not None else 0
res = InfinitePolynomialGen(self, self._names[j])
if i is None:
if not args:
# custom cache, empty arg -> 0
key = ((0,), ())
if key in self._cache__gen:
return self._cache__gen[key]
else:
res = InfinitePolynomialGen(self, self._names[0])
self._cache__gen[key] = res
return res
return res

if len(args) == 1:
arg = args[0]
if isinstance(arg, (int, Integer)):
if arg > len(self._names):
raise ValueError('index is too large')
return InfinitePolynomialGen(self, self._names[arg])
elif isinstance(arg, tuple):
var_name, index = arg
elif isinstance(args[0], str):
var_name, sindex = arg.split('_')
index = int(sindex)
elif len(args) == 2:
var_name, index = args
else:
raise ValueError('too many arguments')

if var_name not in self._names or index < 0:
raise ValueError('invalid input for gen')
return InfinitePolynomialGen(self, var_name)[index]

def _first_ngens(self, n):
"""
Expand All @@ -1260,6 +1319,22 @@ def _first_ngens(self, n):
# but in order.
return self.gens()[-n:]

@cached_method
def all_gens(self):
"""
Return a Family object containing the infinitely many
``{var_name:variable}`` pairs.
EXAMPLES::
sage: R = InfinitePolynomialRing(ZZ, 'a')
sage: D = R.all_gens()
sage: D[('a',5)]
a_5
"""
return Family(cartesian_product([self._names, NonNegativeIntegers()]),
lambda vi: self.gen(vi))

@cached_method
def gens_dict(self):
"""
Expand Down Expand Up @@ -1528,7 +1603,7 @@ def __str__(self):
sage: print(x) # indirect doctest
Generator for the x's in Infinite polynomial ring in x, y over Rational Field
"""
return "Generator for the %s's in %s" % (self._name, self._parent)
return f"Generator for the {self._name}'s in {self._parent}"


##############################################################
Expand Down Expand Up @@ -1623,7 +1698,7 @@ def tensor_with_ring(self, R):
# try to find the correct base ring in other ways:
try:
o = B.one() * R.one()
except Exception:
except (TypeError, ValueError):

Check warning on line 1701 in src/sage/rings/polynomial/infinite_polynomial_ring.py

View check run for this annotation

Codecov / codecov/patch

src/sage/rings/polynomial/infinite_polynomial_ring.py#L1701

Added line #L1701 was not covered by tests
raise TypeError("we cannot tensor with " + repr(R))
return InfinitePolynomialRing(o.parent(), self._names, self._order, implementation='dense')

Expand Down

0 comments on commit 37d3c84

Please sign in to comment.