Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/sage/rings/laurent_series_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from sage.rings.infinity import infinity
from sage.rings.integer_ring import ZZ
from sage.rings.laurent_series_ring_element import LaurentSeries
from sage.structure.element import Expression
from sage.structure.parent import Parent
from sage.structure.unique_representation import UniqueRepresentation

Expand Down Expand Up @@ -542,6 +543,19 @@
sage: P.<x> = LaurentSeriesRing(QQ)
sage: P({-3: 1})
x^-3

Check that :issue:`39839` is fixed::

sage: var("x")
x
sage: f = (1/x+sqrt(x+1)).series(x, 5); f
1*x^(-1) + 1 + 1/2*x + (-1/8)*x^2 + 1/16*x^3 + (-5/128)*x^4 + Order(x^5)
sage: LaurentSeriesRing(QQ, "x")(f)
x^-1 + 1 + 1/2*x - 1/8*x^2 + 1/16*x^3 - 5/128*x^4 + O(x^5)
sage: PowerSeriesRing(QQ, "x")(f)
Traceback (most recent call last):
...
ValueError: cannot return dense coefficient list with negative valuation
"""
from sage.rings.fraction_field_element import FractionFieldElement
from sage.rings.lazy_series import LazyPowerSeries, LazyLaurentSeries
Expand Down Expand Up @@ -589,6 +603,18 @@
x = x.add_bigoh(self.default_prec())
else:
x = x.add_bigoh(prec)
elif isinstance(x, Expression):
from sage.symbolic.expression import SymbolicSeries
if isinstance(x, SymbolicSeries):
v = x.default_variable()
if str(v) == self.variable_name():
R = self.base_ring()
g = self.gen()
return sum(
(R(a)*g**ZZ(e) for a, e in x.coefficients(v, sparse=True)), self.zero()
).add_bigoh(x.degree(x.default_variable()))
else:
raise TypeError("can only convert series into ring with same variable name")

Check warning on line 617 in src/sage/rings/laurent_series_ring.py

View check run for this annotation

Codecov / codecov/patch

src/sage/rings/laurent_series_ring.py#L617

Added line #L617 was not covered by tests
return self.element_class(self, x, n).add_bigoh(prec)

def random_element(self, algorithm='default'):
Expand Down
6 changes: 3 additions & 3 deletions src/sage/rings/lazy_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -2371,7 +2371,7 @@ def cot(self):
TESTS::

sage: L.<z> = LazyLaurentSeriesRing(QQ); x = var("x") # needs sage.symbolic
sage: cot(z)[0:6] == cot(x).series(x, 6).coefficients(sparse=False) # needs sage.symbolic
sage: cot(z)[0:6] == (cot(x)-x^-1).series(x, 6).coefficients(sparse=False) # needs sage.symbolic
True
"""
return ~self.tan()
Expand Down Expand Up @@ -2653,7 +2653,7 @@ def coth(self):
TESTS::

sage: L.<z> = LazyLaurentSeriesRing(SR); x = var("x") # needs sage.symbolic
sage: coth(z)[0:6] == coth(x).series(x, 6).coefficients(sparse=False) # needs sage.symbolic
sage: coth(z)[0:6] == (coth(x)-x^-1).series(x, 6).coefficients(sparse=False)# needs sage.symbolic
True
"""
from sage.arith.misc import bernoulli
Expand Down Expand Up @@ -2716,7 +2716,7 @@ def csch(self):
TESTS::

sage: L.<z> = LazyLaurentSeriesRing(SR); x = var("x") # needs sage.symbolic
sage: csch(z)[0:6] == csch(x).series(x, 6).coefficients(sparse=False) # needs sage.symbolic
sage: csch(z)[0:6] == (csch(x)-x^-1).series(x, 6).coefficients(sparse=False) # needs sage.symbolic
True
"""
from sage.arith.misc import bernoulli
Expand Down
124 changes: 95 additions & 29 deletions src/sage/symbolic/series_impl.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,44 @@ Check that :issue:`22733` is fixed::
# ****************************************************************************


