# Ticket #13412: trac_13412_category_for_power_series_rings.patch

File trac_13412_category_for_power_series_rings.patch, 20.8 KB (added by SimonKing, 7 years ago)

Implement category and coercion framework for power series rings

• ## sage/rings/multi_power_series_ring.py

```# HG changeset patch
# User Simon King <simon.king@uni-jena.de>
# Date 1346332026 -7200
# Node ID 7956267cdba8eebcd928587db2384b629218c021
# Parent  e39420b4a8a950c8a51dabb70a1ca88a3b4df720
#13412: Implement category and coercion framework for power series rings

diff --git a/sage/rings/multi_power_series_ring.py b/sage/rings/multi_power_series_ring.py```
 a sage: R. = PowerSeriesRing(QQ); R Multivariate Power Series Ring in t, u, v over Rational Field sage: TestSuite(R).run() sage: p = -t + 1/2*t^3*u - 1/4*t^4*u + 2/3*v^5 + R.O(6); p -t + 1/2*t^3*u - 1/4*t^4*u + 2/3*v^5 + O(t, u, v)^6 sage: p in R sage: M = PowerSeriesRing(QQ, 4, 'k'); M Multivariate Power Series Ring in k0, k1, k2, k3 over Rational Field sage: loads(dumps(M)) is M True sage: TestSuite(M).run() sage: H = PowerSeriesRing(PolynomialRing(ZZ,3,'z'),4,'f'); H Multivariate Power Series Ring in f0, f1, f2, f3 over Multivariate Polynomial Ring in z0, z1, z2 over Integer Ring sage: TestSuite(H).run() sage: loads(dumps(H)) is H True sage: z = H.base_ring().gens() sage: f = H.gens() -30077*x + 9485*x*y - 6260*y^3 + 12870*x^2*y^2 - 20289*y^4 + O(x, y)^5 sage: s in S True sage: TestSuite(S).run() sage: loads(dumps(S)) is S True - Use double square bracket notation:: sage: T. = PowerSeriesRing(ZZ,order='deglex'); T Multivariate Power Series Ring in a, b over Integer Ring sage: TestSuite(T).run() sage: loads(dumps(T)) is T True sage: T.term_order() Degree lexicographic term order sage: p = - 2*b^6 + a^5*b^2 + a^7 - b^2 - a*b^3 + T.O(9); p sage: type(x) sage: type(S(x)) sage: f = S(2/7 -100*x^2 + 1/3*x*y + y^2).O(3); f 5 - x^2 + 4*x*y + y^2 + O(x, y)^3 AUTHORS: - Niles Johnson (2010-07): initial code - Simon King (2012-08): Use category and coercion framework, :trac:`13412` """ #***************************************************************************** from sage.rings.commutative_ring import is_CommutativeRing from sage.rings.commutative_ring import is_CommutativeRing, CommutativeRing from sage.rings.polynomial.all import PolynomialRing, is_MPolynomialRing, is_PolynomialRing from sage.rings.polynomial.term_order import TermOrder from sage.rings.power_series_ring import PowerSeriesRing, PowerSeriesRing_generic, is_PowerSeriesRing import sage.misc.latex as latex from sage.structure.nonexact import Nonexact from sage.structure.parent_gens import ParentWithGens from sage.rings.multi_power_series_ring_element import MPowerSeries False """ return type(x) == MPowerSeriesRing_generic return isinstance(x, MPowerSeriesRing_generic) class MPowerSeriesRing_generic(PowerSeriesRing_generic, Nonexact): # # variable_names_recursive : works just fine # # __call__ : works just fine # # __contains__ : works just fine # # base_extend : works just fine #### notes # # sparse setting may not be implemented completely # Element = MPowerSeries def __init__(self, base_ring, num_gens, name_list, order='negdeglex', default_prec=10, sparse=False): """ raise ValueError("Multivariate Polynomial Rings must have more than 0 variables.") self._ngens = n self._has_singular = False #cannot convert to Singular by default ParentWithGens.__init__(self, base_ring, name_list) # Multivariate power series rings inherit from power series rings. But # apparently we can not call their initialisation. Instead, initialise # CommutativeRing and Nonexact: CommutativeRing.__init__(self, base_ring, name_list) Nonexact.__init__(self, default_prec) # underlying polynomial ring in which to represent elements self._bg_power_series_ring = PowerSeriesRing(self._poly_ring_, 'Tbg', sparse=sparse, default_prec=default_prec) self._bg_indeterminate = self._bg_power_series_ring.gen() ## use the following in PowerSeriesRing_generic.__call__ self._PowerSeriesRing_generic__power_series_class = MPowerSeries self._is_sparse = sparse self._params = (base_ring, num_gens, name_list, order, default_prec, sparse) self._populate_coercion_lists_() def __reduce__(self): """ EXAMPLES:: sage: R. = PowerSeriesRing(ZZ) sage: S = loads(dumps(R)); S Multivariate Power Series Ring in t, u, v over Integer Ring sage: type(S) sage: R. = PowerSeriesRing(QQ, default_prec=10, sparse=True) sage: S = loads(dumps(R)); S Sparse Multivariate Power Series Ring in x, y, z over Rational Field sage: type(S) """ return unpickle_multi_power_series_ring_v0, self._params def _repr_(self): """ Prints out a multivariate power series ring. - any ring that coerces into the foreground polynomial ring of this ring EXAMPLES:: sage: A = GF(17)[['x','y']] sage: A.has_coerce_map_from(ZZ) True sage: A.has_coerce_map_from(ZZ['x']) True sage: A.has_coerce_map_from(ZZ['y','x']) True sage: A.has_coerce_map_from(ZZ[['x']]) True sage: A.has_coerce_map_from(ZZ[['y','x']]) True sage: A.has_coerce_map_from(ZZ['x','z']) False sage: A.has_coerce_map_from(GF(3)['x','y']) False sage: A.has_coerce_map_from(Frac(ZZ['y','x'])) False TESTS:: return self._poly_ring().has_coerce_map_from(P) def _element_constructor_(self,f): def _element_constructor_(self,f,prec=None): """ TESTS:: Multivariate Power Series Ring in t0, t1, t2, t3, t4 over Integer Ring """ try: prec = f.prec() except AttributeError: prec = infinity return MPowerSeries(self, f, prec) if prec is None: try: prec = f.prec() except AttributeError: prec = infinity return self.element_class(parent=self, x=f, prec=prec) def __cmp__(self, other): """ if n < 0 or n >= self._ngens: raise ValueError("Generator not defined.") #return self(self._poly_ring().gens()[int(n)]) return MPowerSeries(self,self._poly_ring().gens()[int(n)], is_gen=True) return self.element_class(parent=self,x=self._poly_ring().gens()[int(n)], is_gen=True) def ngens(self): """
