Skip to content

Commit

Permalink
Trac #32324: Lazy Taylor Series
Browse files Browse the repository at this point in the history
We implement (multivariate) Taylor series.

Note that there is no easy well-defined notion of multivariate Laurent
series, which is why we create a new class.

URL: https://trac.sagemath.org/32324
Reported by: mantepse
Ticket author(s): Martin Rubey
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Aug 29, 2022
2 parents ae8a36d + e780472 commit 8a41fd6
Show file tree
Hide file tree
Showing 5 changed files with 2,053 additions and 396 deletions.
157 changes: 144 additions & 13 deletions src/sage/data_structures/stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
This module provides lazy implementations of basic operators on
streams. The classes implemented in this module can be used to build
up more complex streams for different kinds of series (Laurent,
Dirichlet, etc).
Dirichlet, etc.).
EXAMPLES:
Expand Down Expand Up @@ -85,6 +85,8 @@

# ****************************************************************************
# Copyright (C) 2019 Kwankyu Lee <ekwankyu@gmail.com>
# 2022 Martin Rubey <martin.rubey at tuwien.ac.at>
# 2022 Travis Scrimshaw <tcscrims at gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand All @@ -96,6 +98,7 @@
from sage.rings.integer_ring import ZZ
from sage.rings.infinity import infinity
from sage.arith.misc import divisors
from sage.combinat.integer_vector_weighted import iterator_fast as wt_int_vec_iter

class Stream():
"""
Expand Down Expand Up @@ -505,7 +508,7 @@ class Stream_exact(Stream):
"""
def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None, order=None):
"""
Initialize a series that is known to be eventually geometric.
Initialize a stream with eventually constant coefficients.
TESTS::
Expand Down Expand Up @@ -837,6 +840,7 @@ def __init__(self, is_sparse, approximate_order):
sage: TestSuite(C).run(skip="_test_pickling")
"""
self._target = None
assert approximate_order is not None, "calling Stream_uninitialized with None as approximate order"
super().__init__(is_sparse, approximate_order)