def _is_order(expr: Expression) -> bool:
"""
Return whether ``expr`` has the form ``Order(...)``.

TESTS::

sage: from sage.symbolic.expression import _is_order
sage: s = (x^2).series(x,1)
sage: expr = Expression.coefficients(s)[0][0]; expr
Order(x)
sage: _is_order(expr)
True
sage: expr = Expression.coefficients(x.series(x, 0))[0][0]; expr
Order(1)
sage: _is_order(expr)
True
sage: expr = Expression.coefficients((x^5).series(x, 3))[0][0]; expr
Order(x^3)
sage: _is_order(expr)
True
sage: from sage.functions.other import Order; _is_order(Order(x))
True
sage: _is_order(Order(x) + 1)
False
sage: _is_order(Order(x) + 1 - 1)
True
sage: var("y")
y
sage: _is_order(Order(y))
True
"""
from sage.functions.other import Order
from sage.symbolic.operators import add_vararg
if expr.operator() is add_vararg and len(expr.operands()) == 1:
expr = expr.operands()[0]
return expr.operator() is Order


cdef class SymbolicSeries(Expression):
def __init__(self, SR):
"""
Expand Down Expand Up @@ -201,22 +239,11 @@ cdef class SymbolicSeries(Expression):

def coefficients(self, x=None, sparse=True):
r"""
Return the coefficients of this symbolic series as a list of pairs.

INPUT:

- ``x`` -- (optional) variable

- ``sparse`` -- boolean (default: ``True``); if ``False`` return a list
with as much entries as the order of the series

OUTPUT: depending on the value of ``sparse``,

- A list of pairs ``(expr, n)``, where ``expr`` is a symbolic
expression and ``n`` is a power (``sparse=True``, default)
Return the coefficients of this symbolic series. See :meth:`Expression.coefficients` for more details.

- A list of expressions where the ``n``-th element is the coefficient of
``x^n`` when ``self`` is seen as polynomial in ``x`` (``sparse=False``).
When ``sparse=False``, unlike :meth:`Expression.coefficients`, the dense list of coefficients
are padded to the degree of the asymptotic term of this series.
Also, this correctly handles the case where there is no term below the asymptotic term.

EXAMPLES::

Expand All @@ -231,24 +258,63 @@ cdef class SymbolicSeries(Expression):
1 + (y + 1)*x + ((y + 1)^2)*x^2 + Order(x^3)
sage: s.coefficients(x, sparse=False)
[1, y + 1, (y + 1)^2]

TESTS::

sage: s = (x^-1 + x^2).series(x,6)
sage: s.coefficients()
[[1, -1], [1, 2]]
sage: s.coefficients(sparse=False)
Traceback (most recent call last):
...
ValueError: cannot return dense coefficient list with negative valuation
sage: Expression.coefficients(s)
[[1, -1], [1, 2]]
sage: Expression.coefficients(s, sparse=False)
Traceback (most recent call last):
...
ValueError: cannot return dense coefficient list with negative valuation

sage: s = (x+x^3).series(x,6)
sage: s.coefficients()
[[1, 1], [1, 3]]
sage: s.coefficients(sparse=False)
[0, 1, 0, 1, 0, 0]
sage: Expression.coefficients(s)
[[1, 1], [1, 3]]
sage: Expression.coefficients(s, sparse=False)
[0, 1, 0, 1]

sage: s = (x^5).series(x,1)
sage: s.coefficients()
[]
sage: s.coefficients(sparse=False)
[0]
sage: Expression.coefficients(s) # incorrect result here, but the user will not see this
[[Order(x), 0]]
sage: Expression.coefficients(s, sparse=False)
[Order(x)]

sage: s = (x^5).series(x,4)
sage: s.coefficients()
[]
sage: s.coefficients(sparse=False)
[0, 0, 0, 0]
sage: Expression.coefficients(s) # incorrect result here, but the user will not see this
[[Order(x^4), 0]]
sage: Expression.coefficients(s, sparse=False)
[Order(x^4)]
"""
if x is None:
x = self.default_variable()
l = [[self.coefficient(x, d), d] for d in range(self.degree(x))]
result = super().coefficients(x, sparse)
if sparse and len(result) == 1 and ZZ(result[0][1]) == 0 and _is_order(result[0][0]):
result = []
if sparse:
return l

from sage.rings.integer_ring import ZZ
if any(not c[1] in ZZ for c in l):
raise ValueError("cannot return dense coefficient list with noninteger exponents")
val = l[0][1]
if val < 0:
raise ValueError("cannot return dense coefficient list with negative valuation")
deg = l[-1][1]
ret = [ZZ(0)] * int(deg+1)
for c in l:
ret[c[1]] = c[0]
return ret
return result
if len(result) == 1 and _is_order(result[0]):
result = []
return result + [ZZ.zero()] * (self.degree(x) - len(result))

def power_series(self, base_ring):
"""
Expand Down
Loading