• ## sage/rings/multi_power_series_ring_element.py

`diff --git a/sage/rings/multi_power_series_ring_element.py b/sage/rings/multi_power_series_ring_element.py`
 a AUTHORS: - Niles Johnson (07/2010): initial code - Simon King (08/2012): Use category and coercion framework, :trac:`13412` """ """ return type(f) == MPowerSeries return isinstance(f, MPowerSeries) sage: loads(dumps(f)) == f True """ from sage.rings.power_series_ring_element import make_element_from_parent_v0 return make_element_from_parent_v0, (self._parent,self._bg_value,self._prec) return self.__class__, (self._parent,self._bg_value,self._prec) def __call__(self, *x, **kwds): """
• ## sage/rings/power_series_poly.pyx

`diff --git a/sage/rings/power_series_poly.pyx b/sage/rings/power_series_poly.pyx`
 a sage: f == loads(dumps(f)) # indirect doctest True """ # do *not* delete old versions. return make_powerseries_poly_v0, (self._parent, self.__f, self._prec, self.__is_gen) return self.__class__, (self._parent, self.__f, self._prec, self.__is_gen) def __richcmp__(left, right, int op): """
• ## sage/rings/power_series_ring.py

`diff --git a/sage/rings/power_series_ring.py b/sage/rings/power_series_ring.py`
 a AUTHORS: - William Stein: the code - Jeremy Cho (2006-05-17): some examples (above) - Niles Johnson (2010-09): implement multivariate power series - Simon King (2012-08): use category and coercion framework, :trac:`13412` TESTS:: sage: R. = PowerSeriesRing(QQ) sage: R == loads(dumps(R)) sage: R is loads(dumps(R)) True sage: TestSuite(R).run() :: sage: R. = PowerSeriesRing(QQ, sparse=True) sage: R == loads(dumps(R)) sage: R is loads(dumps(R)) True sage: TestSuite(R).run() :: sage: M = PowerSeriesRing(QQ, 't,u,v,w', default_prec=20) sage: M == loads(dumps(M)) sage: M is loads(dumps(M)) True sage: TestSuite(M).run() """ from sage.interfaces.magma import MagmaElement from sage.misc.sage_eval import sage_eval from sage.structure.parent_gens import ParentWithGens _cache = {} from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import normalize_names import sage.categories.commutative_rings as commutative_rings def PowerSeriesRing(base_ring, name=None, arg2=None, names=None, sparse=False, default_prec=None, order='negdeglex', num_gens=None): sage: R = PowerSeriesRing(QQ, 10) Traceback (most recent call last): ... ValueError: first letter of variable name must be a letter ValueError: first letter of variable name must be a letter: 10 :: if not names is None: name = names try: name = gens.normalize_names(1, name) name = normalize_names(1, name) except TypeError: raise TypeError, "illegal variable name" raise TypeError, "You must specify the name of the indeterminate of the Power series ring." key = (base_ring, name, default_prec, sparse) if _cache.has_key(key): R = _cache[key]() if not R is None: return R if PowerSeriesRing_generic.__classcall__.is_in_cache(key): return PowerSeriesRing_generic(*key) if isinstance(name, (tuple, list)): assert len(name) == 1 if not (name is None or isinstance(name, str)): raise TypeError, "variable name must be a string or None" if isinstance(base_ring, field.Field): R = PowerSeriesRing_over_field(base_ring, name, default_prec, sparse=sparse) elif isinstance(base_ring, integral_domain.IntegralDomain): R = PowerSeriesRing_generic(base_ring, name, default_prec, sparse=sparse) else: raise TypeError, "base_ring must be a commutative ring" _cache[key] = weakref.ref(R) return R def _multi_variate(base_ring, num_gens=None, names=None, num_gens = len(names) else: raise TypeError("variable names must be a string, tuple or list") names = normalize_names(num_gens, names) num_gens = len(names) if default_prec is None: default_prec = 12 key = (base_ring, num_gens, str(names), order, default_prec, sparse) if _cache.has_key(key): R = _cache[key]() if not R is None: return R if isinstance(base_ring, field.Field): pass elif isinstance(base_ring, integral_domain.IntegralDomain): pass elif isinstance(base_ring, commutative_ring.CommutativeRing): pass else: if base_ring not in commutative_rings.CommutativeRings(): raise TypeError, "base_ring must be a commutative ring" from sage.rings.multi_power_series_ring import MPowerSeriesRing_generic R = MPowerSeriesRing_generic(base_ring, num_gens, names, order=order, default_prec=default_prec, sparse=sparse) _cache[key] = weakref.ref(R) return R else: return False class PowerSeriesRing_generic(commutative_ring.CommutativeRing, Nonexact): class PowerSeriesRing_generic(UniqueRepresentation, commutative_ring.CommutativeRing, Nonexact): """ A power series ring. sage: TestSuite(R).run() """ Element = power_series_poly.PowerSeries_poly def __init__(self, base_ring, name=None, default_prec=20, sparse=False, use_lazy_mpoly_ring=False): """ for it to truncate away higher order terms (this is called automatically before printing). """ commutative_ring.CommutativeRing.__init__(self, base_ring, names=name) Nonexact.__init__(self, default_prec) R = PolynomialRing(base_ring, name, sparse=sparse) self.__poly_ring = R self.__power_series_class = power_series_poly.PowerSeries_poly self.__generator = self.__power_series_class(self, R.gen(), check=True, is_gen=True) self.__is_sparse = sparse self.__params = (base_ring, name, default_prec, sparse) names = K.variable_names() + (name,) self.__mpoly_ring = PolynomialRing(K.base_ring(), names=names) assert is_MPolynomialRing(self.__mpoly_ring) self.__power_series_class = power_series_mpoly.PowerSeries_mpoly self.Element = power_series_mpoly.PowerSeries_mpoly commutative_ring.CommutativeRing.__init__(self, base_ring, names=name) Nonexact.__init__(self, default_prec) self.__generator = self.element_class(self, R.gen(), check=True, is_gen=True) def variable_names_recursive(self, depth=None): r""" all = all[-depth:] return all def __reduce__(self): """ TESTS:: sage: R. = PowerSeriesRing(ZZ) sage: S = loads(dumps(R)); S Power Series Ring in t over Integer Ring sage: type(S) sage: R. = PowerSeriesRing(QQ, default_prec=10, sparse=True); R Sparse Power Series Ring in t over Rational Field sage: S = loads(dumps(R)); S Sparse Power Series Ring in t over Rational Field sage: type(S) """ return unpickle_power_series_ring_v0, self.__params def _repr_(self): """ Prints out a power series ring. EXAMPLES:: sage: R = GF(17)[['y']] sage: latex(R) sage: latex(R)  # indirect doctest \Bold{F}_{17}[[y]] sage: R = GF(17)[['y12']] sage: latex(R) """ return "%s[[%s]]"%(latex.latex(self.base_ring()), self.latex_variable_names()[0]) def __call__(self, f, prec=infinity, check=True): def _coerce_map_from_(self, S): """ A coercion from `S` exists, if `S` coerces into ``self``'s base ring, or if `S` is a univariate polynomial or power series ring with the same variable name as self, defined over a base ring that coerces into ``self``'s base ring. EXAMPLES:: sage: A = GF(17)[['x']] sage: A.has_coerce_map_from(ZZ)  # indirect doctest True sage: A.has_coerce_map_from(ZZ['x']) True sage: A.has_coerce_map_from(ZZ['y']) False sage: A.has_coerce_map_from(ZZ[['x']]) True """ if self.base_ring().has_coerce_map_from(S): return True if (is_PolynomialRing(S) or is_PowerSeriesRing(S)) and self.base_ring().has_coerce_map_from(S.base_ring()) \ and self.variable_names()==S.variable_names(): return True def _element_constructor_(self, f, prec=infinity, check=True): """ Coerce object to this power series ring. EXAMPLES:: sage: R. = PowerSeriesRing(ZZ) sage: R(t+O(t^5)) sage: R(t+O(t^5))    # indirect doctest t + O(t^5) sage: R(13) 13 elif isinstance(f, MagmaElement) and str(f.Type()) == 'RngSerPowElt': v = sage_eval(f.Eltseq()) return self(v) * (self.gen(0)**f.Valuation()) return self.__power_series_class(self, f, prec, check=check) return self.element_class(self, f, prec, check=check) def construction(self): """ EXAMPLES:: sage: R. = PowerSeriesRing(ZZ) sage: R._coerce_(t + t^2) sage: R._coerce_(t + t^2)  # indirect doctest t + t^2 sage: R._coerce_(1/t) Traceback (most recent call last): ... TypeError: no canonical coercion of element into self TypeError: no canonical coercion from Laurent Series Ring in t over Integer Ring to Power Series Ring in t over Integer Ring sage: R._coerce_(5) 5 sage: tt = PolynomialRing(ZZ,'t').gen() sage: R._coerce_(1/2) Traceback (most recent call last): ... TypeError: no canonical coercion of element into self TypeError: no canonical coercion from Rational Field to Power Series Ring in t over Integer Ring sage: S. = PowerSeriesRing(ZZ) sage: R._coerce_(s) Traceback (most recent call last): ... TypeError: no canonical coercion of element into self TypeError: no canonical coercion from Power Series Ring in s over Integer Ring to Power Series Ring in t over Integer Ring We illustrate canonical coercion between power series rings with compatible base rings:: sage: S._coerce_(g) Traceback (most recent call last): ... TypeError: no natural map between bases of power series rings TypeError: no canonical coercion from Power Series Ring in t over Univariate Polynomial Ring in w over Finite Field of size 7 to Power Series Ring in t over Integer Ring """ try: P = x.parent()
• ## sage/rings/power_series_ring_element.pyx

`diff --git a/sage/rings/power_series_ring_element.pyx b/sage/rings/power_series_ring_element.pyx`
 a AUTHORS: - William Stein - David Harvey (2006-09-11): added solve_linear_de() method - Robert Bradshaw (2007-04): sqrt, rmul, lmul, shifting - Robert Bradshaw (2007-04): Cython version - Simon King (2012-08): use category and coercion framework, :trac:`13412` EXAMPLE:: sage: R. = PowerSeriesRing(ZZ) sage: TestSuite(R).run() sage: R([1,2,3]) 1 + 2*x + 3*x^2 sage: R([1,2,3], 10) def make_powerseries_poly_v0(parent,  f, prec, is_gen): # This is only used to unpickle old pickles. The new pickling # works differently! import power_series_poly return power_series_poly.PowerSeries_poly(parent, f, prec, 0, is_gen) def make_element_from_parent_v0(parent, *args): # This is only used to unpickle old pickles. The new pickling # works differently! return parent(*args)