Skip to content

Commit

Permalink
Adding add_bigoh methods to lazy power/Laurent series and coercions t…
Browse files Browse the repository at this point in the history
…o finite precision cases.
  • Loading branch information
tscrim committed May 7, 2024
1 parent 744939e commit 289fcf5
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 15 deletions.
30 changes: 29 additions & 1 deletion src/sage/rings/laurent_series_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,12 @@ def is_LaurentSeriesRing(x):
sage: K.<q> = LaurentSeriesRing(QQ)
sage: is_LaurentSeriesRing(K)
True
sage: L.<z> = LazyLaurentSeriesRing(QQ)
sage: is_LaurentSeriesRing(L)
True
"""
return isinstance(x, LaurentSeriesRing)
from sage.rings.lazy_series_ring import LazyLaurentSeriesRing
return isinstance(x, (LaurentSeriesRing, LazyLaurentSeriesRing))


class LaurentSeriesRing(UniqueRepresentation, CommutativeRing):
Expand Down Expand Up @@ -439,6 +443,17 @@ def _element_constructor_(self, x, n=0, prec=infinity):
1/64*I*u^10 - 1/128*u^12 - 1/256*I*u^14 + 1/512*u^16 +
1/1024*I*u^18 + O(u^20)
Lazy series::
sage: L.<z> = LazyLaurentSeriesRing(ZZ)
sage: R = LaurentSeriesRing(QQ, names='z')
sage: R(z^-5 + 1/(1-z))
z^-5 + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + z^8 + z^9 + z^10
+ z^11 + z^12 + z^13 + z^14 + z^15 + z^16 + z^17 + z^18 + z^19 + O(z^20)
sage: L.<z> = LazyPowerSeriesRing(QQ)
sage: R(5 + z - 5*z^7)
5 + z - 5*z^7
TESTS:
Check that :issue:`28993` is fixed::
Expand Down Expand Up @@ -488,6 +503,7 @@ def _element_constructor_(self, x, n=0, prec=infinity):
x^-3
"""
from sage.rings.fraction_field_element import is_FractionFieldElement
from sage.rings.lazy_series import LazyPowerSeries, LazyLaurentSeries
from sage.rings.polynomial.multi_polynomial import MPolynomial
from sage.rings.polynomial.polynomial_element import Polynomial
from sage.structure.element import parent
Expand Down Expand Up @@ -524,6 +540,14 @@ def _element_constructor_(self, x, n=0, prec=infinity):
and isinstance(x.numerator(), (Polynomial, MPolynomial))):
x = self(x.numerator()) / self(x.denominator())
return (x << n).add_bigoh(prec)
elif isinstance(x, (LazyPowerSeries, LazyLaurentSeries)):
if prec is infinity:
try:
x = self.power_series_ring()(x.polynomial())
except ValueError:
x = x.add_bigoh(self.default_prec())
else:
x = x.add_bigoh(prec)
return self.element_class(self, x, n).add_bigoh(prec)

def random_element(self, algorithm='default'):
Expand Down Expand Up @@ -617,6 +641,10 @@ def _coerce_map_from_(self, P):
True
sage: S.has_coerce_map_from(S)
True
sage: S.has_coerce_map_from(LazyLaurentSeriesRing(QQ, 't'))
True
sage: S.has_coerce_map_from(LazyPowerSeriesRing(ZZ, 't'))
True
sage: S.has_coerce_map_from(QQ)
False
Expand Down
41 changes: 41 additions & 0 deletions src/sage/rings/lazy_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -4746,6 +4746,9 @@ def approximate_series(self, prec, name=None):
R = PowerSeriesRing(S.base_ring(), name=name)
return R([self[i] for i in range(prec)]).add_bigoh(prec)

add_bigoh = approximate_series
O = approximate_series

