Skip to content

Commit f767762

Browse files
Release Managervbraun
Release Manager
authored andcommitted
Trac #24420: Laurent power series fail unique representation
{{{ sage: L.<q> = LaurentSeriesRing(QQ) sage: LaurentSeriesRing(QQ, 'q') is L False }}} to be compared with {{{ sage: R.<q> = PolynomialRing(QQ) sage: PolynomialRing(QQ, 'q') is R True }}} We also fix {{{ sage: LaurentSeriesRing(Zmod(4), 'x').category() Category of fields }}} We also remove the classes `LaurentSeriesRing_generic`, `LaurentSeriesRing_domain`, `LaurentSeriesRing_field` in favor of a unique `LaurentSeriesRing`. URL: https://trac.sagemath.org/24420 Reported by: vdelecroix Ticket author(s): Vincent Delecroix Reviewer(s): Travis Scrimshaw
2 parents c3eb6d1 + bcf577d commit f767762

File tree

3 files changed

+158
-103
lines changed

3 files changed

+158
-103
lines changed

src/sage/rings/laurent_series_ring.py

+151-91
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,38 @@
1919
"""
2020
from __future__ import print_function, absolute_import
2121

22-
from .laurent_series_ring_element import LaurentSeries
22+
from sage.categories.rings import Rings
23+
from sage.categories.integral_domains import IntegralDomains
24+
from sage.categories.fields import Fields
25+
from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationFields
2326

27+
from .laurent_series_ring_element import LaurentSeries
2428
from . import polynomial
25-
from . import ring
29+
from .ring import CommutativeRing
2630

27-
from sage.libs.pari.all import pari_gen
28-
from sage.categories.fields import Fields
29-
from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationFields
3031
from sage.structure.unique_representation import UniqueRepresentation
3132
from sage.misc.cachefunc import cached_method
3233

33-
def LaurentSeriesRing(base_ring, name=None, names=None, default_prec=None, sparse=False):
34+
def is_LaurentSeriesRing(x):
3435
"""
36+
Return ``True`` if this is a *univariate* Laurent series ring.
37+
38+
This is in keeping with the behavior of ``is_PolynomialRing``
39+
versus ``is_MPolynomialRing``.
40+
41+
TESTS::
42+
43+
sage: from sage.rings.laurent_series_ring import is_LaurentSeriesRing
44+
sage: K.<q> = LaurentSeriesRing(QQ)
45+
sage: is_LaurentSeriesRing(K)
46+
True
47+
"""
48+
return isinstance(x, LaurentSeriesRing)
49+
50+
class LaurentSeriesRing(UniqueRepresentation, CommutativeRing):
51+
r"""
52+
Univariate Laurent Series Ring.
53+
3554
EXAMPLES::
3655
3756
sage: R = LaurentSeriesRing(QQ, 'x'); R
@@ -73,6 +92,26 @@ def LaurentSeriesRing(base_ring, name=None, names=None, default_prec=None, spars
7392
sage: W is T
7493
False
7594
95+
sage: K = LaurentSeriesRing(CC, 'q')
96+
sage: K
97+
Laurent Series Ring in q over Complex Field with 53 bits of precision
98+
sage: loads(K.dumps()) == K
99+
True
100+
sage: P = QQ[['x']]
101+
sage: F = Frac(P)
102+
sage: TestSuite(F).run()
103+
104+
When the base ring `k` is a field, the ring `k((x))` is a CDVF, that is
105+
a field equipped with a discrete valuation for which it is complete.
106+
The appropriate (sub)category is automatically set in this case::
107+
108+
sage: k = GF(11)
109+
sage: R.<x> = k[[]]
110+
sage: F = Frac(R)
111+
sage: F.category()
112+
Category of infinite complete discrete valuation fields
113+
sage: TestSuite(F).run()
114+
76115
TESTS:
77116
78117
Check if changing global series precision does it right (and
@@ -87,85 +126,94 @@ def LaurentSeriesRing(base_ring, name=None, names=None, default_prec=None, spars
87126
sage: 1/(1 - 2*x)
88127
1 + 2*x + 4*x^2 + 8*x^3 + 16*x^4 + O(x^5)
89128
sage: set_series_precision(20)
90-
"""
91-
if not names is None:
92-
name = names
93-
if name is None:
94-
raise TypeError("you must specify the name of the indeterminate of the Laurent series ring")
95-
96-
if default_prec is None:
97-
from sage.misc.defaults import series_precision
98-
default_prec = series_precision()
99-
100-
if isinstance(base_ring, ring.Field):
101-
return LaurentSeriesRing_field(base_ring, name, default_prec, sparse)
102-
elif isinstance(base_ring, ring.IntegralDomain):
103-
return LaurentSeriesRing_domain(base_ring, name, default_prec, sparse)
104-
elif isinstance(base_ring, ring.CommutativeRing):
105-
return LaurentSeriesRing_generic(base_ring, name, default_prec, sparse)
106-
else:
107-
raise TypeError("base_ring must be a commutative ring")
108129
109-
def is_LaurentSeriesRing(x):
110-
"""
111-
Return ``True`` if this is a *univariate* Laurent series ring.
112-
113-
This is in keeping with the behavior of ``is_PolynomialRing``
114-
versus ``is_MPolynomialRing``.
115-
116-
TESTS::
130+
Check categories (:trac:`24420`)::
117131
118-
sage: from sage.rings.laurent_series_ring import is_LaurentSeriesRing
119-
sage: K.<q> = LaurentSeriesRing(QQ)
120-
sage: is_LaurentSeriesRing(K)
121-
True
132+
sage: LaurentSeriesRing(ZZ, 'x').category()
133+
Category of infinite integral domains
134+
sage: LaurentSeriesRing(QQ, 'x').category()
135+
Category of infinite complete discrete valuation fields
136+
sage: LaurentSeriesRing(Zmod(4), 'x').category()
137+
Category of infinite commutative rings
122138
"""
123-
return isinstance(x, LaurentSeriesRing_generic)
139+
Element = LaurentSeries
124140

125-
class LaurentSeriesRing_generic(UniqueRepresentation, ring.CommutativeRing):
126-
r"""
127-
Univariate Laurent Series Ring.
141+
@staticmethod
142+
def __classcall__(cls, *args, **kwds):
143+
r"""
144+
TESTS::
128145
129-
EXAMPLES::
146+
sage: L = LaurentSeriesRing(QQ, 'q')
147+
sage: L is LaurentSeriesRing(QQ, name='q')
148+
True
149+
sage: loads(dumps(L)) is L
150+
True
151+
"""
152+
from .power_series_ring import PowerSeriesRing, is_PowerSeriesRing
130153

131-
sage: K = LaurentSeriesRing(CC, 'q')
132-
sage: K
133-
Laurent Series Ring in q over Complex Field with 53 bits of precision
134-
sage: loads(K.dumps()) == K
135-
True
136-
sage: P = QQ[['x']]
137-
sage: F = Frac(P)
138-
sage: TestSuite(F).run()
154+
if not kwds and len(args) == 1 and is_PowerSeriesRing(args[0]):
155+
power_series = args[0]
156+
else:
157+
power_series = PowerSeriesRing(*args, **kwds)
139158

140-
When the base ring `k` is a field, the ring `k((x))` is a CDVF, that is
141-
a field equipped with a discrete valuation for which it is complete.
142-
The appropriate (sub)category is automatically set in this case::
159+
return UniqueRepresentation.__classcall__(cls, power_series)
143160

144-
sage: k = GF(11)
145-
sage: R.<x> = k[[]]
146-
sage: F = Frac(R)
147-
sage: F.category()
148-
Category of complete discrete valuation fields
149-
sage: TestSuite(F).run()
150-
"""
151-
def __init__(self, base_ring, name=None, default_prec=None, sparse=False, category=None):
161+
def __init__(self, power_series):
152162
"""
153163
Initialization
154164
155165
EXAMPLES::
156166
157-
sage: K.<q> = LaurentSeriesRing(QQ,default_prec=4); K
167+
sage: K.<q> = LaurentSeriesRing(QQ, default_prec=4); K
158168
Laurent Series Ring in q over Rational Field
159169
sage: 1 / (q-q^2)
160170
q^-1 + 1 + q + q^2 + O(q^3)
161-
"""
162-
from .power_series_ring import PowerSeriesRing
163-
ring.CommutativeRing.__init__(self, base_ring, names=name,
164-
category=getattr(self, '_default_category', Fields()))
165-
self._power_series_ring = PowerSeriesRing(self.base_ring(),
166-
self.variable_name(),
167-
default_prec=default_prec,
168-
sparse=sparse)
171+
172+
sage: RZZ = LaurentSeriesRing(ZZ, 't')
173+
sage: RZZ.category()
174+
Category of infinite integral domains
175+
sage: TestSuite(RZZ).run()
176+
177+
sage: R1 = LaurentSeriesRing(Zmod(1), 't')
178+
sage: R1.category()
179+
Category of finite commutative rings
180+
sage: TestSuite(R1).run()
181+
182+
sage: R2 = LaurentSeriesRing(Zmod(2), 't')
183+
sage: R2.category()
184+
Category of infinite complete discrete valuation fields
185+
sage: TestSuite(R2).run()
186+
187+
sage: R4 = LaurentSeriesRing(Zmod(4), 't')
188+
sage: R4.category()
189+
Category of infinite commutative rings
190+
sage: TestSuite(R4).run()
191+
192+
sage: RQQ = LaurentSeriesRing(QQ, 't')
193+
sage: RQQ.category()
194+
Category of infinite complete discrete valuation fields
195+
sage: TestSuite(RQQ).run()
196+
"""
197+
base_ring = power_series.base_ring()
198+
if base_ring in Fields():
199+
category = CompleteDiscreteValuationFields()
200+
elif base_ring in IntegralDomains():
201+
category = IntegralDomains()
202+
elif base_ring in Rings().Commutative():
203+
category = Rings().Commutative()
204+
else:
205+
raise ValueError('unrecognized base ring')
206+
207+
if base_ring.is_zero():
208+
category = category.Finite()
209+
else:
210+
category = category.Infinite()
211+
212+
CommutativeRing.__init__(self, base_ring,
213+
names=power_series.variable_names(),
214+
category=category)
215+
216+
self._power_series_ring = power_series
169217

170218
def base_extend(self, R):
171219
"""
@@ -184,6 +232,38 @@ def base_extend(self, R):
184232
else:
185233
raise TypeError("no valid base extension defined")
186234

235+
def fraction_field(self):
236+
r"""
237+
Return the fraction field of this ring of Laurent series.
238+
239+
If the base ring is a field, then Laurent series are already a field.
240+
If the base ring is a domain, then the Laurent series over its fraction
241+
field is returned. Otherwise, raise a ``ValueError``.
242+
243+
EXAMPLES::
244+
245+
sage: R = LaurentSeriesRing(ZZ, 't', 30).fraction_field()
246+
sage: R
247+
Laurent Series Ring in t over Rational Field
248+
sage: R.default_prec()
249+
30
250+
251+
sage: LaurentSeriesRing(Zmod(4), 't').fraction_field()
252+
Traceback (most recent call last):
253+
...
254+
ValueError: must be an integral domain
255+
"""
256+
from sage.categories.integral_domains import IntegralDomains
257+
from sage.categories.fields import Fields
258+
if self in Fields():
259+
return self
260+
elif self in IntegralDomains():
261+
return LaurentSeriesRing(self.base_ring().fraction_field(),
262+
self.variable_names(),
263+
self.default_prec())
264+
else:
265+
raise ValueError('must be an integral domain')
266+
187267
def change_ring(self, R):
188268
"""
189269
EXAMPLES::
@@ -194,7 +274,7 @@ def change_ring(self, R):
194274
sage: R.default_prec()
195275
4
196276
"""
197-
return LaurentSeriesRing(R, self.variable_name(),
277+
return LaurentSeriesRing(R, self.variable_names(),
198278
default_prec=self.default_prec(),
199279
sparse=self.is_sparse())
200280

@@ -248,8 +328,6 @@ def _repr_(self):
248328
s = 'Sparse ' + s
249329
return s
250330

251-
Element = LaurentSeries
252-
253331
def _element_constructor_(self, x, n=0):
254332
r"""
255333
Construct a Laurent series from `x`.
@@ -332,6 +410,7 @@ def _element_constructor_(self, x, n=0):
332410
from sage.rings.polynomial.polynomial_element import is_Polynomial
333411
from sage.rings.polynomial.multi_polynomial_element import is_MPolynomial
334412
from sage.structure.element import parent
413+
from sage.libs.pari.all import pari_gen
335414

336415
P = parent(x)
337416
if isinstance(x, self.element_class) and n == 0 and P is self:
@@ -606,22 +685,3 @@ def power_series_ring(self):
606685
"""
607686
return self._power_series_ring
608687

609-
class LaurentSeriesRing_domain(LaurentSeriesRing_generic, ring.IntegralDomain):
610-
"""
611-
Laurent series ring over a domain.
612-
613-
TESTS::
614-
615-
sage: TestSuite(LaurentSeriesRing(ZZ,'t')).run()
616-
"""
617-
618-
class LaurentSeriesRing_field(LaurentSeriesRing_generic, ring.Field):
619-
"""
620-
Laurent series ring over a field.
621-
622-
TESTS::
623-
624-
sage: TestSuite(LaurentSeriesRing(QQ,'t')).run()
625-
"""
626-
_default_category = CompleteDiscreteValuationFields()
627-

src/sage/rings/laurent_series_ring_element.pyx

+5-10
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ AUTHORS:
6969

7070
from __future__ import print_function, absolute_import
7171

72-
import operator
73-
7472
from .infinity import infinity
7573

7674
import sage.rings.polynomial.polynomial_element as polynomial
@@ -168,7 +166,7 @@ cdef class LaurentSeries(AlgebraElement):
168166
self.__u = f >> val
169167

170168
def __reduce__(self):
171-
return make_element_from_parent, (self._parent, self.__u, self.__n)
169+
return self._parent, (self.__u, self.__n)
172170

173171
def change_ring(self, R):
174172
"""
@@ -710,13 +708,13 @@ cdef class LaurentSeries(AlgebraElement):
710708
def O(self, prec):
711709
r"""
712710
Return the Laurent series of precision at most ``prec`` obtained by
713-
adding `O(q^\text{prec})`, where `q` is the variable.
714-
711+
adding `O(q^\text{prec})`, where `q` is the variable.
712+
715713
The precision of ``self`` and the integer ``prec`` can be arbitrary. The
716714
resulting Laurent series will have precision equal to the minimum of
717715
the precision of ``self`` and ``prec``. The term `O(q^\text{prec})` is the
718716
zero series with precision ``prec``.
719-
717+
720718
EXAMPLES::
721719
722720
sage: R.<t> = LaurentSeriesRing(QQ)
@@ -1183,7 +1181,7 @@ cdef class LaurentSeries(AlgebraElement):
11831181
def precision_relative(self):
11841182
"""
11851183
Return the relative precision of this series, that
1186-
is the difference between its absolute precision
1184+
is the difference between its absolute precision
11871185
and its valuation.
11881186
11891187
By convention, the relative precision of `0` (or
@@ -1513,6 +1511,3 @@ cdef class LaurentSeries(AlgebraElement):
15131511
x = x[0]
15141512

15151513
return self.__u(*x)*(x[0]**self.__n)
1516-
1517-
def make_element_from_parent(parent, *args):
1518-
return parent(*args)

src/sage/rings/power_series_pari.pyx

+2-2
Original file line numberDiff line numberDiff line change
@@ -429,15 +429,15 @@ cdef class PowerSeries_pari(PowerSeries):
429429
from sage.rings.padics.padic_generic import pAdicGeneric
430430
from sage.rings.polynomial.polynomial_ring import PolynomialRing_general
431431
from sage.rings.power_series_ring import PowerSeriesRing_generic
432-
from sage.rings.laurent_series_ring import LaurentSeriesRing_generic
432+
from sage.rings.laurent_series_ring import LaurentSeriesRing
433433
if isinstance(Q, pAdicGeneric):
434434
# Substitution of p-adic numbers in power series is
435435
# currently not implemented in PARI (2.8.0-development).
436436
t = a.valuation()
437437
if t <= 0:
438438
raise ValueError("can only substitute elements of positive valuation")
439439
return Q(self.polynomial()(a)).add_bigoh(t * self._prec)
440-
elif isinstance(Q, (PowerSeriesRing_generic, LaurentSeriesRing_generic)):
440+
elif isinstance(Q, (PowerSeriesRing_generic, LaurentSeriesRing)):
441441
# In Sage, we want an error to be raised when trying to
442442
# substitute a series of non-positive valuation, but PARI
443443
# (2.8.0-development) does not do this. For example,

0 commit comments

Comments
 (0)