def get_coefficient(self, n):
Expand Down Expand Up @@ -1023,7 +1027,7 @@ def __eq__(self, other):
INPUT:
- ``other`` -- a stream of coefficients
- ``other`` -- a :class:`Stream` of coefficients
EXAMPLES::
Expand Down Expand Up @@ -1086,7 +1090,7 @@ def __eq__(self, other):
INPUT:
- ``other`` -- a stream of coefficients
- ``other`` -- a :class:`Stream` of coefficients
EXAMPLES::
Expand Down Expand Up @@ -1211,8 +1215,8 @@ class Stream_add(Stream_binaryCommutative):
INPUT:
- ``left`` -- stream of coefficients on the left side of the operator
- ``right`` -- stream of coefficients on the right side of the operator
- ``left`` -- :class:`Stream` of coefficients on the left side of the operator
- ``right`` -- :class:`Stream` of coefficients on the right side of the operator
EXAMPLES::
Expand Down Expand Up @@ -1271,8 +1275,8 @@ class Stream_sub(Stream_binary):
INPUT:
- ``left`` -- stream of coefficients on the left side of the operator
- ``right`` -- stream of coefficients on the right side of the operator
- ``left`` -- :class:`Stream` of coefficients on the left side of the operator
- ``right`` -- :class:`Stream` of coefficients on the right side of the operator
EXAMPLES::
Expand Down Expand Up @@ -1336,8 +1340,8 @@ class Stream_cauchy_mul(Stream_binary):
INPUT:
- ``left`` -- stream of coefficients on the left side of the operator
- ``right`` -- stream of coefficients on the right side of the operator
- ``left`` -- :class:`Stream` of coefficients on the left side of the operator
- ``right`` -- :class:`Stream` of coefficients on the right side of the operator
EXAMPLES::
Expand Down Expand Up @@ -1422,8 +1426,8 @@ class Stream_dirichlet_convolve(Stream_binary):
INPUT:
- ``left`` -- stream of coefficients on the left side of the operator
- ``right`` -- stream of coefficients on the right side of the operator
- ``left`` -- :class:`Stream` of coefficients on the left side of the operator
- ``right`` -- :class:`Stream` of coefficients on the right side of the operator
The coefficient of `n^{-s}` in the convolution of `l` and `r`
equals `\sum_{k | n} l_k r_{n/k}`.
Expand Down Expand Up @@ -1598,7 +1602,7 @@ def __init__(self, f, g):
sage: g = Stream_function(lambda n: n^2, ZZ, True, 1)
sage: h = Stream_cauchy_compose(f, g)
"""
assert g._approximate_order > 0
#assert g._approximate_order > 0
self._fv = f._approximate_order
self._gv = g._approximate_order
if self._fv < 0:
Expand Down Expand Up @@ -1643,6 +1647,132 @@ def get_coefficient(self, n):
return ret + sum(self._left[i] * self._pos_powers[i][n] for i in range(1, n // self._gv+1))


class Stream_plethysm(Stream_binary):
r"""
Return the plethysm of ``f`` composed by ``g``.
This is the plethysm `f \circ g = f(g)` when `g` is an element of
the ring of symmetric functions.
INPUT:
- ``f`` -- a :class:`Stream`
- ``g`` -- a :class:`Stream` with positive order
- ``p`` -- the powersum symmetric functions
EXAMPLES::
sage: from sage.data_structures.stream import Stream_function, Stream_plethysm
sage: s = SymmetricFunctions(QQ).s()
sage: p = SymmetricFunctions(QQ).p()
sage: f = Stream_function(lambda n: s[n], s, True, 1)
sage: g = Stream_function(lambda n: s[[1]*n], s, True, 1)
sage: h = Stream_plethysm(f, g, p)
sage: [s(h[i]) for i in range(5)]
[0,
s[1],
s[1, 1] + s[2],
2*s[1, 1, 1] + s[2, 1] + s[3],
3*s[1, 1, 1, 1] + 2*s[2, 1, 1] + s[2, 2] + s[3, 1] + s[4]]
sage: u = Stream_plethysm(g, f, p)
sage: [s(u[i]) for i in range(5)]
[0,
s[1],
s[1, 1] + s[2],
s[1, 1, 1] + s[2, 1] + 2*s[3],
s[1, 1, 1, 1] + s[2, 1, 1] + 3*s[3, 1] + 2*s[4]]
"""
def __init__(self, f, g, p):
r"""
Initialize ``self``.
TESTS::
sage: from sage.data_structures.stream import Stream_function, Stream_plethysm
sage: s = SymmetricFunctions(QQ).s()
sage: p = SymmetricFunctions(QQ).p()
sage: f = Stream_function(lambda n: s[n], s, True, 1)
sage: g = Stream_function(lambda n: s[n-1,1], s, True, 2)
sage: h = Stream_plethysm(f, g, p)
"""
#assert g._approximate_order > 0
self._fv = f._approximate_order
self._gv = g._approximate_order
self._p = p
val = self._fv * self._gv
super().__init__(f, g, f._is_sparse, val)

def get_coefficient(self, n):
r"""
Return the ``n``-th coefficient of ``self``.
INPUT:
- ``n`` -- integer; the degree for the coefficient
EXAMPLES::
sage: from sage.data_structures.stream import Stream_function, Stream_plethysm
sage: s = SymmetricFunctions(QQ).s()
sage: p = SymmetricFunctions(QQ).p()
sage: f = Stream_function(lambda n: s[n], s, True, 1)
sage: g = Stream_function(lambda n: s[[1]*n], s, True, 1)
sage: h = Stream_plethysm(f, g, p)
sage: s(h.get_coefficient(5))
4*s[1, 1, 1, 1, 1] + 4*s[2, 1, 1, 1] + 2*s[2, 2, 1] + 2*s[3, 1, 1] + s[3, 2] + s[4, 1] + s[5]
sage: [s(h.get_coefficient(i)) for i in range(6)]
[0,
s[1],
s[1, 1] + s[2],
2*s[1, 1, 1] + s[2, 1] + s[3],
3*s[1, 1, 1, 1] + 2*s[2, 1, 1] + s[2, 2] + s[3, 1] + s[4],
4*s[1, 1, 1, 1, 1] + 4*s[2, 1, 1, 1] + 2*s[2, 2, 1] + 2*s[3, 1, 1] + s[3, 2] + s[4, 1] + s[5]]
"""
if not n: # special case of 0
return self._left[0]

# We assume n > 0
p = self._p
ret = p.zero()
for k in range(n+1):
temp = p(self._left[k])
for la, c in temp:
inner = self._compute_product(n, la, c)
if inner is not None:
ret += inner
return ret

def _compute_product(self, n, la, c):
"""
Compute the product ``c * p[la](self._right)`` in degree ``n``.
EXAMPLES::
sage: from sage.data_structures.stream import Stream_plethysm, Stream_exact, Stream_function
sage: s = SymmetricFunctions(QQ).s()
sage: p = SymmetricFunctions(QQ).p()
sage: f = Stream_function(lambda n: s[n], s, True, 1)
sage: g = Stream_exact([s[2], s[3]], False, 0, 4, 2)
sage: h = Stream_plethysm(f, g, p)
sage: ret = h._compute_product(7, [2, 1], 1); ret
1/12*p[2, 2, 1, 1, 1] + 1/4*p[2, 2, 2, 1] + 1/6*p[3, 2, 2]
+ 1/12*p[4, 1, 1, 1] + 1/4*p[4, 2, 1] + 1/6*p[4, 3]
sage: ret == p[2,1](s[2] + s[3]).homogeneous_component(7)
True
"""
p = self._p
ret = p.zero()
for mu in wt_int_vec_iter(n, la):
temp = c
for i, j in zip(la, mu):
gs = self._right[j]
if not gs:
temp = p.zero()
break
temp *= p[i](gs)
ret += temp
return ret

#####################################################################
# Unary operations

Expand Down Expand Up @@ -2194,3 +2324,4 @@ def is_nonzero(self):
True
"""
return self._series.is_nonzero()

3 changes: 2 additions & 1 deletion src/sage/rings/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@
lazy_import('sage.rings.laurent_series_ring_element', 'LaurentSeries', deprecation=33602)

# Lazy Laurent series ring
lazy_import('sage.rings.lazy_series_ring', ['LazyLaurentSeriesRing', 'LazyDirichletSeriesRing'])
lazy_import('sage.rings.lazy_series_ring', ['LazyLaurentSeriesRing', 'LazyTaylorSeriesRing',
'LazySymmetricFunctions', 'LazyDirichletSeriesRing'])

# Tate algebras
from .tate_algebra import TateAlgebra
Expand Down
Loading

0 comments on commit 8a41fd6

Please sign in to comment.