def polynomial(self, degree=None, name=None):
r"""
Return ``self`` as a Laurent polynomial if ``self`` is actually so.
Expand Down Expand Up @@ -6065,6 +6068,44 @@ def polynomial(self, degree=None, names=None):
return R(self[0:m])
return R.sum(self[0:m])

def add_bigoh(self, prec):
r"""
Return the power series of precision at most ``prec`` obtained by
adding `O(q^\text{prec})` to `f`, where `q` is the variable(s).
EXAMPLES::
sage: L.<x,y> = LazyPowerSeriesRing(QQ)
sage: f = 1 / (1 - x + y)
sage: f
1 + (x-y) + (x^2-2*x*y+y^2) + (x^3-3*x^2*y+3*x*y^2-y^3)
+ (x^4-4*x^3*y+6*x^2*y^2-4*x*y^3+y^4)
+ (x^5-5*x^4*y+10*x^3*y^2-10*x^2*y^3+5*x*y^4-y^5)
+ (x^6-6*x^5*y+15*x^4*y^2-20*x^3*y^3+15*x^2*y^4-6*x*y^5+y^6)
+ O(x,y)^7
sage: f3 = f.add_bigoh(3); f3
1 + x - y + x^2 - 2*x*y + y^2 + O(x, y)^3
sage: f3.parent()
Multivariate Power Series Ring in x, y over Rational Field
sage: R.<t> = QQ[]
sage: L.<x> = LazyPowerSeriesRing(R)
sage: f = 1 / (1 - t^3*x)
sage: f
1 + t^3*x + t^6*x^2 + t^9*x^3 + t^12*x^4 + t^15*x^5 + t^18*x^6 + O(x^7)
sage: f3 = f.add_bigoh(3); f3
1 + t^3*x + t^6*x^2 + O(x^3)
sage: f3.parent()
Power Series Ring in x over Univariate Polynomial Ring in t
over Rational Field
"""
from sage.rings.power_series_ring import PowerSeriesRing
P = self.parent()
PSR = PowerSeriesRing(P.base_ring(), names=P.variable_names())
return PSR(self.polynomial(degree=prec-1), prec=prec)

O = add_bigoh

def _floordiv_(self, other):
r"""
Return ``self`` floor divided by ``other``.
Expand Down
40 changes: 33 additions & 7 deletions src/sage/rings/multi_power_series_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,15 @@ def is_MPowerSeriesRing(x):
True
sage: is_MPowerSeriesRing(T)
False
sage: L = LazyPowerSeriesRing(QQ, 'x')
sage: is_MPowerSeriesRing(L)
True
sage: L = LazyPowerSeriesRing(QQ, 'x, y')
sage: is_MPowerSeriesRing(L)
True
"""
return isinstance(x, MPowerSeriesRing_generic)
from sage.rings.lazy_series_ring import LazyPowerSeriesRing
return isinstance(x, (MPowerSeriesRing_generic, LazyPowerSeriesRing))


class MPowerSeriesRing_generic(PowerSeriesRing_generic, Nonexact):
Expand Down Expand Up @@ -754,13 +760,13 @@ def _coerce_map_from_(self, P):
The rings that canonically coerce to this multivariate power series
ring are:
- this ring itself
- this ring itself
- a polynomial or power series ring in the same variables or a
subset of these variables (possibly empty), over any base
ring that canonically coerces into this ring
- a polynomial or power series ring in the same variables or a
subset of these variables (possibly empty), over any base
ring that canonically coerces into this ring
- any ring that coerces into the foreground polynomial ring of this ring
- any ring that coerces into the foreground polynomial ring of this ring
EXAMPLES::
Expand Down Expand Up @@ -817,6 +823,10 @@ def _coerce_map_from_(self, P):
sage: H._coerce_map_from_(PolynomialRing(ZZ,'z2,f0'))
True
sage: L.<x,y> = LazyPowerSeriesRing(QQ)
sage: R = PowerSeriesRing(QQ, names=('x','y','z'))
sage: R.has_coerce_map_from(L)
True
"""
if is_MPolynomialRing(P) or is_MPowerSeriesRing(P) \
or is_PolynomialRing(P) or is_PowerSeriesRing(P):
Expand Down Expand Up @@ -846,12 +856,28 @@ def _element_constructor_(self, f, prec=None):
sage: M._element_constructor_(p).parent()
Multivariate Power Series Ring in t0, t1, t2, t3, t4 over
Integer Ring
sage: L.<x,y> = LazyPowerSeriesRing(QQ)
sage: R = PowerSeriesRing(QQ, names=('x','y','z'))
sage: R(1/(1-x-y), prec=3)
1 + x + y + x^2 + 2*x*y + y^2 + O(x, y, z)^3
sage: R(x + y^2)
x + y^2
"""
if prec is None:
try:
prec = f.prec()
except AttributeError:
prec = infinity
from sage.rings.lazy_series import LazyPowerSeries
if isinstance(f, LazyPowerSeries):
if prec is infinity:
try:
f = f.polynomial()
except ValueError:
f = f.add_bigoh(self.default_prec())
else:
f = f.add_bigoh(prec)
return self.element_class(parent=self, x=f, prec=prec)

def laurent_series_ring(self):
Expand Down
37 changes: 30 additions & 7 deletions src/sage/rings/power_series_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,13 @@ def is_PowerSeriesRing(R):
False
sage: is_PowerSeriesRing(QQ[['x']])
True
sage: is_PowerSeriesRing(LazyPowerSeriesRing(QQ, 'x'))
True
sage: is_PowerSeriesRing(LazyPowerSeriesRing(QQ, 'x, y'))
False
"""
if isinstance(R, PowerSeriesRing_generic):
from sage.rings.lazy_series_ring import LazyPowerSeriesRing
if isinstance(R, (PowerSeriesRing_generic, LazyPowerSeriesRing)):
return R.ngens() == 1
else:
return False
Expand Down Expand Up @@ -684,8 +689,8 @@ def _latex_(self):

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
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.
Expand All @@ -700,7 +705,8 @@ def _coerce_map_from_(self, S):
False
sage: A.has_coerce_map_from(ZZ[['x']])
True
sage: A.has_coerce_map_from(LazyPowerSeriesRing(ZZ, 'x'))
True
"""
if self.base_ring().has_coerce_map_from(S):
return True
Expand All @@ -712,8 +718,8 @@ def _element_constructor_(self, f, prec=infinity, check=True):
"""
Coerce object to this power series ring.
Returns a new instance unless the parent of f is self, in which
case f is returned (since f is immutable).
Returns a new instance unless the parent of ``f`` is ``self``, in
which case ``f`` is returned (since ``f`` is immutable).
INPUT:
Expand All @@ -726,7 +732,6 @@ def _element_constructor_(self, f, prec=infinity, check=True):
- ``check`` -- bool (default: ``True``), whether to verify
that the coefficients, etc., coerce in correctly.
EXAMPLES::
sage: R.<t> = PowerSeriesRing(ZZ)
Expand Down Expand Up @@ -803,6 +808,14 @@ def _element_constructor_(self, f, prec=infinity, check=True):
...
ValueError: prec (= -5) must be non-negative
From lazy series::
sage: L.<x> = LazyPowerSeriesRing(QQ)
sage: R = PowerSeriesRing(QQ, 'x')
sage: R(1 / (1 + x^3))
1 - x^3 + x^6 - x^9 + x^12 - x^15 + x^18 + O(x^20)
sage: R(2 - x^2 + x^6)
2 - x^2 + x^6
"""
if prec is not infinity:
prec = integer.Integer(prec)
Expand Down Expand Up @@ -832,6 +845,16 @@ def _element_constructor_(self, f, prec=infinity, check=True):
f.degree(f.default_variable()), check=check)
else:
raise TypeError("Can only convert series into ring with same variable name.")
else:
from sage.rings.lazy_series import LazyPowerSeries
if isinstance(f, LazyPowerSeries):
if prec is infinity:
try:
f = f.polynomial()
except ValueError:
f = f.add_bigoh(self.default_prec())
else:
f = f.add_bigoh(prec)
return self.element_class(self, f, prec, check=check)

def construction(self):
Expand Down

0 comments on commit 289fcf5

Please sign in to comment.