Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
add derivative for LazyLaurentSeries
Browse files Browse the repository at this point in the history
  • Loading branch information
mantepse committed Aug 23, 2022
1 parent 6eebb35 commit 4098dbf
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 5 deletions.
113 changes: 112 additions & 1 deletion src/sage/data_structures/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
from sage.rings.integer_ring import ZZ
from sage.rings.infinity import infinity
from sage.arith.misc import divisors
from sage.misc.misc_c import prod
from sage.combinat.integer_vector_weighted import iterator_fast as wt_int_vec_iter

class Stream():
Expand Down Expand Up @@ -2347,7 +2348,7 @@ def __init__(self, series, shift):
sage: from sage.data_structures.stream import Stream_exact
sage: h = Stream_exact([1], False, constant=3)
sage: M = Stream_shift(h, 2)
sage: TestSuite(M).run()
sage: TestSuite(M).run(skip="_test_pickling")
"""
self._series = series
self._shift = shift
Expand Down Expand Up @@ -2424,3 +2425,113 @@ def is_nonzero(self):
True
"""
return self._series.is_nonzero()

class Stream_derivative(Stream_inexact):
"""
Operator for taking derivatives of a stream.
INPUT:
- ``series`` -- a :class:`Stream`
- ``shift`` -- a positive integer
"""
def __init__(self, series, shift):
"""
Initialize ``self``.
EXAMPLES::
sage: from sage.data_structures.stream import Stream_exact, Stream_derivative
sage: f = Stream_exact([1,2,3], False)
sage: f2 = Stream_derivative(f, 2)
sage: TestSuite(f2).run()
"""
self._series = series
self._shift = shift
if 0 <= series._approximate_order <= shift:
aorder = 0
else:
aorder = series._approximate_order - shift
super().__init__(series._is_sparse, aorder)

def __getitem__(self, n):
"""
Return the ``n``-th coefficient of ``self``.
EXAMPLES::
sage: from sage.data_structures.stream import Stream_function, Stream_derivative
sage: f = Stream_function(lambda n: 1/n if n else 0, QQ, True, -2)
sage: [f[i] for i in range(-5, 3)]
[0, 0, 0, -1/2, -1, 0, 1, 1/2]
sage: f2 = Stream_derivative(f, 2)
sage: [f2[i] for i in range(-5, 3)]
[0, -3, -2, 0, 0, 1, 2, 3]
sage: f = Stream_function(lambda n: 1/n, QQ, True, 2)
sage: [f[i] for i in range(-1, 4)]
[0, 0, 0, 1/2, 1/3]
sage: f2 = Stream_derivative(f, 3)
sage: [f2[i] for i in range(-1, 4)]
[0, 2, 6, 12, 20]
"""
return (prod(n+k for k in range(1, self._shift + 1))
* self._series[n + self._shift])

def __hash__(self):
"""
Return the hash of ``self``.
EXAMPLES::
sage: from sage.data_structures.stream import Stream_function
sage: from sage.data_structures.stream import Stream_derivative
sage: a = Stream_function(lambda n: 2*n, ZZ, False, 1)
sage: f = Stream_derivative(a, 1)
sage: g = Stream_derivative(a, 2)
sage: hash(f) == hash(f)
True
sage: hash(f) == hash(g)
False
"""
return hash((type(self), self._series, self._shift))

def __eq__(self, other):
"""
Test equality.
INPUT:
- ``other`` -- a stream of coefficients
EXAMPLES::
sage: from sage.data_structures.stream import Stream_function
sage: from sage.data_structures.stream import Stream_derivative
sage: a = Stream_function(lambda n: 2*n, ZZ, False, 1)
sage: f = Stream_derivative(a, 1)
sage: g = Stream_derivative(a, 2)
sage: f == g
False
sage: f == Stream_derivative(a, 1)
True
"""
return (isinstance(other, type(self)) and self._shift == other._shift
and self._series == other._series)

def is_nonzero(self):
r"""
Return ``True`` if and only if this stream is known
to be nonzero.
EXAMPLES::
sage: from sage.data_structures.stream import Stream_exact, Stream_derivative
sage: f = Stream_exact([1,2], False)
sage: Stream_derivative(f, 1).is_nonzero()
True
sage: Stream_derivative(f, 2).is_nonzero() # it might be nice if this gave False
True
"""
return self._series.is_nonzero()
52 changes: 48 additions & 4 deletions src/sage/rings/lazy_series.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
from sage.structure.richcmp import op_EQ, op_NE
from sage.functions.other import factorial
from sage.arith.power import generic_power
from sage.misc.misc_c import prod
from sage.rings.infinity import infinity
from sage.rings.integer_ring import ZZ
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
Expand All @@ -137,6 +138,7 @@
Stream_uninitialized,
Stream_shift,
Stream_function,
Stream_derivative,
Stream_dirichlet_convolve,
Stream_dirichlet_invert,
Stream_plethysm
Expand Down Expand Up @@ -323,10 +325,10 @@ def map_coefficients(self, func, ring=None):
if not any(initial_coefficients) and not c:
return P.zero()
coeff_stream = Stream_exact(initial_coefficients,
self._coeff_stream._is_sparse,
order=coeff_stream._approximate_order,
degree=coeff_stream._degree,
constant=BR(c))
self._coeff_stream._is_sparse,
order=coeff_stream._approximate_order,
degree=coeff_stream._degree,
constant=BR(c))
return P.element_class(P, coeff_stream)
R = P._internal_poly_ring.base_ring()
coeff_stream = Stream_map_coefficients(self._coeff_stream, func, R)
Expand Down Expand Up @@ -3081,6 +3083,48 @@ def revert(self):

compositional_inverse = revert

def derivative(self, order=1):
"""
Return the derivative of the Laurent series.
INPUT:
- ``order`` -- optional integer (default 1)
EXAMPLES::
sage: L.<z> = LazyLaurentSeriesRing(ZZ)
sage: z.derivative()
1
sage: (1+z+z^2).derivative(3)
0
sage: (1/z).derivative()
-z^-2
sage: (1/(1-z)).derivative()
1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + O(z^7)
"""
P = self.parent()
coeff_stream = self._coeff_stream
if isinstance(coeff_stream, Stream_zero):
return self
BR = P.base_ring()
if (isinstance(coeff_stream, Stream_exact)
and not coeff_stream._constant):
if coeff_stream._approximate_order >= 0 and coeff_stream._degree <= order:
return P.zero()
initial_coefficients = [prod(i-k for k in range(order)) * c
for i, c in enumerate(coeff_stream._initial_coefficients,
coeff_stream._approximate_order)]
coeff_stream = Stream_exact(initial_coefficients,
self._coeff_stream._is_sparse,
order=coeff_stream._approximate_order - order,
constant=coeff_stream._constant)
return P.element_class(P, coeff_stream)

coeff_stream = Stream_derivative(self._coeff_stream, order)
return P.element_class(P, coeff_stream)

def approximate_series(self, prec, name=None):
r"""
Return the Laurent series with absolute precision ``prec`` approximated
Expand Down

0 comments on commit 4098dbf

Please sign in to comment.