From d92d50362a3cbe4b831c4c6fa91de5c23c75d020 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sun, 6 Jun 2021 00:35:06 +0530 Subject: [PATCH 01/98] Checking git trac. --- src/sage/rings/lazy_laurent_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 23e72dd8084..9c8c4917074 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -581,7 +581,7 @@ def _mul_(self, other): op = LazyLaurentSeriesOperator_mul(self, other) a = self._approximate_valuation + other._approximate_valuation - + print("Testing") c = None if self._constant is not None and other._constant is not None: if self._constant[0] == 0 and other._constant[0] == 0: From 5f7e60082b511d8e5251b45086d01ce953de8703 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sun, 6 Jun 2021 00:44:03 +0530 Subject: [PATCH 02/98] Incomplete code for the dense implementation added. --- src/sage/rings/lazy_laurent_series.py | 38 ++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 9c8c4917074..c1ab697b2bf 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -94,7 +94,7 @@ LazyLaurentSeriesOperator_change_ring, LazyLaurentSeriesOperator_truncate ) - +from sage.misc.lazy_list import lazy_list class LazyLaurentSeries(ModuleElement): r""" @@ -151,7 +151,24 @@ class LazyLaurentSeries(ModuleElement): sage: g == f True """ - def __init__(self, parent, coefficient=None, valuation=0, constant=None): + # def __init__(self, parent, coefficient=None, valuation=0, constant=None): + # """ + # Initialize. + + # TESTS:: + + # sage: L = LazyLaurentSeriesRing(GF(2), 'z') + # sage: z = L.gen() + # sage: TestSuite(z).run() + # """ + # ModuleElement.__init__(self, parent) + + # self._coefficient_function = coefficient + # self._approximate_valuation = valuation + # self._constant = constant + + # self._cache = dict() # cache of known coefficients + def __init__(self, parent, coefficient=None, valuation=0, constant=None, implementation="sparse"): """ Initialize. @@ -167,7 +184,21 @@ def __init__(self, parent, coefficient=None, valuation=0, constant=None): self._approximate_valuation = valuation self._constant = constant - self._cache = dict() # cache of known coefficients + if implementation == "sparse": + self._cache = dict() # cache of known coefficients + elif implementation == "dense": + # this might work + class mycache(): + def __init__(self, coefficient, valuation): + self.c = coefficient + self.v = valuation + self.cache = lazy_list(lambda n: self.c(n+self.v)) + def __getitem__(self, n): + return self.cache[n-self.v] + + self._cache = mycache(self._coefficient_function, self._approximate_valuation) + else: + raise ValueError("sparse or dense") def _richcmp_(self, other, op): """ @@ -581,7 +612,6 @@ def _mul_(self, other): op = LazyLaurentSeriesOperator_mul(self, other) a = self._approximate_valuation + other._approximate_valuation - print("Testing") c = None if self._constant is not None and other._constant is not None: if self._constant[0] == 0 and other._constant[0] == 0: From 96fe11193b877f5f6cf21a395b1aaaf8d85e1ad6 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sun, 6 Jun 2021 02:18:09 +0530 Subject: [PATCH 03/98] Test --- src/sage/rings/lazy_laurent_series.py | 64 +++++++++++++-------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index c1ab697b2bf..2ae47dd31c7 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -151,24 +151,7 @@ class LazyLaurentSeries(ModuleElement): sage: g == f True """ - # def __init__(self, parent, coefficient=None, valuation=0, constant=None): - # """ - # Initialize. - - # TESTS:: - - # sage: L = LazyLaurentSeriesRing(GF(2), 'z') - # sage: z = L.gen() - # sage: TestSuite(z).run() - # """ - # ModuleElement.__init__(self, parent) - - # self._coefficient_function = coefficient - # self._approximate_valuation = valuation - # self._constant = constant - - # self._cache = dict() # cache of known coefficients - def __init__(self, parent, coefficient=None, valuation=0, constant=None, implementation="sparse"): + def __init__(self, parent, coefficient=None, valuation=0, constant=None): """ Initialize. @@ -184,21 +167,38 @@ def __init__(self, parent, coefficient=None, valuation=0, constant=None, impleme self._approximate_valuation = valuation self._constant = constant - if implementation == "sparse": - self._cache = dict() # cache of known coefficients - elif implementation == "dense": - # this might work - class mycache(): - def __init__(self, coefficient, valuation): - self.c = coefficient - self.v = valuation - self.cache = lazy_list(lambda n: self.c(n+self.v)) - def __getitem__(self, n): - return self.cache[n-self.v] + self._cache = dict() # cache of known coefficients + # def __init__(self, parent, coefficient=None, valuation=0, constant=None, implementation="sparse"): + # """ + # Initialize. + + # TESTS:: + + # sage: L = LazyLaurentSeriesRing(GF(2), 'z') + # sage: z = L.gen() + # sage: TestSuite(z).run() + # """ + # ModuleElement.__init__(self, parent) + + # self._coefficient_function = coefficient + # self._approximate_valuation = valuation + # self._constant = constant + + # if implementation == "sparse": + # self._cache = dict() # cache of known coefficients + # elif implementation == "dense": + # # this might work + # class mycache(): + # def __init__(self, coefficient, valuation): + # self.c = coefficient + # self.v = valuation + # self.cache = lazy_list(lambda n: self.c(n+self.v)) + # def __getitem__(self, n): + # return self.cache[n-self.v] - self._cache = mycache(self._coefficient_function, self._approximate_valuation) - else: - raise ValueError("sparse or dense") + # self._cache = mycache(self._coefficient_function, self._approximate_valuation) + # else: + # raise ValueError("sparse or dense") def _richcmp_(self, other, op): """ From 70a8e2d104d632df3ba083c5821376bdb432d0db Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Mon, 7 Jun 2021 01:10:50 +0530 Subject: [PATCH 04/98] Added code for dense implementation without lazy lists. However, the dense implementation has some issues with list indices. --- src/sage/rings/lazy_laurent_series.py | 155 ++++++++++++++++----- src/sage/rings/lazy_laurent_series_ring.py | 3 +- 2 files changed, 124 insertions(+), 34 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 2ae47dd31c7..92bce91bd73 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -166,8 +166,18 @@ def __init__(self, parent, coefficient=None, valuation=0, constant=None): self._coefficient_function = coefficient self._approximate_valuation = valuation self._constant = constant - - self._cache = dict() # cache of known coefficients + self._implementation = parent._implementation + # print(parent) + # print("The implementation in the series file is:", self._implementation) + # print("The approx_val rn:", self._approximate_valuation) + # print("The constnt rn:", self._constant) + if self._implementation == 'sparse': + self._cache = dict() # cache of known coefficients + elif self._implementation == 'dense': + self._cache = list() + self._offset = valuation + else: + raise ValueError # def __init__(self, parent, coefficient=None, valuation=0, constant=None, implementation="sparse"): # """ # Initialize. @@ -399,22 +409,63 @@ def coefficient(self, n): sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... """ - R = self.base_ring() - - if self._approximate_valuation == infinity: - return R.zero() - elif n < self._approximate_valuation: - return R.zero() - elif self._constant is not None and n >= self._constant[1]: - return self._constant[0] + if self._implementation == 'sparse': + R = self.base_ring() + # print(R) + # print("The approximate valuation is:", self._approximate_valuation) + if self._approximate_valuation == infinity: + # print("a") + # print(R.zero()) + return R.zero() + elif n < self._approximate_valuation: + # print("b") + # print(R.zero()) + return R.zero() + elif self._constant is not None and n >= self._constant[1]: + # print("c") + # print(self._constant[0]) + # print(self._constant) + return self._constant[0] + + try: + # print('d') + # print(self._cache) + c = self._cache[n] + # print(self._cache) + except KeyError: + # print('e') + c = R(self._coefficient_function(self, n)) + # print(c) + self._cache[n] = c + # print(self._cache) + + return c - try: - c = self._cache[n] - except KeyError: - c = R(self._coefficient_function(self, n)) - self._cache[n] = c - - return c + else: + R = self.base_ring() + + if self._approximate_valuation == infinity: + return R.zero() + elif n < self._approximate_valuation: + return R.zero() + elif self._constant is not None and n >= self._constant[1]: + return self._constant[0] + + # try: + # c = self._cache[n] + # except KeyError: + # c = R(self._coefficient_function(self, n)) + # self._cache[n] = c + # i = self._cache[n - self._offset] + i = self._cache[len(self._cache) - self._offset] + if i >= len(self._cache): + # compute all coefficients up to the requested one + l = [R(self._coefficient_function(self, j)) for j in range(i)] + self._cache.extend(l) + + c = self._cache[i] + + return c def valuation(self): """ @@ -434,29 +485,67 @@ def valuation(self): sage: t.valuation() +Infinity """ - if self._constant is None: - n = self._approximate_valuation - cache = self._cache - while True: - if n in cache: - if cache[n]: + if self._implementation == 'sparse': + if self._constant is None: + n = self._approximate_valuation + cache = self._cache + # print("The cache is", self._cache) + # print("The approx value is", n) + while True: + if n in cache: + # print("The current key is:", n) + if cache[n]: + self._approximate_valuation = n + return n + n += 1 + else: + if self.coefficient(n) != 0: + # print("The value is:", self.coefficient(n)) + self._approximate_valuation = n + return n + n += 1 + else: + n = self._approximate_valuation + m = self._constant[1] + # print("n is:", n) + # print('m is: ', m) + while n <= m: + if self.coefficient(n) != 0: + # print("Self", self._approximate_valuation) + # print(n) self._approximate_valuation = n return n n += 1 - else: + return infinity + else: + if self._constant is None: + n = self._approximate_valuation + cache = self._cache + while True: + if n < len(cache): + if cache[n]: + self._approximate_valuation = n + return n + n += 1 + else: + if self.coefficient(n) != 0: + # print("The value is:", self.coefficient(n)) + self._approximate_valuation = n + return n + n += 1 + else: + n = self._approximate_valuation + m = self._constant[1] + # print("n is:", n) + # print('m is: ', m) + while n <= m: if self.coefficient(n) != 0: + # print("Self", self._approximate_valuation) + # print(n) self._approximate_valuation = n return n n += 1 - else: - n = self._approximate_valuation - m = self._constant[1] - while n <= m: - if self.coefficient(n) != 0: - self._approximate_valuation = n - return n - n += 1 - return infinity + return infinity def prec(self): """ diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index ffa6b4575c7..8b60d3cc6a1 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -98,7 +98,7 @@ class LazyLaurentSeriesRing(UniqueRepresentation, Parent): """ Element = LazyLaurentSeries - def __init__(self, base_ring, names, category=None): + def __init__(self, base_ring, names, category=None, implementation='sparse'): """ Initialize. @@ -109,6 +109,7 @@ def __init__(self, base_ring, names, category=None): """ Parent.__init__(self, base=base_ring, names=names, category=MagmasAndAdditiveMagmas().or_subcategory(category)) + self._implementation = implementation def _repr_(self): """ From 995e7fbb8b8521b6553fbfd40d0f53cf53331a30 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Mon, 7 Jun 2021 22:47:39 +0530 Subject: [PATCH 05/98] First iteration of the dense implementation of Lazy Laurent Series without lists is completed. --- src/sage/rings/lazy_laurent_series.py | 144 ++++++-------------------- 1 file changed, 29 insertions(+), 115 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 92bce91bd73..8f3fe7cf362 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -167,10 +167,7 @@ def __init__(self, parent, coefficient=None, valuation=0, constant=None): self._approximate_valuation = valuation self._constant = constant self._implementation = parent._implementation - # print(parent) - # print("The implementation in the series file is:", self._implementation) - # print("The approx_val rn:", self._approximate_valuation) - # print("The constnt rn:", self._constant) + if self._implementation == 'sparse': self._cache = dict() # cache of known coefficients elif self._implementation == 'dense': @@ -178,37 +175,6 @@ def __init__(self, parent, coefficient=None, valuation=0, constant=None): self._offset = valuation else: raise ValueError - # def __init__(self, parent, coefficient=None, valuation=0, constant=None, implementation="sparse"): - # """ - # Initialize. - - # TESTS:: - - # sage: L = LazyLaurentSeriesRing(GF(2), 'z') - # sage: z = L.gen() - # sage: TestSuite(z).run() - # """ - # ModuleElement.__init__(self, parent) - - # self._coefficient_function = coefficient - # self._approximate_valuation = valuation - # self._constant = constant - - # if implementation == "sparse": - # self._cache = dict() # cache of known coefficients - # elif implementation == "dense": - # # this might work - # class mycache(): - # def __init__(self, coefficient, valuation): - # self.c = coefficient - # self.v = valuation - # self.cache = lazy_list(lambda n: self.c(n+self.v)) - # def __getitem__(self, n): - # return self.cache[n-self.v] - - # self._cache = mycache(self._coefficient_function, self._approximate_valuation) - # else: - # raise ValueError("sparse or dense") def _richcmp_(self, other, op): """ @@ -409,64 +375,32 @@ def coefficient(self, n): sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... """ + + R = self.base_ring() + if self._approximate_valuation == infinity: + return R.zero() + elif n < self._approximate_valuation: + return R.zero() + elif self._constant is not None and n >= self._constant[1]: + return self._constant[0] + if self._implementation == 'sparse': - R = self.base_ring() - # print(R) - # print("The approximate valuation is:", self._approximate_valuation) - if self._approximate_valuation == infinity: - # print("a") - # print(R.zero()) - return R.zero() - elif n < self._approximate_valuation: - # print("b") - # print(R.zero()) - return R.zero() - elif self._constant is not None and n >= self._constant[1]: - # print("c") - # print(self._constant[0]) - # print(self._constant) - return self._constant[0] - - try: - # print('d') - # print(self._cache) + try: c = self._cache[n] - # print(self._cache) except KeyError: - # print('e') c = R(self._coefficient_function(self, n)) - # print(c) self._cache[n] = c - # print(self._cache) - return c else: - R = self.base_ring() - - if self._approximate_valuation == infinity: - return R.zero() - elif n < self._approximate_valuation: - return R.zero() - elif self._constant is not None and n >= self._constant[1]: - return self._constant[0] - - # try: - # c = self._cache[n] - # except KeyError: - # c = R(self._coefficient_function(self, n)) - # self._cache[n] = c - # i = self._cache[n - self._offset] - i = self._cache[len(self._cache) - self._offset] - if i >= len(self._cache): - # compute all coefficients up to the requested one - l = [R(self._coefficient_function(self, j)) for j in range(i)] - self._cache.extend(l) - - c = self._cache[i] - + i = len(self._cache) + n + if i >= len(self._cache) or -i >= len(self._cache): + l = [R(self._coefficient_function(self, j)) for j in range(1, i)] if i >= 0 else [R(self._coefficient_function(self, -j)) for j in range(-i, -1, -1)] + self._cache.extend(l) + c = self._cache[i - 1] return c + def valuation(self): """ Return the valuation of the series. @@ -485,38 +419,32 @@ def valuation(self): sage: t.valuation() +Infinity """ - if self._implementation == 'sparse': + if self._constant is not None: + n = self._approximate_valuation + m = self._constant[1] + while n <= m: + if self.coefficient(n) != 0: + self._approximate_valuation = n + return n + n += 1 + return infinity + + elif self._implementation == 'sparse': if self._constant is None: n = self._approximate_valuation cache = self._cache - # print("The cache is", self._cache) - # print("The approx value is", n) while True: if n in cache: - # print("The current key is:", n) if cache[n]: self._approximate_valuation = n return n n += 1 else: if self.coefficient(n) != 0: - # print("The value is:", self.coefficient(n)) self._approximate_valuation = n return n n += 1 - else: - n = self._approximate_valuation - m = self._constant[1] - # print("n is:", n) - # print('m is: ', m) - while n <= m: - if self.coefficient(n) != 0: - # print("Self", self._approximate_valuation) - # print(n) - self._approximate_valuation = n - return n - n += 1 - return infinity + else: if self._constant is None: n = self._approximate_valuation @@ -529,23 +457,9 @@ def valuation(self): n += 1 else: if self.coefficient(n) != 0: - # print("The value is:", self.coefficient(n)) self._approximate_valuation = n return n n += 1 - else: - n = self._approximate_valuation - m = self._constant[1] - # print("n is:", n) - # print('m is: ', m) - while n <= m: - if self.coefficient(n) != 0: - # print("Self", self._approximate_valuation) - # print(n) - self._approximate_valuation = n - return n - n += 1 - return infinity def prec(self): """ From bd0071bb506b0a7d95013c521e4c2b2c42d22154 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Mon, 7 Jun 2021 23:16:49 +0530 Subject: [PATCH 06/98] First iteration of the dense implementation of Lazy Laurent Series without lists is completed. Fixed a small bug. --- src/sage/rings/lazy_laurent_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 8f3fe7cf362..855d2b57790 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -450,7 +450,7 @@ def valuation(self): n = self._approximate_valuation cache = self._cache while True: - if n < len(cache): + if n - self._offset < len(cache): if cache[n]: self._approximate_valuation = n return n From 271672ce1dae228cb279ed927712ee04b72d2c45 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 9 Jun 2021 00:13:30 +0530 Subject: [PATCH 07/98] Second version of the dense implementation of the lazy Laurent Series without lazy lists. Fixed the issues with the cache. --- src/sage/rings/lazy_laurent_series.py | 28 +++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 855d2b57790..178805d026f 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -393,13 +393,29 @@ def coefficient(self, n): return c else: - i = len(self._cache) + n - if i >= len(self._cache) or -i >= len(self._cache): - l = [R(self._coefficient_function(self, j)) for j in range(1, i)] if i >= 0 else [R(self._coefficient_function(self, -j)) for j in range(-i, -1, -1)] + if n - self._offset < len(self._cache): + c = self._cache[n - self._offset] + return c + + if len(self._cache) != 0: + i = n + 1 + calculated_index = len(self._cache) + self._offset + l = [R(self._coefficient_function(self, j)) for j in range(calculated_index, i)] self._cache.extend(l) - c = self._cache[i - 1] - return c - + c = self._cache[n - self._offset] + return c + else: + i = n - self._offset + 1 if self._offset <= 0 else n + 1 + if self._offset <= 0: + l = [R(self._coefficient_function(self, -j)) for j in range(-self._offset, -1, -1)] + self._cache.extend(l) + c = self._cache[n - self._offset] + return c + else: + l = [R(self._coefficient_function(self, j)) for j in range(self._offset, i)] + self._cache.extend(l) + c = self._cache[i - 1 - self._offset] + return c def valuation(self): """ From a012fbc3f57bd81e301099918a91b6fc4b56911a Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 9 Jun 2021 19:09:54 +0530 Subject: [PATCH 08/98] Fixed the error in valuation code and also shortened the coefficient code to three lines. --- src/sage/rings/lazy_laurent_series.py | 50 +++++++++------------------ 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 178805d026f..76810e64e0f 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -96,6 +96,7 @@ ) from sage.misc.lazy_list import lazy_list + class LazyLaurentSeries(ModuleElement): r""" Return a lazy Laurent series. @@ -167,9 +168,9 @@ def __init__(self, parent, coefficient=None, valuation=0, constant=None): self._approximate_valuation = valuation self._constant = constant self._implementation = parent._implementation - + if self._implementation == 'sparse': - self._cache = dict() # cache of known coefficients + self._cache = dict() # cache of known coefficients elif self._implementation == 'dense': self._cache = list() self._offset = valuation @@ -301,7 +302,7 @@ def _repr_(self): n = self.valuation() if self._constant is None: - m = n + 7 # long enough + m = n + 7 # long enough elif self._constant[0] != 0: m = self._constant[1] + 3 else: @@ -317,16 +318,16 @@ def _repr_(self): if not atomic_repr and n > 0 and (x[1:].find('+') != -1 or x[1:].find('-') != -1): x = '({})'.format(x) if n > 1 or n < 0: - var = '*{}^{}'.format(X,n) + var = '*{}^{}'.format(X, n) elif n == 1: var = '*{}'.format(X) else: # n == 0 var = '' - s += '{}{}'.format(x,var) + s += '{}{}'.format(x, var) first = False n += 1 - s = s.replace(" + -", " - ").replace(" 1*"," ").replace(" -1*", " -")[1:] + s = s.replace(" + -", " - ").replace(" 1*", " ").replace(" -1*", " -")[1:] if not s: # zero series s = '0' @@ -375,7 +376,7 @@ def coefficient(self, n): sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... """ - + R = self.base_ring() if self._approximate_valuation == infinity: return R.zero() @@ -385,7 +386,7 @@ def coefficient(self, n): return self._constant[0] if self._implementation == 'sparse': - try: + try: c = self._cache[n] except KeyError: c = R(self._coefficient_function(self, n)) @@ -393,29 +394,10 @@ def coefficient(self, n): return c else: - if n - self._offset < len(self._cache): - c = self._cache[n - self._offset] - return c - - if len(self._cache) != 0: - i = n + 1 - calculated_index = len(self._cache) + self._offset - l = [R(self._coefficient_function(self, j)) for j in range(calculated_index, i)] - self._cache.extend(l) - c = self._cache[n - self._offset] - return c - else: - i = n - self._offset + 1 if self._offset <= 0 else n + 1 - if self._offset <= 0: - l = [R(self._coefficient_function(self, -j)) for j in range(-self._offset, -1, -1)] - self._cache.extend(l) - c = self._cache[n - self._offset] - return c - else: - l = [R(self._coefficient_function(self, j)) for j in range(self._offset, i)] - self._cache.extend(l) - c = self._cache[i - 1 - self._offset] - return c + a = len(self._cache) + self._offset + self._cache.extend([R(self._coefficient_function(self, j)) for j in range(a, n + 1)]) + c = self._cache[n - self._offset] + return c def valuation(self): """ @@ -467,7 +449,7 @@ def valuation(self): cache = self._cache while True: if n - self._offset < len(cache): - if cache[n]: + if cache[n - self._offset]: self._approximate_valuation = n return n n += 1 @@ -553,7 +535,7 @@ def polynomial(self, degree=None, name=None): from sage.rings.all import LaurentPolynomialRing R = LaurentPolynomialRing(S.base_ring(), name=name) n = self.valuation() - return R([self.coefficient(i) for i in range(n,m)]).shift(n) + return R([self.coefficient(i) for i in range(n, m)]).shift(n) else: from sage.rings.all import PolynomialRing R = PolynomialRing(S.base_ring(), name=name) @@ -603,7 +585,7 @@ def approximate_series(self, prec, name=None): from sage.rings.all import LaurentSeriesRing R = LaurentSeriesRing(S.base_ring(), name=name) n = self.valuation() - return R([self.coefficient(i) for i in range(n,prec)], n).add_bigoh(prec) + return R([self.coefficient(i) for i in range(n, prec)], n).add_bigoh(prec) else: from sage.rings.all import PowerSeriesRing R = PowerSeriesRing(S.base_ring(), name=name) From 5011d4436c25f2902921b51d1aa981ef23d1dfba Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 10 Jun 2021 00:00:00 +0530 Subject: [PATCH 09/98] Fixed the recursion bug in the dense implementation. Added a doctest for the dense version of the coefficient code. --- src/sage/rings/lazy_laurent_series.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 76810e64e0f..23541280b29 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -375,6 +375,22 @@ def coefficient(self, n): 16796 sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + + TESTS:: + sage: def g(s, i): + ....: if i == 0: + ....: return 1 + ....: else: + ....: return sum(s.coefficient(j)*s.coefficient(i - 1 - j) for j in [0..i-1]) + ....: + sage: L = LazyLaurentSeriesRing(ZZ,'z',implementation='dense') + sage: e = L.series(g, valuation = 0) + sage: e.coefficient(10) + 16796 + sage: e + 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + + """ R = self.base_ring() @@ -395,7 +411,8 @@ def coefficient(self, n): else: a = len(self._cache) + self._offset - self._cache.extend([R(self._coefficient_function(self, j)) for j in range(a, n + 1)]) + old_len = len(self._cache) + self._cache.extend([R(self._coefficient_function(self, j)) for j in range(a, n + 1)][(len(self._cache) - old_len):]) c = self._cache[n - self._offset] return c From 2c2c157c024bd8457839bf3b33a688e06bc36750 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 10 Jun 2021 00:02:10 +0200 Subject: [PATCH 10/98] explain computation of cache, minor optimisations --- src/sage/rings/lazy_laurent_series.py | 35 +++++++++++++++------------ 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 23541280b29..c784cc9d21d 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -94,8 +94,6 @@ LazyLaurentSeriesOperator_change_ring, LazyLaurentSeriesOperator_truncate ) -from sage.misc.lazy_list import lazy_list - class LazyLaurentSeries(ModuleElement): r""" @@ -377,24 +375,27 @@ def coefficient(self, n): 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... TESTS:: + sage: def g(s, i): ....: if i == 0: ....: return 1 ....: else: ....: return sum(s.coefficient(j)*s.coefficient(i - 1 - j) for j in [0..i-1]) ....: - sage: L = LazyLaurentSeriesRing(ZZ,'z',implementation='dense') + sage: L = LazyLaurentSeriesRing(ZZ, 'z', implementation='dense') sage: e = L.series(g, valuation = 0) - sage: e.coefficient(10) - 16796 sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - + sage: e._cache + [1, 1, 2, 5, 14, 42, 132] + sage: e.coefficient(10) + 16796 + sage: e._cache + [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796] """ - R = self.base_ring() - if self._approximate_valuation == infinity: + if self._approximate_valuation is infinity: return R.zero() elif n < self._approximate_valuation: return R.zero() @@ -407,14 +408,18 @@ def coefficient(self, n): except KeyError: c = R(self._coefficient_function(self, n)) self._cache[n] = c - return c else: - a = len(self._cache) + self._offset - old_len = len(self._cache) - self._cache.extend([R(self._coefficient_function(self, j)) for j in range(a, n + 1)][(len(self._cache) - old_len):]) - c = self._cache[n - self._offset] - return c + i = n - self._offset + if i >= len(self._cache): + a = len(self._cache) + self._offset + # it is important to extend by generator: + # self._coefficient_function might recurse, and + # thereby extend the cache itself, too + self._cache.extend(R(self._coefficient_function(self, j)) for j in range(a, n+1)) + c = self._cache[i] + + return c def valuation(self): """ @@ -765,7 +770,7 @@ def __invert__(self): """ v = self.valuation() - if v == infinity: + if v is infinity: raise ZeroDivisionError('cannot invert zero') R = self.parent() From f0a9208572ef0cc31a26264427949232daf6480e Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 16 Jun 2021 20:21:39 +0530 Subject: [PATCH 11/98] Added the file for the new design. --- src/sage/rings/lazy_laurent_series_ring.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 8b60d3cc6a1..5e45cc4c81d 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -73,6 +73,7 @@ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing_generic from .lazy_laurent_series import LazyLaurentSeries +from .lazy_laurent_series_new import LLS from .lazy_laurent_series_operator import ( LazyLaurentSeriesOperator_gen, LazyLaurentSeriesOperator_constant, @@ -97,6 +98,7 @@ class LazyLaurentSeriesRing(UniqueRepresentation, Parent): Lazy Laurent Series Ring in t over Integer Ring """ Element = LazyLaurentSeries + # Element = LLS def __init__(self, base_ring, names, category=None, implementation='sparse'): """ From 79eaf4ae6ee99298ca561e797495a5e6d86a597f Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 16 Jun 2021 21:06:23 +0530 Subject: [PATCH 12/98] Added the file for the new design. --- src/sage/rings/lazy_laurent_series_new.py | 150 ++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 src/sage/rings/lazy_laurent_series_new.py diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py new file mode 100644 index 00000000000..0dabcb02011 --- /dev/null +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -0,0 +1,150 @@ +from .infinity import infinity +from sage.structure.element import ModuleElement + +class LLS(ModuleElement): + def __init__(self, parent, aux): + ModuleElement.__init__(self, parent) + self._aux = aux + + def __getitem__(self, n): + return self.base_ring()(self._aux[n]) + + def _mul_(self, other): + P = self.parent() + return P.element_class(P, LLS_mul(self._aux, other._aux)) + +class LLS_aux(): + def __init__(self, is_sparse, approximate_valuation, constant=None): + self._approximate_valuation = approximate_valuation + self._constant = constant + self._is_sparse = is_sparse + + if self._is_sparse: + self._cache = dict() # cache of known coefficients + else: + self._cache = list() + self._offset = approximate_valuation + self._iter = self.iterate_coefficients() + + def __getitem__(self, n): + if self._approximate_valuation is infinity: + return ZZ.zero() + elif n < self._approximate_valuation: + return ZZ.zero() + elif self._constant is not None and n >= self._constant[1]: + return self._constant[0] + + if self._is_sparse: + try: + c = self._cache[n] + except KeyError: + c = self.get_coefficient(n) + self._cache[n] = c + + else: + i = n - self._offset + if i >= len(self._cache): + a = len(self._cache) + self._offset + # it is important to extend by generator: + # self._coefficient_function might recurse, and + # thereby extend the cache itself, too + self._cache.extend(next(self._iter) for _ in range(a, n+1)) + c = self._cache[i] + + return c + +class LLS_coefficient_function(LLS_aux): + """ + + EXAMPLES:: + + sage: s = LLS_coefficient_function(lambda n: 1, True, 0) + sage: [s[i] for i in range(-5, 5)] + [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] + sage: t = LLS_mul(s, s) + sage: [t[i] for i in range(-5, 5)] + [0, 0, 0, 0, 0, 1, 2, 3, 4, 5] + + TESTS:: + + sage: s = LLS_coefficient_function(lambda n: 1, False, 0) + sage: t = LLS_mul(s, s) + sage: [t[i] for i in range(-5, 5)] + [0, 0, 0, 0, 0, 1, 2, 3, 4, 5] + + sage: s._cache + [1, 1, 1, 1, 1] + + """ + def __init__(self, coefficient_function, is_sparse, approximate_valuation, constant=None): + self._coefficient_function = coefficient_function + super().__init__(is_sparse, approximate_valuation, constant) + + def get_coefficient(self, n): + return self._coefficient_function(n) + + def iterate_coefficients(self): + n = self._offset + while True: + yield self._coefficient_function(n) + n += 1 + + +class LLS_mul(LLS_aux): + """ + Operator for multiplication. + """ + def __init__(self, left, right): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) * 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._left = left + self._right = right + a = left._approximate_valuation + right._approximate_valuation + c = None + if left._constant is not None and right._constant is not None: + if left._constant[0] == 0 and right._constant[0] == 0: + c = (left._constant[0], left._constant[1] + right._constant[1] - 1) + + if left._is_sparse != right._is_sparse: + raise NotImplementedError + super().__init__(left._is_sparse, a, c) + + + def get_coefficient(self, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = (1 + z)*(1 - z) + sage: f.coefficient(2) + -1 + """ + c = ZZ.zero() + for k in range(self._left._approximate_valuation, + n - self._right._approximate_valuation + 1): + l = self._left[k] + if l: + c += l * self._right[n-k] + return c + + def iterate_coefficients(self): + n = self._offset + while True: + c = ZZ.zero() + for k in range(self._left._approximate_valuation, + n - self._right._approximate_valuation + 1): + l = self._left[k] + if l: + c += l * self._right[n-k] + yield c + n += 1 \ No newline at end of file From cbbc75b42abd02aefcdc42979b93a3a2f00f3972 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 17 Jun 2021 19:42:06 +0530 Subject: [PATCH 13/98] Implemented generation and multiplication in the new design. --- src/sage/rings/lazy_laurent_series_new.py | 1 + src/sage/rings/lazy_laurent_series_ring.py | 26 +++++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 0dabcb02011..a56147e1a20 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -1,5 +1,6 @@ from .infinity import infinity from sage.structure.element import ModuleElement +from .integer_ring import ZZ class LLS(ModuleElement): def __init__(self, parent, aux): diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 5e45cc4c81d..f0db12e7ce7 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -73,7 +73,10 @@ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing_generic from .lazy_laurent_series import LazyLaurentSeries -from .lazy_laurent_series_new import LLS +from .lazy_laurent_series_new import ( + LLS, + LLS_coefficient_function +) from .lazy_laurent_series_operator import ( LazyLaurentSeriesOperator_gen, LazyLaurentSeriesOperator_constant, @@ -97,10 +100,10 @@ class LazyLaurentSeriesRing(UniqueRepresentation, Parent): sage: LazyLaurentSeriesRing(ZZ, 't') Lazy Laurent Series Ring in t over Integer Ring """ - Element = LazyLaurentSeries - # Element = LLS + # Element = LazyLaurentSeries + Element = LLS - def __init__(self, base_ring, names, category=None, implementation='sparse'): + def __init__(self, base_ring, names, category=None): """ Initialize. @@ -111,7 +114,6 @@ def __init__(self, base_ring, names, category=None, implementation='sparse'): """ Parent.__init__(self, base=base_ring, names=names, category=MagmasAndAdditiveMagmas().or_subcategory(category)) - self._implementation = implementation def _repr_(self): """ @@ -262,7 +264,8 @@ def zero(self): """ return self._element_constructor_(0) - def series(self, coefficient, valuation, constant=None): + # def series(self, coefficient, valuation, constant=None): + def series(self, coefficient_function, is_sparse, approximate_valuation, constant=None): r""" Return a lazy Laurent series. @@ -325,24 +328,25 @@ def series(self, coefficient, valuation, constant=None): sage: g z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... """ - if isinstance(coefficient, (tuple, list)): + if isinstance(coefficient_function, (tuple, list)): if isinstance(constant, tuple): constant = constant[0] if constant is None: constant = self.base_ring().zero() elif constant not in self.base_ring(): raise ValueError("constant is not an element of the base ring") - constant = (constant, valuation + len(coefficient)) - coefficient = LazyLaurentSeriesOperator_list(self, coefficient, valuation) + constant = (constant, approximate_valuation + len(coefficient_function)) + coefficient_function = LazyLaurentSeriesOperator_list(self, coefficient_function, approximate_valuation) elif constant is not None: try: c,m = constant except TypeError: raise TypeError('not a tuple') - if valuation > m and c: # weird case + if approximate_valuation > m and c: # weird case raise ValueError('inappropriate valuation') constant = (self.base_ring()(c), m) - return self.element_class(self, coefficient=coefficient, valuation=valuation, constant=constant) + aux = LLS_coefficient_function(coefficient_function=coefficient_function, is_sparse=is_sparse, approximate_valuation=approximate_valuation, constant=constant) + return self.element_class(self, aux) \ No newline at end of file From 27755ecc4b68947a8e86a576a0fa3a26ee9ba04a Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 17 Jun 2021 20:55:45 +0530 Subject: [PATCH 14/98] Implemented addition for the new design. --- src/sage/rings/lazy_laurent_series_new.py | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index a56147e1a20..c2013a15ddd 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -13,6 +13,10 @@ def __getitem__(self, n): def _mul_(self, other): P = self.parent() return P.element_class(P, LLS_mul(self._aux, other._aux)) + + def _add_(self, other): + P = self.parent() + return P.element_class(P, LLS_add(self._aux, other._aux)) class LLS_aux(): def __init__(self, is_sparse, approximate_valuation, constant=None): @@ -148,4 +152,58 @@ def iterate_coefficients(self): if l: c += l * self._right[n-k] yield c + n += 1 + + +class LLS_add(LLS_aux): + """ + Operator for addition. + """ + def __init__(self, left, right): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) * 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._left = left + self._right = right + a = min(left._approximate_valuation, right._approximate_valuation) + + if left._constant is not None and right._constant is not None: + c = (left._constant[0] + right._constant[0], + max(left._constant[1], right._constant[1])) + else: + c = None + + if left._is_sparse != right._is_sparse: + raise NotImplementedError + + super().__init__(left._is_sparse, a, c) + + def get_coefficient(self, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = (1 + z)*(1 - z) + sage: f.coefficient(2) + -1 + """ + c = ZZ.zero() + c = self._left[n] + self._right[n] + return c + + def iterate_coefficients(self): + n = self._offset + while True: + c = ZZ.zero() + c = self._left[n] + self._right[n] + yield c n += 1 \ No newline at end of file From 15ef8f16f315c0f3dc92f3c0a34764a865f9d8a7 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 17 Jun 2021 21:46:40 +0530 Subject: [PATCH 15/98] Implemented representation for the new design. --- src/sage/rings/lazy_laurent_series_new.py | 54 ++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index c2013a15ddd..2c778fe1450 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -17,6 +17,58 @@ def _mul_(self, other): def _add_(self, other): P = self.parent() return P.element_class(P, LLS_add(self._aux, other._aux)) + + def _repr_(self): + """ + Return the string representation of this Laurent series. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: -1/(1 + 2*z) + -1 + 2*z - 4*z^2 + 8*z^3 - 16*z^4 + 32*z^5 - 64*z^6 + ... + """ + atomic_repr = self.base_ring()._repr_option('element_is_atomic') + X = self.parent().variable_name() + + n = self._aux._approximate_valuation + + if self._aux._constant is None: + m = n + 7 # long enough + elif self._aux._constant[0] != 0: + m = self._aux._constant[1] + 3 + else: + m = self._aux._constant[1] + + s = ' ' + first = True + while n < m: + x = repr(self._aux[n]) + if x != '0': + if not first: + s += ' + ' + if not atomic_repr and n > 0 and (x[1:].find('+') != -1 or x[1:].find('-') != -1): + x = '({})'.format(x) + if n > 1 or n < 0: + var = '*{}^{}'.format(X, n) + elif n == 1: + var = '*{}'.format(X) + else: # n == 0 + var = '' + s += '{}{}'.format(x, var) + first = False + n += 1 + + s = s.replace(" + -", " - ").replace(" 1*", " ").replace(" -1*", " -")[1:] + + if not s: # zero series + s = '0' + + if self._aux._constant is None or self._aux._constant[1] > m or self._aux._constant[0] != 0: + s += ' + {}'.format('...') + + return s + class LLS_aux(): def __init__(self, is_sparse, approximate_valuation, constant=None): @@ -57,10 +109,10 @@ def __getitem__(self, n): c = self._cache[i] return c + class LLS_coefficient_function(LLS_aux): """ - EXAMPLES:: sage: s = LLS_coefficient_function(lambda n: 1, True, 0) From b0b438cd03ae8d64e5aa57ba37b04674715ef4dd Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 17 Jun 2021 23:23:23 +0530 Subject: [PATCH 16/98] Implemented scalar multiplication for the new implementation. --- src/sage/rings/lazy_laurent_series_new.py | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 2c778fe1450..6be081c1ec6 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -18,6 +18,10 @@ def _add_(self, other): P = self.parent() return P.element_class(P, LLS_add(self._aux, other._aux)) + def _rmul_(self, scalar): + P = self.parent() + return P.element_class(P, LLS_rmul(self._aux, scalar)) + def _repr_(self): """ Return the string representation of this Laurent series. @@ -258,4 +262,62 @@ def iterate_coefficients(self): c = ZZ.zero() c = self._left[n] + self._right[n] yield c + n += 1 + +class LLS_rmul(LLS_aux): + """ + Operator for multiplying with a scalar. + """ + def __init__(self, series, scalar): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) * 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._series = series + self._scalar = scalar + a = series._approximate_valuation + + # if left._constant is not None and right._constant is not None: + # c = (left._constant[0] + right._constant[0], + # max(left._constant[1], right._constant[1])) + # else: + # c = None + + # if left._is_sparse != right._is_sparse: + # raise NotImplementedError + + if series._constant is not None: + c = (scalar * series._constant[0], series._constant[1]) + else: + c = None + + # return R.element_class(R, coefficient=op, valuation=a, constant=c) + + super().__init__(series._is_sparse, a, c) + + def get_coefficient(self, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = (1 + z)*(1 - z) + sage: f.coefficient(2) + -1 + """ + c = self._series[n] * self._scalar + return c + + def iterate_coefficients(self): + n = self._offset + while True: + c = self._series[n] * self._scalar + yield c n += 1 \ No newline at end of file From daaad21ca76b977421df9678afd98c98a101b8cf Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 17 Jun 2021 23:24:10 +0530 Subject: [PATCH 17/98] Implemented scalar multiplication for the new implementation and fixed the formatting. --- src/sage/rings/lazy_laurent_series_new.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 6be081c1ec6..dbcfc238b27 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -281,23 +281,12 @@ def __init__(self, series, scalar): """ self._series = series self._scalar = scalar - a = series._approximate_valuation - - # if left._constant is not None and right._constant is not None: - # c = (left._constant[0] + right._constant[0], - # max(left._constant[1], right._constant[1])) - # else: - # c = None - - # if left._is_sparse != right._is_sparse: - # raise NotImplementedError + a = series._approximate_valuation if series._constant is not None: c = (scalar * series._constant[0], series._constant[1]) else: c = None - - # return R.element_class(R, coefficient=op, valuation=a, constant=c) super().__init__(series._is_sparse, a, c) From 9c999044ee9c2b36f8821f17ab83a189d82346bb Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 18:36:41 +0530 Subject: [PATCH 18/98] Implemented comparision for the new series. --- src/sage/rings/lazy_laurent_series_new.py | 62 +++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index dbcfc238b27..74a4d2f682c 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -1,6 +1,8 @@ from .infinity import infinity from sage.structure.element import ModuleElement from .integer_ring import ZZ +from sage.structure.richcmp import op_EQ, op_NE + class LLS(ModuleElement): def __init__(self, parent, aux): @@ -73,6 +75,65 @@ def _repr_(self): return s + def _richcmp_(self, other, op): + """ + Compare ``self` with ``other`` with respect to the comparison operator ``op``. + + Equality is verified if the corresponding coefficients of both series + can be checked for equality without computing coefficients + indefinitely. Otherwise an exception is raised to declare that + equality is not decidable. + + Inequality is not defined for lazy Laurent series. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: z + z^2 == z^2 + z + True + sage: z + z^2 != z^2 + z + False + sage: z + z^2 > z^2 + z + False + sage: z + z^2 < z^2 + z + False + """ + if op is op_EQ: + if self._aux._constant is None: + if other._aux._constant is None: + n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) + m = max(self._aux._approximate_valuation, other._aux._approximate_valuation) + for i in range(n, m): + if self[i] != other[i]: + return False + if self._aux._coefficient_function == other._aux._coefficient_function: + return True + raise ValueError("undecidable as lazy Laurent series") + else: + raise ValueError("undecidable as lazy Laurent series") + elif other._aux._constant is None: + raise ValueError("undecidable as lazy Laurent series") + + sc, sm = self._aux._constant + oc, om = other._aux._constant + + if sc != oc: + return False + + n = self._aux._approximate_valuation + m = max(sm, om) + + for i in range(n, m): + if self[i] != other[i]: + return False + + return True + + if op is op_NE: + return not (self == other) + + return False + class LLS_aux(): def __init__(self, is_sparse, approximate_valuation, constant=None): @@ -264,6 +325,7 @@ def iterate_coefficients(self): yield c n += 1 + class LLS_rmul(LLS_aux): """ Operator for multiplying with a scalar. From 0777a414a796e4b5f8b0d6de32c536a45f937d6c Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 18:39:43 +0530 Subject: [PATCH 19/98] Implemented hash for the new definition. --- src/sage/rings/lazy_laurent_series_new.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 74a4d2f682c..07d0b38023a 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -134,6 +134,10 @@ def _richcmp_(self, other, op): return False + def __hash__(self): + return hash((type(self), self._aux._coefficient_function, + self._aux._approximate_valuation, self._aux._constant)) + class LLS_aux(): def __init__(self, is_sparse, approximate_valuation, constant=None): From be1b15f86122de9c3ab88c61ecbe5d9e366d260a Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 18:45:13 +0530 Subject: [PATCH 20/98] Implemented bool for the new definition. --- src/sage/rings/lazy_laurent_series_new.py | 34 ++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 07d0b38023a..1e698a32d43 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -136,7 +136,39 @@ def _richcmp_(self, other, op): def __hash__(self): return hash((type(self), self._aux._coefficient_function, - self._aux._approximate_valuation, self._aux._constant)) + self._aux._approximate_valuation, self._aux._constant)) + + def __bool__(self): + """ + Test whether ``self`` is not zero. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(GF(2)) + sage: (z-z).is_zero() + True + sage: f = 1/(1 - z) + sage: f.is_zero() + False + """ + if self._aux._constant is None: + for a in self._aux._cache: + if a: + return True + if self[self._aux._approximate_valuation]: + return True + raise ValueError("undecidable as lazy Laurent series") + + sc, sm = self._aux._constant + + if sc: + return True + + for i in range(self._aux._approximate_valuation, sm): + if self[i]: + return True + + return False class LLS_aux(): From d37c78a4e76627bf134a551a88fb0f57e96a8082 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 18:50:42 +0530 Subject: [PATCH 21/98] Implemented subtraction for the new design. --- src/sage/rings/lazy_laurent_series_new.py | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 1e698a32d43..2c128aaa68e 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -24,6 +24,10 @@ def _rmul_(self, scalar): P = self.parent() return P.element_class(P, LLS_rmul(self._aux, scalar)) + def _sub_(self, other): + P = self.parent() + return P.element_class(P, LLS_sub(self._aux, other._aux)) + def _repr_(self): """ Return the string representation of this Laurent series. @@ -362,6 +366,60 @@ def iterate_coefficients(self): n += 1 +class LLS_sub(LLS_aux): + """ + Operator for subtraction. + """ + def __init__(self, left, right): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) * 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._left = left + self._right = right + a = min(left._approximate_valuation, right._approximate_valuation) + + if left._constant is not None and right._constant is not None: + c = (left._constant[0] - right._constant[0], + max(left._constant[1], right._constant[1])) + else: + c = None + + if left._is_sparse != right._is_sparse: + raise NotImplementedError + + super().__init__(left._is_sparse, a, c) + + def get_coefficient(self, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = (1 + z)*(1 - z) + sage: f.coefficient(2) + -1 + """ + c = ZZ.zero() + c = self._left[n] - self._right[n] + return c + + def iterate_coefficients(self): + n = self._offset + while True: + c = ZZ.zero() + c = self._left[n] - self._right[n] + yield c + n += 1 + + class LLS_rmul(LLS_aux): """ Operator for multiplying with a scalar. From a126841f3f5c7cc40e01f8f9b630bf834dd8471f Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 18:56:36 +0530 Subject: [PATCH 22/98] Implemented neg for the new design. --- src/sage/rings/lazy_laurent_series_new.py | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 2c128aaa68e..e37090ce0f8 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -28,6 +28,10 @@ def _sub_(self, other): P = self.parent() return P.element_class(P, LLS_sub(self._aux, other._aux)) + def _neg_(self): + P = self.parent() + return P.element_class(P, LLS_neg(self._aux)) + def _repr_(self): """ Return the string representation of this Laurent series. @@ -465,4 +469,51 @@ def iterate_coefficients(self): while True: c = self._series[n] * self._scalar yield c + n += 1 + + +class LLS_neg(LLS_aux): + """ + Operator for negative of the series. + """ + def __init__(self, series): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) * 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._series = series + a = series._approximate_valuation + + if series._constant is not None: + c = (-series._constant[0], series._constant[1]) + else: + c = None + + super().__init__(series._is_sparse, a, c) + + def get_coefficient(self, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = (1 + z)*(1 - z) + sage: f.coefficient(2) + -1 + """ + c = -1 * self._series[n] + return c + + def iterate_coefficients(self): + n = self._offset + while True: + c = -1 * self._series[n] + yield c n += 1 \ No newline at end of file From 023b4b40e0b385b0f3ab2fa7ec331cd856649562 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 19:03:53 +0530 Subject: [PATCH 23/98] Implemented valuation for the new design. --- src/sage/rings/lazy_laurent_series_new.py | 60 +++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index e37090ce0f8..73d7088a322 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -32,6 +32,66 @@ def _neg_(self): P = self.parent() return P.element_class(P, LLS_neg(self._aux)) + def valuation(self): + """ + Return the valuation of the series. + + This method determines the valuation of the series by looking for a + nonzero coefficient. Hence if the series happens to be zero, then it + may run forever. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: s = 1/(1 - z) - 1/(1 - 2*z) + sage: s.valuation() + 1 + sage: t = z - z + sage: t.valuation() + +Infinity + """ + if self._aux._constant is not None: + n = self._aux._approximate_valuation + m = self._aux._constant[1] + while n <= m: + if self[n] != 0: + self._aux._approximate_valuation = n + return n + n += 1 + return infinity + + elif self._aux._is_sparse: + if self._aux._constant is None: + n = self._aux._approximate_valuation + cache = self._aux._cache + while True: + if n in cache: + if cache[n]: + self._aux._approximate_valuation = n + return n + n += 1 + else: + if self[n] != 0: + self._aux._approximate_valuation = n + return n + n += 1 + + else: + if self._aux._constant is None: + n = self._aux._approximate_valuation + cache = self._aux._cache + while True: + if n - self._aux._offset < len(cache): + if cache[n - self._aux._offset]: + self._aux._approximate_valuation = n + return n + n += 1 + else: + if self[n] != 0: + self._aux._approximate_valuation = n + return n + n += 1 + def _repr_(self): """ Return the string representation of this Laurent series. From 3fc85da6a327ae8bc0384d9d9a505aa9083035e2 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 19:09:31 +0530 Subject: [PATCH 24/98] Implemented polynomial for the new design. --- src/sage/rings/lazy_laurent_series_new.py | 69 +++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 73d7088a322..9ae80541cb3 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -32,6 +32,75 @@ def _neg_(self): P = self.parent() return P.element_class(P, LLS_neg(self._aux)) + def polynomial(self, degree=None, name=None): + """ + Return the polynomial or Laurent polynomial if the series is actually so. + + INPUT: + + - ``degree`` -- ``None`` or an integer + + - ``name`` -- name of the variable; if it is ``None``, the name of the variable + of the series is used + + OUTPUT: a Laurent polynomial if the valuation of the series is negative or + a polynomial otherwise. + + If ``degree`` is not ``None``, the terms of the series of degree + greater than ``degree`` are truncated first. If ``degree`` is ``None`` + and the series is not a polynomial or a Laurent polynomial, a + ``ValueError`` is raised. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f = L.series([1,0,0,2,0,0,0,3], 5); f + z^5 + 2*z^8 + 3*z^12 + sage: f.polynomial() + 3*z^12 + 2*z^8 + z^5 + + :: + + sage: g = L.series([1,0,0,2,0,0,0,3], -5); g + z^-5 + 2*z^-2 + 3*z^2 + sage: g.polynomial() + z^-5 + 2*z^-2 + 3*z^2 + + :: + + sage: z = L.gen() + sage: f = (1 + z)/(z^3 - z^5) + sage: f + z^-3 + z^-2 + z^-1 + 1 + z + z^2 + z^3 + ... + sage: f.polynomial(5) + z^-3 + z^-2 + z^-1 + 1 + z + z^2 + z^3 + z^4 + z^5 + sage: f.polynomial(0) + z^-3 + z^-2 + z^-1 + 1 + sage: f.polynomial(-5) + 0 + """ + if degree is None: + if self._aux._constant is None or not self._aux._constant[0].is_zero(): + raise ValueError("not a polynomial") + m = self._aux._constant[1] + else: + m = degree + 1 + + S = self.parent() + + if name is None: + name = S.variable_name() + + if self.valuation() < 0: + from sage.rings.all import LaurentPolynomialRing + R = LaurentPolynomialRing(S.base_ring(), name=name) + n = self.valuation() + return R([self[i] for i in range(n, m)]).shift(n) + else: + from sage.rings.all import PolynomialRing + R = PolynomialRing(S.base_ring(), name=name) + return R([self[i] for i in range(m)]) + def valuation(self): """ Return the valuation of the series. From a667a3c5107f19068b731952710e359555b0c2b3 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 19:11:07 +0530 Subject: [PATCH 25/98] Implemented precision for the new design. --- src/sage/rings/lazy_laurent_series_new.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 9ae80541cb3..fca0bcc3953 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -32,6 +32,19 @@ def _neg_(self): P = self.parent() return P.element_class(P, LLS_neg(self._aux)) + def prec(self): + """ + Return the precision of the series, which is infinity. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + sage: f.prec() + +Infinity + """ + return infinity + def polynomial(self, degree=None, name=None): """ Return the polynomial or Laurent polynomial if the series is actually so. From 42abfa24cfd9d215c49cafa996413499e8c2e9be Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 19:14:31 +0530 Subject: [PATCH 26/98] Implemented approximate series for the new design. --- src/sage/rings/lazy_laurent_series_new.py | 50 +++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index fca0bcc3953..945a992a39c 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -32,6 +32,56 @@ def _neg_(self): P = self.parent() return P.element_class(P, LLS_neg(self._aux)) + def approximate_series(self, prec, name=None): + """ + Return the Laurent series with absolute precision ``prec`` approximated + from this series. + + INPUT: + + - ``prec`` -- an integer + + - ``name`` -- name of the variable; if it is ``None``, the name of the variable + of the series is used + + OUTPUT: a Laurent series with absolute precision ``prec`` + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: z = L.gen() + sage: f = (z - 2*z^3)^5/(1 - 2*z) + sage: f + z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + 32*z^10 - 16*z^11 + ... + sage: g = f.approximate_series(10) + sage: g + z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + O(z^10) + sage: g.parent() + Power Series Ring in z over Integer Ring + + :: + + sage: h = (f^-1).approximate_series(3) + sage: h + z^-5 - 2*z^-4 + 10*z^-3 - 20*z^-2 + 60*z^-1 - 120 + 280*z - 560*z^2 + O(z^3) + sage: h.parent() + Laurent Series Ring in z over Integer Ring + """ + S = self.parent() + + if name is None: + name = S.variable_name() + + if self.valuation() < 0: + from sage.rings.all import LaurentSeriesRing + R = LaurentSeriesRing(S.base_ring(), name=name) + n = self.valuation() + return R([self[i] for i in range(n, prec)], n).add_bigoh(prec) + else: + from sage.rings.all import PowerSeriesRing + R = PowerSeriesRing(S.base_ring(), name=name) + return R([self[i] for i in range(prec)]).add_bigoh(prec) + def prec(self): """ Return the precision of the series, which is infinity. From 693c46b79dd3e8b9c148c657d14aaa1ac12202d5 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 19:42:10 +0530 Subject: [PATCH 27/98] Implemented inversion for the new design. Still errors. --- src/sage/rings/lazy_laurent_series_new.py | 65 +++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 945a992a39c..39a6fd34971 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -32,6 +32,10 @@ def _neg_(self): P = self.parent() return P.element_class(P, LLS_neg(self._aux)) + def __invert__(self): + P = self.parent() + return P.element_class(P, LLS_inv(self._aux)) + def approximate_series(self, prec, name=None): """ Return the Laurent series with absolute precision ``prec`` approximated @@ -708,4 +712,65 @@ def iterate_coefficients(self): while True: c = -1 * self._series[n] yield c + n += 1 + + +class LLS_inv(LLS_aux): + """ + Operator for multiplicative inverse of the series. + """ + def __init__(self, series): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) * 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._series = series + self._v = series.valuation() + self._ainv = ~series[self._v] + self._zero = series.base_ring().zero() + v = series.valuation() + + if v is infinity: + raise ZeroDivisionError('cannot invert zero') + + super().__init__(series._is_sparse, -v, series._constant) + + def get_coefficient(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = (1 + z)*(1 - z) + sage: f.coefficient(2) + -1 + """ + + v = self._v + if n == -v: + return self._ainv + c = self._zero + for k in range(-v, n): + c += s[k] * self._series[n + v - k] + return -c * self._ainv + + def iterate_coefficients(self): + n = self._offset + while True: + v = self._v + if n == -v: + yield self._ainv + n += 1 + continue + c = self._zero + for k in range(-v, n): + c += s[k] * self._series[n + v - k] + yield -c * self._ainv n += 1 \ No newline at end of file From aa37cd1303afad82f9718e203f5d5a36e4bebbe7 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 19:43:23 +0530 Subject: [PATCH 28/98] Implemented inversion for the new design. Still errors. --- src/sage/rings/lazy_laurent_series_new.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 39a6fd34971..493aab8d898 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -761,7 +761,7 @@ def get_coefficient(self, s, n): c += s[k] * self._series[n + v - k] return -c * self._ainv - def iterate_coefficients(self): + def iterate_coefficients(self, s): n = self._offset while True: v = self._v From adf075f88371cff4d3e75f4a6c286b9223d8e7f0 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 18 Jun 2021 19:53:06 +0530 Subject: [PATCH 29/98] Implemented inversion for the new design. Still errors. --- src/sage/rings/lazy_laurent_series_new.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 493aab8d898..4c3ab321d3d 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -734,9 +734,8 @@ def __init__(self, series): self._v = series.valuation() self._ainv = ~series[self._v] self._zero = series.base_ring().zero() - v = series.valuation() - if v is infinity: + if self._v is infinity: raise ZeroDivisionError('cannot invert zero') super().__init__(series._is_sparse, -v, series._constant) From a241afe6201db2b0bb8b734d2c70a23443a39f2e Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 24 Jun 2021 00:26:49 +0530 Subject: [PATCH 30/98] Valuation is now in the aux, inversion is still an issue. --- src/sage/rings/lazy_laurent_series_new.py | 186 ++++++++++++++++++++-- 1 file changed, 171 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 4c3ab321d3d..b5cf1a777e1 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -36,6 +36,9 @@ def __invert__(self): P = self.parent() return P.element_class(P, LLS_inv(self._aux)) + def v(self): + return LLS_inv(self._aux).valuation(self._aux) + def approximate_series(self, prec, name=None): """ Return the Laurent series with absolute precision ``prec`` approximated @@ -388,7 +391,7 @@ def __init__(self, is_sparse, approximate_valuation, constant=None): self._offset = approximate_valuation self._iter = self.iterate_coefficients() - def __getitem__(self, n): + def __getitem__(self, n, s=None): if self._approximate_valuation is infinity: return ZZ.zero() elif n < self._approximate_valuation: @@ -400,7 +403,7 @@ def __getitem__(self, n): try: c = self._cache[n] except KeyError: - c = self.get_coefficient(n) + c = self.get_coefficient(n, s) self._cache[n] = c else: @@ -415,6 +418,68 @@ def __getitem__(self, n): return c + def valuation(self, series): + print("I am not even being called.") + """ + Return the valuation of the series. + + This method determines the valuation of the series by looking for a + nonzero coefficient. Hence if the series happens to be zero, then it + may run forever. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: s = 1/(1 - z) - 1/(1 - 2*z) + sage: s.valuation() + 1 + sage: t = z - z + sage: t.valuation() + +Infinity + """ + if series._constant is not None: + n = series._approximate_valuation + m = series._constant[1] + while n <= m: + if series[n] != 0: + series._approximate_valuation = n + return n + n += 1 + return infinity + + elif series._is_sparse: + print("Yahaan") + if series._constant is None: + n = series._approximate_valuation + cache = series._cache + while True: + if n in cache: + if cache[n]: + series._approximate_valuation = n + return n + n += 1 + else: + if series[n] != 0: + series._approximate_valuation = n + return n + n += 1 + + else: + if series._constant is None: + n = series._approximate_valuation + cache = series._cache + while True: + if n - series._offset < len(cache): + if cache[n - series._offset]: + series._approximate_valuation = n + return n + n += 1 + else: + if series[n] != 0: + series._approximate_valuation = n + return n + n += 1 + class LLS_coefficient_function(LLS_aux): """ @@ -442,7 +507,7 @@ def __init__(self, coefficient_function, is_sparse, approximate_valuation, const self._coefficient_function = coefficient_function super().__init__(is_sparse, approximate_valuation, constant) - def get_coefficient(self, n): + def get_coefficient(self, n, s=None): return self._coefficient_function(n) def iterate_coefficients(self): @@ -480,7 +545,7 @@ def __init__(self, left, right): super().__init__(left._is_sparse, a, c) - def get_coefficient(self, n): + def get_coefficient(self, n, s=None): """ Return the `n`-th coefficient of the series ``s``. @@ -542,7 +607,7 @@ def __init__(self, left, right): super().__init__(left._is_sparse, a, c) - def get_coefficient(self, n): + def get_coefficient(self, n, s=None): """ Return the `n`-th coefficient of the series ``s``. @@ -596,7 +661,7 @@ def __init__(self, left, right): super().__init__(left._is_sparse, a, c) - def get_coefficient(self, n): + def get_coefficient(self, n, s=None): """ Return the `n`-th coefficient of the series ``s``. @@ -646,7 +711,7 @@ def __init__(self, series, scalar): super().__init__(series._is_sparse, a, c) - def get_coefficient(self, n): + def get_coefficient(self, n, s=None): """ Return the `n`-th coefficient of the series ``s``. @@ -693,7 +758,7 @@ def __init__(self, series): super().__init__(series._is_sparse, a, c) - def get_coefficient(self, n): + def get_coefficient(self, n, s=None): """ Return the `n`-th coefficient of the series ``s``. @@ -731,16 +796,89 @@ def __init__(self, series): True """ self._series = series - self._v = series.valuation() + super().__init__(series._is_sparse, series._approximate_valuation, series._constant) + print("I came here") + self._approximate_valuation = -self.valuation(series) + print('approx val', self._approximate_valuation) + print("Even this happened.") + self._v = self.valuation(series) self._ainv = ~series[self._v] - self._zero = series.base_ring().zero() + print('yeh bhi ho gaya') + self._zero = ZZ.zero() if self._v is infinity: raise ZeroDivisionError('cannot invert zero') - - super().__init__(series._is_sparse, -v, series._constant) + + # self._v = series.valuation() + # self._ainv = ~series[self._v] + # self._zero = series.base_ring().zero() + + # if self._v is infinity: + # raise ZeroDivisionError('cannot invert zero') + # print("Came here") - def get_coefficient(self, s, n): + # def valuation(self, series): + + # """ + # Return the valuation of the series. + + # This method determines the valuation of the series by looking for a + # nonzero coefficient. Hence if the series happens to be zero, then it + # may run forever. + + # EXAMPLES:: + + # sage: L. = LazyLaurentSeriesRing(ZZ) + # sage: s = 1/(1 - z) - 1/(1 - 2*z) + # sage: s.valuation() + # 1 + # sage: t = z - z + # sage: t.valuation() + # +Infinity + # """ + # if series._constant is not None: + # n = series._approximate_valuation + # m = series._constant[1] + # while n <= m: + # if series[n] != 0: + # series._approximate_valuation = n + # return n + # n += 1 + # return infinity + + # elif series._is_sparse: + # if series._constant is None: + # n = series._approximate_valuation + # cache = series._cache + # while True: + # if n in cache: + # if cache[n]: + # series._approximate_valuation = n + # return n + # n += 1 + # else: + # if series[n] != 0: + # series._approximate_valuation = n + # return n + # n += 1 + + # else: + # if series._constant is None: + # n = series._approximate_valuation + # cache = series._cache + # while True: + # if n - series._offset < len(cache): + # if cache[n - series._offset]: + # series._approximate_valuation = n + # return n + # n += 1 + # else: + # if series[n] != 0: + # series._approximate_valuation = n + # return n + # n += 1 + + def get_coefficient(self, n, s): """ Return the `n`-th coefficient of the series ``s``. @@ -751,11 +889,29 @@ def get_coefficient(self, s, n): sage: f.coefficient(2) -1 """ - + print("This gets called first.") + # print("Came here as well") + # v = self.valuation() + # print("Valuation was found") + # self._approximate_valuation = -v + # ainv = ~self._series[v] + # zero = ZZ.zero() + # if v is infinity: + # raise ZeroDivisionError('cannot invert zero') + # if n == -v: + # return ainv + # c = zero + # for k in range(-v, n): + # c += s[k] * self._series[n + v - k] + # return -c * ainv v = self._v + print('v is', v) + print('n is', n) if n == -v: + print("It came here") return self._ainv - c = self._zero + c = ZZ.zero() + print('s is', s) for k in range(-v, n): c += s[k] * self._series[n + v - k] return -c * self._ainv From f92875567aeb2fa1367b6e06d1d7e2138e95ece9 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 25 Jun 2021 20:18:03 +0530 Subject: [PATCH 31/98] Corrected all the functions in the rings code. It is entirely based on the new design now. --- src/sage/rings/lazy_laurent_series_new.py | 4 +-- src/sage/rings/lazy_laurent_series_ring.py | 30 +++++++++++++--------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index b5cf1a777e1..cffbef9c830 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -391,7 +391,7 @@ def __init__(self, is_sparse, approximate_valuation, constant=None): self._offset = approximate_valuation self._iter = self.iterate_coefficients() - def __getitem__(self, n, s=None): + def __getitem__(self, n): if self._approximate_valuation is infinity: return ZZ.zero() elif n < self._approximate_valuation: @@ -403,7 +403,7 @@ def __getitem__(self, n, s=None): try: c = self._cache[n] except KeyError: - c = self.get_coefficient(n, s) + c = self.get_coefficient(n) self._cache[n] = c else: diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index f0db12e7ce7..e8028367e41 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -141,12 +141,15 @@ def gen(self, n=0): ... IndexError: there is only one generator """ + # Always a sparse implementation. if n != 0: raise IndexError("there is only one generator") - op = LazyLaurentSeriesOperator_gen(self) + op = lambda n: self.base_ring().one() if n == 1 else self.base_ring().zero() c = (self.base_ring().zero(), 2) - return self.element_class(self, coefficient=op, valuation=1, constant=c) + aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=1, constant=c) + + return self.element_class(self, aux) def ngens(self): """ @@ -194,7 +197,8 @@ def make_series_from(poly): op = LazyLaurentSeriesOperator_polynomial(self, poly) a = poly.valuation() c = (self.base_ring().zero(), poly.degree() + 1) - return self.element_class(self, coefficient=op, valuation=a, constant=c) + aux = LLS_coefficient_function(op, True, a, c) + return self.element_class(self, aux) return SetMorphism(Hom(S, self, Sets()), make_series_from) @@ -214,11 +218,13 @@ def _element_constructor_(self, x): """ R = self.base_ring() - op = LazyLaurentSeriesOperator_constant(self, R(x)) + # op = LazyLaurentSeriesOperator_constant(self, R(x)) + op = lambda n: R(x) if n == 0 else self.base_ring().zero() - return self.element_class(self, coefficient=op, valuation=0, constant=(R.zero(), 1)) + aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=0, constant=(R.zero(), 1)) + return self.element_class(self, aux) - def _an_element_(self): + def _an_element_(self, is_sparse=True): """ Return a Laurent series in this ring. @@ -231,14 +237,14 @@ def _an_element_(self): N = 10 e = self.base_ring().an_element() - - def r(s, i): - return self.base_ring().an_element() + + op = lambda i: self.base_ring().an_element() n = random.randint(-N,N) m = random.randint(0,N) - return self.element_class(self, coefficient=r, valuation=n, constant=(e,n+m)) + aux = LLS_coefficient_function(op, is_sparse, n, (e, n + m)) + return self.element_class(self, aux) def one(self): """ @@ -271,9 +277,9 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan INPUT: - - ``coefficient`` -- Python function that computes coefficients + - ``coefficient_function`` -- Python function that computes coefficients - - ``valuation`` -- integer; approximate valuation of the series + - ``approximate_valuation`` -- integer; approximate valuation of the series - ``constant`` -- either ``None`` or pair of an element of the base ring and an integer From 36230d96ebe8ab84eaa2536afcb30bb6891a8961 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 25 Jun 2021 20:50:41 +0530 Subject: [PATCH 32/98] Added functions for apply to coefficients and power of a series. --- src/sage/rings/lazy_laurent_series_new.py | 239 +++++++--------------- 1 file changed, 78 insertions(+), 161 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index cffbef9c830..15c0d0165fe 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -2,6 +2,7 @@ from sage.structure.element import ModuleElement from .integer_ring import ZZ from sage.structure.richcmp import op_EQ, op_NE +from sage.arith.power import generic_power class LLS(ModuleElement): @@ -36,8 +37,30 @@ def __invert__(self): P = self.parent() return P.element_class(P, LLS_inv(self._aux)) - def v(self): - return LLS_inv(self._aux).valuation(self._aux) + def apply_to_coefficients(self, newfunction): + P = self.parent() + return P.element_class(P, LLS_apply_coeff(self._aux, newfunction)) + + def __pow__(self, n): + """ + Return the `n`-th power of the series. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: (1 - z)^-1 + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: (1 - z)^0 + 1 + sage: (1 - z)^3 + 1 - 3*z + 3*z^2 - z^3 + sage: (1 - z)^-3 + 1 + 3*z + 6*z^2 + 10*z^3 + 15*z^4 + 21*z^5 + 28*z^6 + ... + """ + if n == 0: + return self.parent().one() + + return generic_power(self, n) def approximate_series(self, prec, name=None): """ @@ -172,64 +195,7 @@ def polynomial(self, degree=None, name=None): return R([self[i] for i in range(m)]) def valuation(self): - """ - Return the valuation of the series. - - This method determines the valuation of the series by looking for a - nonzero coefficient. Hence if the series happens to be zero, then it - may run forever. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: s = 1/(1 - z) - 1/(1 - 2*z) - sage: s.valuation() - 1 - sage: t = z - z - sage: t.valuation() - +Infinity - """ - if self._aux._constant is not None: - n = self._aux._approximate_valuation - m = self._aux._constant[1] - while n <= m: - if self[n] != 0: - self._aux._approximate_valuation = n - return n - n += 1 - return infinity - - elif self._aux._is_sparse: - if self._aux._constant is None: - n = self._aux._approximate_valuation - cache = self._aux._cache - while True: - if n in cache: - if cache[n]: - self._aux._approximate_valuation = n - return n - n += 1 - else: - if self[n] != 0: - self._aux._approximate_valuation = n - return n - n += 1 - - else: - if self._aux._constant is None: - n = self._aux._approximate_valuation - cache = self._aux._cache - while True: - if n - self._aux._offset < len(cache): - if cache[n - self._aux._offset]: - self._aux._approximate_valuation = n - return n - n += 1 - else: - if self[n] != 0: - self._aux._approximate_valuation = n - return n - n += 1 + return LLS_inv(self._aux).valuation(self._aux) def _repr_(self): """ @@ -419,7 +385,6 @@ def __getitem__(self, n): return c def valuation(self, series): - print("I am not even being called.") """ Return the valuation of the series. @@ -448,7 +413,6 @@ def valuation(self, series): return infinity elif series._is_sparse: - print("Yahaan") if series._constant is None: n = series._approximate_valuation cache = series._cache @@ -507,7 +471,7 @@ def __init__(self, coefficient_function, is_sparse, approximate_valuation, const self._coefficient_function = coefficient_function super().__init__(is_sparse, approximate_valuation, constant) - def get_coefficient(self, n, s=None): + def get_coefficient(self, n): return self._coefficient_function(n) def iterate_coefficients(self): @@ -545,7 +509,7 @@ def __init__(self, left, right): super().__init__(left._is_sparse, a, c) - def get_coefficient(self, n, s=None): + def get_coefficient(self, n): """ Return the `n`-th coefficient of the series ``s``. @@ -607,7 +571,7 @@ def __init__(self, left, right): super().__init__(left._is_sparse, a, c) - def get_coefficient(self, n, s=None): + def get_coefficient(self, n): """ Return the `n`-th coefficient of the series ``s``. @@ -661,7 +625,7 @@ def __init__(self, left, right): super().__init__(left._is_sparse, a, c) - def get_coefficient(self, n, s=None): + def get_coefficient(self, n): """ Return the `n`-th coefficient of the series ``s``. @@ -711,7 +675,7 @@ def __init__(self, series, scalar): super().__init__(series._is_sparse, a, c) - def get_coefficient(self, n, s=None): + def get_coefficient(self, n): """ Return the `n`-th coefficient of the series ``s``. @@ -758,7 +722,7 @@ def __init__(self, series): super().__init__(series._is_sparse, a, c) - def get_coefficient(self, n, s=None): + def get_coefficient(self, n): """ Return the `n`-th coefficient of the series ``s``. @@ -797,88 +761,15 @@ def __init__(self, series): """ self._series = series super().__init__(series._is_sparse, series._approximate_valuation, series._constant) - print("I came here") self._approximate_valuation = -self.valuation(series) - print('approx val', self._approximate_valuation) - print("Even this happened.") self._v = self.valuation(series) self._ainv = ~series[self._v] - print('yeh bhi ho gaya') self._zero = ZZ.zero() if self._v is infinity: raise ZeroDivisionError('cannot invert zero') - # self._v = series.valuation() - # self._ainv = ~series[self._v] - # self._zero = series.base_ring().zero() - - # if self._v is infinity: - # raise ZeroDivisionError('cannot invert zero') - # print("Came here") - - # def valuation(self, series): - - # """ - # Return the valuation of the series. - - # This method determines the valuation of the series by looking for a - # nonzero coefficient. Hence if the series happens to be zero, then it - # may run forever. - - # EXAMPLES:: - - # sage: L. = LazyLaurentSeriesRing(ZZ) - # sage: s = 1/(1 - z) - 1/(1 - 2*z) - # sage: s.valuation() - # 1 - # sage: t = z - z - # sage: t.valuation() - # +Infinity - # """ - # if series._constant is not None: - # n = series._approximate_valuation - # m = series._constant[1] - # while n <= m: - # if series[n] != 0: - # series._approximate_valuation = n - # return n - # n += 1 - # return infinity - - # elif series._is_sparse: - # if series._constant is None: - # n = series._approximate_valuation - # cache = series._cache - # while True: - # if n in cache: - # if cache[n]: - # series._approximate_valuation = n - # return n - # n += 1 - # else: - # if series[n] != 0: - # series._approximate_valuation = n - # return n - # n += 1 - - # else: - # if series._constant is None: - # n = series._approximate_valuation - # cache = series._cache - # while True: - # if n - series._offset < len(cache): - # if cache[n - series._offset]: - # series._approximate_valuation = n - # return n - # n += 1 - # else: - # if series[n] != 0: - # series._approximate_valuation = n - # return n - # n += 1 - - def get_coefficient(self, n, s): + def get_coefficient(self, n): """ Return the `n`-th coefficient of the series ``s``. @@ -889,26 +780,8 @@ def get_coefficient(self, n, s): sage: f.coefficient(2) -1 """ - print("This gets called first.") - # print("Came here as well") - # v = self.valuation() - # print("Valuation was found") - # self._approximate_valuation = -v - # ainv = ~self._series[v] - # zero = ZZ.zero() - # if v is infinity: - # raise ZeroDivisionError('cannot invert zero') - # if n == -v: - # return ainv - # c = zero - # for k in range(-v, n): - # c += s[k] * self._series[n + v - k] - # return -c * ainv v = self._v - print('v is', v) - print('n is', n) if n == -v: - print("It came here") return self._ainv c = ZZ.zero() print('s is', s) @@ -916,7 +789,7 @@ def get_coefficient(self, n, s): c += s[k] * self._series[n + v - k] return -c * self._ainv - def iterate_coefficients(self, s): + def iterate_coefficients(self): n = self._offset while True: v = self._v @@ -928,4 +801,48 @@ def iterate_coefficients(self, s): for k in range(-v, n): c += s[k] * self._series[n + v - k] yield -c * self._ainv + n += 1 + + +class LLS_apply_coeff(LLS_aux): + """ + Return the series with ``function`` applied to each coefficient of this series. + + INPUT: + + - ``function`` -- Python function + + Python function ``function`` returns a new coefficient for input coefficient. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: s = z/(1 - 2*z) + sage: t = s.apply_to_coefficients(lambda c: c + 1) + sage: s + z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... + sage: t + 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... + """ + def __init__(self, series, function): + self._series = series + self._function = function + a = series._approximate_valuation + + if series._constant: + c = (function(series._constant[0]), series._constant[1]) + else: + c = None + + super().__init__(series._is_sparse, a, c) + + def get_coefficient(self, n): + c = self._function(self._series[n]) + return c + + def iterate_coefficients(self): + n = self._offset + while True: + c = self._function(self._series[n]) + yield c n += 1 \ No newline at end of file From 300432d738e08a15870726178669eba5d3b0094d Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 25 Jun 2021 23:04:57 +0530 Subject: [PATCH 33/98] Added functions for truncate and change base ring. --- src/sage/rings/lazy_laurent_series_new.py | 59 ++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 15c0d0165fe..a72e2cb903f 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -40,6 +40,15 @@ def __invert__(self): def apply_to_coefficients(self, newfunction): P = self.parent() return P.element_class(P, LLS_apply_coeff(self._aux, newfunction)) + + def change_ring(self, ring): + from .lazy_laurent_series_ring import LazyLaurentSeriesRing + Q = LazyLaurentSeriesRing(ring, names=self.parent().variable_name()) + return Q.element_class(Q, LLS_coefficient_function(self._aux._coefficient_function, self._aux._is_sparse, self._aux._approximate_valuation, self._aux._constant)) + + def truncate(self, d): + P = self.parent() + return P.element_class(P, LLS_trunc(self._aux, d)) def __pow__(self, n): """ @@ -845,4 +854,52 @@ def iterate_coefficients(self): while True: c = self._function(self._series[n]) yield c - n += 1 \ No newline at end of file + n += 1 + + +class LLS_trunc(LLS_aux): + """ + Return this series with its terms of degree >= ``d`` truncated. + + INPUT: + + - ``d`` -- integer + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: alpha = 1/(1-z) + sage: alpha + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: beta = alpha.truncate(5) + sage: beta + 1 + z + z^2 + z^3 + z^4 + sage: alpha - beta + z^5 + z^6 + z^7 + z^8 + z^9 + z^10 + z^11 + ... + """ + def __init__(self, series, d): + self._series = series + self._d = d + self._zero = ZZ.zero() + a = series._approximate_valuation + c = (ZZ.zero(), d) + super().__init__(series._is_sparse, a, c) + + def get_coefficient(self, n): + if n <= self._d: + c = self._series[n] + return c + else: + c = self._zero + return c + + def iterate_coefficients(self): + n = self._offset + while True: + c = self._function(self._series[n]) + if n <= self._d: + c = self._series[n] + else: + c = self._zero + yield c + n += 1 From 533d59384c45587754d551d695f70021d18cbd29 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sun, 27 Jun 2021 19:23:33 +0530 Subject: [PATCH 34/98] Added functions for division and inversion. --- src/sage/rings/lazy_laurent_series_new.py | 73 +++++++++++++++++++++-- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index a72e2cb903f..3c22c41adf0 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -20,6 +20,10 @@ def _mul_(self, other): def _add_(self, other): P = self.parent() return P.element_class(P, LLS_add(self._aux, other._aux)) + + def _div_(self, other): + P = self.parent() + return P.element_class(P, LLS_div(self._aux, other._aux)) def _rmul_(self, scalar): P = self.parent() @@ -777,7 +781,7 @@ def __init__(self, series): if self._v is infinity: raise ZeroDivisionError('cannot invert zero') - + def get_coefficient(self, n): """ Return the `n`-th coefficient of the series ``s``. @@ -793,9 +797,8 @@ def get_coefficient(self, n): if n == -v: return self._ainv c = ZZ.zero() - print('s is', s) for k in range(-v, n): - c += s[k] * self._series[n + v - k] + c += self[k] * self._series[n + v - k] return -c * self._ainv def iterate_coefficients(self): @@ -808,7 +811,7 @@ def iterate_coefficients(self): continue c = self._zero for k in range(-v, n): - c += s[k] * self._series[n + v - k] + c += self[k] * self._series[n + v - k] yield -c * self._ainv n += 1 @@ -903,3 +906,65 @@ def iterate_coefficients(self): c = self._zero yield c n += 1 + + +class LLS_div(LLS_aux): + """ + Return ``self`` divided by ``other``. + + INPUT: + + - ``other`` -- nonzero series + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: z/(1 - z) + z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... + """ + def __init__(self, left, right): + + self._left = left + self._right = right + + # if right.is_zero(): + # raise ZeroDivisionError("division by zero series") + + # if left.is_zero(): + # return ZZ.zero() + + super().__init__(left._is_sparse, left._approximate_valuation, None) + + self._approximate_valuation = left.valuation(left) - right.valuation(right) + + lv = left.valuation(left) + rv = right.valuation(right) + self._lv = lv + self._rv = rv + self._ainv = ~right[rv] + + def get_coefficient(self, n): + lv = self._lv + rv = self._rv + + if n == lv - rv: + return self._left[lv]/self._right[rv] + c = self._left[n + rv] + for k in range(lv - rv, n): + c -= self._left[k] * self._right[n + rv - k] + return c * self._ainv + + def iterate_coefficients(self): + n = self._offset + lv = self._lv + rv = self._rv + while True: + if n == lv - rv: + yield self._left[lv]/self._right[rv] + n += 1 + continue + c = self._left[n + rv] + for k in range(lv - rv, n): + c -= self._left[k] * self._right[n + rv - k] + yield c * self._ainv + n += 1 \ No newline at end of file From 05561a403a886c6636506212a549dbb8f350e449 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Tue, 29 Jun 2021 20:02:41 +0530 Subject: [PATCH 35/98] Comments for the inversion and division code. --- src/sage/rings/lazy_laurent_series_new.py | 41 ++++++++--------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 3c22c41adf0..d99d0f985c2 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -384,7 +384,6 @@ def __getitem__(self, n): except KeyError: c = self.get_coefficient(n) self._cache[n] = c - else: i = n - self._offset if i >= len(self._cache): @@ -762,41 +761,24 @@ class LLS_inv(LLS_aux): Operator for multiplicative inverse of the series. """ def __init__(self, series): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) * 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ self._series = series super().__init__(series._is_sparse, series._approximate_valuation, series._constant) self._approximate_valuation = -self.valuation(series) - self._v = self.valuation(series) - self._ainv = ~series[self._v] - self._zero = ZZ.zero() + v = self.valuation(series) - if self._v is infinity: + if v is infinity: raise ZeroDivisionError('cannot invert zero') + + # Trying with series now + self._v = v + self._ainv = ~series[self._v] + self._zero = ZZ.zero() def get_coefficient(self, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = (1 + z)*(1 - z) - sage: f.coefficient(2) - -1 - """ v = self._v if n == -v: return self._ainv - c = ZZ.zero() + c = self._zero for k in range(-v, n): c += self[k] * self._series[n + v - k] return -c * self._ainv @@ -946,12 +928,17 @@ def __init__(self, left, right): def get_coefficient(self, n): lv = self._lv rv = self._rv - if n == lv - rv: return self._left[lv]/self._right[rv] c = self._left[n + rv] + print('n is', n) for k in range(lv - rv, n): + print('k is', k) + print('LS is', self._left[k]) + print('RS is', self._right[n + rv - k]) c -= self._left[k] * self._right[n + rv - k] + print('c is', c) + print('ainv is', self._ainv) return c * self._ainv def iterate_coefficients(self): From 8aeb3fec004ae5046fa6d98f7e1222764385b75d Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 30 Jun 2021 02:39:27 +0530 Subject: [PATCH 36/98] Fixed division code except for dividing by zero. --- src/sage/rings/lazy_laurent_series_new.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index d99d0f985c2..63cc78a3b8e 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -931,14 +931,8 @@ def get_coefficient(self, n): if n == lv - rv: return self._left[lv]/self._right[rv] c = self._left[n + rv] - print('n is', n) for k in range(lv - rv, n): - print('k is', k) - print('LS is', self._left[k]) - print('RS is', self._right[n + rv - k]) - c -= self._left[k] * self._right[n + rv - k] - print('c is', c) - print('ainv is', self._ainv) + c -= self[k] * self._right[n + rv - k] return c * self._ainv def iterate_coefficients(self): @@ -952,6 +946,6 @@ def iterate_coefficients(self): continue c = self._left[n + rv] for k in range(lv - rv, n): - c -= self._left[k] * self._right[n + rv - k] + c -= self[k] * self._right[n + rv - k] yield c * self._ainv n += 1 \ No newline at end of file From 7e9a059ef91b185ad01c0278804ed63cf2e1f3fc Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 7 Jul 2021 00:46:26 +0530 Subject: [PATCH 37/98] Added tests. --- src/sage/rings/lazy_laurent_series_new.py | 842 +++++++++++++++------ src/sage/rings/lazy_laurent_series_ring.py | 32 +- 2 files changed, 612 insertions(+), 262 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 63cc78a3b8e..f126137ae35 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -1,3 +1,84 @@ +r""" +Lazy Laurent Series + +A lazy Laurent series is a Laurent series whose coefficients are computed as +demanded or needed. Unlike the usual Laurent series in Sage, lazy Laurent +series do not have precisions because a lazy Laurent series knows (can be +computed, lazily) all its coefficients. + +EXAMPLES: + +Generating functions are Laurent series over the integer ring:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + +This defines the generating function of Fibonacci sequence:: + + sage: def coeff(s, i): + ....: if i in [0, 1]: + ....: return 1 + ....: else: + ....: return s.coefficient(i - 1) + s.coefficient(i - 2) + sage: f = L.series(coeff, True, valuation=0); f + 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... + sage: f = L.series(coeff, False, valuation=0); f + 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... + +The 100th element of Fibonacci sequence can be obtained from the generating +function:: + + sage: f.coefficient(100) + 573147844013817084101 + +Coefficients are computed and cached only when necessary:: + + sage: f._aux._cache[100] + 573147844013817084101 + sage: f._aux._cache[101] + Traceback (most recent call last): + ... + KeyError: 101 + +You can do arithmetic with lazy power series:: + + sage: f + 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... + sage: f^-1 + 1 - z - z^2 + ... + sage: f + f^-1 + 2 + z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... + sage: g = (f + f^-1)*(f - f^-1); g + 4*z + 6*z^2 + 8*z^3 + 19*z^4 + 38*z^5 + 71*z^6 + ... + +You may need to change the base ring:: + + sage: h = g.change_ring(QQ) + sage: h.parent() + Lazy Laurent Series Ring in z over Rational Field + sage: h + 4*z + 6*z^2 + 8*z^3 + 19*z^4 + 38*z^5 + 71*z^6 + ... + sage: h^-1 + 1/4*z^-1 - 3/8 + 1/16*z - 17/32*z^2 + 5/64*z^3 - 29/128*z^4 + 165/256*z^5 + ... + sage: _.valuation() + -1 + +AUTHORS: + +- Kwankyu Lee (2019-02-24): initial version + +""" + +# **************************************************************************** +# Copyright (C) 2019 Kwankyu Lee +# +# 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 +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + + from .infinity import infinity from sage.structure.element import ModuleElement from .integer_ring import ZZ @@ -6,51 +87,455 @@ class LLS(ModuleElement): + r""" + Return a lazy Laurent series. + + INPUT: + + - ``coefficient_function`` -- Python function that computes coefficients + + - ``issparse`` -- Boolean that determines whether the implementation is sparse or dense + + - ``approximate_valuation`` -- integer; approximate valuation of the series + + - ``constant`` -- either ``None`` or pair of an element of the base ring and an integer + + Let the coefficient of index `i` mean the coefficient of the term of the + series with exponent `i`. + + Python function ``coefficient`` returns the value of the coefficient of + index `i` from input. + + Let ``approximate_valuation`` be `n`. All coefficients of index below `n` are zero. If + ``constant`` is ``None``, then the ``coefficient`` function is responsible + to compute the values of all coefficients of index `\ge n`. If ``constant`` + is a pair `(c,m)`, then the ``coefficient`` function is responsible to + compute the values of all coefficients of index `\ge n` and `< m` and all + the coefficients of index `\ge m` is the constant `c`. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L.series(lambda i: i, is_sparse=True, approximate_valuation=-3, constant=(-1,3)) + -3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ... + sage: L.series(lambda i: i, is_sparse=False, approximate_valuation=-3, constant=(-1,3)) + -3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ... + + :: + + sage: def coeff(s, i): + ....: if i in [0, 1]: + ....: return 1 + ....: else: + ....: return s.coefficient(i - 1) + s.coefficient(i - 2) + sage: f = L.series(coeff, True, valuation=0); f + 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... + sage: f.coefficient(100) + 573147844013817084101 + + Lazy Laurent series is picklable:: + + sage: z = L.gen() + sage: f = 1/(1 - z - z^2) + sage: f + 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... + sage: g = loads(dumps(f)) + sage: g + 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... + sage: g == f + True + """ def __init__(self, parent, aux): + """ + Initialize the series. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(GF(2), 'z') + sage: z = L.gen() + sage: TestSuite(z).run() + """ ModuleElement.__init__(self, parent) self._aux = aux def __getitem__(self, n): + """ + Return the coefficient of the term with exponent ``n`` of the series. + + INPUT: + + - ``n`` -- integer + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = z/(1 - 2*z^3) + sage: [f[n] for n in range(20)] + [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] + sage: M = L.series(lambda n: n, True, 0) + sage: [M[n] for n in range(20)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + sage: M = L.series(lambda n: n, False, 0) + sage: [M[n] for n in range(20)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + + """ return self.base_ring()(self._aux[n]) def _mul_(self, other): + """ + Return the product of this series with ``other``. + + INPUT: + + - ``other`` -- other series + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: (1 - z)*(1 - z) + 1 - 2*z + z^2 + sage: (1 - z)*(1 - z)*(1 - z) + 1 - 3*z + 3*z^2 - z^3 + sage: M = L.series(lambda n: n, True, 0) + sage: M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M * (1 - M) + sage: N + z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 + ... + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L.series(lambda n: 1, False, 0); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M * N; P + z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + ... + """ P = self.parent() return P.element_class(P, LLS_mul(self._aux, other._aux)) def _add_(self, other): + """ + Return the sum of this series with ``other``. + + INPUT: + + - ``other`` -- other series + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: (1 - z)*(1 - z) + 1 - 2*z + z^2 + sage: (1 - z)*(1 - z)*(1 - z) + 1 - 3*z + 3*z^2 - z^3 + sage: z + z + 2*z + sage: z^2 + 3*z^2 + 4*z^2 + sage: M = L.series(lambda n: n, True, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L.series(lambda n: 1, True, 0); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M + N; P + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L.series(lambda n: 1, False, 0); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M + N; P + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + """ P = self.parent() return P.element_class(P, LLS_add(self._aux, other._aux)) def _div_(self, other): + """ + Return ``self`` divided by ``other``. + + INPUT: + + - ``other`` -- nonzero series + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: z/(1 - z) + z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L.series(lambda n: 1, False, 0); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M / N; P + z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... + sage: M = L.series(lambda n: n, True, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L.series(lambda n: 1, True, 0); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M / N; P + z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... + """ P = self.parent() return P.element_class(P, LLS_div(self._aux, other._aux)) def _rmul_(self, scalar): + """ + Return the scalar multiplication of this series by ``scalar``. + + INPUT: + + - ``scalar`` -- an element of the base ring + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: 2*z + 2*z + sage: -1*z + -z + sage: 0*z + 0 + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M._rmul_(3) + 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... + sage: N = L.series(lambda n: 1, False, 0); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: N._rmul_(4) + 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... + """ P = self.parent() return P.element_class(P, LLS_rmul(self._aux, scalar)) def _sub_(self, other): + """ + Return the series of this series minus ``other`` series. + + INPUT: + + - ``other`` -- other series + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: z - z + 0 + sage: 3*z - 2*z + z + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L.series(lambda n: 1, False, 0); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M - N; P + -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... + sage: M = L.series(lambda n: n, True, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L.series(lambda n: 1, True, 0); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M - N; P + -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... + """ P = self.parent() return P.element_class(P, LLS_sub(self._aux, other._aux)) def _neg_(self): + """ + Return the negative of this series. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: z = L.gen() + sage: -(1 - z) + -1 + z + sage: M = L.series(lambda n: n, True, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: P = -M; P + -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: P = -M; P + -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... + sage: -(z^2 + 3*z - 4*z^3) + -3*z - z^2 + 4*z^3 + """ P = self.parent() return P.element_class(P, LLS_neg(self._aux)) def __invert__(self): + """ + Return the multiplicative inverse of the element. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: ~(1 - z) + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: P = ~M; P + z^-1 - 2 + z + ... + sage: M = L.series(lambda n: n, True, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: P = ~M; P + z^-1 - 2 + z + ... + """ P = self.parent() return P.element_class(P, LLS_inv(self._aux)) + def coefficient(self, n): + """ + Return the coefficient of the term with exponent ``n`` of the series. + + INPUT: + + - ``n`` -- integer + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: def g(s, i): + ....: if i == 0: + ....: return 1 + ....: else: + ....: return sum(s.coefficient(j)*s.coefficient(i - 1 -j) for j in [0..i-1]) + sage: e = L.series(g, True, valuation=0) + sage: e.coefficient(10) + 16796 + sage: e + 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + + TESTS:: + + sage: def g(s, i): + ....: if i == 0: + ....: return 1 + ....: else: + ....: return sum(s.coefficient(j)*s.coefficient(i - 1 - j) for j in [0..i-1]) + ....: + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: e = L.series(g, False, valuation = 0) + sage: e + 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + sage: e._aux._cache + [1, 1, 2, 5, 14, 42, 132] + sage: e.coefficient(10) + 16796 + sage: e._aux._cache + [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796] + sage: M = L.series(lambda n: n^2, False, 0); M + z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... + sage: M._aux._cache + [0, 1, 4, 9, 16, 25, 36] + sage: M.coefficient(9) + 81 + sage: M._aux._cache + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + sage: M = L.series(lambda n: n^2, True, 0); M + z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... + sage: M._aux._cache + {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36} + sage: M.coefficient(10) + 100 + sage: M._aux._cache + {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 10: 100} + """ + return self.__getitem__(n) + def apply_to_coefficients(self, newfunction): + """ + Return the series with ``newfunction`` applied to each coefficient of this series. + + INPUT: + + - ``newfunction`` -- Python function + + Python function ``newfunction`` returns a new coefficient for input coefficient. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: s = z/(1 - 2*z) + sage: t = s.apply_to_coefficients(lambda c: c + 1) + sage: s + z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... + sage: t + 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... + sage: M = L.series(lambda n: n, True, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M.apply_to_coefficients(lambda c: c + 1); N + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M.apply_to_coefficients(lambda c: c + 1); N + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + """ P = self.parent() return P.element_class(P, LLS_apply_coeff(self._aux, newfunction)) def change_ring(self, ring): + """ + Return this series with coefficients converted to elements of ``ring``. + + INPUT: + + - ``ring`` -- a ring + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: s = 2 + z + sage: t = s.change_ring(QQ) + sage: t^-1 + 1/2 - 1/4*z + 1/8*z^2 - 1/16*z^3 + 1/32*z^4 - 1/64*z^5 + 1/128*z^6 + ... + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M.change_ring(QQ) + sage: N.parent() + Lazy Laurent Series Ring in z over Rational Field + sage: M.parent() + Lazy Laurent Series Ring in z over Integer Ring + sage: M = L.series(lambda n: n, True, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M.parent() + Lazy Laurent Series Ring in z over Integer Ring + sage: N = M.change_ring(QQ) + sage: N.parent() + Lazy Laurent Series Ring in z over Rational Field + sage: M ^-1 + z^-1 - 2 + z + ... + """ from .lazy_laurent_series_ring import LazyLaurentSeriesRing Q = LazyLaurentSeriesRing(ring, names=self.parent().variable_name()) return Q.element_class(Q, LLS_coefficient_function(self._aux._coefficient_function, self._aux._is_sparse, self._aux._approximate_valuation, self._aux._constant)) def truncate(self, d): + """ + Return this series with its terms of degree >= ``d`` truncated. + + INPUT: + + - ``d`` -- integer + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: alpha = 1/(1-z) + sage: alpha + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: beta = alpha.truncate(5) + sage: beta + 1 + z + z^2 + z^3 + z^4 + sage: alpha - beta + z^5 + z^6 + ... + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M.truncate(4) + z + 2*z^2 + 3*z^3 + sage: M = L.series(lambda n: n, True, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M.truncate(4) + z + 2*z^2 + 3*z^3 + """ P = self.parent() return P.element_class(P, LLS_trunc(self._aux, d)) @@ -58,7 +543,7 @@ def __pow__(self, n): """ Return the `n`-th power of the series. - EXAMPLES:: + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: (1 - z)^-1 @@ -69,6 +554,14 @@ def __pow__(self, n): 1 - 3*z + 3*z^2 - z^3 sage: (1 - z)^-3 1 + 3*z + 6*z^2 + 10*z^3 + 15*z^4 + 21*z^5 + 28*z^6 + ... + sage: M = L.series(lambda n: n, True, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M ^ 2 + z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... + sage: M = L.series(lambda n: n, False, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M ^ 2 + z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... """ if n == 0: return self.parent().one() @@ -89,7 +582,7 @@ def approximate_series(self, prec, name=None): OUTPUT: a Laurent series with absolute precision ``prec`` - EXAMPLES:: + TESTS:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') sage: z = L.gen() @@ -101,9 +594,6 @@ def approximate_series(self, prec, name=None): z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + O(z^10) sage: g.parent() Power Series Ring in z over Integer Ring - - :: - sage: h = (f^-1).approximate_series(3) sage: h z^-5 - 2*z^-4 + 10*z^-3 - 20*z^-2 + 60*z^-1 - 120 + 280*z - 560*z^2 + O(z^3) @@ -129,7 +619,7 @@ def prec(self): """ Return the precision of the series, which is infinity. - EXAMPLES:: + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: f = 1/(1 - z) @@ -160,20 +650,17 @@ def polynomial(self, degree=None, name=None): EXAMPLES:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,0,0,2,0,0,0,3], 5); f + sage: f = L.series([1,0,0,2,0,0,0,3], True, 5); f z^5 + 2*z^8 + 3*z^12 sage: f.polynomial() 3*z^12 + 2*z^8 + z^5 - :: + TESTS:: sage: g = L.series([1,0,0,2,0,0,0,3], -5); g z^-5 + 2*z^-2 + 3*z^2 sage: g.polynomial() z^-5 + 2*z^-2 + 3*z^2 - - :: - sage: z = L.gen() sage: f = (1 + z)/(z^3 - z^5) sage: f @@ -184,6 +671,12 @@ def polynomial(self, degree=None, name=None): z^-3 + z^-2 + z^-1 + 1 sage: f.polynomial(-5) 0 + sage: M = L.series(lambda n: n^2, False, 0) + sage: M.polynomial(3) + 9*z^3 + 4*z^2 + z + sage: M = L.series(lambda n: n^2, True, 0) + sage: M.polynomial(5) + 25*z^5 + 16*z^4 + 9*z^3 + 4*z^2 + z """ if degree is None: if self._aux._constant is None or not self._aux._constant[0].is_zero(): @@ -208,13 +701,36 @@ def polynomial(self, degree=None, name=None): return R([self[i] for i in range(m)]) def valuation(self): - return LLS_inv(self._aux).valuation(self._aux) - + """ + Return the valuation of the series. + + This method determines the valuation of the series by looking for a + nonzero coefficient. Hence if the series happens to be zero, then it + may run forever. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: s = 1/(1 - z) - 1/(1 - 2*z) + sage: s.valuation() + 1 + sage: t = z - z + sage: t.valuation() + +Infinity + sage: M = L.series(lambda n: n^2, True, 0) + sage: M.valuation() + 1 + sage: M = L.series(lambda n: n^2, False, 0) + sage: M.valuation() + 1 + """ + return self._aux.valuation() + def _repr_(self): """ Return the string representation of this Laurent series. - EXAMPLES:: + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: -1/(1 + 2*z) @@ -321,6 +837,17 @@ def _richcmp_(self, other, op): return False def __hash__(self): + """ + Return the hash of ``self`` + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f = L.series([1,2,3,4], True, -5) + sage: g = (1 + f)/(1 - f)^2 + sage: {g: 1} + {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} + """ return hash((type(self), self._aux._coefficient_function, self._aux._approximate_valuation, self._aux._constant)) @@ -328,7 +855,7 @@ def __bool__(self): """ Test whether ``self`` is not zero. - EXAMPLES:: + TESTS:: sage: L. = LazyLaurentSeriesRing(GF(2)) sage: (z-z).is_zero() @@ -336,6 +863,10 @@ def __bool__(self): sage: f = 1/(1 - z) sage: f.is_zero() False + sage: M = L.series(lambda n: n, True, 0); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M.is_zero() + False """ if self._aux._constant is None: for a in self._aux._cache: @@ -396,89 +927,48 @@ def __getitem__(self, n): return c - def valuation(self, series): - """ - Return the valuation of the series. - - This method determines the valuation of the series by looking for a - nonzero coefficient. Hence if the series happens to be zero, then it - may run forever. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: s = 1/(1 - z) - 1/(1 - 2*z) - sage: s.valuation() - 1 - sage: t = z - z - sage: t.valuation() - +Infinity - """ - if series._constant is not None: - n = series._approximate_valuation - m = series._constant[1] + def valuation(self): + if self._constant is not None: + n = self._approximate_valuation + m = self._constant[1] while n <= m: - if series[n] != 0: - series._approximate_valuation = n + if self[n] != 0: + self._approximate_valuation = n return n n += 1 return infinity - elif series._is_sparse: - if series._constant is None: - n = series._approximate_valuation - cache = series._cache - while True: - if n in cache: - if cache[n]: - series._approximate_valuation = n - return n - n += 1 - else: - if series[n] != 0: - series._approximate_valuation = n - return n - n += 1 - + if self._is_sparse: + n = self._approximate_valuation + cache = self._cache + while True: + if n in cache: + if cache[n]: + self._approximate_valuation = n + return n + n += 1 + else: + if self[n] != 0: + self._approximate_valuation = n + return n + n += 1 else: - if series._constant is None: - n = series._approximate_valuation - cache = series._cache - while True: - if n - series._offset < len(cache): - if cache[n - series._offset]: - series._approximate_valuation = n - return n - n += 1 - else: - if series[n] != 0: - series._approximate_valuation = n - return n - n += 1 + n = self._approximate_valuation + cache = self._cache + while True: + if n - self._offset < len(cache): + if cache[n - self._offset]: + self._approximate_valuation = n + return n + n += 1 + else: + if self[n] != 0: + self._approximate_valuation = n + return n + n += 1 class LLS_coefficient_function(LLS_aux): - """ - EXAMPLES:: - - sage: s = LLS_coefficient_function(lambda n: 1, True, 0) - sage: [s[i] for i in range(-5, 5)] - [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] - sage: t = LLS_mul(s, s) - sage: [t[i] for i in range(-5, 5)] - [0, 0, 0, 0, 0, 1, 2, 3, 4, 5] - - TESTS:: - - sage: s = LLS_coefficient_function(lambda n: 1, False, 0) - sage: t = LLS_mul(s, s) - sage: [t[i] for i in range(-5, 5)] - [0, 0, 0, 0, 0, 1, 2, 3, 4, 5] - - sage: s._cache - [1, 1, 1, 1, 1] - - """ def __init__(self, coefficient_function, is_sparse, approximate_valuation, constant=None): self._coefficient_function = coefficient_function super().__init__(is_sparse, approximate_valuation, constant) @@ -498,16 +988,6 @@ class LLS_mul(LLS_aux): Operator for multiplication. """ def __init__(self, left, right): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) * 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ self._left = left self._right = right a = left._approximate_valuation + right._approximate_valuation @@ -522,16 +1002,6 @@ def __init__(self, left, right): def get_coefficient(self, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = (1 + z)*(1 - z) - sage: f.coefficient(2) - -1 - """ c = ZZ.zero() for k in range(self._left._approximate_valuation, n - self._right._approximate_valuation + 1): @@ -558,16 +1028,6 @@ class LLS_add(LLS_aux): Operator for addition. """ def __init__(self, left, right): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) * 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ self._left = left self._right = right a = min(left._approximate_valuation, right._approximate_valuation) @@ -584,16 +1044,6 @@ def __init__(self, left, right): super().__init__(left._is_sparse, a, c) def get_coefficient(self, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = (1 + z)*(1 - z) - sage: f.coefficient(2) - -1 - """ c = ZZ.zero() c = self._left[n] + self._right[n] return c @@ -612,16 +1062,6 @@ class LLS_sub(LLS_aux): Operator for subtraction. """ def __init__(self, left, right): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) * 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ self._left = left self._right = right a = min(left._approximate_valuation, right._approximate_valuation) @@ -666,16 +1106,6 @@ class LLS_rmul(LLS_aux): Operator for multiplying with a scalar. """ def __init__(self, series, scalar): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) * 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ self._series = series self._scalar = scalar @@ -688,16 +1118,6 @@ def __init__(self, series, scalar): super().__init__(series._is_sparse, a, c) def get_coefficient(self, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = (1 + z)*(1 - z) - sage: f.coefficient(2) - -1 - """ c = self._series[n] * self._scalar return c @@ -714,16 +1134,6 @@ class LLS_neg(LLS_aux): Operator for negative of the series. """ def __init__(self, series): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) * 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ self._series = series a = series._approximate_valuation @@ -735,16 +1145,6 @@ def __init__(self, series): super().__init__(series._is_sparse, a, c) def get_coefficient(self, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = (1 + z)*(1 - z) - sage: f.coefficient(2) - -1 - """ c = -1 * self._series[n] return c @@ -762,38 +1162,36 @@ class LLS_inv(LLS_aux): """ def __init__(self, series): self._series = series - super().__init__(series._is_sparse, series._approximate_valuation, series._constant) - self._approximate_valuation = -self.valuation(series) - v = self.valuation(series) + v = series.valuation() + # self._constant can be refined + super().__init__(series._is_sparse, -v, None) if v is infinity: raise ZeroDivisionError('cannot invert zero') - # Trying with series now - self._v = v - self._ainv = ~series[self._v] + self._ainv = series[v].inverse_of_unit() self._zero = ZZ.zero() def get_coefficient(self, n): - v = self._v - if n == -v: + v = self._approximate_valuation + if n == v: return self._ainv c = self._zero - for k in range(-v, n): - c += self[k] * self._series[n + v - k] + for k in range(v, n): + c += self[k] * self._series[n - v - k] return -c * self._ainv def iterate_coefficients(self): n = self._offset while True: - v = self._v - if n == -v: + v = self._approximate_valuation + if n == v: yield self._ainv n += 1 continue c = self._zero - for k in range(-v, n): - c += self[k] * self._series[n + v - k] + for k in range(v, n): + c += self[k] * self._series[n - v - k] yield -c * self._ainv n += 1 @@ -801,22 +1199,6 @@ def iterate_coefficients(self): class LLS_apply_coeff(LLS_aux): """ Return the series with ``function`` applied to each coefficient of this series. - - INPUT: - - - ``function`` -- Python function - - Python function ``function`` returns a new coefficient for input coefficient. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: s = z/(1 - 2*z) - sage: t = s.apply_to_coefficients(lambda c: c + 1) - sage: s - z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... - sage: t - 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... """ def __init__(self, series, function): self._series = series @@ -845,22 +1227,6 @@ def iterate_coefficients(self): class LLS_trunc(LLS_aux): """ Return this series with its terms of degree >= ``d`` truncated. - - INPUT: - - - ``d`` -- integer - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: alpha = 1/(1-z) - sage: alpha - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: beta = alpha.truncate(5) - sage: beta - 1 + z + z^2 + z^3 + z^4 - sage: alpha - beta - z^5 + z^6 + z^7 + z^8 + z^9 + z^10 + z^11 + ... """ def __init__(self, series, d): self._series = series @@ -881,7 +1247,6 @@ def get_coefficient(self, n): def iterate_coefficients(self): n = self._offset while True: - c = self._function(self._series[n]) if n <= self._d: c = self._series[n] else: @@ -893,37 +1258,20 @@ def iterate_coefficients(self): class LLS_div(LLS_aux): """ Return ``self`` divided by ``other``. - - INPUT: - - - ``other`` -- nonzero series - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: z/(1 - z) - z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... """ def __init__(self, left, right): self._left = left self._right = right - - # if right.is_zero(): - # raise ZeroDivisionError("division by zero series") - - # if left.is_zero(): - # return ZZ.zero() - super().__init__(left._is_sparse, left._approximate_valuation, None) - self._approximate_valuation = left.valuation(left) - right.valuation(right) + self._approximate_valuation = left.valuation() - right.valuation() - lv = left.valuation(left) - rv = right.valuation(right) + lv = left.valuation() + rv = right.valuation() self._lv = lv self._rv = rv - self._ainv = ~right[rv] + self._ainv = right[rv].inverse_of_unit() def get_coefficient(self, n): lv = self._lv diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index e8028367e41..b07d6e154b2 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -39,7 +39,7 @@ Power series can be defined recursively:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: L.series(lambda s,n: (1 + z*s^2)[n], valuation=0) + sage: L.series(lambda s,n: (1 + z*s^2)[n], True, approximate_valuation=0) 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... AUTHORS: @@ -218,13 +218,14 @@ def _element_constructor_(self, x): """ R = self.base_ring() - # op = LazyLaurentSeriesOperator_constant(self, R(x)) + # Always a sparse implementation. op = lambda n: R(x) if n == 0 else self.base_ring().zero() aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=0, constant=(R.zero(), 1)) return self.element_class(self, aux) def _an_element_(self, is_sparse=True): + # Always a sparse implementation. """ Return a Laurent series in this ring. @@ -270,7 +271,6 @@ def zero(self): """ return self._element_constructor_(0) - # def series(self, coefficient, valuation, constant=None): def series(self, coefficient_function, is_sparse, approximate_valuation, constant=None): r""" Return a lazy Laurent series. @@ -279,6 +279,8 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan - ``coefficient_function`` -- Python function that computes coefficients + - ``issparse`` -- Boolean that determines whether the implementation is sparse or dense + - ``approximate_valuation`` -- integer; approximate valuation of the series - ``constant`` -- either ``None`` or pair of an element of the base ring and an integer @@ -286,20 +288,20 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan Let the coefficient of index `i` mean the coefficient of the term of the series with exponent `i`. - Python function ``coefficient`` returns the value of the coefficient of - index `i` from input `s` and `i` where `s` is the series itself. + Python function ``coefficient_function`` returns the value of the coefficient of + index `i` from input. - Let ``valuation`` be `n`. All coefficients of index below `n` are zero. - If ``constant`` is ``None``, then the ``coefficient`` function is responsible to - compute the values of all coefficients of index `\ge n`. If - ``constant`` is a pair `(c,m)`, then the ``coefficient`` function is responsible - to compute the values of all coefficients of index `\ge n` and `< m` - and all the coefficients of index `\ge m` is the constant `c`. + Let ``approximate_valuation`` be `n`. All coefficients of index below `n` are zero. If + ``constant`` is ``None``, then the ``coefficient_function`` function is responsible + to compute the values of all coefficients of index `\ge n`. If ``constant`` + is a pair `(c,m)`, then the ``coefficient_function`` function is responsible to + compute the values of all coefficients of index `\ge n` and `< m` and all + the coefficients of index `\ge m` is the constant `c`. EXAMPLES:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: L.series(lambda s, i: i, 5, (1,10)) + sage: L.series(lambda i: i, True, 5, (1,10)) 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... sage: def g(s, i): @@ -318,7 +320,7 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan sage: f.coefficient(30) -219 - Alternatively, the ``coefficient`` can be a list of elements of the + Alternatively, the ``coefficient_function`` can be a list of elements of the base ring. Then these elements are read as coefficients of the terms of degrees starting from the ``valuation``. In this case, ``constant`` may be just an element of the base ring instead of a tuple or can be @@ -327,10 +329,10 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan EXAMPLES:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], -5) + sage: f = L.series([1,2,3,4], True -5) sage: f z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 - sage: g = L.series([1,3,5,7,9], 5, -1) + sage: g = L.series([1,3,5,7,9], True, 5, -1) sage: g z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... """ From a64d8085f00470525edd1604c2a579e38ef642f4 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 7 Jul 2021 03:51:31 +0530 Subject: [PATCH 38/98] Fixed doctests. --- src/sage/rings/lazy_laurent_series_new.py | 10 +++++----- src/sage/rings/lazy_laurent_series_ring.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index f126137ae35..923ab8b51d4 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -19,9 +19,9 @@ ....: return 1 ....: else: ....: return s.coefficient(i - 1) + s.coefficient(i - 2) - sage: f = L.series(coeff, True, valuation=0); f + sage: f = L.series(coeff, True, approximate_valuation=0); f 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... - sage: f = L.series(coeff, False, valuation=0); f + sage: f = L.series(coeff, False, approximate_valuation=0); f 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... The 100th element of Fibonacci sequence can be obtained from the generating @@ -128,7 +128,7 @@ class LLS(ModuleElement): ....: return 1 ....: else: ....: return s.coefficient(i - 1) + s.coefficient(i - 2) - sage: f = L.series(coeff, True, valuation=0); f + sage: f = L.series(coeff, True, approximate_valuation=0); f 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... sage: f.coefficient(100) 573147844013817084101 @@ -398,7 +398,7 @@ def coefficient(self, n): ....: return 1 ....: else: ....: return sum(s.coefficient(j)*s.coefficient(i - 1 -j) for j in [0..i-1]) - sage: e = L.series(g, True, valuation=0) + sage: e = L.series(g, True, approximate_valuation=0) sage: e.coefficient(10) 16796 sage: e @@ -657,7 +657,7 @@ def polynomial(self, degree=None, name=None): TESTS:: - sage: g = L.series([1,0,0,2,0,0,0,3], -5); g + sage: g = L.series([1,0,0,2,0,0,0,3], True, -5); g z^-5 + 2*z^-2 + 3*z^2 sage: g.polynomial() z^-5 + 2*z^-2 + 3*z^2 diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index b07d6e154b2..ac6e17f7998 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -309,7 +309,7 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan ....: return 1 ....: else: ....: return s.coefficient(i - 1) + i - sage: e = L.series(g, -5); e + sage: e = L.series(g, True, -5); e z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... sage: f = e^-1; f z^5 - z^6 - z^11 + ... @@ -329,7 +329,7 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan EXAMPLES:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], True -5) + sage: f = L.series([1,2,3,4], True, -5) sage: f z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 sage: g = L.series([1,3,5,7,9], True, 5, -1) From 76220f5b0cb91a9b54c729b9074c2636992ee852 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 8 Jul 2021 18:42:58 +0530 Subject: [PATCH 39/98] Fixed the issues with no coefficient function. --- src/sage/rings/lazy_laurent_series_new.py | 14 ++++++++++++-- src/sage/rings/lazy_laurent_series_ring.py | 3 ++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 923ab8b51d4..3bcdc4d992d 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -84,6 +84,7 @@ from .integer_ring import ZZ from sage.structure.richcmp import op_EQ, op_NE from sage.arith.power import generic_power +import copy class LLS(ModuleElement): @@ -413,7 +414,7 @@ def coefficient(self, n): ....: return sum(s.coefficient(j)*s.coefficient(i - 1 - j) for j in [0..i-1]) ....: sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: e = L.series(g, False, valuation = 0) + sage: e = L.series(g, False, approximate_valuation = 0) sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... sage: e._aux._cache @@ -506,7 +507,7 @@ def change_ring(self, ring): """ from .lazy_laurent_series_ring import LazyLaurentSeriesRing Q = LazyLaurentSeriesRing(ring, names=self.parent().variable_name()) - return Q.element_class(Q, LLS_coefficient_function(self._aux._coefficient_function, self._aux._is_sparse, self._aux._approximate_valuation, self._aux._constant)) + return Q.element_class(Q, self._aux) def truncate(self, d): """ @@ -999,6 +1000,7 @@ def __init__(self, left, right): if left._is_sparse != right._is_sparse: raise NotImplementedError super().__init__(left._is_sparse, a, c) + self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): @@ -1042,6 +1044,7 @@ def __init__(self, left, right): raise NotImplementedError super().__init__(left._is_sparse, a, c) + self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): c = ZZ.zero() @@ -1076,6 +1079,7 @@ def __init__(self, left, right): raise NotImplementedError super().__init__(left._is_sparse, a, c) + self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): """ @@ -1116,6 +1120,7 @@ def __init__(self, series, scalar): c = None super().__init__(series._is_sparse, a, c) + self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): c = self._series[n] * self._scalar @@ -1143,6 +1148,7 @@ def __init__(self, series): c = None super().__init__(series._is_sparse, a, c) + self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): c = -1 * self._series[n] @@ -1171,6 +1177,7 @@ def __init__(self, series): self._ainv = series[v].inverse_of_unit() self._zero = ZZ.zero() + self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): v = self._approximate_valuation @@ -1211,6 +1218,7 @@ def __init__(self, series, function): c = None super().__init__(series._is_sparse, a, c) + self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): c = self._function(self._series[n]) @@ -1235,6 +1243,7 @@ def __init__(self, series, d): a = series._approximate_valuation c = (ZZ.zero(), d) super().__init__(series._is_sparse, a, c) + self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): if n <= self._d: @@ -1272,6 +1281,7 @@ def __init__(self, left, right): self._lv = lv self._rv = rv self._ainv = right[rv].inverse_of_unit() + self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): lv = self._lv diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index ac6e17f7998..bdda1d671a3 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -344,7 +344,8 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan elif constant not in self.base_ring(): raise ValueError("constant is not an element of the base ring") constant = (constant, approximate_valuation + len(coefficient_function)) - coefficient_function = LazyLaurentSeriesOperator_list(self, coefficient_function, approximate_valuation) + ll_list = tuple([self.base_ring()(e) for e in coefficient_function]) + coefficient_function = lambda i: self.base_ring()(ll_list[i - approximate_valuation]) elif constant is not None: try: c,m = constant From c1c6e48d2f971ae62dccb22c20170557f08aee5d Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 8 Jul 2021 19:04:40 +0530 Subject: [PATCH 40/98] Fixed the issues with no coefficient function. --- src/sage/rings/lazy_laurent_series_ring.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index bdda1d671a3..8dcfd674804 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -144,7 +144,6 @@ def gen(self, n=0): # Always a sparse implementation. if n != 0: raise IndexError("there is only one generator") - op = lambda n: self.base_ring().one() if n == 1 else self.base_ring().zero() c = (self.base_ring().zero(), 2) aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=1, constant=c) @@ -220,7 +219,6 @@ def _element_constructor_(self, x): # Always a sparse implementation. op = lambda n: R(x) if n == 0 else self.base_ring().zero() - aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=0, constant=(R.zero(), 1)) return self.element_class(self, aux) @@ -240,7 +238,6 @@ def _an_element_(self, is_sparse=True): e = self.base_ring().an_element() op = lambda i: self.base_ring().an_element() - n = random.randint(-N,N) m = random.randint(0,N) From 4b37cb584f9ac9b4ee8567341efd82cd09cb5095 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 8 Jul 2021 19:30:13 +0530 Subject: [PATCH 41/98] Fixed the issues with pickling. --- src/sage/rings/lazy_laurent_series_operator.py | 6 +++--- src/sage/rings/lazy_laurent_series_ring.py | 13 ++++++------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_operator.py b/src/sage/rings/lazy_laurent_series_operator.py index ed1948fbb60..e6ff95c8634 100644 --- a/src/sage/rings/lazy_laurent_series_operator.py +++ b/src/sage/rings/lazy_laurent_series_operator.py @@ -735,7 +735,7 @@ def __init__(self, ring): self._one = ring.base_ring().one() self._zero = ring.base_ring().zero() - def __call__(self, s, n): + def __call__(self, n): """ Return the `n`-th coefficient of the series ``s``. @@ -800,7 +800,7 @@ def __init__(self, ring, constant): self._zero = ring.base_ring().zero() - def __call__(self, s, n): + def __call__(self, n): """ Return the `n`-th coefficient of the series ``s``. @@ -870,7 +870,7 @@ def __init__(self, ring, l, v): self._list = tuple([ring.base_ring()(e) for e in l]) self._valuation = v - def __call__(self, s, n): + def __call__(self, n): """ Return the `n`-th coefficient of the series ``s``. diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 8dcfd674804..e69cd8ae9f9 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -144,7 +144,7 @@ def gen(self, n=0): # Always a sparse implementation. if n != 0: raise IndexError("there is only one generator") - op = lambda n: self.base_ring().one() if n == 1 else self.base_ring().zero() + op = LazyLaurentSeriesOperator_gen(self) c = (self.base_ring().zero(), 2) aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=1, constant=c) @@ -218,7 +218,7 @@ def _element_constructor_(self, x): R = self.base_ring() # Always a sparse implementation. - op = lambda n: R(x) if n == 0 else self.base_ring().zero() + op = LazyLaurentSeriesOperator_constant(self, R(x)) aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=0, constant=(R.zero(), 1)) return self.element_class(self, aux) @@ -236,12 +236,12 @@ def _an_element_(self, is_sparse=True): N = 10 e = self.base_ring().an_element() - - op = lambda i: self.base_ring().an_element() + def r(i): + return self.base_ring().an_element() n = random.randint(-N,N) m = random.randint(0,N) - aux = LLS_coefficient_function(op, is_sparse, n, (e, n + m)) + aux = LLS_coefficient_function(r, is_sparse, n, (e, n + m)) return self.element_class(self, aux) def one(self): @@ -341,8 +341,7 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan elif constant not in self.base_ring(): raise ValueError("constant is not an element of the base ring") constant = (constant, approximate_valuation + len(coefficient_function)) - ll_list = tuple([self.base_ring()(e) for e in coefficient_function]) - coefficient_function = lambda i: self.base_ring()(ll_list[i - approximate_valuation]) + coefficient_function = LazyLaurentSeriesOperator_list(self, coefficient_function, approximate_valuation) elif constant is not None: try: c,m = constant From 2e15677808d24e83d675b7ee2158edc5a843570f Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sat, 10 Jul 2021 00:08:29 +0530 Subject: [PATCH 42/98] Added a new ABC --- src/sage/rings/lazy_laurent_series_new.py | 99 +++++++++++++++++++---- 1 file changed, 83 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 3bcdc4d992d..2f2c8b6d4c4 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -84,7 +84,7 @@ from .integer_ring import ZZ from sage.structure.richcmp import op_EQ, op_NE from sage.arith.power import generic_power -import copy +from abc import ABC class LLS(ModuleElement): @@ -802,6 +802,8 @@ def _richcmp_(self, other, op): False """ if op is op_EQ: + if '_richcmp_' in dir(self._aux): + return self._aux._richcmp_(other) if self._aux._constant is None: if other._aux._constant is None: n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) @@ -849,6 +851,8 @@ def __hash__(self): sage: {g: 1} {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} """ + if '_richcmp_' in dir(self._aux): + return self._aux.__hash__() return hash((type(self), self._aux._coefficient_function, self._aux._approximate_valuation, self._aux._constant)) @@ -887,7 +891,55 @@ def __bool__(self): return True return False + + +class LLS_binary(ABC): + + def __init__(self, left, right): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: loads(dumps(f)) == f + True + sage: f = 1/(1 - z) - 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._left = left + self._right = right + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: {f: 1} + {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} + """ + return hash((type(self), self._left, self._right)) + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: g = 1/(1 - z) + 1/(1 + z) + sage: f == g + True + """ + return (isinstance(other._aux, type(self)) and + self._left == other._aux._left and self._right == other._aux._right) + class LLS_aux(): def __init__(self, is_sparse, approximate_valuation, constant=None): @@ -984,7 +1036,7 @@ def iterate_coefficients(self): n += 1 -class LLS_mul(LLS_aux): +class LLS_mul(LLS_aux, LLS_binary): """ Operator for multiplication. """ @@ -1000,8 +1052,6 @@ def __init__(self, left, right): if left._is_sparse != right._is_sparse: raise NotImplementedError super().__init__(left._is_sparse, a, c) - self._coefficient_function = copy.copy(self.get_coefficient) - def get_coefficient(self, n): c = ZZ.zero() @@ -1023,9 +1073,15 @@ def iterate_coefficients(self): c += l * self._right[n-k] yield c n += 1 + + def _richcmp_(self, other): + self.__eq__(other) + + def __hash__(self): + return super.__hash__(self) -class LLS_add(LLS_aux): +class LLS_add(LLS_aux, LLS_binary): """ Operator for addition. """ @@ -1044,7 +1100,6 @@ def __init__(self, left, right): raise NotImplementedError super().__init__(left._is_sparse, a, c) - self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): c = ZZ.zero() @@ -1058,9 +1113,15 @@ def iterate_coefficients(self): c = self._left[n] + self._right[n] yield c n += 1 + + def _richcmp_(self, other): + self.__eq__(other) + + def __hash__(self): + return super.__hash__(self) -class LLS_sub(LLS_aux): +class LLS_sub(LLS_aux, LLS_binary): """ Operator for subtraction. """ @@ -1079,7 +1140,6 @@ def __init__(self, left, right): raise NotImplementedError super().__init__(left._is_sparse, a, c) - self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): """ @@ -1103,6 +1163,12 @@ def iterate_coefficients(self): c = self._left[n] - self._right[n] yield c n += 1 + + def _richcmp_(self, other): + self.__eq__(other) + + def __hash__(self): + return super.__hash__(self) class LLS_rmul(LLS_aux): @@ -1120,7 +1186,6 @@ def __init__(self, series, scalar): c = None super().__init__(series._is_sparse, a, c) - self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): c = self._series[n] * self._scalar @@ -1148,7 +1213,6 @@ def __init__(self, series): c = None super().__init__(series._is_sparse, a, c) - self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): c = -1 * self._series[n] @@ -1175,9 +1239,9 @@ def __init__(self, series): if v is infinity: raise ZeroDivisionError('cannot invert zero') + # self._ainv = ~series[v] self._ainv = series[v].inverse_of_unit() self._zero = ZZ.zero() - self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): v = self._approximate_valuation @@ -1218,7 +1282,6 @@ def __init__(self, series, function): c = None super().__init__(series._is_sparse, a, c) - self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): c = self._function(self._series[n]) @@ -1243,7 +1306,6 @@ def __init__(self, series, d): a = series._approximate_valuation c = (ZZ.zero(), d) super().__init__(series._is_sparse, a, c) - self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): if n <= self._d: @@ -1264,7 +1326,7 @@ def iterate_coefficients(self): n += 1 -class LLS_div(LLS_aux): +class LLS_div(LLS_aux, LLS_binary): """ Return ``self`` divided by ``other``. """ @@ -1281,7 +1343,6 @@ def __init__(self, left, right): self._lv = lv self._rv = rv self._ainv = right[rv].inverse_of_unit() - self._coefficient_function = copy.copy(self.get_coefficient) def get_coefficient(self, n): lv = self._lv @@ -1306,4 +1367,10 @@ def iterate_coefficients(self): for k in range(lv - rv, n): c -= self[k] * self._right[n + rv - k] yield c * self._ainv - n += 1 \ No newline at end of file + n += 1 + + def _richcmp_(self, other): + self.__eq__(other) + + def __hash__(self): + return super.__hash__(self) \ No newline at end of file From 86c97cb11b745c01609308af6f7899cfd9ac1927 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sat, 10 Jul 2021 02:39:56 +0530 Subject: [PATCH 43/98] Small correction to the ABC code. --- src/sage/rings/lazy_laurent_series_new.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 2f2c8b6d4c4..b3d36544a1e 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -835,6 +835,8 @@ def _richcmp_(self, other, op): return True if op is op_NE: + if '_richcmp_' in dir(self._aux): + return not (self._aux._richcmp_(other)) return not (self == other) return False @@ -851,7 +853,7 @@ def __hash__(self): sage: {g: 1} {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} """ - if '_richcmp_' in dir(self._aux): + if '__hash__' in dir(self._aux): return self._aux.__hash__() return hash((type(self), self._aux._coefficient_function, self._aux._approximate_valuation, self._aux._constant)) @@ -1342,6 +1344,7 @@ def __init__(self, left, right): rv = right.valuation() self._lv = lv self._rv = rv + # self._ainv = ~right[rv] self._ainv = right[rv].inverse_of_unit() def get_coefficient(self, n): From b7fbf4fe5f5d51a984ab764fd7fd3a7c9117310e Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sat, 10 Jul 2021 21:38:54 +0530 Subject: [PATCH 44/98] Fixed the equality, and the comparisions. --- src/sage/rings/lazy_laurent_series_new.py | 228 ++++++++++++++-------- 1 file changed, 143 insertions(+), 85 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index b3d36544a1e..6bca788eb05 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -84,7 +84,6 @@ from .integer_ring import ZZ from sage.structure.richcmp import op_EQ, op_NE from sage.arith.power import generic_power -from abc import ABC class LLS(ModuleElement): @@ -802,8 +801,6 @@ def _richcmp_(self, other, op): False """ if op is op_EQ: - if '_richcmp_' in dir(self._aux): - return self._aux._richcmp_(other) if self._aux._constant is None: if other._aux._constant is None: n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) @@ -811,8 +808,11 @@ def _richcmp_(self, other, op): for i in range(n, m): if self[i] != other[i]: return False - if self._aux._coefficient_function == other._aux._coefficient_function: - return True + try: + if self._aux._coefficient_function == other._aux._coefficient_function: + return True + except AttributeError: + return self._aux.__eq__(other._aux) raise ValueError("undecidable as lazy Laurent series") else: raise ValueError("undecidable as lazy Laurent series") @@ -835,8 +835,6 @@ def _richcmp_(self, other, op): return True if op is op_NE: - if '_richcmp_' in dir(self._aux): - return not (self._aux._richcmp_(other)) return not (self == other) return False @@ -853,10 +851,7 @@ def __hash__(self): sage: {g: 1} {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} """ - if '__hash__' in dir(self._aux): - return self._aux.__hash__() - return hash((type(self), self._aux._coefficient_function, - self._aux._approximate_valuation, self._aux._constant)) + return hash((type(self), self._aux._approximate_valuation, self._aux._constant)) def __bool__(self): """ @@ -895,54 +890,6 @@ def __bool__(self): return False -class LLS_binary(ABC): - - def __init__(self, left, right): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: loads(dumps(f)) == f - True - sage: f = 1/(1 - z) - 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ - self._left = left - self._right = right - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: {f: 1} - {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} - """ - return hash((type(self), self._left, self._right)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g - True - """ - return (isinstance(other._aux, type(self)) and - self._left == other._aux._left and self._right == other._aux._right) - - class LLS_aux(): def __init__(self, is_sparse, approximate_valuation, constant=None): self._approximate_valuation = approximate_valuation @@ -1021,9 +968,114 @@ def valuation(self): self._approximate_valuation = n return n n += 1 + + +class LLS_unary: + """ + Abstract base class for unary operators. + + INPUT: + + - ``series`` -- series upon which the operator operates + + """ + def __init__(self, series): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = -1/(1 - z) + sage: f + -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... + sage: loads(dumps(f)) == f + True + """ + self._series = series + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 - z) + sage: {f: 1} + {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} + """ + return hash((type(self), self._series)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: g = 1/(1 - z) + 1/(1 + z) + sage: f == g + True + sage: f = ~(1 - z) + sage: g = ~(1 - z) + sage: f == g + True + """ + return (isinstance(other, type(self)) and self._cache == other._cache + and self._approximate_valuation == other._approximate_valuation) + + +class LLS_binary: + + def __init__(self, left, right): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: loads(dumps(f)) == f + True + sage: f = 1/(1 - z) - 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._left = left + self._right = right + def __hash__(self): + """ + Return the hash of ``self``. -class LLS_coefficient_function(LLS_aux): + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: {f: 1} + {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} + """ + return hash((type(self), self._left, self._right)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: g = 1/(1 - z) + 1/(1 + z) + sage: f == g + True + """ + return (isinstance(other, type(self)) and + self._left == other._left and self._right == other._right) + + +class LLS_coefficient_function(LLS_aux, LLS_unary): def __init__(self, coefficient_function, is_sparse, approximate_valuation, constant=None): self._coefficient_function = coefficient_function super().__init__(is_sparse, approximate_valuation, constant) @@ -1036,6 +1088,9 @@ def iterate_coefficients(self): while True: yield self._coefficient_function(n) n += 1 + + def __eq__(self, other): + return super().__eq__(other) class LLS_mul(LLS_aux, LLS_binary): @@ -1076,11 +1131,8 @@ def iterate_coefficients(self): yield c n += 1 - def _richcmp_(self, other): - self.__eq__(other) - - def __hash__(self): - return super.__hash__(self) + def __eq__(self, other): + return super().__eq__(other) class LLS_add(LLS_aux, LLS_binary): @@ -1116,11 +1168,8 @@ def iterate_coefficients(self): yield c n += 1 - def _richcmp_(self, other): - self.__eq__(other) - - def __hash__(self): - return super.__hash__(self) + def __eq__(self, other): + return super().__eq__(other) class LLS_sub(LLS_aux, LLS_binary): @@ -1166,14 +1215,11 @@ def iterate_coefficients(self): yield c n += 1 - def _richcmp_(self, other): - self.__eq__(other) - - def __hash__(self): - return super.__hash__(self) + def __eq__(self, other): + return super().__eq__(other) -class LLS_rmul(LLS_aux): +class LLS_rmul(LLS_aux, LLS_unary): """ Operator for multiplying with a scalar. """ @@ -1199,9 +1245,12 @@ def iterate_coefficients(self): c = self._series[n] * self._scalar yield c n += 1 + + def __eq__(self, other): + return super().__eq__(other) -class LLS_neg(LLS_aux): +class LLS_neg(LLS_aux, LLS_unary): """ Operator for negative of the series. """ @@ -1226,9 +1275,12 @@ def iterate_coefficients(self): c = -1 * self._series[n] yield c n += 1 + + def __eq__(self, other): + return super().__eq__(other) -class LLS_inv(LLS_aux): +class LLS_inv(LLS_aux, LLS_unary): """ Operator for multiplicative inverse of the series. """ @@ -1267,9 +1319,12 @@ def iterate_coefficients(self): c += self[k] * self._series[n - v - k] yield -c * self._ainv n += 1 + + def __eq__(self, other): + return super().__eq__(other) -class LLS_apply_coeff(LLS_aux): +class LLS_apply_coeff(LLS_aux, LLS_unary): """ Return the series with ``function`` applied to each coefficient of this series. """ @@ -1295,9 +1350,12 @@ def iterate_coefficients(self): c = self._function(self._series[n]) yield c n += 1 + + def __eq__(self, other): + return super().__eq__(other) -class LLS_trunc(LLS_aux): +class LLS_trunc(LLS_aux, LLS_unary): """ Return this series with its terms of degree >= ``d`` truncated. """ @@ -1326,6 +1384,9 @@ def iterate_coefficients(self): c = self._zero yield c n += 1 + + def __eq__(self, other): + return super().__eq__(other) class LLS_div(LLS_aux, LLS_binary): @@ -1372,8 +1433,5 @@ def iterate_coefficients(self): yield c * self._ainv n += 1 - def _richcmp_(self, other): - self.__eq__(other) - - def __hash__(self): - return super.__hash__(self) \ No newline at end of file + def __eq__(self, other): + return super().__eq__(other) \ No newline at end of file From 3970194e5eeda30dea6a2b38283aca96a2b9dee2 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sat, 10 Jul 2021 23:33:12 +0530 Subject: [PATCH 45/98] Added checks to ensure that the coefficients are in the base ring in the apply_coeff method. --- src/sage/rings/lazy_laurent_series_new.py | 30 +++++++++++++++-------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 6bca788eb05..541d3f1c321 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -441,7 +441,7 @@ def coefficient(self, n): """ return self.__getitem__(n) - def apply_to_coefficients(self, newfunction): + def apply_to_coefficients(self, newfunction, ring): """ Return the series with ``newfunction`` applied to each coefficient of this series. @@ -449,28 +449,30 @@ def apply_to_coefficients(self, newfunction): - ``newfunction`` -- Python function + - ``ring`` -- Base Ring of the series + Python function ``newfunction`` returns a new coefficient for input coefficient. TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: s = z/(1 - 2*z) - sage: t = s.apply_to_coefficients(lambda c: c + 1) + sage: t = s.apply_to_coefficients(lambda c: c + 1, L) sage: s z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... sage: t 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... sage: M = L.series(lambda n: n, True, 0); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.apply_to_coefficients(lambda c: c + 1); N + sage: N = M.apply_to_coefficients(lambda c: c + 1, L); N 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... sage: M = L.series(lambda n: n, False, 0); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.apply_to_coefficients(lambda c: c + 1); N + sage: N = M.apply_to_coefficients(lambda c: c + 1, L); N 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... """ P = self.parent() - return P.element_class(P, LLS_apply_coeff(self._aux, newfunction)) + return P.element_class(P, LLS_apply_coeff(self._aux, newfunction, ring)) def change_ring(self, ring): """ @@ -1328,9 +1330,10 @@ class LLS_apply_coeff(LLS_aux, LLS_unary): """ Return the series with ``function`` applied to each coefficient of this series. """ - def __init__(self, series, function): + def __init__(self, series, function, ring): self._series = series self._function = function + self._ring = ring a = series._approximate_valuation if series._constant: @@ -1341,15 +1344,22 @@ def __init__(self, series, function): super().__init__(series._is_sparse, a, c) def get_coefficient(self, n): - c = self._function(self._series[n]) - return c + try: + c = self._ring(self._function(self._series[n])) + return c + except TypeError: + raise ValueError("The coefficients are not in the base ring.") def iterate_coefficients(self): n = self._offset while True: c = self._function(self._series[n]) - yield c - n += 1 + try: + c = self._ring(self._function(self._series[n])) + yield c + n += 1 + except TypeError: + raise ValueError("The coefficients are not in the base ring.") def __eq__(self, other): return super().__eq__(other) From 45f0bc415f81f6177e5bba0734c427d26ecc16d3 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Tue, 13 Jul 2021 18:37:40 +0530 Subject: [PATCH 46/98] Created new files. --- src/sage/rings/all.py | 3 +- src/sage/rings/lazy_laurent_series_new.py | 68 +++---- ...py => lazy_laurent_series_operator_new.py} | 178 +++++++++--------- ...ing.py => lazy_laurent_series_ring_new.py} | 57 +++--- 4 files changed, 153 insertions(+), 153 deletions(-) rename src/sage/rings/{lazy_laurent_series_operator.py => lazy_laurent_series_operator_new.py} (79%) rename src/sage/rings/{lazy_laurent_series_ring.py => lazy_laurent_series_ring_new.py} (87%) diff --git a/src/sage/rings/all.py b/src/sage/rings/all.py index 50f0a4e190c..0b939427c14 100644 --- a/src/sage/rings/all.py +++ b/src/sage/rings/all.py @@ -120,7 +120,8 @@ from .laurent_series_ring_element import LaurentSeries # Lazy Laurent series ring -lazy_import('sage.rings.lazy_laurent_series_ring', 'LazyLaurentSeriesRing') +lazy_import('sage.rings.lazy_laurent_series_ring_new', 'LLSRing') +# lazy_import('sage.rings.lazy_laurent_series_ring', 'LazyLaurentSeriesRing') # Tate algebras from .tate_algebra import TateAlgebra diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 541d3f1c321..6621d53e4d4 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -10,7 +10,7 @@ Generating functions are Laurent series over the integer ring:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) This defines the generating function of Fibonacci sequence:: @@ -115,7 +115,7 @@ class LLS(ModuleElement): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: L.series(lambda i: i, is_sparse=True, approximate_valuation=-3, constant=(-1,3)) -3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ... sage: L.series(lambda i: i, is_sparse=False, approximate_valuation=-3, constant=(-1,3)) @@ -151,7 +151,7 @@ def __init__(self, parent, aux): TESTS:: - sage: L = LazyLaurentSeriesRing(GF(2), 'z') + sage: L = LLSRing(GF(2), 'z') sage: z = L.gen() sage: TestSuite(z).run() """ @@ -168,7 +168,7 @@ def __getitem__(self, n): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = z/(1 - 2*z^3) sage: [f[n] for n in range(20)] [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] @@ -192,7 +192,7 @@ def _mul_(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: (1 - z)*(1 - z) 1 - 2*z + z^2 sage: (1 - z)*(1 - z)*(1 - z) @@ -223,7 +223,7 @@ def _add_(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: (1 - z)*(1 - z) 1 - 2*z + z^2 sage: (1 - z)*(1 - z)*(1 - z) @@ -258,7 +258,7 @@ def _div_(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: z/(1 - z) z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... sage: M = L.series(lambda n: n, False, 0); M @@ -287,7 +287,7 @@ def _rmul_(self, scalar): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: 2*z 2*z sage: -1*z @@ -316,7 +316,7 @@ def _sub_(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: z - z 0 sage: 3*z - 2*z @@ -343,7 +343,7 @@ def _neg_(self): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: z = L.gen() sage: -(1 - z) -1 + z @@ -367,7 +367,7 @@ def __invert__(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: ~(1 - z) 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: M = L.series(lambda n: n, False, 0); M @@ -392,7 +392,7 @@ def coefficient(self, n): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: def g(s, i): ....: if i == 0: ....: return 1 @@ -412,7 +412,7 @@ def coefficient(self, n): ....: else: ....: return sum(s.coefficient(j)*s.coefficient(i - 1 - j) for j in [0..i-1]) ....: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: e = L.series(g, False, approximate_valuation = 0) sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... @@ -455,7 +455,7 @@ def apply_to_coefficients(self, newfunction, ring): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: s = z/(1 - 2*z) sage: t = s.apply_to_coefficients(lambda c: c + 1, L) sage: s @@ -484,7 +484,7 @@ def change_ring(self, ring): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: s = 2 + z sage: t = s.change_ring(QQ) sage: t^-1 @@ -506,8 +506,8 @@ def change_ring(self, ring): sage: M ^-1 z^-1 - 2 + z + ... """ - from .lazy_laurent_series_ring import LazyLaurentSeriesRing - Q = LazyLaurentSeriesRing(ring, names=self.parent().variable_name()) + from .lazy_laurent_series_ring_new import LLSRing + Q = LLSRing(ring, names=self.parent().variable_name()) return Q.element_class(Q, self._aux) def truncate(self, d): @@ -520,7 +520,7 @@ def truncate(self, d): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: alpha = 1/(1-z) sage: alpha 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... @@ -547,7 +547,7 @@ def __pow__(self, n): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: (1 - z)^-1 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: (1 - z)^0 @@ -586,7 +586,7 @@ def approximate_series(self, prec, name=None): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: z = L.gen() sage: f = (z - 2*z^3)^5/(1 - 2*z) sage: f @@ -623,7 +623,7 @@ def prec(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) sage: f.prec() +Infinity @@ -651,7 +651,7 @@ def polynomial(self, degree=None, name=None): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: f = L.series([1,0,0,2,0,0,0,3], True, 5); f z^5 + 2*z^8 + 3*z^12 sage: f.polynomial() @@ -712,7 +712,7 @@ def valuation(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: s = 1/(1 - z) - 1/(1 - 2*z) sage: s.valuation() 1 @@ -734,7 +734,7 @@ def _repr_(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: -1/(1 + 2*z) -1 + 2*z - 4*z^2 + 8*z^3 - 16*z^4 + 32*z^5 - 64*z^6 + ... """ @@ -792,7 +792,7 @@ def _richcmp_(self, other, op): TESTS:: - sage: L. = LazyLaurentSeriesRing(QQ) + sage: L. = LLSRing(QQ) sage: z + z^2 == z^2 + z True sage: z + z^2 != z^2 + z @@ -847,7 +847,7 @@ def __hash__(self): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: f = L.series([1,2,3,4], True, -5) sage: g = (1 + f)/(1 - f)^2 sage: {g: 1} @@ -861,7 +861,7 @@ def __bool__(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(GF(2)) + sage: L. = LLSRing(GF(2)) sage: (z-z).is_zero() True sage: f = 1/(1 - z) @@ -987,7 +987,7 @@ def __init__(self, series): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = -1/(1 - z) sage: f -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... @@ -1002,7 +1002,7 @@ def __hash__(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 - z) sage: {f: 1} {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} @@ -1015,7 +1015,7 @@ def __eq__(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) + 1/(1 + z) sage: g = 1/(1 - z) + 1/(1 + z) sage: f == g @@ -1037,7 +1037,7 @@ def __init__(self, left, right): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) + 1/(1 + z) sage: loads(dumps(f)) == f True @@ -1054,7 +1054,7 @@ def __hash__(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) + 1/(1 + z) sage: {f: 1} {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} @@ -1067,7 +1067,7 @@ def __eq__(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) + 1/(1 + z) sage: g = 1/(1 - z) + 1/(1 + z) sage: f == g @@ -1200,7 +1200,7 @@ def get_coefficient(self, n): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = (1 + z)*(1 - z) sage: f.coefficient(2) -1 diff --git a/src/sage/rings/lazy_laurent_series_operator.py b/src/sage/rings/lazy_laurent_series_operator_new.py similarity index 79% rename from src/sage/rings/lazy_laurent_series_operator.py rename to src/sage/rings/lazy_laurent_series_operator_new.py index e6ff95c8634..1acf5743053 100644 --- a/src/sage/rings/lazy_laurent_series_operator.py +++ b/src/sage/rings/lazy_laurent_series_operator_new.py @@ -11,7 +11,7 @@ EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - 2*z) sage: g = 1/(1 + z^2) @@ -22,7 +22,7 @@ :: - sage: L.series([1,2,3,4], -10) + sage: L.series([1,2,3,4], True, -10) z^-10 + 2*z^-9 + 3*z^-8 + 4*z^-7 :: @@ -79,7 +79,7 @@ :: - sage: f.apply_to_coefficients(lambda c: c^2) + sage: f.apply_to_coefficients(lambda c: c^2, GF(3)) 1 + 4*z + 16*z^2 + 64*z^3 + 256*z^4 + 1024*z^5 + 4096*z^6 + ... :: @@ -104,7 +104,7 @@ # **************************************************************************** -class LazyLaurentSeriesOperator(object): +class LLSOperator(object): """ Base class for operators computing coefficients of a lazy Laurent series. @@ -117,7 +117,7 @@ def __ne__(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) + 1/(1 + z) sage: g = 1/(1 - z) + 1/(1 + z) sage: f != g @@ -126,7 +126,7 @@ def __ne__(self, other): return not (self == other) -class LazyLaurentSeriesBinaryOperator(LazyLaurentSeriesOperator): +class LLSBinaryOperator(LLSOperator): """ Abstract base class for binary operators. @@ -143,7 +143,7 @@ def __init__(self, left, right): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) + 1/(1 + z) sage: loads(dumps(f)) == f True @@ -160,7 +160,7 @@ def __hash__(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) + 1/(1 + z) sage: {f: 1} {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} @@ -173,7 +173,7 @@ def __eq__(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) + 1/(1 + z) sage: g = 1/(1 - z) + 1/(1 + z) sage: f == g @@ -183,7 +183,7 @@ def __eq__(self, other): self._left == other._left and self._right == other._right) -class LazyLaurentSeriesUnaryOperator(LazyLaurentSeriesOperator): +class LLSUnaryOperator(LLSOperator): """ Abstract base class for unary operators. @@ -198,7 +198,7 @@ def __init__(self, series): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = -1/(1 - z) sage: f -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... @@ -213,7 +213,7 @@ def __hash__(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 - z) sage: {f: 1} {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} @@ -226,7 +226,7 @@ def __eq__(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) + 1/(1 + z) sage: g = 1/(1 - z) + 1/(1 + z) sage: f == g @@ -239,7 +239,7 @@ def __eq__(self, other): return isinstance(other, type(self)) and self._series == other._series -class LazyLaurentSeriesOperator_add(LazyLaurentSeriesBinaryOperator): +class LLSOperator_add(LLSBinaryOperator): """ Operator for addition. """ @@ -249,14 +249,14 @@ def __call__(self, s, n): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = z + z^2 + z sage: f.coefficient(1) 2 """ return self._left.coefficient(n) + self._right.coefficient(n) -class LazyLaurentSeriesOperator_sub(LazyLaurentSeriesBinaryOperator): +class LLSOperator_sub(LLSBinaryOperator): """ Operator for subtraction. """ @@ -266,14 +266,14 @@ def __call__(self, s, n): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1 + 3*z - z sage: f.coefficient(1) 2 """ return self._left.coefficient(n) - self._right.coefficient(n) -class LazyLaurentSeriesOperator_mul(LazyLaurentSeriesBinaryOperator): +class LLSOperator_mul(LLSBinaryOperator): """ Operator for multiplication. """ @@ -283,12 +283,12 @@ def __init__(self, left, right): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) * 1/(1 + z) sage: loads(dumps(f)) == f True """ - LazyLaurentSeriesBinaryOperator.__init__(self, left, right) + LLSBinaryOperator.__init__(self, left, right) self._zero = left.base_ring().zero() def __call__(self, s, n): @@ -297,7 +297,7 @@ def __call__(self, s, n): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = (1 + z)*(1 - z) sage: f.coefficient(2) -1 @@ -307,7 +307,7 @@ def __call__(self, s, n): c += self._left.coefficient(k) * self._right.coefficient(n-k) return c -class LazyLaurentSeriesOperator_neg(LazyLaurentSeriesUnaryOperator): +class LLSOperator_neg(LLSUnaryOperator): """ Operator for negation. @@ -322,14 +322,14 @@ def __call__(self, s, n): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = -(1 + z) sage: f.coefficient(1) -1 """ return -self._series.coefficient(n) -class LazyLaurentSeriesOperator_inv(LazyLaurentSeriesUnaryOperator): +class LLSOperator_inv(LLSUnaryOperator): """ Operator for inversion. @@ -344,14 +344,14 @@ def __init__(self, series): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 - z) sage: f 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: loads(dumps(f)) == f True """ - LazyLaurentSeriesUnaryOperator.__init__(self, series) + LLSUnaryOperator.__init__(self, series) self._v = series.valuation() self._ainv = ~series.coefficient(self._v) @@ -363,7 +363,7 @@ def __call__(self, s, n): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 - z) sage: f.coefficient(2) 1 @@ -376,7 +376,7 @@ def __call__(self, s, n): c += s.coefficient(k) * self._series.coefficient(n + v - k) return -c * self._ainv -class LazyLaurentSeriesOperator_div(LazyLaurentSeriesBinaryOperator): +class LLSOperator_div(LLSBinaryOperator): """ Operator for division. """ @@ -386,12 +386,12 @@ def __init__(self, left, right): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = (1 - z)/(1 + z) sage: loads(dumps(f)) == f True """ - LazyLaurentSeriesBinaryOperator.__init__(self, left, right) + LLSBinaryOperator.__init__(self, left, right) lv = left.valuation() rv = right.valuation() @@ -406,7 +406,7 @@ def __call__(self, s, n): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = (1 + z)/(1 - z) sage: f.coefficient(2) 2 @@ -421,7 +421,7 @@ def __call__(self, s, n): c -= s.coefficient(k) * self._right.coefficient(n + rv - k) return c * self._ainv -class LazyLaurentSeriesOperator_scale(LazyLaurentSeriesOperator): +class LLSOperator_scale(LLSOperator): """ Operator for scalar multiplication of ``series`` with ``scalar``. @@ -438,7 +438,7 @@ def __init__(self, series, scalar): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: g = 2*z sage: loads(dumps(g)) == g True @@ -452,7 +452,7 @@ def __call__(self, s, n): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 2*(z + z^2) sage: f.coefficient(2) 2 @@ -467,7 +467,7 @@ def __hash__(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 2*(z + z^2) sage: {f: 1} {2*z + 2*z^2: 1} @@ -480,16 +480,16 @@ def __eq__(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 2*z sage: g = 2*z sage: f == g True """ - return (isinstance(other, LazyLaurentSeriesOperator_scale) + return (isinstance(other, LLSOperator_scale) and self._series == other._series and self._scalar == other._scalar) -class LazyLaurentSeriesOperator_change_ring(LazyLaurentSeriesOperator): +class LLSOperator_change_ring(LLSOperator): """ Operator for changing the base ring of the ``series`` to ``ring``. @@ -506,7 +506,7 @@ def __init__(self, series, ring): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 - 2*z) sage: g = f.change_ring(GF(3)) sage: g @@ -523,7 +523,7 @@ def __call__(self, s, n): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 - 2*z) sage: f 1 + 2*z + 4*z^2 + 8*z^3 + 16*z^4 + 32*z^5 + 64*z^6 + ... @@ -539,7 +539,7 @@ def __hash__(self): """ Return the hash of ``self``. - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 - 2*z) sage: g = f.change_ring(GF(2)) sage: {g: 1} @@ -553,16 +553,16 @@ def __eq__(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 - z) sage: g = 1/(1 - z) sage: f == g True """ - return (isinstance(other, LazyLaurentSeriesOperator_change_ring) + return (isinstance(other, LLSOperator_change_ring) and self._series == other._series and self._ring == other._ring) -class LazyLaurentSeriesOperator_apply(LazyLaurentSeriesOperator): +class LLSOperator_apply(LLSOperator): """ Operator for applying a function. @@ -579,9 +579,9 @@ def __init__(self, series, function): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 + z) - sage: g = f.apply_to_coefficients(abs) + sage: g = f.apply_to_coefficients(abs, ZZ) sage: g 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: loads(dumps(g)) @@ -596,11 +596,11 @@ def __call__(self, s, n): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 + z) sage: f 1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ... - sage: f.apply_to_coefficients(lambda c: c if c >= 0 else -c) + sage: f.apply_to_coefficients(lambda c: c if c >= 0 else -c, ZZ) 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... """ return self._function(self._series.coefficient(n)) @@ -611,8 +611,8 @@ def __hash__(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 + z).apply_to_coefficients(lambda c: c if c >= 0 else -c) + sage: L. = LLSRing(ZZ) + sage: f = ~(1 + z).apply_to_coefficients(lambda c: c if c >= 0 else -c, ZZ) sage: {f: 1} {1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ...: 1} """ @@ -624,18 +624,18 @@ def __eq__(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 + z) - sage: g = f.apply_to_coefficients(abs) - sage: h = f.apply_to_coefficients(abs) + sage: g = f.apply_to_coefficients(abs, ZZ) + sage: h = f.apply_to_coefficients(abs, ZZ) sage: g == h True """ - return (isinstance(other, LazyLaurentSeriesOperator_apply) + return (isinstance(other, LLSOperator_apply) and self._series == other._series and self._function is other._function) -class LazyLaurentSeriesOperator_truncate(LazyLaurentSeriesOperator): +class LLSOperator_truncate(LLSOperator): """ Operator for truncation. @@ -652,7 +652,7 @@ def __init__(self, series, d): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 + z) sage: g = f.truncate(4) sage: loads(dumps(g)) == g @@ -669,7 +669,7 @@ def __call__(self, s, n): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 + z) sage: f 1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ... @@ -687,7 +687,7 @@ def __hash__(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = ~(1 + z) sage: {f: 1} {1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ...: 1} @@ -700,17 +700,17 @@ def __eq__(self, other): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: f = 1/(1 + z) sage: g = f.truncate(4) sage: h = f.truncate(4) sage: g == h True """ - return (isinstance(other, LazyLaurentSeriesOperator_truncate) + return (isinstance(other, LLSOperator_truncate) and self._series == other._series and self._d == other._d) -class LazyLaurentSeriesOperator_gen(LazyLaurentSeriesOperator): +class LLSOperator_gen(LLSOperator): """ Operator for the generator element. @@ -725,7 +725,7 @@ def __init__(self, ring): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: z = L.gen() sage: loads(dumps(z)) == z True @@ -741,7 +741,7 @@ def __call__(self, n): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: L.gen() z """ @@ -753,7 +753,7 @@ def __hash__(self): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: {z: 1} {z: 1} """ @@ -765,15 +765,15 @@ def __eq__(self, other): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: z1 = L.gen() sage: z2 = L.gen() sage: z1 == z2 True """ - return (isinstance(other, LazyLaurentSeriesOperator_gen) and self._ring == other._ring) + return (isinstance(other, LLSOperator_gen) and self._ring == other._ring) -class LazyLaurentSeriesOperator_constant(LazyLaurentSeriesOperator): +class LLSOperator_constant(LLSOperator): """ Operator for the generator element. @@ -790,7 +790,7 @@ def __init__(self, ring, constant): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: f = L(10) sage: loads(dumps(f)) == f True @@ -806,7 +806,7 @@ def __call__(self, n): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: f = L(10); f 10 sage: f.coefficient(0) @@ -822,7 +822,7 @@ def __hash__(self): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: f = L(10) sage: {f: 1} {10: 1} @@ -835,16 +835,16 @@ def __eq__(self, other): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: z1 = L(10) sage: z2 = L(10) sage: z1 == z2 True """ - return (isinstance(other, LazyLaurentSeriesOperator_constant) + return (isinstance(other, LLSOperator_constant) and self._ring == other._ring and self._constant == other._constant) -class LazyLaurentSeriesOperator_list(LazyLaurentSeriesOperator): +class LLSOperator_list(LLSOperator): """ Operator for the series defined by a list. @@ -861,8 +861,8 @@ def __init__(self, ring, l, v): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], -5) + sage: L = LLSRing(ZZ, 'z') + sage: f = L.series([1,2,3,4], True, -5) sage: loads(dumps(f)) == f True """ @@ -876,8 +876,8 @@ def __call__(self, n): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], -5) + sage: L = LLSRing(ZZ, 'z') + sage: f = L.series([1,2,3,4], True, -5) sage: f z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 """ @@ -889,8 +889,8 @@ def __hash__(self): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], -5) + sage: L = LLSRing(ZZ, 'z') + sage: f = L.series([1,2,3,4], True, -5) sage: {f: 1} {z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2: 1} """ @@ -902,17 +902,17 @@ def __eq__(self, other): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f1 = L.series([1,2,3,4], -5) - sage: f2 = L.series([1,2,3,4,0], -5) + sage: L = LLSRing(ZZ, 'z') + sage: f1 = L.series([1,2,3,4], True, -5) + sage: f2 = L.series([1,2,3,4,0], True, -5) sage: f1 == f2 True """ - return (isinstance(other, LazyLaurentSeriesOperator_list) and + return (isinstance(other, LLSOperator_list) and self._ring == other._ring and self._list == other._list and self._valuation == other._valuation) -class LazyLaurentSeriesOperator_polynomial(LazyLaurentSeriesOperator): +class LLSOperator_polynomial(LLSOperator): """ Operator for the series coerced from a polynomial or a Laurent polynomial. @@ -929,7 +929,7 @@ def __init__(self, ring, poly): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: P. = ZZ[] sage: p = 1 + 2*x + x^10 sage: f = L(p) @@ -945,7 +945,7 @@ def __call__(self, s, n): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: P. = ZZ[] sage: p = (1 + 2*x + 3*x)^3 sage: f = L(p) @@ -960,7 +960,7 @@ def __hash__(self): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: P. = ZZ[] sage: p = (1 + 2*x)^3 sage: f = L(p) @@ -975,7 +975,7 @@ def __eq__(self, other): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: P. = ZZ[] sage: p = (1 + 2*x)^3 sage: f = L(p) @@ -983,5 +983,5 @@ def __eq__(self, other): sage: f == g True """ - return (isinstance(other, LazyLaurentSeriesOperator_polynomial) and + return (isinstance(other, LLSOperator_polynomial) and self._ring == other._ring and self._poly == other._poly) diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring_new.py similarity index 87% rename from src/sage/rings/lazy_laurent_series_ring.py rename to src/sage/rings/lazy_laurent_series_ring_new.py index e69cd8ae9f9..a6575440e76 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring_new.py @@ -10,7 +10,7 @@ The definition of Laurent series rings is not initially imported into the global namespace. You need to import it explicitly to use it:: - sage: L. = LazyLaurentSeriesRing(QQ) + sage: L. = LLSRing(QQ) sage: L.category() Category of magmas and additive magmas sage: 1/(1 - z) @@ -20,7 +20,7 @@ Lazy Laurent series ring over a finite field:: - sage: L. = LazyLaurentSeriesRing(GF(3)); L + sage: L. = LLSRing(GF(3)); L Lazy Laurent Series Ring in z over Finite Field of size 3 sage: e = 1/(1 + z) sage: e.coefficient(100) @@ -31,14 +31,14 @@ Generating functions of integer sequences are Laurent series over the integer ring:: - sage: L. = LazyLaurentSeriesRing(ZZ); L + sage: L. = LLSRing(ZZ); L Lazy Laurent Series Ring in z over Integer Ring sage: 1/(1 - 2*z)^3 1 + 6*z + 24*z^2 + 80*z^3 + 240*z^4 + 672*z^5 + 1792*z^6 + ... Power series can be defined recursively:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: L.series(lambda s,n: (1 + z*s^2)[n], True, approximate_valuation=0) 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... @@ -72,20 +72,19 @@ from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing_generic -from .lazy_laurent_series import LazyLaurentSeries from .lazy_laurent_series_new import ( LLS, LLS_coefficient_function ) -from .lazy_laurent_series_operator import ( - LazyLaurentSeriesOperator_gen, - LazyLaurentSeriesOperator_constant, - LazyLaurentSeriesOperator_list, - LazyLaurentSeriesOperator_polynomial +from .lazy_laurent_series_operator_new import ( + LLSOperator_gen, + LLSOperator_constant, + LLSOperator_list, + LLSOperator_polynomial ) -class LazyLaurentSeriesRing(UniqueRepresentation, Parent): +class LLSRing(UniqueRepresentation, Parent): """ Lazy Laurent series ring. @@ -97,10 +96,10 @@ class LazyLaurentSeriesRing(UniqueRepresentation, Parent): EXAMPLES:: - sage: LazyLaurentSeriesRing(ZZ, 't') + sage: LLSRing(ZZ, 't') Lazy Laurent Series Ring in t over Integer Ring """ - # Element = LazyLaurentSeries + # Element = LLS Element = LLS def __init__(self, base_ring, names, category=None): @@ -109,7 +108,7 @@ def __init__(self, base_ring, names, category=None): TESTS:: - sage: L = LazyLaurentSeriesRing(ZZ, 't') + sage: L = LLSRing(ZZ, 't') sage: TestSuite(L).run(skip='_test_elements') """ Parent.__init__(self, base=base_ring, names=names, @@ -121,7 +120,7 @@ def _repr_(self): EXAMPLES:: - sage: LazyLaurentSeriesRing(GF(2), 'z') + sage: LLSRing(GF(2), 'z') Lazy Laurent Series Ring in z over Finite Field of size 2 """ return "Lazy Laurent Series Ring in {} over {}".format(self.variable_name(), self.base_ring()) @@ -133,7 +132,7 @@ def gen(self, n=0): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: L.gen() z sage: L.gen(3) @@ -144,7 +143,7 @@ def gen(self, n=0): # Always a sparse implementation. if n != 0: raise IndexError("there is only one generator") - op = LazyLaurentSeriesOperator_gen(self) + op = LLSOperator_gen(self) c = (self.base_ring().zero(), 2) aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=1, constant=c) @@ -158,7 +157,7 @@ def ngens(self): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: L.ngens() 1 """ @@ -170,7 +169,7 @@ def gens(self): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L. = LLSRing(ZZ) sage: 1/(1 - z) 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... """ @@ -182,7 +181,7 @@ def _coerce_map_from_(self, S): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(GF(2), 'z') + sage: L = LLSRing(GF(2), 'z') sage: L.has_coerce_map_from(ZZ) True sage: L.has_coerce_map_from(GF(2)) @@ -193,7 +192,7 @@ def _coerce_map_from_(self, S): if isinstance(S, (PolynomialRing_general, LaurentPolynomialRing_generic)) and S.ngens() == 1: def make_series_from(poly): - op = LazyLaurentSeriesOperator_polynomial(self, poly) + op = LLSOperator_polynomial(self, poly) a = poly.valuation() c = (self.base_ring().zero(), poly.degree() + 1) aux = LLS_coefficient_function(op, True, a, c) @@ -209,7 +208,7 @@ def _element_constructor_(self, x): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(GF(2), 'z') + sage: L = LLSRing(GF(2), 'z') sage: L(2) 0 sage: L(3) @@ -218,7 +217,7 @@ def _element_constructor_(self, x): R = self.base_ring() # Always a sparse implementation. - op = LazyLaurentSeriesOperator_constant(self, R(x)) + op = LLSOperator_constant(self, R(x)) aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=0, constant=(R.zero(), 1)) return self.element_class(self, aux) @@ -229,7 +228,7 @@ def _an_element_(self, is_sparse=True): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: L.an_element() # random z^-10 + z^-9 + z^-8 + z^-7 + z^-6 + z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + ... """ @@ -250,7 +249,7 @@ def one(self): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: L.one() 1 """ @@ -262,7 +261,7 @@ def zero(self): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: L.zero() 0 """ @@ -297,7 +296,7 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: L.series(lambda i: i, True, 5, (1,10)) 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... @@ -325,7 +324,7 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L = LLSRing(ZZ, 'z') sage: f = L.series([1,2,3,4], True, -5) sage: f z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 @@ -341,7 +340,7 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan elif constant not in self.base_ring(): raise ValueError("constant is not an element of the base ring") constant = (constant, approximate_valuation + len(coefficient_function)) - coefficient_function = LazyLaurentSeriesOperator_list(self, coefficient_function, approximate_valuation) + coefficient_function = LLSOperator_list(self, coefficient_function, approximate_valuation) elif constant is not None: try: c,m = constant From 6f135ecbcf1f6cfa5338e3d414410e4b78fdb1fc Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Tue, 13 Jul 2021 20:00:59 +0530 Subject: [PATCH 47/98] Re-created old design. --- src/sage/rings/all.py | 2 +- .../rings/lazy_laurent_series_operator.py | 987 ++++++++++++++++++ src/sage/rings/lazy_laurent_series_ring.py | 346 ++++++ 3 files changed, 1334 insertions(+), 1 deletion(-) create mode 100644 src/sage/rings/lazy_laurent_series_operator.py create mode 100644 src/sage/rings/lazy_laurent_series_ring.py diff --git a/src/sage/rings/all.py b/src/sage/rings/all.py index 0b939427c14..9acbcffd881 100644 --- a/src/sage/rings/all.py +++ b/src/sage/rings/all.py @@ -121,7 +121,7 @@ # Lazy Laurent series ring lazy_import('sage.rings.lazy_laurent_series_ring_new', 'LLSRing') -# lazy_import('sage.rings.lazy_laurent_series_ring', 'LazyLaurentSeriesRing') +lazy_import('sage.rings.lazy_laurent_series_ring', 'LazyLaurentSeriesRing') # Tate algebras from .tate_algebra import TateAlgebra diff --git a/src/sage/rings/lazy_laurent_series_operator.py b/src/sage/rings/lazy_laurent_series_operator.py new file mode 100644 index 00000000000..ed1948fbb60 --- /dev/null +++ b/src/sage/rings/lazy_laurent_series_operator.py @@ -0,0 +1,987 @@ +r""" +Lazy Laurent Series Operators + +This module implements operators internally used to construct lazy Laurent +series. The job of an operator attached to a series is to compute the `n`-th +coefficient of the series if `n` is not less than the valuation of the series +and the `n`-th coefficient is not declared to be a constant. + +If a new operator is added to this module, an example of how it is used should be +added below. + +EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - 2*z) + sage: g = 1/(1 + z^2) + +Constructors:: + + sage: L(1) + 1 + +:: + + sage: L.series([1,2,3,4], -10) + z^-10 + 2*z^-9 + 3*z^-8 + 4*z^-7 + +:: + + sage: L.gen() + z + +:: + + sage: P. = LaurentPolynomialRing(ZZ) + sage: p = (1 + 1/x)^3 + (1 + x)^4 + sage: L(p) + z^-3 + 3*z^-2 + 3*z^-1 + 2 + 4*z + 6*z^2 + 4*z^3 + z^4 + +Unary operators:: + + sage: -f + -1 - 2*z - 4*z^2 - 8*z^3 - 16*z^4 - 32*z^5 - 64*z^6 + ... + +:: + + sage: ~f + 1 - 2*z + ... + +Binary operators:: + + sage: f + g + 2 + 2*z + 3*z^2 + 8*z^3 + 17*z^4 + 32*z^5 + 63*z^6 + ... + +:: + + sage: f - g + 2*z + 5*z^2 + 8*z^3 + 15*z^4 + 32*z^5 + 65*z^6 + 128*z^7 + ... + +:: + + sage: f * g + 1 + 2*z + 3*z^2 + 6*z^3 + 13*z^4 + 26*z^5 + 51*z^6 + ... + +:: + + sage: f / g + 1 + 2*z + 5*z^2 + 10*z^3 + 20*z^4 + 40*z^5 + 80*z^6 + ... + +Transformers:: + + sage: 2*f + 2 + 4*z + 8*z^2 + 16*z^3 + 32*z^4 + 64*z^5 + 128*z^6 + ... + +:: + + sage: f.change_ring(GF(3)) + 1 + 2*z + z^2 + 2*z^3 + z^4 + 2*z^5 + z^6 + ... + +:: + + sage: f.apply_to_coefficients(lambda c: c^2) + 1 + 4*z + 16*z^2 + 64*z^3 + 256*z^4 + 1024*z^5 + 4096*z^6 + ... + +:: + + sage: f.truncate(5) + 1 + 2*z + 4*z^2 + 8*z^3 + 16*z^4 + +AUTHORS: + +- Kwankyu Lee (2019-02-24): initial version + +""" + +# **************************************************************************** +# Copyright (C) 2019 Kwankyu Lee +# +# 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 +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + + +class LazyLaurentSeriesOperator(object): + """ + Base class for operators computing coefficients of a lazy Laurent series. + + Subclasses of this class are used to implement arithmetic operations for + lazy Laurent series. These classes are not to be used directly by the user. + """ + def __ne__(self, other): + """ + Test inequality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: g = 1/(1 - z) + 1/(1 + z) + sage: f != g + False + """ + return not (self == other) + + +class LazyLaurentSeriesBinaryOperator(LazyLaurentSeriesOperator): + """ + Abstract base class for binary operators. + + INPUT: + + - ``left`` -- series on the left side of the binary operator + + - ``right`` -- series on the right side of the binary operator + + """ + def __init__(self, left, right): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: loads(dumps(f)) == f + True + sage: f = 1/(1 - z) - 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._left = left + self._right = right + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: {f: 1} + {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} + """ + return hash((type(self), self._left, self._right)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: g = 1/(1 - z) + 1/(1 + z) + sage: f == g + True + """ + return (isinstance(other, type(self)) and + self._left == other._left and self._right == other._right) + + +class LazyLaurentSeriesUnaryOperator(LazyLaurentSeriesOperator): + """ + Abstract base class for unary operators. + + INPUT: + + - ``series`` -- series upon which the operator operates + + """ + def __init__(self, series): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = -1/(1 - z) + sage: f + -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... + sage: loads(dumps(f)) == f + True + """ + self._series = series + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 - z) + sage: {f: 1} + {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} + """ + return hash((type(self), self._series)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: g = 1/(1 - z) + 1/(1 + z) + sage: f == g + True + sage: f = ~(1 - z) + sage: g = ~(1 - z) + sage: f == g + True + """ + return isinstance(other, type(self)) and self._series == other._series + + +class LazyLaurentSeriesOperator_add(LazyLaurentSeriesBinaryOperator): + """ + Operator for addition. + """ + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = z + z^2 + z + sage: f.coefficient(1) + 2 + """ + return self._left.coefficient(n) + self._right.coefficient(n) + +class LazyLaurentSeriesOperator_sub(LazyLaurentSeriesBinaryOperator): + """ + Operator for subtraction. + """ + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1 + 3*z - z + sage: f.coefficient(1) + 2 + """ + return self._left.coefficient(n) - self._right.coefficient(n) + +class LazyLaurentSeriesOperator_mul(LazyLaurentSeriesBinaryOperator): + """ + Operator for multiplication. + """ + def __init__(self, left, right): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) * 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + LazyLaurentSeriesBinaryOperator.__init__(self, left, right) + self._zero = left.base_ring().zero() + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = (1 + z)*(1 - z) + sage: f.coefficient(2) + -1 + """ + c = self._zero + for k in range(self._left._approximate_valuation, n - self._right._approximate_valuation + 1): + c += self._left.coefficient(k) * self._right.coefficient(n-k) + return c + +class LazyLaurentSeriesOperator_neg(LazyLaurentSeriesUnaryOperator): + """ + Operator for negation. + + INPUT: + + - ``series`` -- a lazy Laurent series + + """ + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = -(1 + z) + sage: f.coefficient(1) + -1 + """ + return -self._series.coefficient(n) + +class LazyLaurentSeriesOperator_inv(LazyLaurentSeriesUnaryOperator): + """ + Operator for inversion. + + INPUT: + + - ``series`` -- a lazy Laurent series + + """ + def __init__(self, series): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 - z) + sage: f + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: loads(dumps(f)) == f + True + """ + LazyLaurentSeriesUnaryOperator.__init__(self, series) + + self._v = series.valuation() + self._ainv = ~series.coefficient(self._v) + self._zero = series.base_ring().zero() + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 - z) + sage: f.coefficient(2) + 1 + """ + v = self._v + if n == -v: + return self._ainv + c = self._zero + for k in range(-v, n): + c += s.coefficient(k) * self._series.coefficient(n + v - k) + return -c * self._ainv + +class LazyLaurentSeriesOperator_div(LazyLaurentSeriesBinaryOperator): + """ + Operator for division. + """ + def __init__(self, left, right): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = (1 - z)/(1 + z) + sage: loads(dumps(f)) == f + True + """ + LazyLaurentSeriesBinaryOperator.__init__(self, left, right) + + lv = left.valuation() + rv = right.valuation() + + self._lv = lv + self._rv = rv + self._ainv = ~right.coefficient(rv) + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = (1 + z)/(1 - z) + sage: f.coefficient(2) + 2 + """ + lv = self._lv + rv = self._rv + + if n == lv - rv: + return self._left.coefficient(lv)/self._right.coefficient(rv) + c = self._left.coefficient(n + rv) + for k in range(lv - rv, n): + c -= s.coefficient(k) * self._right.coefficient(n + rv - k) + return c * self._ainv + +class LazyLaurentSeriesOperator_scale(LazyLaurentSeriesOperator): + """ + Operator for scalar multiplication of ``series`` with ``scalar``. + + INPUT: + + - ``series`` -- a lazy Laurent series + + - ``scalar`` -- an element of the base ring + + """ + def __init__(self, series, scalar): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: g = 2*z + sage: loads(dumps(g)) == g + True + """ + self._series = series + self._scalar = scalar + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 2*(z + z^2) + sage: f.coefficient(2) + 2 + sage: f + 2*z + 2*z^2 + """ + return self._scalar * self._series.coefficient(n) + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 2*(z + z^2) + sage: {f: 1} + {2*z + 2*z^2: 1} + """ + return hash((type(self), self._series, self._scalar)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 2*z + sage: g = 2*z + sage: f == g + True + """ + return (isinstance(other, LazyLaurentSeriesOperator_scale) + and self._series == other._series and self._scalar == other._scalar) + +class LazyLaurentSeriesOperator_change_ring(LazyLaurentSeriesOperator): + """ + Operator for changing the base ring of the ``series`` to ``ring``. + + INPUT: + + - ``series`` -- a lazy Laurent series + + - ``ring`` -- a ring + + """ + def __init__(self, series, ring): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 - 2*z) + sage: g = f.change_ring(GF(3)) + sage: g + 1 + 2*z + z^2 + 2*z^3 + z^4 + 2*z^5 + z^6 + ... + sage: loads(dumps(g)) == g + True + """ + self._series = series + self._ring = ring + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 - 2*z) + sage: f + 1 + 2*z + 4*z^2 + 8*z^3 + 16*z^4 + 32*z^5 + 64*z^6 + ... + sage: g = f.change_ring(GF(2)) + sage: g.coefficient(3) + 0 + sage: g + 1 + ... + """ + return self._ring(self._series.coefficient(n)) + + def __hash__(self): + """ + Return the hash of ``self``. + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 - 2*z) + sage: g = f.change_ring(GF(2)) + sage: {g: 1} + {1 + ...: 1} + """ + return hash((type(self), self._series, self._ring)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + sage: g = 1/(1 - z) + sage: f == g + True + """ + return (isinstance(other, LazyLaurentSeriesOperator_change_ring) + and self._series == other._series and self._ring == other._ring) + +class LazyLaurentSeriesOperator_apply(LazyLaurentSeriesOperator): + """ + Operator for applying a function. + + INPUT: + + - ``series`` -- a lazy Laurent series + + - ``function`` -- a Python function to apply to each coefficient of the series + + """ + def __init__(self, series, function): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 + z) + sage: g = f.apply_to_coefficients(abs) + sage: g + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: loads(dumps(g)) + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + """ + self._series = series + self._function = function + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 + z) + sage: f + 1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ... + sage: f.apply_to_coefficients(lambda c: c if c >= 0 else -c) + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + """ + return self._function(self._series.coefficient(n)) + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 + z).apply_to_coefficients(lambda c: c if c >= 0 else -c) + sage: {f: 1} + {1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ...: 1} + """ + return hash((type(self), self._series, self._function)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 + z) + sage: g = f.apply_to_coefficients(abs) + sage: h = f.apply_to_coefficients(abs) + sage: g == h + True + """ + return (isinstance(other, LazyLaurentSeriesOperator_apply) + and self._series == other._series and self._function is other._function) + + +class LazyLaurentSeriesOperator_truncate(LazyLaurentSeriesOperator): + """ + Operator for truncation. + + INPUT: + + - ``series`` -- a lazy Laurent series + + - ``d`` -- an integer; the series is truncated the terms of degree `> d` + + """ + def __init__(self, series, d): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 + z) + sage: g = f.truncate(4) + sage: loads(dumps(g)) == g + True + """ + self._series = series + self._d = d + + self._zero = series.base_ring().zero() + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 + z) + sage: f + 1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ... + sage: f.truncate(4) + 1 - z + z^2 - z^3 + """ + if n <= self._d: + return self._series.coefficient(n) + else: + return self._zero + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 + z) + sage: {f: 1} + {1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ...: 1} + """ + return hash((type(self), self._series, self._d)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 + z) + sage: g = f.truncate(4) + sage: h = f.truncate(4) + sage: g == h + True + """ + return (isinstance(other, LazyLaurentSeriesOperator_truncate) + and self._series == other._series and self._d == other._d) + +class LazyLaurentSeriesOperator_gen(LazyLaurentSeriesOperator): + """ + Operator for the generator element. + + INPUT: + + - ``ring`` -- a lazy Laurent series ring + + """ + def __init__(self, ring): + """ + Initialize. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: z = L.gen() + sage: loads(dumps(z)) == z + True + """ + self._ring = ring + + self._one = ring.base_ring().one() + self._zero = ring.base_ring().zero() + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L.gen() + z + """ + return self._one if n == 1 else self._zero + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: {z: 1} + {z: 1} + """ + return hash((type(self), self._ring)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: z1 = L.gen() + sage: z2 = L.gen() + sage: z1 == z2 + True + """ + return (isinstance(other, LazyLaurentSeriesOperator_gen) and self._ring == other._ring) + +class LazyLaurentSeriesOperator_constant(LazyLaurentSeriesOperator): + """ + Operator for the generator element. + + INPUT: + + - ``ring`` -- a lazy Laurent series ring + + - ``constant`` -- a constant of the base ring of ``ring`` + + """ + def __init__(self, ring, constant): + """ + Initialize. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f = L(10) + sage: loads(dumps(f)) == f + True + """ + self._ring = ring + self._constant = constant + + self._zero = ring.base_ring().zero() + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f = L(10); f + 10 + sage: f.coefficient(0) + 10 + sage: f.coefficient(1) + 0 + """ + return self._constant if n == 0 else self._zero + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f = L(10) + sage: {f: 1} + {10: 1} + """ + return hash((type(self), self._ring, self._constant)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: z1 = L(10) + sage: z2 = L(10) + sage: z1 == z2 + True + """ + return (isinstance(other, LazyLaurentSeriesOperator_constant) + and self._ring == other._ring and self._constant == other._constant) + +class LazyLaurentSeriesOperator_list(LazyLaurentSeriesOperator): + """ + Operator for the series defined by a list. + + INPUT: + + - ``l`` -- list + + - ``v`` -- integer + + """ + def __init__(self, ring, l, v): + """ + Initialize. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f = L.series([1,2,3,4], -5) + sage: loads(dumps(f)) == f + True + """ + self._ring = ring + self._list = tuple([ring.base_ring()(e) for e in l]) + self._valuation = v + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f = L.series([1,2,3,4], -5) + sage: f + z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 + """ + return self._ring.base_ring()(self._list[n - self._valuation]) + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f = L.series([1,2,3,4], -5) + sage: {f: 1} + {z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2: 1} + """ + return hash((type(self), self._ring, self._list, self._valuation)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f1 = L.series([1,2,3,4], -5) + sage: f2 = L.series([1,2,3,4,0], -5) + sage: f1 == f2 + True + """ + return (isinstance(other, LazyLaurentSeriesOperator_list) and + self._ring == other._ring and self._list == other._list and + self._valuation == other._valuation) + +class LazyLaurentSeriesOperator_polynomial(LazyLaurentSeriesOperator): + """ + Operator for the series coerced from a polynomial or a Laurent polynomial. + + INPUT: + + - ``ring`` -- a lazy Laurent series ring + + - ``poly`` -- a polynomial or a Laurent polynomial + + """ + def __init__(self, ring, poly): + """ + Initialize. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: P. = ZZ[] + sage: p = 1 + 2*x + x^10 + sage: f = L(p) + sage: loads(dumps(f)) == f + True + """ + self._ring = ring + self._poly = poly + + def __call__(self, s, n): + """ + Return the `n`-th coefficient of the series ``s``. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: P. = ZZ[] + sage: p = (1 + 2*x + 3*x)^3 + sage: f = L(p) + sage: f + 1 + 15*z + 75*z^2 + 125*z^3 + """ + return self._poly[n] + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: P. = ZZ[] + sage: p = (1 + 2*x)^3 + sage: f = L(p) + sage: {f: 1} + {1 + 6*z + 12*z^2 + 8*z^3: 1} + """ + return hash((type(self), self._ring, self._poly)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: P. = ZZ[] + sage: p = (1 + 2*x)^3 + sage: f = L(p) + sage: g = L(p) + sage: f == g + True + """ + return (isinstance(other, LazyLaurentSeriesOperator_polynomial) and + self._ring == other._ring and self._poly == other._poly) diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py new file mode 100644 index 00000000000..9886307a440 --- /dev/null +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -0,0 +1,346 @@ +r""" +Lazy Laurent Series Rings + +The ring of lazy Laurent series over a ring has usual arithmetic operations, +but it is actually not a ring in the usual sense since every +arithmetic operation gives a new series. + +EXAMPLES: + +The definition of Laurent series rings is not initially imported into the +global namespace. You need to import it explicitly to use it:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: L.category() + Category of magmas and additive magmas + sage: 1/(1 - z) + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: 1/(1 - z) == 1/(1 - z) + True + +Lazy Laurent series ring over a finite field:: + + sage: L. = LazyLaurentSeriesRing(GF(3)); L + Lazy Laurent Series Ring in z over Finite Field of size 3 + sage: e = 1/(1 + z) + sage: e.coefficient(100) + 1 + sage: e.coefficient(100).parent() + Finite Field of size 3 + +Generating functions of integer sequences are Laurent series over the integer +ring:: + + sage: L. = LazyLaurentSeriesRing(ZZ); L + Lazy Laurent Series Ring in z over Integer Ring + sage: 1/(1 - 2*z)^3 + 1 + 6*z + 24*z^2 + 80*z^3 + 240*z^4 + 672*z^5 + 1792*z^6 + ... + +Power series can be defined recursively:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L.series(lambda s,n: (1 + z*s^2)[n], valuation=0) + 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + +AUTHORS: + +- Kwankyu Lee (2019-02-24): initial version + +""" + +# **************************************************************************** +# Copyright (C) 2019 Kwankyu Lee +# +# 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 +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** +import random + +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent + +from sage.categories.magmas_and_additive_magmas import MagmasAndAdditiveMagmas +from sage.categories.morphism import SetMorphism +from sage.categories.homset import Hom +from sage.categories.sets_cat import Sets + +from sage.misc.cachefunc import cached_method + +from sage.rings.polynomial.polynomial_ring import PolynomialRing_general +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing_generic + +from .lazy_laurent_series import LazyLaurentSeries +from .lazy_laurent_series_operator import ( + LazyLaurentSeriesOperator_gen, + LazyLaurentSeriesOperator_constant, + LazyLaurentSeriesOperator_list, + LazyLaurentSeriesOperator_polynomial +) + + +class LazyLaurentSeriesRing(UniqueRepresentation, Parent): + """ + Lazy Laurent series ring. + + INPUT: + + - ``base_ring`` -- base ring of this Laurent series ring + + - ``names`` -- name of the generator of this Laurent series ring + + EXAMPLES:: + + sage: LazyLaurentSeriesRing(ZZ, 't') + Lazy Laurent Series Ring in t over Integer Ring + """ + Element = LazyLaurentSeries + + def __init__(self, base_ring, names, category=None, implementation='sparse'): + """ + Initialize. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 't') + sage: TestSuite(L).run(skip='_test_elements') + """ + Parent.__init__(self, base=base_ring, names=names, + category=MagmasAndAdditiveMagmas().or_subcategory(category)) + self._implementation = implementation + + def _repr_(self): + """ + String representation of this Laurent series ring. + + EXAMPLES:: + + sage: LazyLaurentSeriesRing(GF(2), 'z') + Lazy Laurent Series Ring in z over Finite Field of size 2 + """ + return "Lazy Laurent Series Ring in {} over {}".format(self.variable_name(), self.base_ring()) + + @cached_method + def gen(self, n=0): + """ + Return the generator of this Laurent series ring. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L.gen() + z + sage: L.gen(3) + Traceback (most recent call last): + ... + IndexError: there is only one generator + """ + if n != 0: + raise IndexError("there is only one generator") + + op = LazyLaurentSeriesOperator_gen(self) + c = (self.base_ring().zero(), 2) + return self.element_class(self, coefficient=op, valuation=1, constant=c) + + def ngens(self): + """ + Return the number of generators of this Laurent series ring. + + This is always 1. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L.ngens() + 1 + """ + return 1 + + def gens(self): + """ + Return the tuple of the generator. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: 1/(1 - z) + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + """ + return (self.gen(),) + + def _coerce_map_from_(self, S): + """ + Return ``True`` if a coercion from ``S`` exists. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(GF(2), 'z') + sage: L.has_coerce_map_from(ZZ) + True + sage: L.has_coerce_map_from(GF(2)) + True + """ + if self.base_ring().has_coerce_map_from(S): + return True + + if isinstance(S, (PolynomialRing_general, LaurentPolynomialRing_generic)) and S.ngens() == 1: + def make_series_from(poly): + op = LazyLaurentSeriesOperator_polynomial(self, poly) + a = poly.valuation() + c = (self.base_ring().zero(), poly.degree() + 1) + return self.element_class(self, coefficient=op, valuation=a, constant=c) + + return SetMorphism(Hom(S, self, Sets()), make_series_from) + + return False + + def _element_constructor_(self, x): + """ + Construct a Laurent series from ``x``. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(GF(2), 'z') + sage: L(2) + 0 + sage: L(3) + 1 + """ + R = self.base_ring() + + op = LazyLaurentSeriesOperator_constant(self, R(x)) + + return self.element_class(self, coefficient=op, valuation=0, constant=(R.zero(), 1)) + + def _an_element_(self): + """ + Return a Laurent series in this ring. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L.an_element() # random + z^-10 + z^-9 + z^-8 + z^-7 + z^-6 + z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + ... + """ + N = 10 + + e = self.base_ring().an_element() + + def r(s, i): + return self.base_ring().an_element() + + n = random.randint(-N,N) + m = random.randint(0,N) + + return self.element_class(self, coefficient=r, valuation=n, constant=(e,n+m)) + + def one(self): + """ + Return the constant series `1`. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L.one() + 1 + """ + return self._element_constructor_(1) + + def zero(self): + """ + Return the zero series. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L.zero() + 0 + """ + return self._element_constructor_(0) + + def series(self, coefficient, valuation, constant=None): + r""" + Return a lazy Laurent series. + + INPUT: + + - ``coefficient`` -- Python function that computes coefficients + + - ``valuation`` -- integer; approximate valuation of the series + + - ``constant`` -- either ``None`` or pair of an element of the base ring and an integer + + Let the coefficient of index `i` mean the coefficient of the term of the + series with exponent `i`. + + Python function ``coefficient`` returns the value of the coefficient of + index `i` from input `s` and `i` where `s` is the series itself. + + Let ``valuation`` be `n`. All coefficients of index below `n` are zero. + If ``constant`` is ``None``, then the ``coefficient`` function is responsible to + compute the values of all coefficients of index `\ge n`. If + ``constant`` is a pair `(c,m)`, then the ``coefficient`` function is responsible + to compute the values of all coefficients of index `\ge n` and `< m` + and all the coefficients of index `\ge m` is the constant `c`. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: L.series(lambda s, i: i, 5, (1,10)) + 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... + + sage: def g(s, i): + ....: if i < 0: + ....: return 1 + ....: else: + ....: return s.coefficient(i - 1) + i + sage: e = L.series(g, -5); e + z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... + sage: f = e^-1; f + z^5 - z^6 - z^11 + ... + sage: f.coefficient(10) + 0 + sage: f.coefficient(20) + 9 + sage: f.coefficient(30) + -219 + + Alternatively, the ``coefficient`` can be a list of elements of the + base ring. Then these elements are read as coefficients of the terms of + degrees starting from the ``valuation``. In this case, ``constant`` + may be just an element of the base ring instead of a tuple or can be + simply omitted if it is zero. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f = L.series([1,2,3,4], -5) + sage: f + z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 + sage: g = L.series([1,3,5,7,9], 5, -1) + sage: g + z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... + """ + if isinstance(coefficient, (tuple, list)): + if isinstance(constant, tuple): + constant = constant[0] + if constant is None: + constant = self.base_ring().zero() + elif constant not in self.base_ring(): + raise ValueError("constant is not an element of the base ring") + constant = (constant, valuation + len(coefficient)) + coefficient = LazyLaurentSeriesOperator_list(self, coefficient, valuation) + elif constant is not None: + try: + c,m = constant + except TypeError: + raise TypeError('not a tuple') + + if valuation > m and c: # weird case + raise ValueError('inappropriate valuation') + + constant = (self.base_ring()(c), m) + + return self.element_class(self, coefficient=coefficient, valuation=valuation, constant=constant) \ No newline at end of file From deb4e6c7abcd7f4ca79e32e4e89c68a92d872dfb Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 14 Jul 2021 01:41:58 +0530 Subject: [PATCH 48/98] Fixed the issue in the binary comparsion and added the binary commutative class. --- src/sage/rings/lazy_laurent_series_new.py | 40 +++++++++++++++++++---- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 6621d53e4d4..c929a1662ed 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -805,6 +805,7 @@ def _richcmp_(self, other, op): if op is op_EQ: if self._aux._constant is None: if other._aux._constant is None: + # Implement the checking of the caches here. n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) m = max(self._aux._approximate_valuation, other._aux._approximate_valuation) for i in range(n, m): @@ -814,6 +815,7 @@ def _richcmp_(self, other, op): if self._aux._coefficient_function == other._aux._coefficient_function: return True except AttributeError: + # Checking of the parse tree. return self._aux.__eq__(other._aux) raise ValueError("undecidable as lazy Laurent series") else: @@ -970,9 +972,18 @@ def valuation(self): self._approximate_valuation = n return n n += 1 + + # def __eq__(self, other): + # Implement for sparse and dense variations separately. + # n = min(self._approximate_valuation, other._approximate_valuation) + # m = max(self._approximate_valuation, other._approximate_valuation) + # for i in range(n, m): + # if self[i] != other[i]: + # return False + -class LLS_unary: +class LLS_unary(): """ Abstract base class for unary operators. @@ -1025,11 +1036,10 @@ def __eq__(self, other): sage: f == g True """ - return (isinstance(other, type(self)) and self._cache == other._cache - and self._approximate_valuation == other._approximate_valuation) + return (isinstance(other, type(self)) and self._cache == other._cache) -class LLS_binary: +class LLS_binary(): def __init__(self, left, right): """ @@ -1073,8 +1083,26 @@ def __eq__(self, other): sage: f == g True """ - return (isinstance(other, type(self)) and - self._left == other._left and self._right == other._right) + if not isinstance(other, type(self)): + return False + if self._left == other._left and self._right == other._right: + return True + raise ValueError("undecidable as lazy Laurent series") + + +class LLS_binary_commutative(): + + def __hash__(self): + return hash((type(self), frozenset([self._left, self._right]))) + + def __eq__(self, other): + if not isinstance(other, type(self)): + return False + if self._left == other._left and self._right == other._right: + return True + if self._left == other._right and self._right == other._left: + return True + raise ValueError("undecidable as lazy Laurent series") class LLS_coefficient_function(LLS_aux, LLS_unary): From 14a2447688f0115817408dcb9575e65b3a710f55 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 14 Jul 2021 12:32:13 +0530 Subject: [PATCH 49/98] New design. --- src/sage/rings/lazy_laurent_series_new.py | 117 +++++++--------------- 1 file changed, 35 insertions(+), 82 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index c929a1662ed..979df6e5404 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -740,7 +740,6 @@ def _repr_(self): """ atomic_repr = self.base_ring()._repr_option('element_is_atomic') X = self.parent().variable_name() - n = self._aux._approximate_valuation if self._aux._constant is None: @@ -811,12 +810,8 @@ def _richcmp_(self, other, op): for i in range(n, m): if self[i] != other[i]: return False - try: - if self._aux._coefficient_function == other._aux._coefficient_function: - return True - except AttributeError: - # Checking of the parse tree. - return self._aux.__eq__(other._aux) + if self._aux == other._aux: + return True raise ValueError("undecidable as lazy Laurent series") else: raise ValueError("undecidable as lazy Laurent series") @@ -899,7 +894,7 @@ def __init__(self, is_sparse, approximate_valuation, constant=None): self._approximate_valuation = approximate_valuation self._constant = constant self._is_sparse = is_sparse - + if self._is_sparse: self._cache = dict() # cache of known coefficients else: @@ -983,7 +978,7 @@ def valuation(self): -class LLS_unary(): +class LLS_unary(LLS_aux): """ Abstract base class for unary operators. @@ -992,7 +987,7 @@ class LLS_unary(): - ``series`` -- series upon which the operator operates """ - def __init__(self, series): + def __init__(self, series, *args, **kwargs): """ Initialize. @@ -1006,6 +1001,7 @@ def __init__(self, series): True """ self._series = series + super().__init__(*args, **kwargs) def __hash__(self): """ @@ -1036,12 +1032,12 @@ def __eq__(self, other): sage: f == g True """ - return (isinstance(other, type(self)) and self._cache == other._cache) + return isinstance(other, type(self)) and self._series == other._series -class LLS_binary(): +class LLS_binary(LLS_aux): - def __init__(self, left, right): + def __init__(self, left, right, *args, **kwargs): """ Initialize. @@ -1057,6 +1053,7 @@ def __init__(self, left, right): """ self._left = left self._right = right + super().__init__(*args, **kwargs) def __hash__(self): """ @@ -1085,13 +1082,11 @@ def __eq__(self, other): """ if not isinstance(other, type(self)): return False - if self._left == other._left and self._right == other._right: - return True - raise ValueError("undecidable as lazy Laurent series") + return self._left == other._left and self._right == other._right -class LLS_binary_commutative(): - +class LLS_binary_commutative(LLS_binary): + def __hash__(self): return hash((type(self), frozenset([self._left, self._right]))) @@ -1102,10 +1097,9 @@ def __eq__(self, other): return True if self._left == other._right and self._right == other._left: return True - raise ValueError("undecidable as lazy Laurent series") - + return False -class LLS_coefficient_function(LLS_aux, LLS_unary): +class LLS_coefficient_function(LLS_aux): def __init__(self, coefficient_function, is_sparse, approximate_valuation, constant=None): self._coefficient_function = coefficient_function super().__init__(is_sparse, approximate_valuation, constant) @@ -1119,17 +1113,14 @@ def iterate_coefficients(self): yield self._coefficient_function(n) n += 1 - def __eq__(self, other): - return super().__eq__(other) - -class LLS_mul(LLS_aux, LLS_binary): +class LLS_mul(LLS_binary): """ Operator for multiplication. + + We are assuming commutativity of the coefficient ring here. """ def __init__(self, left, right): - self._left = left - self._right = right a = left._approximate_valuation + right._approximate_valuation c = None if left._constant is not None and right._constant is not None: @@ -1138,7 +1129,7 @@ def __init__(self, left, right): if left._is_sparse != right._is_sparse: raise NotImplementedError - super().__init__(left._is_sparse, a, c) + super().__init__(left, right, left._is_sparse, a, c) def get_coefficient(self, n): c = ZZ.zero() @@ -1160,18 +1151,13 @@ def iterate_coefficients(self): c += l * self._right[n-k] yield c n += 1 - - def __eq__(self, other): - return super().__eq__(other) -class LLS_add(LLS_aux, LLS_binary): +class LLS_add(LLS_binary): """ Operator for addition. """ def __init__(self, left, right): - self._left = left - self._right = right a = min(left._approximate_valuation, right._approximate_valuation) if left._constant is not None and right._constant is not None: @@ -1183,7 +1169,7 @@ def __init__(self, left, right): if left._is_sparse != right._is_sparse: raise NotImplementedError - super().__init__(left._is_sparse, a, c) + super().__init__(left, right, left._is_sparse, a, c) def get_coefficient(self, n): c = ZZ.zero() @@ -1197,18 +1183,13 @@ def iterate_coefficients(self): c = self._left[n] + self._right[n] yield c n += 1 - - def __eq__(self, other): - return super().__eq__(other) -class LLS_sub(LLS_aux, LLS_binary): +class LLS_sub(LLS_binary): """ Operator for subtraction. """ def __init__(self, left, right): - self._left = left - self._right = right a = min(left._approximate_valuation, right._approximate_valuation) if left._constant is not None and right._constant is not None: @@ -1220,7 +1201,7 @@ def __init__(self, left, right): if left._is_sparse != right._is_sparse: raise NotImplementedError - super().__init__(left._is_sparse, a, c) + super().__init__(left, right, left._is_sparse, a, c) def get_coefficient(self, n): """ @@ -1244,17 +1225,13 @@ def iterate_coefficients(self): c = self._left[n] - self._right[n] yield c n += 1 - - def __eq__(self, other): - return super().__eq__(other) -class LLS_rmul(LLS_aux, LLS_unary): +class LLS_rmul(LLS_unary): """ Operator for multiplying with a scalar. """ def __init__(self, series, scalar): - self._series = series self._scalar = scalar a = series._approximate_valuation @@ -1263,7 +1240,7 @@ def __init__(self, series, scalar): else: c = None - super().__init__(series._is_sparse, a, c) + super().__init__(series, series._is_sparse, a, c) def get_coefficient(self, n): c = self._series[n] * self._scalar @@ -1275,17 +1252,13 @@ def iterate_coefficients(self): c = self._series[n] * self._scalar yield c n += 1 - - def __eq__(self, other): - return super().__eq__(other) -class LLS_neg(LLS_aux, LLS_unary): +class LLS_neg(LLS_unary): """ Operator for negative of the series. """ def __init__(self, series): - self._series = series a = series._approximate_valuation if series._constant is not None: @@ -1293,7 +1266,7 @@ def __init__(self, series): else: c = None - super().__init__(series._is_sparse, a, c) + super().__init__(series, series._is_sparse, a, c) def get_coefficient(self, n): c = -1 * self._series[n] @@ -1305,20 +1278,16 @@ def iterate_coefficients(self): c = -1 * self._series[n] yield c n += 1 - - def __eq__(self, other): - return super().__eq__(other) -class LLS_inv(LLS_aux, LLS_unary): +class LLS_inv(LLS_unary): """ Operator for multiplicative inverse of the series. """ def __init__(self, series): - self._series = series v = series.valuation() # self._constant can be refined - super().__init__(series._is_sparse, -v, None) + super().__init__(series, series._is_sparse, -v, None) if v is infinity: raise ZeroDivisionError('cannot invert zero') @@ -1349,17 +1318,13 @@ def iterate_coefficients(self): c += self[k] * self._series[n - v - k] yield -c * self._ainv n += 1 - - def __eq__(self, other): - return super().__eq__(other) -class LLS_apply_coeff(LLS_aux, LLS_unary): +class LLS_apply_coeff(LLS_unary): """ Return the series with ``function`` applied to each coefficient of this series. """ def __init__(self, series, function, ring): - self._series = series self._function = function self._ring = ring a = series._approximate_valuation @@ -1369,7 +1334,7 @@ def __init__(self, series, function, ring): else: c = None - super().__init__(series._is_sparse, a, c) + super().__init__(series, series._is_sparse, a, c) def get_coefficient(self, n): try: @@ -1388,22 +1353,18 @@ def iterate_coefficients(self): n += 1 except TypeError: raise ValueError("The coefficients are not in the base ring.") - - def __eq__(self, other): - return super().__eq__(other) -class LLS_trunc(LLS_aux, LLS_unary): +class LLS_trunc(LLS_unary): """ Return this series with its terms of degree >= ``d`` truncated. """ def __init__(self, series, d): - self._series = series self._d = d self._zero = ZZ.zero() a = series._approximate_valuation c = (ZZ.zero(), d) - super().__init__(series._is_sparse, a, c) + super().__init__(series, series._is_sparse, a, c) def get_coefficient(self, n): if n <= self._d: @@ -1422,20 +1383,15 @@ def iterate_coefficients(self): c = self._zero yield c n += 1 - - def __eq__(self, other): - return super().__eq__(other) -class LLS_div(LLS_aux, LLS_binary): +class LLS_div(LLS_binary): """ Return ``self`` divided by ``other``. """ def __init__(self, left, right): - self._left = left - self._right = right - super().__init__(left._is_sparse, left._approximate_valuation, None) + super().__init__(left, right, left._is_sparse, left._approximate_valuation, None) self._approximate_valuation = left.valuation() - right.valuation() @@ -1470,6 +1426,3 @@ def iterate_coefficients(self): c -= self[k] * self._right[n + rv - k] yield c * self._ainv n += 1 - - def __eq__(self, other): - return super().__eq__(other) \ No newline at end of file From dbeedf4345dee2ddd53890e6ebb858500dae769e Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 14 Jul 2021 15:52:11 +0530 Subject: [PATCH 50/98] Not working. --- src/sage/rings/lazy_laurent_series_new.py | 176 +++++++++++------- .../rings/lazy_laurent_series_ring_new.py | 87 ++++----- 2 files changed, 145 insertions(+), 118 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 979df6e5404..c78cfdb10cd 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -79,7 +79,7 @@ # **************************************************************************** -from .infinity import infinity +from .infinity import Infinity, infinity from sage.structure.element import ModuleElement from .integer_ring import ZZ from sage.structure.richcmp import op_EQ, op_NE @@ -246,6 +246,7 @@ def _add_(self, other): 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... """ P = self.parent() + # Check whether self + other is eventaully geometric. return P.element_class(P, LLS_add(self._aux, other._aux)) def _div_(self, other): @@ -275,6 +276,7 @@ def _div_(self, other): z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... """ P = self.parent() + # Check whether self / other is eventaully geometric. return P.element_class(P, LLS_div(self._aux, other._aux)) def _rmul_(self, scalar): @@ -681,9 +683,12 @@ def polynomial(self, degree=None, name=None): 25*z^5 + 16*z^4 + 9*z^3 + 4*z^2 + z """ if degree is None: - if self._aux._constant is None or not self._aux._constant[0].is_zero(): + if isinstance(self._aux, LLS_constant): + m = 1 + elif isinstance(self._aux, LLS_eventually_geometric) and self._aux._constant[0]: + m = self._aux._constant[1] + else: raise ValueError("not a polynomial") - m = self._aux._constant[1] else: m = degree + 1 @@ -742,9 +747,9 @@ def _repr_(self): X = self.parent().variable_name() n = self._aux._approximate_valuation - if self._aux._constant is None: + if not isinstance(self._aux, LLS_eventually_geometric): m = n + 7 # long enough - elif self._aux._constant[0] != 0: + elif self._aux._constant[0]: m = self._aux._constant[1] + 3 else: m = self._aux._constant[1] @@ -772,8 +777,7 @@ def _repr_(self): if not s: # zero series s = '0' - - if self._aux._constant is None or self._aux._constant[1] > m or self._aux._constant[0] != 0: + if not isinstance(self._aux, (LLS_constant, LLS_eventually_geometric)) or self._aux._constant[1] > m or self._aux._constant[0]: s += ' + {}'.format('...') return s @@ -802,8 +806,8 @@ def _richcmp_(self, other, op): False """ if op is op_EQ: - if self._aux._constant is None: - if other._aux._constant is None: + if not isinstance(self._aux, LLS_eventually_geometric): + if not isinstance(other._aux, LLS_eventually_geometric): # Implement the checking of the caches here. n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) m = max(self._aux._approximate_valuation, other._aux._approximate_valuation) @@ -815,7 +819,7 @@ def _richcmp_(self, other, op): raise ValueError("undecidable as lazy Laurent series") else: raise ValueError("undecidable as lazy Laurent series") - elif other._aux._constant is None: + elif not isinstance(other._aux, LLS_eventually_geometric): raise ValueError("undecidable as lazy Laurent series") sc, sm = self._aux._constant @@ -890,9 +894,8 @@ def __bool__(self): class LLS_aux(): - def __init__(self, is_sparse, approximate_valuation, constant=None): + def __init__(self, is_sparse, approximate_valuation): self._approximate_valuation = approximate_valuation - self._constant = constant self._is_sparse = is_sparse if self._is_sparse: @@ -907,8 +910,6 @@ def __getitem__(self, n): return ZZ.zero() elif n < self._approximate_valuation: return ZZ.zero() - elif self._constant is not None and n >= self._constant[1]: - return self._constant[0] if self._is_sparse: try: @@ -929,16 +930,6 @@ def __getitem__(self, n): return c def valuation(self): - if self._constant is not None: - n = self._approximate_valuation - m = self._constant[1] - while n <= m: - if self[n] != 0: - self._approximate_valuation = n - return n - n += 1 - return infinity - if self._is_sparse: n = self._approximate_valuation cache = self._cache @@ -967,15 +958,6 @@ def valuation(self): self._approximate_valuation = n return n n += 1 - - # def __eq__(self, other): - # Implement for sparse and dense variations separately. - # n = min(self._approximate_valuation, other._approximate_valuation) - # m = max(self._approximate_valuation, other._approximate_valuation) - # for i in range(n, m): - # if self[i] != other[i]: - # return False - class LLS_unary(LLS_aux): @@ -1085,6 +1067,8 @@ def __eq__(self, other): return self._left == other._left and self._right == other._right + + class LLS_binary_commutative(LLS_binary): def __hash__(self): @@ -1099,10 +1083,59 @@ def __eq__(self, other): return True return False + +class LLS_eventually_geometric(LLS_aux): + + def __init__(self, laurent_polynomial, constant=None): + if constant is None: + self._constant = (ZZ.zero(), laurent_polynomial.degree() + 1) + else: + self._constant = constant + assert(not laurent_polynomial or laurent_polynomial.degree() < constant[1]) + self._laurent_polynomial = laurent_polynomial + v = self._laurent_polynomial.valuation() + if v is Infinity: + if self._constant[0]: # Non-zero series + v = self._constant[1] + self._approximate_valuation = v + self._is_sparse = False #TODO Remove this + + def __getitem__(self, n): + if n >= self._constant[1]: + return self._constant[0] + return self._laurent_polynomial[n] + + def valuation(self): + return self._approximate_valuation + + def __eq__(self, other): + if not isinstance(other, type(self)): + return False + return self._laurent_polynomial == other._laurent_polynomial and self._constant == other._constant + + +class LLS_constant(LLS_aux): + + def __init__(self, a): + self._a = a + if self._a: + self._approximate_valuation = ZZ.zero() + self._approximate_valuation = Infinity + self._constant = (ZZ.zero(), 1) + self._is_sparse = False # TODO Remove + + def __getitem__(self, n): + if n: + return ZZ.zero() + return self._a + + def valuation(self): + return self._approximate_valuation + class LLS_coefficient_function(LLS_aux): - def __init__(self, coefficient_function, is_sparse, approximate_valuation, constant=None): + def __init__(self, coefficient_function, is_sparse, approximate_valuation): self._coefficient_function = coefficient_function - super().__init__(is_sparse, approximate_valuation, constant) + super().__init__(is_sparse, approximate_valuation) def get_coefficient(self, n): return self._coefficient_function(n) @@ -1122,14 +1155,14 @@ class LLS_mul(LLS_binary): """ def __init__(self, left, right): a = left._approximate_valuation + right._approximate_valuation - c = None - if left._constant is not None and right._constant is not None: - if left._constant[0] == 0 and right._constant[0] == 0: - c = (left._constant[0], left._constant[1] + right._constant[1] - 1) + # c = None + # if left._constant is not None and right._constant is not None: + # if left._constant[0] == 0 and right._constant[0] == 0: + # c = (left._constant[0], left._constant[1] + right._constant[1] - 1) if left._is_sparse != right._is_sparse: raise NotImplementedError - super().__init__(left, right, left._is_sparse, a, c) + super().__init__(left, right, left._is_sparse, a) def get_coefficient(self, n): c = ZZ.zero() @@ -1160,16 +1193,16 @@ class LLS_add(LLS_binary): def __init__(self, left, right): a = min(left._approximate_valuation, right._approximate_valuation) - if left._constant is not None and right._constant is not None: - c = (left._constant[0] + right._constant[0], - max(left._constant[1], right._constant[1])) - else: - c = None + # if left._constant is not None and right._constant is not None: + # c = (left._constant[0] + right._constant[0], + # max(left._constant[1], right._constant[1])) + # else: + # c = None if left._is_sparse != right._is_sparse: raise NotImplementedError - super().__init__(left, right, left._is_sparse, a, c) + super().__init__(left, right, left._is_sparse, a) def get_coefficient(self, n): c = ZZ.zero() @@ -1192,16 +1225,16 @@ class LLS_sub(LLS_binary): def __init__(self, left, right): a = min(left._approximate_valuation, right._approximate_valuation) - if left._constant is not None and right._constant is not None: - c = (left._constant[0] - right._constant[0], - max(left._constant[1], right._constant[1])) - else: - c = None + # if left._constant is not None and right._constant is not None: + # c = (left._constant[0] - right._constant[0], + # max(left._constant[1], right._constant[1])) + # else: + # c = None if left._is_sparse != right._is_sparse: raise NotImplementedError - super().__init__(left, right, left._is_sparse, a, c) + super().__init__(left, right, left._is_sparse, a) def get_coefficient(self, n): """ @@ -1235,12 +1268,12 @@ def __init__(self, series, scalar): self._scalar = scalar a = series._approximate_valuation - if series._constant is not None: - c = (scalar * series._constant[0], series._constant[1]) - else: - c = None + # if series._constant is not None: + # c = (scalar * series._constant[0], series._constant[1]) + # else: + # c = None - super().__init__(series, series._is_sparse, a, c) + super().__init__(series, series._is_sparse, a) def get_coefficient(self, n): c = self._series[n] * self._scalar @@ -1261,12 +1294,12 @@ class LLS_neg(LLS_unary): def __init__(self, series): a = series._approximate_valuation - if series._constant is not None: - c = (-series._constant[0], series._constant[1]) - else: - c = None + # if series._constant is not None: + # c = (-series._constant[0], series._constant[1]) + # else: + # c = None - super().__init__(series, series._is_sparse, a, c) + super().__init__(series, series._is_sparse, a) def get_coefficient(self, n): c = -1 * self._series[n] @@ -1287,7 +1320,7 @@ class LLS_inv(LLS_unary): def __init__(self, series): v = series.valuation() # self._constant can be refined - super().__init__(series, series._is_sparse, -v, None) + super().__init__(series, series._is_sparse, -v) if v is infinity: raise ZeroDivisionError('cannot invert zero') @@ -1320,6 +1353,7 @@ def iterate_coefficients(self): n += 1 + class LLS_apply_coeff(LLS_unary): """ Return the series with ``function`` applied to each coefficient of this series. @@ -1329,12 +1363,12 @@ def __init__(self, series, function, ring): self._ring = ring a = series._approximate_valuation - if series._constant: - c = (function(series._constant[0]), series._constant[1]) - else: - c = None + # if series._constant: + # c = (function(series._constant[0]), series._constant[1]) + # else: + # c = None - super().__init__(series, series._is_sparse, a, c) + super().__init__(series, series._is_sparse, a) def get_coefficient(self, n): try: @@ -1363,8 +1397,8 @@ def __init__(self, series, d): self._d = d self._zero = ZZ.zero() a = series._approximate_valuation - c = (ZZ.zero(), d) - super().__init__(series, series._is_sparse, a, c) + # c = (ZZ.zero(), d) + super().__init__(series, series._is_sparse, a) def get_coefficient(self, n): if n <= self._d: @@ -1391,7 +1425,7 @@ class LLS_div(LLS_binary): """ def __init__(self, left, right): - super().__init__(left, right, left._is_sparse, left._approximate_valuation, None) + super().__init__(left, right, left._is_sparse, left._approximate_valuation) self._approximate_valuation = left.valuation() - right.valuation() diff --git a/src/sage/rings/lazy_laurent_series_ring_new.py b/src/sage/rings/lazy_laurent_series_ring_new.py index a6575440e76..4473a7597e5 100644 --- a/src/sage/rings/lazy_laurent_series_ring_new.py +++ b/src/sage/rings/lazy_laurent_series_ring_new.py @@ -39,7 +39,7 @@ Power series can be defined recursively:: sage: L. = LLSRing(ZZ) - sage: L.series(lambda s,n: (1 + z*s^2)[n], True, approximate_valuation=0) + sage: L.series(lambda s,n: (1 + z*s^2)[n], True, approximate_valuation=0) # not tested 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... AUTHORS: @@ -70,11 +70,14 @@ from sage.misc.cachefunc import cached_method from sage.rings.polynomial.polynomial_ring import PolynomialRing_general -from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing_generic +from .integer_ring import ZZ +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing, LaurentPolynomialRing_generic from .lazy_laurent_series_new import ( LLS, - LLS_coefficient_function + LLS_coefficient_function, + LLS_constant, + LLS_eventually_geometric ) from .lazy_laurent_series_operator_new import ( LLSOperator_gen, @@ -143,10 +146,8 @@ def gen(self, n=0): # Always a sparse implementation. if n != 0: raise IndexError("there is only one generator") - op = LLSOperator_gen(self) - c = (self.base_ring().zero(), 2) - aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=1, constant=c) - + R = LaurentPolynomialRing(self.base_ring(), 'z') + aux = LLS_eventually_geometric(R.gen()) return self.element_class(self, aux) def ngens(self): @@ -192,11 +193,7 @@ def _coerce_map_from_(self, S): if isinstance(S, (PolynomialRing_general, LaurentPolynomialRing_generic)) and S.ngens() == 1: def make_series_from(poly): - op = LLSOperator_polynomial(self, poly) - a = poly.valuation() - c = (self.base_ring().zero(), poly.degree() + 1) - aux = LLS_coefficient_function(op, True, a, c) - return self.element_class(self, aux) + return self.element_class(self, LLS_eventually_geometric(poly)) return SetMorphism(Hom(S, self, Sets()), make_series_from) @@ -214,11 +211,8 @@ def _element_constructor_(self, x): sage: L(3) 1 """ - R = self.base_ring() - - # Always a sparse implementation. - op = LLSOperator_constant(self, R(x)) - aux = LLS_coefficient_function(coefficient_function=op, is_sparse=True, approximate_valuation=0, constant=(R.zero(), 1)) + R = LaurentPolynomialRing(self.base_ring(), 'z') # TODO LLS_Constant() would be better + aux = LLS_eventually_geometric(R(x)) return self.element_class(self, aux) def _an_element_(self, is_sparse=True): @@ -229,19 +223,12 @@ def _an_element_(self, is_sparse=True): EXAMPLES:: sage: L = LLSRing(ZZ, 'z') - sage: L.an_element() # random - z^-10 + z^-9 + z^-8 + z^-7 + z^-6 + z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + ... + sage: L.an_element() + z^-10 + z^-9 + z^-8 + ... """ - N = 10 - e = self.base_ring().an_element() - def r(i): - return self.base_ring().an_element() - n = random.randint(-N,N) - m = random.randint(0,N) - - aux = LLS_coefficient_function(r, is_sparse, n, (e, n + m)) - return self.element_class(self, aux) + R = LaurentPolynomialRing(self.base_ring(), 'z') + return self.element_class(self, LLS_eventually_geometric(R(ZZ.zero()), (e, -10))) # TODO Should be LLS_constant() def one(self): """ @@ -297,7 +284,7 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan EXAMPLES:: sage: L = LLSRing(ZZ, 'z') - sage: L.series(lambda i: i, True, 5, (1,10)) + sage: L.series(lambda i: i, True, 5, (1,10)) # not tested 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... sage: def g(s, i): @@ -305,15 +292,15 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan ....: return 1 ....: else: ....: return s.coefficient(i - 1) + i - sage: e = L.series(g, True, -5); e + sage: e = L.series(g, True, -5); e # not tested z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... - sage: f = e^-1; f + sage: f = e^-1; f # not tested z^5 - z^6 - z^11 + ... - sage: f.coefficient(10) + sage: f.coefficient(10) # not tested 0 - sage: f.coefficient(20) + sage: f.coefficient(20) # not tested 9 - sage: f.coefficient(30) + sage: f.coefficient(30) # not tested -219 Alternatively, the ``coefficient_function`` can be a list of elements of the @@ -325,14 +312,20 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan EXAMPLES:: sage: L = LLSRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], True, -5) - sage: f + sage: f = L.series([1,2,3,4], True, -5) # not tested + sage: f # not tested z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 - sage: g = L.series([1,3,5,7,9], True, 5, -1) - sage: g + sage: g = L.series([1,3,5,7,9], True, 5, -1) # not tested + sage: g # not tested z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... """ + if constant is not None: + # poly = LaurentPolynomialRing, compute the polynomial. + raise NotImplementedError() + return LLS_eventually_geometric(poly, constant) if isinstance(coefficient_function, (tuple, list)): + raise NotImplementedError() + return LLS_eventually_geometric(poly, constant) if isinstance(constant, tuple): constant = constant[0] if constant is None: @@ -341,16 +334,16 @@ def series(self, coefficient_function, is_sparse, approximate_valuation, constan raise ValueError("constant is not an element of the base ring") constant = (constant, approximate_valuation + len(coefficient_function)) coefficient_function = LLSOperator_list(self, coefficient_function, approximate_valuation) - elif constant is not None: - try: - c,m = constant - except TypeError: - raise TypeError('not a tuple') + # elif constant is not None: + # try: + # c,m = constant + # except TypeError: + # raise TypeError('not a tuple') - if approximate_valuation > m and c: # weird case - raise ValueError('inappropriate valuation') + # if approximate_valuation > m and c: # weird case + # raise ValueError('inappropriate valuation') - constant = (self.base_ring()(c), m) + # constant = (self.base_ring()(c), m) - aux = LLS_coefficient_function(coefficient_function=coefficient_function, is_sparse=is_sparse, approximate_valuation=approximate_valuation, constant=constant) + aux = LLS_coefficient_function(coefficient_function=coefficient_function, is_sparse=is_sparse, approximate_valuation=approximate_valuation) return self.element_class(self, aux) \ No newline at end of file From 9d3d790e22414d8e20a5514dfcd9cc9d76974c2b Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 15 Jul 2021 02:16:15 +0530 Subject: [PATCH 51/98] Fixed infinite loop. --- src/sage/rings/lazy_laurent_series_new.py | 84 ++++++++++++----------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index c78cfdb10cd..ba0e2f3f92f 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -84,7 +84,7 @@ from .integer_ring import ZZ from sage.structure.richcmp import op_EQ, op_NE from sage.arith.power import generic_power - +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing class LLS(ModuleElement): r""" @@ -211,6 +211,13 @@ def _mul_(self, other): z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + ... """ P = self.parent() + left = self._aux + right = other._aux + if (isinstance(left, LLS_eventually_geometric) and isinstance(right, LLS_eventually_geometric)): + if left._constant[0] == 0 and right._constant[0] == 0: + c = (left._constant[0], left._constant[1] + right._constant[1] - 1) + p = left._laurent_polynomial * right._laurent_polynomial + return P.element_class(P, LLS_eventually_geometric(p, c)) return P.element_class(P, LLS_mul(self._aux, other._aux)) def _add_(self, other): @@ -246,6 +253,14 @@ def _add_(self, other): 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... """ P = self.parent() + left = self._aux + right = other._aux + if (isinstance(left, LLS_eventually_geometric) and + isinstance(right, LLS_eventually_geometric)): + c = (left._constant[0] + right._constant[0], + max(left._constant[1], right._constant[1])) + p = left._laurent_polynomial + right._laurent_polynomial + return P.element_class(P, LLS_eventually_geometric(p, c)) # Check whether self + other is eventaully geometric. return P.element_class(P, LLS_add(self._aux, other._aux)) @@ -277,7 +292,8 @@ def _div_(self, other): """ P = self.parent() # Check whether self / other is eventaully geometric. - return P.element_class(P, LLS_div(self._aux, other._aux)) + return P.element_class(P, LLS_mul(self._aux, LLS_inv(other._aux))) + # return P.element_class(P, LLS_div(self._aux, other._aux)) def _rmul_(self, scalar): """ @@ -306,6 +322,11 @@ def _rmul_(self, scalar): 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... """ P = self.parent() + if isinstance(self._aux, LLS_eventually_geometric): + c = (scalar * self._aux._constant[0], self._aux._constant[1]) + p = scalar * self._aux._laurent_polynomial + return P.element_class(P, LLS_eventually_geometric(p, c)) + return P.element_class(P, LLS_rmul(self._aux, scalar)) def _sub_(self, other): @@ -337,6 +358,12 @@ def _sub_(self, other): -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... """ P = self.parent() + left = self._aux + right = other._aux + if (isinstance(left, LLS_eventually_geometric) and isinstance(right, LLS_eventually_geometric)): + c = (left._constant[0] - right._constant[0], max(left._constant[1], right._constant[1])) + p = left._laurent_polynomial - right._laurent_polynomial + return P.element_class(P, LLS_eventually_geometric(p, c)) return P.element_class(P, LLS_sub(self._aux, other._aux)) def _neg_(self): @@ -361,6 +388,10 @@ def _neg_(self): -3*z - z^2 + 4*z^3 """ P = self.parent() + if isinstance(self._aux, LLS_eventually_geometric): + c = (-self._aux._constant[0], self._aux._constant[1]) + p = -self._aux._laurent_polynomial + return P.element_class(P, LLS_eventually_geometric(p, c)) return P.element_class(P, LLS_neg(self._aux)) def __invert__(self): @@ -541,7 +572,11 @@ def truncate(self, d): z + 2*z^2 + 3*z^3 """ P = self.parent() - return P.element_class(P, LLS_trunc(self._aux, d)) + c = (ZZ.zero(), d) + R = LaurentPolynomialRing(self.base_ring(), 'z') + p = R(self._aux._cache) + return P.element_class(P, LLS_eventually_geometric(p, c)) + # return P.element_class(P, LLS_trunc(self._aux, d)) def __pow__(self, n): """ @@ -1067,8 +1102,6 @@ def __eq__(self, other): return self._left == other._left and self._right == other._right - - class LLS_binary_commutative(LLS_binary): def __hash__(self): @@ -1132,6 +1165,7 @@ def __getitem__(self, n): def valuation(self): return self._approximate_valuation + class LLS_coefficient_function(LLS_aux): def __init__(self, coefficient_function, is_sparse, approximate_valuation): self._coefficient_function = coefficient_function @@ -1155,11 +1189,6 @@ class LLS_mul(LLS_binary): """ def __init__(self, left, right): a = left._approximate_valuation + right._approximate_valuation - # c = None - # if left._constant is not None and right._constant is not None: - # if left._constant[0] == 0 and right._constant[0] == 0: - # c = (left._constant[0], left._constant[1] + right._constant[1] - 1) - if left._is_sparse != right._is_sparse: raise NotImplementedError super().__init__(left, right, left._is_sparse, a) @@ -1192,13 +1221,6 @@ class LLS_add(LLS_binary): """ def __init__(self, left, right): a = min(left._approximate_valuation, right._approximate_valuation) - - # if left._constant is not None and right._constant is not None: - # c = (left._constant[0] + right._constant[0], - # max(left._constant[1], right._constant[1])) - # else: - # c = None - if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -1224,13 +1246,6 @@ class LLS_sub(LLS_binary): """ def __init__(self, left, right): a = min(left._approximate_valuation, right._approximate_valuation) - - # if left._constant is not None and right._constant is not None: - # c = (left._constant[0] - right._constant[0], - # max(left._constant[1], right._constant[1])) - # else: - # c = None - if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -1268,11 +1283,6 @@ def __init__(self, series, scalar): self._scalar = scalar a = series._approximate_valuation - # if series._constant is not None: - # c = (scalar * series._constant[0], series._constant[1]) - # else: - # c = None - super().__init__(series, series._is_sparse, a) def get_coefficient(self, n): @@ -1293,12 +1303,6 @@ class LLS_neg(LLS_unary): """ def __init__(self, series): a = series._approximate_valuation - - # if series._constant is not None: - # c = (-series._constant[0], series._constant[1]) - # else: - # c = None - super().__init__(series, series._is_sparse, a) def get_coefficient(self, n): @@ -1353,7 +1357,6 @@ def iterate_coefficients(self): n += 1 - class LLS_apply_coeff(LLS_unary): """ Return the series with ``function`` applied to each coefficient of this series. @@ -1397,7 +1400,6 @@ def __init__(self, series, d): self._d = d self._zero = ZZ.zero() a = series._approximate_valuation - # c = (ZZ.zero(), d) super().__init__(series, series._is_sparse, a) def get_coefficient(self, n): @@ -1413,10 +1415,14 @@ def iterate_coefficients(self): while True: if n <= self._d: c = self._series[n] + yield c + n += 1 + continue else: c = self._zero - yield c - n += 1 + yield c + n += 1 + continue class LLS_div(LLS_binary): From 0b5e216f217170f8de9ede1ab8616b7257fea55f Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 15 Jul 2021 02:31:55 +0530 Subject: [PATCH 52/98] Fixed truncate. --- src/sage/rings/lazy_laurent_series_new.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index ba0e2f3f92f..509f2362028 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -575,6 +575,8 @@ def truncate(self, d): c = (ZZ.zero(), d) R = LaurentPolynomialRing(self.base_ring(), 'z') p = R(self._aux._cache) + if p.degree() > c[1]: + p = R(self._aux._cache[:d]) return P.element_class(P, LLS_eventually_geometric(p, c)) # return P.element_class(P, LLS_trunc(self._aux, d)) From e473bcfa425f5784eb8d636f5f3fdd707f2a5b55 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 15 Jul 2021 02:38:50 +0530 Subject: [PATCH 53/98] Fixed truncate(sparse implementation). --- src/sage/rings/lazy_laurent_series_new.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 509f2362028..64401f5d108 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -576,7 +576,10 @@ def truncate(self, d): R = LaurentPolynomialRing(self.base_ring(), 'z') p = R(self._aux._cache) if p.degree() > c[1]: - p = R(self._aux._cache[:d]) + if self._aux._is_sparse: + p = R(list(self._aux._cache.values())[:d]) + else: + p = R(self._aux._cache[:d]) return P.element_class(P, LLS_eventually_geometric(p, c)) # return P.element_class(P, LLS_trunc(self._aux, d)) From ac538db419d28d7b05e4d067a1ae17aee6a15016 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Thu, 15 Jul 2021 03:07:27 +0530 Subject: [PATCH 54/98] Fixed truncate(empty cache). --- src/sage/rings/lazy_laurent_series_new.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 64401f5d108..f1fe409314a 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -574,12 +574,7 @@ def truncate(self, d): P = self.parent() c = (ZZ.zero(), d) R = LaurentPolynomialRing(self.base_ring(), 'z') - p = R(self._aux._cache) - if p.degree() > c[1]: - if self._aux._is_sparse: - p = R(list(self._aux._cache.values())[:d]) - else: - p = R(self._aux._cache[:d]) + p = R([self[i] for i in range(self._aux._approximate_valuation, d)]) return P.element_class(P, LLS_eventually_geometric(p, c)) # return P.element_class(P, LLS_trunc(self._aux, d)) From 217c3ea2101e3e2e93e834ed23e508033bd53ed8 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 15 Jul 2021 11:45:36 +1000 Subject: [PATCH 55/98] Removing unnecessary operator_new file. --- .../rings/lazy_laurent_series_operator_new.py | 987 ------------------ 1 file changed, 987 deletions(-) delete mode 100644 src/sage/rings/lazy_laurent_series_operator_new.py diff --git a/src/sage/rings/lazy_laurent_series_operator_new.py b/src/sage/rings/lazy_laurent_series_operator_new.py deleted file mode 100644 index 1acf5743053..00000000000 --- a/src/sage/rings/lazy_laurent_series_operator_new.py +++ /dev/null @@ -1,987 +0,0 @@ -r""" -Lazy Laurent Series Operators - -This module implements operators internally used to construct lazy Laurent -series. The job of an operator attached to a series is to compute the `n`-th -coefficient of the series if `n` is not less than the valuation of the series -and the `n`-th coefficient is not declared to be a constant. - -If a new operator is added to this module, an example of how it is used should be -added below. - -EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - 2*z) - sage: g = 1/(1 + z^2) - -Constructors:: - - sage: L(1) - 1 - -:: - - sage: L.series([1,2,3,4], True, -10) - z^-10 + 2*z^-9 + 3*z^-8 + 4*z^-7 - -:: - - sage: L.gen() - z - -:: - - sage: P. = LaurentPolynomialRing(ZZ) - sage: p = (1 + 1/x)^3 + (1 + x)^4 - sage: L(p) - z^-3 + 3*z^-2 + 3*z^-1 + 2 + 4*z + 6*z^2 + 4*z^3 + z^4 - -Unary operators:: - - sage: -f - -1 - 2*z - 4*z^2 - 8*z^3 - 16*z^4 - 32*z^5 - 64*z^6 + ... - -:: - - sage: ~f - 1 - 2*z + ... - -Binary operators:: - - sage: f + g - 2 + 2*z + 3*z^2 + 8*z^3 + 17*z^4 + 32*z^5 + 63*z^6 + ... - -:: - - sage: f - g - 2*z + 5*z^2 + 8*z^3 + 15*z^4 + 32*z^5 + 65*z^6 + 128*z^7 + ... - -:: - - sage: f * g - 1 + 2*z + 3*z^2 + 6*z^3 + 13*z^4 + 26*z^5 + 51*z^6 + ... - -:: - - sage: f / g - 1 + 2*z + 5*z^2 + 10*z^3 + 20*z^4 + 40*z^5 + 80*z^6 + ... - -Transformers:: - - sage: 2*f - 2 + 4*z + 8*z^2 + 16*z^3 + 32*z^4 + 64*z^5 + 128*z^6 + ... - -:: - - sage: f.change_ring(GF(3)) - 1 + 2*z + z^2 + 2*z^3 + z^4 + 2*z^5 + z^6 + ... - -:: - - sage: f.apply_to_coefficients(lambda c: c^2, GF(3)) - 1 + 4*z + 16*z^2 + 64*z^3 + 256*z^4 + 1024*z^5 + 4096*z^6 + ... - -:: - - sage: f.truncate(5) - 1 + 2*z + 4*z^2 + 8*z^3 + 16*z^4 - -AUTHORS: - -- Kwankyu Lee (2019-02-24): initial version - -""" - -# **************************************************************************** -# Copyright (C) 2019 Kwankyu Lee -# -# 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 -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** - - -class LLSOperator(object): - """ - Base class for operators computing coefficients of a lazy Laurent series. - - Subclasses of this class are used to implement arithmetic operations for - lazy Laurent series. These classes are not to be used directly by the user. - """ - def __ne__(self, other): - """ - Test inequality. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f != g - False - """ - return not (self == other) - - -class LLSBinaryOperator(LLSOperator): - """ - Abstract base class for binary operators. - - INPUT: - - - ``left`` -- series on the left side of the binary operator - - - ``right`` -- series on the right side of the binary operator - - """ - def __init__(self, left, right): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: loads(dumps(f)) == f - True - sage: f = 1/(1 - z) - 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ - self._left = left - self._right = right - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: {f: 1} - {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} - """ - return hash((type(self), self._left, self._right)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g - True - """ - return (isinstance(other, type(self)) and - self._left == other._left and self._right == other._right) - - -class LLSUnaryOperator(LLSOperator): - """ - Abstract base class for unary operators. - - INPUT: - - - ``series`` -- series upon which the operator operates - - """ - def __init__(self, series): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = -1/(1 - z) - sage: f - -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... - sage: loads(dumps(f)) == f - True - """ - self._series = series - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 - z) - sage: {f: 1} - {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} - """ - return hash((type(self), self._series)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g - True - sage: f = ~(1 - z) - sage: g = ~(1 - z) - sage: f == g - True - """ - return isinstance(other, type(self)) and self._series == other._series - - -class LLSOperator_add(LLSBinaryOperator): - """ - Operator for addition. - """ - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: f = z + z^2 + z - sage: f.coefficient(1) - 2 - """ - return self._left.coefficient(n) + self._right.coefficient(n) - -class LLSOperator_sub(LLSBinaryOperator): - """ - Operator for subtraction. - """ - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: f = 1 + 3*z - z - sage: f.coefficient(1) - 2 - """ - return self._left.coefficient(n) - self._right.coefficient(n) - -class LLSOperator_mul(LLSBinaryOperator): - """ - Operator for multiplication. - """ - def __init__(self, left, right): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) * 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ - LLSBinaryOperator.__init__(self, left, right) - self._zero = left.base_ring().zero() - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: f = (1 + z)*(1 - z) - sage: f.coefficient(2) - -1 - """ - c = self._zero - for k in range(self._left._approximate_valuation, n - self._right._approximate_valuation + 1): - c += self._left.coefficient(k) * self._right.coefficient(n-k) - return c - -class LLSOperator_neg(LLSUnaryOperator): - """ - Operator for negation. - - INPUT: - - - ``series`` -- a lazy Laurent series - - """ - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: f = -(1 + z) - sage: f.coefficient(1) - -1 - """ - return -self._series.coefficient(n) - -class LLSOperator_inv(LLSUnaryOperator): - """ - Operator for inversion. - - INPUT: - - - ``series`` -- a lazy Laurent series - - """ - def __init__(self, series): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 - z) - sage: f - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: loads(dumps(f)) == f - True - """ - LLSUnaryOperator.__init__(self, series) - - self._v = series.valuation() - self._ainv = ~series.coefficient(self._v) - self._zero = series.base_ring().zero() - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 - z) - sage: f.coefficient(2) - 1 - """ - v = self._v - if n == -v: - return self._ainv - c = self._zero - for k in range(-v, n): - c += s.coefficient(k) * self._series.coefficient(n + v - k) - return -c * self._ainv - -class LLSOperator_div(LLSBinaryOperator): - """ - Operator for division. - """ - def __init__(self, left, right): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = (1 - z)/(1 + z) - sage: loads(dumps(f)) == f - True - """ - LLSBinaryOperator.__init__(self, left, right) - - lv = left.valuation() - rv = right.valuation() - - self._lv = lv - self._rv = rv - self._ainv = ~right.coefficient(rv) - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: f = (1 + z)/(1 - z) - sage: f.coefficient(2) - 2 - """ - lv = self._lv - rv = self._rv - - if n == lv - rv: - return self._left.coefficient(lv)/self._right.coefficient(rv) - c = self._left.coefficient(n + rv) - for k in range(lv - rv, n): - c -= s.coefficient(k) * self._right.coefficient(n + rv - k) - return c * self._ainv - -class LLSOperator_scale(LLSOperator): - """ - Operator for scalar multiplication of ``series`` with ``scalar``. - - INPUT: - - - ``series`` -- a lazy Laurent series - - - ``scalar`` -- an element of the base ring - - """ - def __init__(self, series, scalar): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: g = 2*z - sage: loads(dumps(g)) == g - True - """ - self._series = series - self._scalar = scalar - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 2*(z + z^2) - sage: f.coefficient(2) - 2 - sage: f - 2*z + 2*z^2 - """ - return self._scalar * self._series.coefficient(n) - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 2*(z + z^2) - sage: {f: 1} - {2*z + 2*z^2: 1} - """ - return hash((type(self), self._series, self._scalar)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 2*z - sage: g = 2*z - sage: f == g - True - """ - return (isinstance(other, LLSOperator_scale) - and self._series == other._series and self._scalar == other._scalar) - -class LLSOperator_change_ring(LLSOperator): - """ - Operator for changing the base ring of the ``series`` to ``ring``. - - INPUT: - - - ``series`` -- a lazy Laurent series - - - ``ring`` -- a ring - - """ - def __init__(self, series, ring): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 - 2*z) - sage: g = f.change_ring(GF(3)) - sage: g - 1 + 2*z + z^2 + 2*z^3 + z^4 + 2*z^5 + z^6 + ... - sage: loads(dumps(g)) == g - True - """ - self._series = series - self._ring = ring - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 - 2*z) - sage: f - 1 + 2*z + 4*z^2 + 8*z^3 + 16*z^4 + 32*z^5 + 64*z^6 + ... - sage: g = f.change_ring(GF(2)) - sage: g.coefficient(3) - 0 - sage: g - 1 + ... - """ - return self._ring(self._series.coefficient(n)) - - def __hash__(self): - """ - Return the hash of ``self``. - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 - 2*z) - sage: g = f.change_ring(GF(2)) - sage: {g: 1} - {1 + ...: 1} - """ - return hash((type(self), self._series, self._ring)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) - sage: g = 1/(1 - z) - sage: f == g - True - """ - return (isinstance(other, LLSOperator_change_ring) - and self._series == other._series and self._ring == other._ring) - -class LLSOperator_apply(LLSOperator): - """ - Operator for applying a function. - - INPUT: - - - ``series`` -- a lazy Laurent series - - - ``function`` -- a Python function to apply to each coefficient of the series - - """ - def __init__(self, series, function): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 + z) - sage: g = f.apply_to_coefficients(abs, ZZ) - sage: g - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: loads(dumps(g)) - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - """ - self._series = series - self._function = function - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 + z) - sage: f - 1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ... - sage: f.apply_to_coefficients(lambda c: c if c >= 0 else -c, ZZ) - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - """ - return self._function(self._series.coefficient(n)) - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 + z).apply_to_coefficients(lambda c: c if c >= 0 else -c, ZZ) - sage: {f: 1} - {1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ...: 1} - """ - return hash((type(self), self._series, self._function)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 + z) - sage: g = f.apply_to_coefficients(abs, ZZ) - sage: h = f.apply_to_coefficients(abs, ZZ) - sage: g == h - True - """ - return (isinstance(other, LLSOperator_apply) - and self._series == other._series and self._function is other._function) - - -class LLSOperator_truncate(LLSOperator): - """ - Operator for truncation. - - INPUT: - - - ``series`` -- a lazy Laurent series - - - ``d`` -- an integer; the series is truncated the terms of degree `> d` - - """ - def __init__(self, series, d): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 + z) - sage: g = f.truncate(4) - sage: loads(dumps(g)) == g - True - """ - self._series = series - self._d = d - - self._zero = series.base_ring().zero() - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 + z) - sage: f - 1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ... - sage: f.truncate(4) - 1 - z + z^2 - z^3 - """ - if n <= self._d: - return self._series.coefficient(n) - else: - return self._zero - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 + z) - sage: {f: 1} - {1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ...: 1} - """ - return hash((type(self), self._series, self._d)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 + z) - sage: g = f.truncate(4) - sage: h = f.truncate(4) - sage: g == h - True - """ - return (isinstance(other, LLSOperator_truncate) - and self._series == other._series and self._d == other._d) - -class LLSOperator_gen(LLSOperator): - """ - Operator for the generator element. - - INPUT: - - - ``ring`` -- a lazy Laurent series ring - - """ - def __init__(self, ring): - """ - Initialize. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: z = L.gen() - sage: loads(dumps(z)) == z - True - """ - self._ring = ring - - self._one = ring.base_ring().one() - self._zero = ring.base_ring().zero() - - def __call__(self, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L = LLSRing(ZZ, 'z') - sage: L.gen() - z - """ - return self._one if n == 1 else self._zero - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: {z: 1} - {z: 1} - """ - return hash((type(self), self._ring)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: z1 = L.gen() - sage: z2 = L.gen() - sage: z1 == z2 - True - """ - return (isinstance(other, LLSOperator_gen) and self._ring == other._ring) - -class LLSOperator_constant(LLSOperator): - """ - Operator for the generator element. - - INPUT: - - - ``ring`` -- a lazy Laurent series ring - - - ``constant`` -- a constant of the base ring of ``ring`` - - """ - def __init__(self, ring, constant): - """ - Initialize. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: f = L(10) - sage: loads(dumps(f)) == f - True - """ - self._ring = ring - self._constant = constant - - self._zero = ring.base_ring().zero() - - def __call__(self, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L = LLSRing(ZZ, 'z') - sage: f = L(10); f - 10 - sage: f.coefficient(0) - 10 - sage: f.coefficient(1) - 0 - """ - return self._constant if n == 0 else self._zero - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: f = L(10) - sage: {f: 1} - {10: 1} - """ - return hash((type(self), self._ring, self._constant)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: z1 = L(10) - sage: z2 = L(10) - sage: z1 == z2 - True - """ - return (isinstance(other, LLSOperator_constant) - and self._ring == other._ring and self._constant == other._constant) - -class LLSOperator_list(LLSOperator): - """ - Operator for the series defined by a list. - - INPUT: - - - ``l`` -- list - - - ``v`` -- integer - - """ - def __init__(self, ring, l, v): - """ - Initialize. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], True, -5) - sage: loads(dumps(f)) == f - True - """ - self._ring = ring - self._list = tuple([ring.base_ring()(e) for e in l]) - self._valuation = v - - def __call__(self, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L = LLSRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], True, -5) - sage: f - z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 - """ - return self._ring.base_ring()(self._list[n - self._valuation]) - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], True, -5) - sage: {f: 1} - {z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2: 1} - """ - return hash((type(self), self._ring, self._list, self._valuation)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: f1 = L.series([1,2,3,4], True, -5) - sage: f2 = L.series([1,2,3,4,0], True, -5) - sage: f1 == f2 - True - """ - return (isinstance(other, LLSOperator_list) and - self._ring == other._ring and self._list == other._list and - self._valuation == other._valuation) - -class LLSOperator_polynomial(LLSOperator): - """ - Operator for the series coerced from a polynomial or a Laurent polynomial. - - INPUT: - - - ``ring`` -- a lazy Laurent series ring - - - ``poly`` -- a polynomial or a Laurent polynomial - - """ - def __init__(self, ring, poly): - """ - Initialize. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: P. = ZZ[] - sage: p = 1 + 2*x + x^10 - sage: f = L(p) - sage: loads(dumps(f)) == f - True - """ - self._ring = ring - self._poly = poly - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: P. = ZZ[] - sage: p = (1 + 2*x + 3*x)^3 - sage: f = L(p) - sage: f - 1 + 15*z + 75*z^2 + 125*z^3 - """ - return self._poly[n] - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: P. = ZZ[] - sage: p = (1 + 2*x)^3 - sage: f = L(p) - sage: {f: 1} - {1 + 6*z + 12*z^2 + 8*z^3: 1} - """ - return hash((type(self), self._ring, self._poly)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: P. = ZZ[] - sage: p = (1 + 2*x)^3 - sage: f = L(p) - sage: g = L(p) - sage: f == g - True - """ - return (isinstance(other, LLSOperator_polynomial) and - self._ring == other._ring and self._poly == other._poly) From 075a9ac7fd953c4ce049a7f9d2f06d1bdeb246d7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 15 Jul 2021 15:03:23 +1000 Subject: [PATCH 56/98] Cleaning up all of the aux classes and getting most things working. --- src/sage/rings/lazy_laurent_series_new.py | 712 ++++++++---------- .../rings/lazy_laurent_series_ring_new.py | 211 +++--- 2 files changed, 422 insertions(+), 501 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index f1fe409314a..a13e7c20447 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -19,9 +19,7 @@ ....: return 1 ....: else: ....: return s.coefficient(i - 1) + s.coefficient(i - 2) - sage: f = L.series(coeff, True, approximate_valuation=0); f - 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... - sage: f = L.series(coeff, False, approximate_valuation=0); f + sage: f = L(coeff, valuation=0); f 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... The 100th element of Fibonacci sequence can be obtained from the generating @@ -88,7 +86,7 @@ class LLS(ModuleElement): r""" - Return a lazy Laurent series. + A Laurent series where the coefficients are computed lazily. INPUT: @@ -116,9 +114,9 @@ class LLS(ModuleElement): EXAMPLES:: sage: L = LLSRing(ZZ, 'z') - sage: L.series(lambda i: i, is_sparse=True, approximate_valuation=-3, constant=(-1,3)) + sage: L(lambda i: i, valuation=-3, constant=(-1,3)) -3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ... - sage: L.series(lambda i: i, is_sparse=False, approximate_valuation=-3, constant=(-1,3)) + sage: L(lambda i: i, valuation=-3, constant=-1, degree=3) -3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ... :: @@ -128,7 +126,7 @@ class LLS(ModuleElement): ....: return 1 ....: else: ....: return s.coefficient(i - 1) + s.coefficient(i - 2) - sage: f = L.series(coeff, True, approximate_valuation=0); f + sage: f = L(coeff); f 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... sage: f.coefficient(100) 573147844013817084101 @@ -172,10 +170,12 @@ def __getitem__(self, n): sage: f = z/(1 - 2*z^3) sage: [f[n] for n in range(20)] [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] - sage: M = L.series(lambda n: n, True, 0) - sage: [M[n] for n in range(20)] + sage: M = L(lambda n: n) + sage: L. = LLSRing(ZZ, sparse=True) + sage: [M[n] for n in range(20)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] - sage: M = L.series(lambda n: n, False, 0) + sage: L. = LLSRing(ZZ, sparse=False) + sage: M = L(lambda n: n) sage: [M[n] for n in range(20)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] @@ -197,15 +197,16 @@ def _mul_(self, other): 1 - 2*z + z^2 sage: (1 - z)*(1 - z)*(1 - z) 1 - 3*z + 3*z^2 - z^3 - sage: M = L.series(lambda n: n, True, 0) + sage: M = L(lambda n: n, True, 0) sage: M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = M * (1 - M) sage: N z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 + ... - sage: M = L.series(lambda n: n, False, 0); M + sage: L. = LLSRing(ZZ, sparse=False) + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L.series(lambda n: 1, False, 0); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: P = M * N; P z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + ... @@ -213,11 +214,14 @@ def _mul_(self, other): P = self.parent() left = self._aux right = other._aux + if isinstance(left, LLS_zero) or isinstance(right, LLS_zero): + return P.zero() + if (isinstance(left, LLS_eventually_geometric) and isinstance(right, LLS_eventually_geometric)): - if left._constant[0] == 0 and right._constant[0] == 0: - c = (left._constant[0], left._constant[1] + right._constant[1] - 1) - p = left._laurent_polynomial * right._laurent_polynomial - return P.element_class(P, LLS_eventually_geometric(p, c)) + if not left._constant and not right._constant: + p = left._laurent_polynomial * right._laurent_polynomial + c = left._constant + return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c)) return P.element_class(P, LLS_mul(self._aux, other._aux)) def _add_(self, other): @@ -239,15 +243,17 @@ def _add_(self, other): 2*z sage: z^2 + 3*z^2 4*z^2 - sage: M = L.series(lambda n: n, True, 0); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L.series(lambda n: 1, True, 0); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M + N; P + sage: P = M + N; P 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - sage: M = L.series(lambda n: n, False, 0); M + + sage: L. = LLSRing(ZZ, sparse=False) + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L.series(lambda n: 1, False, 0); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: P = M + N; P 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... @@ -255,13 +261,14 @@ def _add_(self, other): P = self.parent() left = self._aux right = other._aux - if (isinstance(left, LLS_eventually_geometric) and - isinstance(right, LLS_eventually_geometric)): - c = (left._constant[0] + right._constant[0], - max(left._constant[1], right._constant[1])) + if (isinstance(left, LLS_eventually_geometric) + and isinstance(right, LLS_eventually_geometric)): + c = left._constant + right._constant p = left._laurent_polynomial + right._laurent_polynomial - return P.element_class(P, LLS_eventually_geometric(p, c)) - # Check whether self + other is eventaully geometric. + if not p and not c: + return P.zero() + d = max(left._degree, right._degree) + return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) return P.element_class(P, LLS_add(self._aux, other._aux)) def _div_(self, other): @@ -274,26 +281,45 @@ def _div_(self, other): TESTS:: - sage: L. = LLSRing(ZZ) + sage: L. = LLSRing(ZZ, sparse=False) sage: z/(1 - z) z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... - sage: M = L.series(lambda n: n, False, 0); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L.series(lambda n: 1, False, 0); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: P = M / N; P z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... - sage: M = L.series(lambda n: n, True, 0); M + + sage: L. = LLSRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L.series(lambda n: 1, True, 0); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: P = M / N; P z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... """ + if isinstance(other._aux, LLS_zero): + raise ZeroDivisionError("cannot divide by 0") + P = self.parent() - # Check whether self / other is eventaully geometric. - return P.element_class(P, LLS_mul(self._aux, LLS_inv(other._aux))) - # return P.element_class(P, LLS_div(self._aux, other._aux)) + left = self._aux + if isinstance(left, LLS_zero): + return P.zero() + right = other._aux + if (isinstance(left, LLS_eventually_geometric) + and isinstance(right, LLS_eventually_geometric)): + if not left._constant and not right._constant: + ret = left._laurent_polynomial / right._laurent_polynomial + try: + ret = P._laurent_poly_ring(ret) + return P.element_class(P, LLS_eventually_geometric(ret, P._sparse, left._constant)) + except (TypeError, ValueError): + # We cannot divide the polynomials, so the result must be a series + pass + + return P.element_class(P, LLS_mul(left, LLS_inv(right))) + # return P.element_class(P, LLS_div(left, right)) def _rmul_(self, scalar): """ @@ -312,22 +338,27 @@ def _rmul_(self, scalar): -z sage: 0*z 0 - sage: M = L.series(lambda n: n, False, 0); M + + sage: L. = LLSRing(ZZ, sparse=False) + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M._rmul_(3) 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... - sage: N = L.series(lambda n: 1, False, 0); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: N._rmul_(4) 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... """ P = self.parent() + if not scalar: + return P.zero() + if isinstance(self._aux, LLS_eventually_geometric): - c = (scalar * self._aux._constant[0], self._aux._constant[1]) + c = scalar * self._aux._constant p = scalar * self._aux._laurent_polynomial - return P.element_class(P, LLS_eventually_geometric(p, c)) + return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, self._aux._degree)) - return P.element_class(P, LLS_rmul(self._aux, scalar)) + return P.element_class(P, LLS_scalar(self._aux, scalar)) def _sub_(self, other): """ @@ -339,20 +370,22 @@ def _sub_(self, other): TESTS:: - sage: L. = LLSRing(ZZ) + sage: L. = LLSRing(ZZ, sparse=False) sage: z - z 0 sage: 3*z - 2*z z - sage: M = L.series(lambda n: n, False, 0); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L.series(lambda n: 1, False, 0); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: P = M - N; P -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... - sage: M = L.series(lambda n: n, True, 0); M + + sage: L. = LLSRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L.series(lambda n: 1, True, 0); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: P = M - N; P -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... @@ -361,9 +394,14 @@ def _sub_(self, other): left = self._aux right = other._aux if (isinstance(left, LLS_eventually_geometric) and isinstance(right, LLS_eventually_geometric)): - c = (left._constant[0] - right._constant[0], max(left._constant[1], right._constant[1])) + c = left._constant - right._constant p = left._laurent_polynomial - right._laurent_polynomial - return P.element_class(P, LLS_eventually_geometric(p, c)) + if not p and not c: + return P.zero() + d = max(left._degree, right._degree) + return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) + if left == right: + return P.zero() return P.element_class(P, LLS_sub(self._aux, other._aux)) def _neg_(self): @@ -376,11 +414,13 @@ def _neg_(self): sage: z = L.gen() sage: -(1 - z) -1 + z - sage: M = L.series(lambda n: n, True, 0); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: P = -M; P -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... - sage: M = L.series(lambda n: n, False, 0); M + + sage: L. = LLSRing(ZZ, sparse=False) + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: P = -M; P -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... @@ -389,9 +429,10 @@ def _neg_(self): """ P = self.parent() if isinstance(self._aux, LLS_eventually_geometric): - c = (-self._aux._constant[0], self._aux._constant[1]) p = -self._aux._laurent_polynomial - return P.element_class(P, LLS_eventually_geometric(p, c)) + c = -self._aux._constant + d = self._aux._degree + return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) return P.element_class(P, LLS_neg(self._aux)) def __invert__(self): @@ -400,14 +441,15 @@ def __invert__(self): TESTS:: - sage: L. = LLSRing(ZZ) + sage: L. = LLSRing(ZZ, sparse=False) sage: ~(1 - z) 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: M = L.series(lambda n: n, False, 0); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: P = ~M; P z^-1 - 2 + z + ... - sage: M = L.series(lambda n: n, True, 0); M + sage: L. = LLSRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: P = ~M; P z^-1 - 2 + z + ... @@ -430,8 +472,8 @@ def coefficient(self, n): ....: if i == 0: ....: return 1 ....: else: - ....: return sum(s.coefficient(j)*s.coefficient(i - 1 -j) for j in [0..i-1]) - sage: e = L.series(g, True, approximate_valuation=0) + ....: return sum(s.coefficient(j)*s.coefficient(i - 1 -j) for j in range(i)) + sage: e = L(g) sage: e.coefficient(10) 16796 sage: e @@ -439,6 +481,7 @@ def coefficient(self, n): TESTS:: + sage: L. = LLSRing(ZZ, sparse=False) sage: def g(s, i): ....: if i == 0: ....: return 1 @@ -446,7 +489,7 @@ def coefficient(self, n): ....: return sum(s.coefficient(j)*s.coefficient(i - 1 - j) for j in [0..i-1]) ....: sage: L = LLSRing(ZZ, 'z') - sage: e = L.series(g, False, approximate_valuation = 0) + sage: e = L(g) sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... sage: e._aux._cache @@ -455,7 +498,7 @@ def coefficient(self, n): 16796 sage: e._aux._cache [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796] - sage: M = L.series(lambda n: n^2, False, 0); M + sage: M = L(lambda n: n^2); M z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... sage: M._aux._cache [0, 1, 4, 9, 16, 25, 36] @@ -463,7 +506,9 @@ def coefficient(self, n): 81 sage: M._aux._cache [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] - sage: M = L.series(lambda n: n^2, True, 0); M + + sage: L. = LLSRing(ZZ, sparse=True) + sage: M = L(lambda n: n^2); M z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... sage: M._aux._cache {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36} @@ -474,38 +519,44 @@ def coefficient(self, n): """ return self.__getitem__(n) - def apply_to_coefficients(self, newfunction, ring): + def map_coefficients(self, func, ring=None): """ - Return the series with ``newfunction`` applied to each coefficient of this series. + Return the series with ``func`` applied to each coefficient of this series. INPUT: - - ``newfunction`` -- Python function - - - ``ring`` -- Base Ring of the series - - Python function ``newfunction`` returns a new coefficient for input coefficient. + - ``func`` -- Python function that takes in a coefficient and returns + a new coefficient TESTS:: sage: L. = LLSRing(ZZ) sage: s = z/(1 - 2*z) - sage: t = s.apply_to_coefficients(lambda c: c + 1, L) + sage: t = s.map_coefficients(lambda c: c + 1, L) sage: s z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... sage: t 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... - sage: M = L.series(lambda n: n, True, 0); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.apply_to_coefficients(lambda c: c + 1, L); N + sage: N = M.map_coefficients(lambda c: c + 1, L); N 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - sage: M = L.series(lambda n: n, False, 0); M + + sage: L. = LLSRing(ZZ, sparse=False) + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.apply_to_coefficients(lambda c: c + 1, L); N + sage: N = M.map_coefficients(lambda c: c + 1, L); N 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... """ P = self.parent() - return P.element_class(P, LLS_apply_coeff(self._aux, newfunction, ring)) + R = P.base_ring() + if isinstance(self._aux, LLS_eventually_geometric): + p = p.map_coefficients(func) + c = func(c) + if not p and not c: + return P.zero() + return P.element_class(P, LLS_eventually_geometric(p, self._aux._is_sparse, c, d)) + return P.element_class(P, LLS_apply_coeff(self._aux, func, R)) def change_ring(self, ring): """ @@ -517,19 +568,21 @@ def change_ring(self, ring): TESTS:: - sage: L. = LLSRing(ZZ) + sage: L. = LLSRing(ZZ, sparse=False) sage: s = 2 + z sage: t = s.change_ring(QQ) sage: t^-1 1/2 - 1/4*z + 1/8*z^2 - 1/16*z^3 + 1/32*z^4 - 1/64*z^5 + 1/128*z^6 + ... - sage: M = L.series(lambda n: n, False, 0); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = M.change_ring(QQ) sage: N.parent() Lazy Laurent Series Ring in z over Rational Field sage: M.parent() Lazy Laurent Series Ring in z over Integer Ring - sage: M = L.series(lambda n: n, True, 0); M + + sage: L. = LLSRing(ZZ, sparse=True) + sage: M = L(lambda n: n, True, 0); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M.parent() Lazy Laurent Series Ring in z over Integer Ring @@ -540,7 +593,7 @@ def change_ring(self, ring): z^-1 - 2 + z + ... """ from .lazy_laurent_series_ring_new import LLSRing - Q = LLSRing(ring, names=self.parent().variable_name()) + Q = LLSRing(ring, names=self.parent().variable_names()) return Q.element_class(Q, self._aux) def truncate(self, d): @@ -553,7 +606,7 @@ def truncate(self, d): TESTS:: - sage: L. = LLSRing(ZZ) + sage: L. = LLSRing(ZZ, sparse=False) sage: alpha = 1/(1-z) sage: alpha 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... @@ -562,25 +615,26 @@ def truncate(self, d): 1 + z + z^2 + z^3 + z^4 sage: alpha - beta z^5 + z^6 + ... - sage: M = L.series(lambda n: n, False, 0); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M.truncate(4) z + 2*z^2 + 3*z^3 - sage: M = L.series(lambda n: n, True, 0); M + + sage: L. = LLSRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M.truncate(4) z + 2*z^2 + 3*z^3 """ P = self.parent() - c = (ZZ.zero(), d) - R = LaurentPolynomialRing(self.base_ring(), 'z') - p = R([self[i] for i in range(self._aux._approximate_valuation, d)]) - return P.element_class(P, LLS_eventually_geometric(p, c)) - # return P.element_class(P, LLS_trunc(self._aux, d)) + R = P._laurent_poly_ring + z = R.gen() + p = R.sum(self[i] * z**i for i in range(self._aux._approximate_valuation, d)) + return P.element_class(P, LLS_eventually_geometric(p, P._sparse, ZZ.zero(), d)) def __pow__(self, n): """ - Return the `n`-th power of the series. + Return the ``n``-th power of the series. TESTS:: @@ -593,11 +647,13 @@ def __pow__(self, n): 1 - 3*z + 3*z^2 - z^3 sage: (1 - z)^-3 1 + 3*z + 6*z^2 + 10*z^3 + 15*z^4 + 21*z^5 + 28*z^6 + ... - sage: M = L.series(lambda n: n, True, 0); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M ^ 2 z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... - sage: M = L.series(lambda n: n, False, 0); M + + sage: L. = LLSRing(ZZ, sparse=False) + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M ^ 2 z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... @@ -689,14 +745,14 @@ def polynomial(self, degree=None, name=None): EXAMPLES:: sage: L = LLSRing(ZZ, 'z') - sage: f = L.series([1,0,0,2,0,0,0,3], True, 5); f + sage: f = L([1,0,0,2,0,0,0,3], 5); f z^5 + 2*z^8 + 3*z^12 sage: f.polynomial() 3*z^12 + 2*z^8 + z^5 TESTS:: - sage: g = L.series([1,0,0,2,0,0,0,3], True, -5); g + sage: g = L([1,0,0,2,0,0,0,3], -5); g z^-5 + 2*z^-2 + 3*z^2 sage: g.polynomial() z^-5 + 2*z^-2 + 3*z^2 @@ -710,18 +766,19 @@ def polynomial(self, degree=None, name=None): z^-3 + z^-2 + z^-1 + 1 sage: f.polynomial(-5) 0 - sage: M = L.series(lambda n: n^2, False, 0) + sage: M = L(lambda n: n^2, 0) sage: M.polynomial(3) 9*z^3 + 4*z^2 + z - sage: M = L.series(lambda n: n^2, True, 0) + sage: M = L(lambda n: n^2, 0) sage: M.polynomial(5) 25*z^5 + 16*z^4 + 9*z^3 + 4*z^2 + z """ if degree is None: - if isinstance(self._aux, LLS_constant): - m = 1 - elif isinstance(self._aux, LLS_eventually_geometric) and self._aux._constant[0]: - m = self._aux._constant[1] + if isinstance(self._aux, LLS_zero): + from sage.rings.all import PolynomialRing + return PolynomialRing(S.base_ring(), name=name).zero() + elif isinstance(self._aux, LLS_eventually_geometric) and self._aux._constant: + m = self._aux._degree else: raise ValueError("not a polynomial") else: @@ -759,10 +816,10 @@ def valuation(self): sage: t = z - z sage: t.valuation() +Infinity - sage: M = L.series(lambda n: n^2, True, 0) + sage: M = L(lambda n: n^2, 0) sage: M.valuation() 1 - sage: M = L.series(lambda n: n^2, False, 0) + sage: M = L(lambda n: n^2, 0) sage: M.valuation() 1 """ @@ -778,44 +835,26 @@ def _repr_(self): sage: -1/(1 + 2*z) -1 + 2*z - 4*z^2 + 8*z^3 - 16*z^4 + 32*z^5 - 64*z^6 + ... """ + if isinstance(self._aux, LLS_zero): + return '0' + atomic_repr = self.base_ring()._repr_option('element_is_atomic') X = self.parent().variable_name() - n = self._aux._approximate_valuation + v = self._aux._approximate_valuation if not isinstance(self._aux, LLS_eventually_geometric): - m = n + 7 # long enough - elif self._aux._constant[0]: - m = self._aux._constant[1] + 3 + m = v + 7 # long enough + elif not self._aux._constant: + # Just a polynonial, so let that print itself + return repr(self._aux._laurent_polynomial) else: - m = self._aux._constant[1] - - s = ' ' - first = True - while n < m: - x = repr(self._aux[n]) - if x != '0': - if not first: - s += ' + ' - if not atomic_repr and n > 0 and (x[1:].find('+') != -1 or x[1:].find('-') != -1): - x = '({})'.format(x) - if n > 1 or n < 0: - var = '*{}^{}'.format(X, n) - elif n == 1: - var = '*{}'.format(X) - else: # n == 0 - var = '' - s += '{}{}'.format(x, var) - first = False - n += 1 + m = self._aux._degree + 3 - s = s.replace(" + -", " - ").replace(" 1*", " ").replace(" -1*", " -")[1:] - - if not s: # zero series - s = '0' - if not isinstance(self._aux, (LLS_constant, LLS_eventually_geometric)) or self._aux._constant[1] > m or self._aux._constant[0]: - s += ' + {}'.format('...') - - return s + # Use the polynomial printing + R = self.parent()._laurent_poly_ring + ret = repr(R([self._aux[i] for i in range(v, m)]).shift(v)) + # TODO: Better handling when ret == 0 but we have not checked up to the constant term + return ret + ' + ...' def _richcmp_(self, other, op): """ @@ -841,36 +880,26 @@ def _richcmp_(self, other, op): False """ if op is op_EQ: - if not isinstance(self._aux, LLS_eventually_geometric): - if not isinstance(other._aux, LLS_eventually_geometric): - # Implement the checking of the caches here. - n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) - m = max(self._aux._approximate_valuation, other._aux._approximate_valuation) - for i in range(n, m): - if self[i] != other[i]: - return False - if self._aux == other._aux: - return True - raise ValueError("undecidable as lazy Laurent series") - else: - raise ValueError("undecidable as lazy Laurent series") - elif not isinstance(other._aux, LLS_eventually_geometric): - raise ValueError("undecidable as lazy Laurent series") - - sc, sm = self._aux._constant - oc, om = other._aux._constant - - if sc != oc: + if isinstance(self._aux, LLS_zero): # self == 0 + return isinstance(other._aux, LLS_zero) + if isinstance(other._aux, LLS_zero): # self != 0 but other == 0 return False - n = self._aux._approximate_valuation - m = max(sm, om) - - for i in range(n, m): - if self[i] != other[i]: - return False + if (not isinstance(self._aux, LLS_eventually_geometric) + or not isinstance(other._aux, LLS_eventually_geometric)): + # One of the lazy laurent series is not known to eventually be constant + # Implement the checking of the caches here. + n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) + m = max(self._aux._approximate_valuation, other._aux._approximate_valuation) + for i in range(n, m): + if self[i] != other[i]: + return False + if self._aux == other._aux: + return True + raise ValueError("undecidable as lazy Laurent series") - return True + # Both are LLS_eventually_geometric, which implements a full check + return self._aux == other._aux if op is op_NE: return not (self == other) @@ -884,12 +913,12 @@ def __hash__(self): TESTS:: sage: L = LLSRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], True, -5) + sage: f = L([1,2,3,4], -5) sage: g = (1 + f)/(1 - f)^2 sage: {g: 1} {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} """ - return hash((type(self), self._aux._approximate_valuation, self._aux._constant)) + return hash(self._aux) def __bool__(self): """ @@ -903,35 +932,35 @@ def __bool__(self): sage: f = 1/(1 - z) sage: f.is_zero() False - sage: M = L.series(lambda n: n, True, 0); M + sage: M = L(lambda n: n, 0); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M.is_zero() False """ - if self._aux._constant is None: - for a in self._aux._cache: - if a: - return True - if self[self._aux._approximate_valuation]: - return True - raise ValueError("undecidable as lazy Laurent series") - - sc, sm = self._aux._constant - - if sc: - return True + if isinstance(self._aux, LLS_zero): + return False + if isinstance(self._aux, LLS_eventually_geometric): + # This should always end up being True, but let's be careful about it for now... + return self._aux._laurent_polynomial or self._aux._constant - for i in range(self._aux._approximate_valuation, sm): - if self[i]: + for a in self._aux._cache: + if a: return True - - return False - + if self[self._aux._approximate_valuation]: + return True + raise ValueError("undecidable as lazy Laurent series") class LLS_aux(): + """ + Abstract base class for all auxillary LLS. + """ + def __init__(self, sparse=False): + self._is_sparse = sparse + +class LLS_inexact(LLS_aux): def __init__(self, is_sparse, approximate_valuation): self._approximate_valuation = approximate_valuation - self._is_sparse = is_sparse + super().__init__(is_sparse) if self._is_sparse: self._cache = dict() # cache of known coefficients @@ -941,9 +970,7 @@ def __init__(self, is_sparse, approximate_valuation): self._iter = self.iterate_coefficients() def __getitem__(self, n): - if self._approximate_valuation is infinity: - return ZZ.zero() - elif n < self._approximate_valuation: + if n < self._approximate_valuation: return ZZ.zero() if self._is_sparse: @@ -995,7 +1022,7 @@ def valuation(self): n += 1 -class LLS_unary(LLS_aux): +class LLS_unary(LLS_inexact): """ Abstract base class for unary operators. @@ -1052,7 +1079,7 @@ def __eq__(self, other): return isinstance(other, type(self)) and self._series == other._series -class LLS_binary(LLS_aux): +class LLS_binary(LLS_inexact): def __init__(self, left, right, *args, **kwargs): """ @@ -1118,55 +1145,51 @@ def __eq__(self, other): class LLS_eventually_geometric(LLS_aux): - - def __init__(self, laurent_polynomial, constant=None): + def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): if constant is None: - self._constant = (ZZ.zero(), laurent_polynomial.degree() + 1) + constant = ZZ.zero() + if degree is None: + if not laurent_polynomial: + raise ValueError("you must specify the degree for the polynomial 0") + degree = laurent_polynomial.degree() + 1 else: - self._constant = constant - assert(not laurent_polynomial or laurent_polynomial.degree() < constant[1]) + # Consistency check + assert not laurent_polynomial or laurent_polynomial.degree() < degree + + self._constant = constant + self._degree = degree self._laurent_polynomial = laurent_polynomial - v = self._laurent_polynomial.valuation() - if v is Infinity: - if self._constant[0]: # Non-zero series - v = self._constant[1] - self._approximate_valuation = v - self._is_sparse = False #TODO Remove this + self._approximate_valuation = self._laurent_polynomial.valuation() + super().__init__(is_sparse) def __getitem__(self, n): - if n >= self._constant[1]: - return self._constant[0] + if n >= self._degree: + return self._constant return self._laurent_polynomial[n] def valuation(self): return self._approximate_valuation - - def __eq__(self, other): - if not isinstance(other, type(self)): - return False - return self._laurent_polynomial == other._laurent_polynomial and self._constant == other._constant + def __hash__(self): + return hash((self._laurent_polynomial, self._degree, self._constant)) -class LLS_constant(LLS_aux): + def __eq__(self, other): + return (isinstance(other, type(self)) + and self._degree == other._degree + and self._laurent_polynomial == other._laurent_polynomial + and self._constant == other._constant) - def __init__(self, a): - self._a = a - if self._a: - self._approximate_valuation = ZZ.zero() - self._approximate_valuation = Infinity - self._constant = (ZZ.zero(), 1) - self._is_sparse = False # TODO Remove - +class LLS_zero(LLS_aux): def __getitem__(self, n): - if n: - return ZZ.zero() - return self._a - + return ZZ.zero() + def valuation(self): - return self._approximate_valuation + return Infinity + def __hash__(self): + return 0 -class LLS_coefficient_function(LLS_aux): +class LLS_coefficient_function(LLS_inexact): def __init__(self, coefficient_function, is_sparse, approximate_valuation): self._coefficient_function = coefficient_function super().__init__(is_sparse, approximate_valuation) @@ -1179,76 +1202,39 @@ def iterate_coefficients(self): while True: yield self._coefficient_function(n) n += 1 - - -class LLS_mul(LLS_binary): - """ - Operator for multiplication. - - We are assuming commutativity of the coefficient ring here. - """ - def __init__(self, left, right): - a = left._approximate_valuation + right._approximate_valuation - if left._is_sparse != right._is_sparse: - raise NotImplementedError - super().__init__(left, right, left._is_sparse, a) - - def get_coefficient(self, n): - c = ZZ.zero() - for k in range(self._left._approximate_valuation, - n - self._right._approximate_valuation + 1): - l = self._left[k] - if l: - c += l * self._right[n-k] - return c - - def iterate_coefficients(self): - n = self._offset - while True: - c = ZZ.zero() - for k in range(self._left._approximate_valuation, - n - self._right._approximate_valuation + 1): - l = self._left[k] - if l: - c += l * self._right[n-k] - yield c - n += 1 +##################################################################### +## Binary operations class LLS_add(LLS_binary): """ Operator for addition. """ def __init__(self, left, right): - a = min(left._approximate_valuation, right._approximate_valuation) if left._is_sparse != right._is_sparse: raise NotImplementedError + a = min(left._approximate_valuation, right._approximate_valuation) super().__init__(left, right, left._is_sparse, a) def get_coefficient(self, n): - c = ZZ.zero() - c = self._left[n] + self._right[n] - return c + return self._left[n] + self._right[n] def iterate_coefficients(self): n = self._offset while True: - c = ZZ.zero() - c = self._left[n] + self._right[n] - yield c + yield self._left[n] + self._right[n] n += 1 - class LLS_sub(LLS_binary): """ Operator for subtraction. """ def __init__(self, left, right): - a = min(left._approximate_valuation, right._approximate_valuation) if left._is_sparse != right._is_sparse: raise NotImplementedError - + + a = min(left._approximate_valuation, right._approximate_valuation) super().__init__(left, right, left._is_sparse, a) def get_coefficient(self, n): @@ -1262,75 +1248,131 @@ def get_coefficient(self, n): sage: f.coefficient(2) -1 """ + return self._left[n] - self._right[n] + + def iterate_coefficients(self): + n = self._offset + while True: + yield self._left[n] - self._right[n] + n += 1 + +class LLS_mul(LLS_binary): + """ + Operator for multiplication. + + We are assuming commutativity of the coefficient ring here. + """ + def __init__(self, left, right): + if left._is_sparse != right._is_sparse: + raise NotImplementedError + + a = left._approximate_valuation + right._approximate_valuation + super().__init__(left, right, left._is_sparse, a) + + def get_coefficient(self, n): c = ZZ.zero() - c = self._left[n] - self._right[n] + for k in range(self._left._approximate_valuation, + n - self._right._approximate_valuation + 1): + l = self._left[k] + if l: + c += l * self._right[n-k] return c def iterate_coefficients(self): n = self._offset while True: c = ZZ.zero() - c = self._left[n] - self._right[n] + for k in range(self._left._approximate_valuation, + n - self._right._approximate_valuation + 1): + l = self._left[k] + if l: + c += l * self._right[n-k] yield c n += 1 +class LLS_div(LLS_binary): + """ + Return ``left`` divided by ``right``. + """ + def __init__(self, left, right): + lv = left.valuation() + rv = right.valuation() + self._lv = lv + self._rv = rv + self._ainv = ~right[rv] + super().__init__(left, right, left._is_sparse, lv - rv) + + def get_coefficient(self, n): + lv = self._lv + rv = self._rv + if n == lv - rv: + return self._left[lv] / self._right[rv] + c = self._left[n + rv] + for k in range(lv - rv, n): + c -= self[k] * self._right[n + rv - k] + return c * self._ainv + + def iterate_coefficients(self): + n = self._offset + lv = self._lv + rv = self._rv + while True: + if n == lv - rv: + yield self._left[lv] / self._right[rv] + n += 1 + continue + c = self._left[n + rv] + for k in range(lv - rv, n): + c -= self[k] * self._right[n + rv - k] + yield c * self._ainv + n += 1 + +##################################################################### +## Unary operations -class LLS_rmul(LLS_unary): +class LLS_scalar(LLS_unary): """ Operator for multiplying with a scalar. """ def __init__(self, series, scalar): self._scalar = scalar - a = series._approximate_valuation - super().__init__(series, series._is_sparse, a) + super().__init__(series, series._is_sparse, series._approximate_valuation) def get_coefficient(self, n): - c = self._series[n] * self._scalar - return c + return self._series[n] * self._scalar def iterate_coefficients(self): n = self._offset while True: - c = self._series[n] * self._scalar - yield c + yield self._series[n] * self._scalar n += 1 - class LLS_neg(LLS_unary): """ Operator for negative of the series. """ def __init__(self, series): - a = series._approximate_valuation - super().__init__(series, series._is_sparse, a) + super().__init__(series, series._is_sparse, series._approximate_valuation) def get_coefficient(self, n): - c = -1 * self._series[n] - return c + return -self._series[n] def iterate_coefficients(self): n = self._offset while True: - c = -1 * self._series[n] - yield c + yield -self._series[n] n += 1 - class LLS_inv(LLS_unary): """ Operator for multiplicative inverse of the series. """ def __init__(self, series): v = series.valuation() - # self._constant can be refined super().__init__(series, series._is_sparse, -v) - if v is infinity: - raise ZeroDivisionError('cannot invert zero') - - # self._ainv = ~series[v] - self._ainv = series[v].inverse_of_unit() + self._ainv = ~series[v] self._zero = ZZ.zero() def get_coefficient(self, n): @@ -1356,113 +1398,23 @@ def iterate_coefficients(self): yield -c * self._ainv n += 1 - class LLS_apply_coeff(LLS_unary): """ - Return the series with ``function`` applied to each coefficient of this series. + Return the series with ``function`` applied to each coefficient of this series. """ def __init__(self, series, function, ring): self._function = function self._ring = ring - a = series._approximate_valuation - - # if series._constant: - # c = (function(series._constant[0]), series._constant[1]) - # else: - # c = None - - super().__init__(series, series._is_sparse, a) + super().__init__(series, series._is_sparse, series._approximate_valuation) def get_coefficient(self, n): - try: - c = self._ring(self._function(self._series[n])) - return c - except TypeError: - raise ValueError("The coefficients are not in the base ring.") - - def iterate_coefficients(self): - n = self._offset - while True: - c = self._function(self._series[n]) - try: - c = self._ring(self._function(self._series[n])) - yield c - n += 1 - except TypeError: - raise ValueError("The coefficients are not in the base ring.") - - -class LLS_trunc(LLS_unary): - """ - Return this series with its terms of degree >= ``d`` truncated. - """ - def __init__(self, series, d): - self._d = d - self._zero = ZZ.zero() - a = series._approximate_valuation - super().__init__(series, series._is_sparse, a) - - def get_coefficient(self, n): - if n <= self._d: - c = self._series[n] - return c - else: - c = self._zero - return c - - def iterate_coefficients(self): - n = self._offset - while True: - if n <= self._d: - c = self._series[n] - yield c - n += 1 - continue - else: - c = self._zero - yield c - n += 1 - continue - - -class LLS_div(LLS_binary): - """ - Return ``self`` divided by ``other``. - """ - def __init__(self, left, right): - - super().__init__(left, right, left._is_sparse, left._approximate_valuation) - - self._approximate_valuation = left.valuation() - right.valuation() - - lv = left.valuation() - rv = right.valuation() - self._lv = lv - self._rv = rv - # self._ainv = ~right[rv] - self._ainv = right[rv].inverse_of_unit() - - def get_coefficient(self, n): - lv = self._lv - rv = self._rv - if n == lv - rv: - return self._left[lv]/self._right[rv] - c = self._left[n + rv] - for k in range(lv - rv, n): - c -= self[k] * self._right[n + rv - k] - return c * self._ainv + c = self._ring(self._function(self._series[n])) + return c def iterate_coefficients(self): n = self._offset - lv = self._lv - rv = self._rv while True: - if n == lv - rv: - yield self._left[lv]/self._right[rv] - n += 1 - continue - c = self._left[n + rv] - for k in range(lv - rv, n): - c -= self[k] * self._right[n + rv - k] - yield c * self._ainv + c = self._ring(self._function(self._series[n])) + yield c n += 1 + diff --git a/src/sage/rings/lazy_laurent_series_ring_new.py b/src/sage/rings/lazy_laurent_series_ring_new.py index 4473a7597e5..0a93bff5b3c 100644 --- a/src/sage/rings/lazy_laurent_series_ring_new.py +++ b/src/sage/rings/lazy_laurent_series_ring_new.py @@ -76,16 +76,9 @@ from .lazy_laurent_series_new import ( LLS, LLS_coefficient_function, - LLS_constant, + LLS_zero, LLS_eventually_geometric ) -from .lazy_laurent_series_operator_new import ( - LLSOperator_gen, - LLSOperator_constant, - LLSOperator_list, - LLSOperator_polynomial -) - class LLSRing(UniqueRepresentation, Parent): """ @@ -105,7 +98,7 @@ class LLSRing(UniqueRepresentation, Parent): # Element = LLS Element = LLS - def __init__(self, base_ring, names, category=None): + def __init__(self, base_ring, names, sparse=False, category=None): """ Initialize. @@ -114,6 +107,8 @@ def __init__(self, base_ring, names, category=None): sage: L = LLSRing(ZZ, 't') sage: TestSuite(L).run(skip='_test_elements') """ + self._sparse = sparse + self._laurent_poly_ring = LaurentPolynomialRing(base_ring, names, sparse=sparse) Parent.__init__(self, base=base_ring, names=names, category=MagmasAndAdditiveMagmas().or_subcategory(category)) @@ -143,11 +138,10 @@ def gen(self, n=0): ... IndexError: there is only one generator """ - # Always a sparse implementation. if n != 0: raise IndexError("there is only one generator") - R = LaurentPolynomialRing(self.base_ring(), 'z') - aux = LLS_eventually_geometric(R.gen()) + R = self._laurent_poly_ring + aux = LLS_eventually_geometric(R.gen(n), self._sparse, R.zero(), 2) return self.element_class(self, aux) def ngens(self): @@ -164,6 +158,7 @@ def ngens(self): """ return 1 + @cached_method def gens(self): """ Return the tuple of the generator. @@ -174,7 +169,7 @@ def gens(self): sage: 1/(1 - z) 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... """ - return (self.gen(),) + return tuple([self.gen(n) for n in range(self.ngens())]) def _coerce_map_from_(self, S): """ @@ -191,15 +186,15 @@ def _coerce_map_from_(self, S): if self.base_ring().has_coerce_map_from(S): return True - if isinstance(S, (PolynomialRing_general, LaurentPolynomialRing_generic)) and S.ngens() == 1: + R = self._laurent_poly_ring + if R.has_coerce_map_from(S): def make_series_from(poly): - return self.element_class(self, LLS_eventually_geometric(poly)) - + return self.element_class(self, LLS_eventually_geometric(R(poly), self._sparse)) return SetMorphism(Hom(S, self, Sets()), make_series_from) return False - def _element_constructor_(self, x): + def _element_constructor_(self, x, valuation=None, constant=None, degree=None): """ Construct a Laurent series from ``x``. @@ -210,13 +205,77 @@ def _element_constructor_(self, x): 0 sage: L(3) 1 - """ - R = LaurentPolynomialRing(self.base_ring(), 'z') # TODO LLS_Constant() would be better - aux = LLS_eventually_geometric(R(x)) - return self.element_class(self, aux) - def _an_element_(self, is_sparse=True): - # Always a sparse implementation. + sage: L = LLSRing(ZZ, 'z') + + sage: L(lambda i: i, 5, 1, 10) + 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... + sage: L(lambda i: i, 5, (1,10)) + 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... + + sage: def g(s, i): + ....: if i < 0: + ....: return 1 + ....: else: + ....: return s.coefficient(i - 1) + i + sage: e = L(g, -5); e + z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... + sage: f = e^-1; f + z^5 - z^6 - z^11 + ... + sage: f.coefficient(10) + 0 + sage: f.coefficient(20) + 9 + sage: f.coefficient(30) + -219 + + Alternatively, the ``coefficient_function`` can be a list of elements of the + base ring. Then these elements are read as coefficients of the terms of + degrees starting from the ``valuation``. In this case, ``constant`` + may be just an element of the base ring instead of a tuple or can be + simply omitted if it is zero:: + + sage: f = L([1,2,3,4], -5) + sage: f + z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 + sage: g = L([1,3,5,7,9], 5, -1) + sage: g + z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... + """ + R = self._laurent_poly_ring + try: + # Try to build stuff using the polynomial ring constructor + x = R(x) + except (TypeError, ValueError): + pass + if isinstance(constant, (tuple, list)): + constant, degree = constant + if x in R: + if not x: + aux = LLS_zero() + else: + if valuation: + x = x.shift(valuation - x.valuation()) + aux = LLS_eventually_geometric(R(x), self._sparse, constant, degree) + return self.element_class(self, aux) + if isinstance(x, LLS): + if x._aux._is_sparse is self._sparse: + return self.element_class(self, x._aux) + # TODO: Implement a way to make a self._sparse copy + raise NotImplementedError("cannot convert between sparse and dense") + if callable(x): + if valuation is None: + valuation = 0 + if degree: + if constant is None: + constant = self.base_ring().zero() + z = R.gen() + p = R.sum(x(i) * z**i for i in range(valuation, degree)) + self.element_class(self, LLS_eventually_geometric(p, self._sparse, constant, degree)) + return self.element_class(self, LLS_coefficient_function(x, self._sparse, valuation)) + raise ValueError(f"unable to convert {x} into a lazy Laurent series") + + def _an_element_(self): """ Return a Laurent series in this ring. @@ -226,10 +285,11 @@ def _an_element_(self, is_sparse=True): sage: L.an_element() z^-10 + z^-9 + z^-8 + ... """ - e = self.base_ring().an_element() - R = LaurentPolynomialRing(self.base_ring(), 'z') - return self.element_class(self, LLS_eventually_geometric(R(ZZ.zero()), (e, -10))) # TODO Should be LLS_constant() + c = self.base_ring().an_element() + R = self._laurent_poly_ring + return self.element_class(self, LLS_eventually_geometric(R.zero(), self._sparse, c, -10)) + @cached_method def one(self): """ Return the constant series `1`. @@ -240,8 +300,10 @@ def one(self): sage: L.one() 1 """ - return self._element_constructor_(1) + R = self._laurent_poly_ring + return self.element_class(self, LLS_eventually_geometric(R.one(), self._sparse, 0, 1)) + @cached_method def zero(self): """ Return the zero series. @@ -252,98 +314,5 @@ def zero(self): sage: L.zero() 0 """ - return self._element_constructor_(0) - - def series(self, coefficient_function, is_sparse, approximate_valuation, constant=None): - r""" - Return a lazy Laurent series. - - INPUT: + return self.element_class(self, LLS_zero(self._sparse)) - - ``coefficient_function`` -- Python function that computes coefficients - - - ``issparse`` -- Boolean that determines whether the implementation is sparse or dense - - - ``approximate_valuation`` -- integer; approximate valuation of the series - - - ``constant`` -- either ``None`` or pair of an element of the base ring and an integer - - Let the coefficient of index `i` mean the coefficient of the term of the - series with exponent `i`. - - Python function ``coefficient_function`` returns the value of the coefficient of - index `i` from input. - - Let ``approximate_valuation`` be `n`. All coefficients of index below `n` are zero. If - ``constant`` is ``None``, then the ``coefficient_function`` function is responsible - to compute the values of all coefficients of index `\ge n`. If ``constant`` - is a pair `(c,m)`, then the ``coefficient_function`` function is responsible to - compute the values of all coefficients of index `\ge n` and `< m` and all - the coefficients of index `\ge m` is the constant `c`. - - EXAMPLES:: - - sage: L = LLSRing(ZZ, 'z') - sage: L.series(lambda i: i, True, 5, (1,10)) # not tested - 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... - - sage: def g(s, i): - ....: if i < 0: - ....: return 1 - ....: else: - ....: return s.coefficient(i - 1) + i - sage: e = L.series(g, True, -5); e # not tested - z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... - sage: f = e^-1; f # not tested - z^5 - z^6 - z^11 + ... - sage: f.coefficient(10) # not tested - 0 - sage: f.coefficient(20) # not tested - 9 - sage: f.coefficient(30) # not tested - -219 - - Alternatively, the ``coefficient_function`` can be a list of elements of the - base ring. Then these elements are read as coefficients of the terms of - degrees starting from the ``valuation``. In this case, ``constant`` - may be just an element of the base ring instead of a tuple or can be - simply omitted if it is zero. - - EXAMPLES:: - - sage: L = LLSRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], True, -5) # not tested - sage: f # not tested - z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 - sage: g = L.series([1,3,5,7,9], True, 5, -1) # not tested - sage: g # not tested - z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... - """ - if constant is not None: - # poly = LaurentPolynomialRing, compute the polynomial. - raise NotImplementedError() - return LLS_eventually_geometric(poly, constant) - if isinstance(coefficient_function, (tuple, list)): - raise NotImplementedError() - return LLS_eventually_geometric(poly, constant) - if isinstance(constant, tuple): - constant = constant[0] - if constant is None: - constant = self.base_ring().zero() - elif constant not in self.base_ring(): - raise ValueError("constant is not an element of the base ring") - constant = (constant, approximate_valuation + len(coefficient_function)) - coefficient_function = LLSOperator_list(self, coefficient_function, approximate_valuation) - # elif constant is not None: - # try: - # c,m = constant - # except TypeError: - # raise TypeError('not a tuple') - - # if approximate_valuation > m and c: # weird case - # raise ValueError('inappropriate valuation') - - # constant = (self.base_ring()(c), m) - - aux = LLS_coefficient_function(coefficient_function=coefficient_function, is_sparse=is_sparse, approximate_valuation=approximate_valuation) - return self.element_class(self, aux) \ No newline at end of file From 03fd8d0f069b9e13b2cbd95fbb20935e2bd5e971 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 15 Jul 2021 15:12:20 +1000 Subject: [PATCH 57/98] Adding NotImplementedError for non-trivial p input for sparse polynomials. --- src/sage/rings/polynomial/polynomial_element_generic.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index d9578903c64..6e6204fdc38 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -180,7 +180,7 @@ def exponents(self): """ return sorted(self.__coeffs) - def valuation(self): + def valuation(self, p=None): """ Return the valuation of ``self``. @@ -197,6 +197,13 @@ def valuation(self): """ if not self.__coeffs: return infinity + + if p is infinity: + return -self.degree() + + if p is not None: + raise NotImplementedError("input p is not support for sparse polynomials") + return ZZ(min(self.__coeffs)) def _derivative(self, var=None): From 6d0d9635cb9da2561478b30052174888e0b2531a Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 15 Jul 2021 15:44:08 +1000 Subject: [PATCH 58/98] Implementing uninitialized series and some other fixes. --- src/sage/rings/lazy_laurent_series_new.py | 136 +++++++++--------- .../rings/lazy_laurent_series_ring_new.py | 10 +- 2 files changed, 80 insertions(+), 66 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index a13e7c20447..58cb2f0e123 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -14,12 +14,7 @@ This defines the generating function of Fibonacci sequence:: - sage: def coeff(s, i): - ....: if i in [0, 1]: - ....: return 1 - ....: else: - ....: return s.coefficient(i - 1) + s.coefficient(i - 2) - sage: f = L(coeff, valuation=0); f + sage: f = 1 / (1 - z - z^2) 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... The 100th element of Fibonacci sequence can be obtained from the generating @@ -35,7 +30,7 @@ sage: f._aux._cache[101] Traceback (most recent call last): ... - KeyError: 101 + IndexError: list index out of range You can do arithmetic with lazy power series:: @@ -113,7 +108,7 @@ class LLS(ModuleElement): EXAMPLES:: - sage: L = LLSRing(ZZ, 'z') + sage: L. = LLSRing(ZZ) sage: L(lambda i: i, valuation=-3, constant=(-1,3)) -3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ... sage: L(lambda i: i, valuation=-3, constant=-1, degree=3) @@ -121,22 +116,13 @@ class LLS(ModuleElement): :: - sage: def coeff(s, i): - ....: if i in [0, 1]: - ....: return 1 - ....: else: - ....: return s.coefficient(i - 1) + s.coefficient(i - 2) - sage: f = L(coeff); f + sage: f = 1 / (1 - z - z^2); f 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... sage: f.coefficient(100) 573147844013817084101 Lazy Laurent series is picklable:: - sage: z = L.gen() - sage: f = 1/(1 - z - z^2) - sage: f - 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... sage: g = loads(dumps(f)) sage: g 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... @@ -170,15 +156,13 @@ def __getitem__(self, n): sage: f = z/(1 - 2*z^3) sage: [f[n] for n in range(20)] [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] - sage: M = L(lambda n: n) - sage: L. = LLSRing(ZZ, sparse=True) - sage: [M[n] for n in range(20)] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] - sage: L. = LLSRing(ZZ, sparse=False) sage: M = L(lambda n: n) sage: [M[n] for n in range(20)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] - + sage: L. = LLSRing(ZZ, sparse=True) + sage: M = L(lambda n: n) + sage: [M[n] for n in range(20)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] """ return self.base_ring()(self._aux[n]) @@ -197,18 +181,18 @@ def _mul_(self, other): 1 - 2*z + z^2 sage: (1 - z)*(1 - z)*(1 - z) 1 - 3*z + 3*z^2 - z^3 - sage: M = L(lambda n: n, True, 0) + sage: M = L(lambda n: n) sage: M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = M * (1 - M) sage: N z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 + ... - sage: L. = LLSRing(ZZ, sparse=False) + sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M * N; P + sage: M * N z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + ... """ P = self.parent() @@ -250,7 +234,7 @@ def _add_(self, other): sage: P = M + N; P 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - sage: L. = LLSRing(ZZ, sparse=False) + sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = L(lambda n: 1); N @@ -281,7 +265,7 @@ def _div_(self, other): TESTS:: - sage: L. = LLSRing(ZZ, sparse=False) + sage: L. = LLSRing(ZZ) sage: z/(1 - z) z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... sage: M = L(lambda n: n); M @@ -338,15 +322,19 @@ def _rmul_(self, scalar): -z sage: 0*z 0 + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M * 3 + 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... - sage: L. = LLSRing(ZZ, sparse=False) + sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M._rmul_(3) + sage: M * 3 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: N._rmul_(4) + sage: N * 4 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... """ P = self.parent() @@ -370,7 +358,7 @@ def _sub_(self, other): TESTS:: - sage: L. = LLSRing(ZZ, sparse=False) + sage: L. = LLSRing(ZZ) sage: z - z 0 sage: 3*z - 2*z @@ -419,7 +407,7 @@ def _neg_(self): sage: P = -M; P -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... - sage: L. = LLSRing(ZZ, sparse=False) + sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: P = -M; P @@ -446,7 +434,7 @@ def __invert__(self): 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = ~M; P + sage: P = ~M; P z^-1 - 2 + z + ... sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M @@ -467,29 +455,19 @@ def coefficient(self, n): EXAMPLES:: - sage: L = LLSRing(ZZ, 'z') - sage: def g(s, i): - ....: if i == 0: - ....: return 1 - ....: else: - ....: return sum(s.coefficient(j)*s.coefficient(i - 1 -j) for j in range(i)) - sage: e = L(g) - sage: e.coefficient(10) + sage: L. = LLSRing(ZZ) + sage: F = L(None) + sage: F.define(1 + z*F^2) + sage: F.coefficient(10) 16796 - sage: e + sage: F 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... TESTS:: - sage: L. = LLSRing(ZZ, sparse=False) - sage: def g(s, i): - ....: if i == 0: - ....: return 1 - ....: else: - ....: return sum(s.coefficient(j)*s.coefficient(i - 1 - j) for j in [0..i-1]) - ....: - sage: L = LLSRing(ZZ, 'z') - sage: e = L(g) + sage: L. = LLSRing(ZZ, sparse=False) + sage: e = L(None) + sage: e.define(1 + z*e^2) sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... sage: e._aux._cache @@ -507,7 +485,7 @@ def coefficient(self, n): sage: M._aux._cache [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] - sage: L. = LLSRing(ZZ, sparse=True) + sage: L = LLSRing(ZZ, 'z', sparse=True) sage: M = L(lambda n: n^2); M z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... sage: M._aux._cache @@ -532,20 +510,20 @@ def map_coefficients(self, func, ring=None): sage: L. = LLSRing(ZZ) sage: s = z/(1 - 2*z) - sage: t = s.map_coefficients(lambda c: c + 1, L) + sage: t = s.map_coefficients(lambda c: c + 1) sage: s z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... sage: t 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.map_coefficients(lambda c: c + 1, L); N + sage: N = M.map_coefficients(lambda c: c + 1); N 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - sage: L. = LLSRing(ZZ, sparse=False) + sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.map_coefficients(lambda c: c + 1, L); N + sage: N = M.map_coefficients(lambda c: c + 1); N 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... """ P = self.parent() @@ -652,7 +630,7 @@ def __pow__(self, n): sage: M ^ 2 z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... - sage: L. = LLSRing(ZZ, sparse=False) + sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M ^ 2 @@ -837,6 +815,8 @@ def _repr_(self): """ if isinstance(self._aux, LLS_zero): return '0' + if isinstance(self._aux, LLS_uninitialized) and self._aux._target is None: + return 'Uninitialized LLS' atomic_repr = self.base_ring()._repr_option('element_is_atomic') X = self.parent().variable_name() @@ -950,17 +930,29 @@ def __bool__(self): return True raise ValueError("undecidable as lazy Laurent series") + def define(self, s): + """ + Define an equation by ``self = s``. + """ + if not isinstance(self._aux, LLS_uninitialized): + raise ValueError("series already defined") + self._aux._target = s._aux + class LLS_aux(): """ Abstract base class for all auxillary LLS. """ - def __init__(self, sparse=False): + def __init__(self, sparse, approximate_valuation): self._is_sparse = sparse + self._approximate_valuation = approximate_valuation class LLS_inexact(LLS_aux): + """ + LLS aux class when it is not or we do not know if it is + eventually geometric. + """ def __init__(self, is_sparse, approximate_valuation): - self._approximate_valuation = approximate_valuation - super().__init__(is_sparse) + super().__init__(is_sparse, approximate_valuation) if self._is_sparse: self._cache = dict() # cache of known coefficients @@ -1159,8 +1151,7 @@ def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): self._constant = constant self._degree = degree self._laurent_polynomial = laurent_polynomial - self._approximate_valuation = self._laurent_polynomial.valuation() - super().__init__(is_sparse) + super().__init__(is_sparse, self._laurent_polynomial.valuation()) def __getitem__(self, n): if n >= self._degree: @@ -1180,6 +1171,9 @@ def __eq__(self, other): and self._constant == other._constant) class LLS_zero(LLS_aux): + def __init__(self, sparse): + return super().__init__(sparse, 0) + def __getitem__(self, n): return ZZ.zero() @@ -1203,6 +1197,20 @@ def iterate_coefficients(self): yield self._coefficient_function(n) n += 1 +class LLS_uninitialized(LLS_inexact): + def __init__(self, is_sparse, approximate_valuation): + self._target = None + super().__init__(is_sparse, approximate_valuation) + + def get_coefficient(self, n): + return self._target[n] + + def iterate_coefficients(self): + n = self._approximate_valuation + while True: + yield self._target[n] + n += 1 + ##################################################################### ## Binary operations diff --git a/src/sage/rings/lazy_laurent_series_ring_new.py b/src/sage/rings/lazy_laurent_series_ring_new.py index 0a93bff5b3c..741c7e0821b 100644 --- a/src/sage/rings/lazy_laurent_series_ring_new.py +++ b/src/sage/rings/lazy_laurent_series_ring_new.py @@ -77,7 +77,8 @@ LLS, LLS_coefficient_function, LLS_zero, - LLS_eventually_geometric + LLS_eventually_geometric, + LLS_uninitialized ) class LLSRing(UniqueRepresentation, Parent): @@ -194,7 +195,7 @@ def make_series_from(poly): return False - def _element_constructor_(self, x, valuation=None, constant=None, degree=None): + def _element_constructor_(self, x=None, valuation=None, constant=None, degree=None): """ Construct a Laurent series from ``x``. @@ -242,6 +243,11 @@ def _element_constructor_(self, x, valuation=None, constant=None, degree=None): sage: g z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... """ + if x is None: + if valuation is None: + valuation = 0 + return self.element_class(self, LLS_uninitialized(self._sparse, valuation)) + R = self._laurent_poly_ring try: # Try to build stuff using the polynomial ring constructor From 944b10e0e9959073b388bbbdb6bfcef0a2309b55 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 15 Jul 2021 17:56:41 +1000 Subject: [PATCH 59/98] Fixing some bugs and making sure output of coeff_func are better. --- src/sage/rings/lazy_laurent_series_new.py | 136 +++++++++++------- .../rings/lazy_laurent_series_ring_new.py | 12 +- 2 files changed, 89 insertions(+), 59 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 58cb2f0e123..207fc7eb52e 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -14,7 +14,8 @@ This defines the generating function of Fibonacci sequence:: - sage: f = 1 / (1 - z - z^2) + sage: f = 1 / (1 - z - z^2) + sage: f 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... The 100th element of Fibonacci sequence can be obtained from the generating @@ -234,6 +235,11 @@ def _add_(self, other): sage: P = M + N; P 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + sage: A = L(1, constant=2, degree=3) + sage: B = L(2, constant=-2, degree=5) + sage: A + B + 3 + 2*z^3 + 2*z^4 + sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... @@ -247,14 +253,80 @@ def _add_(self, other): right = other._aux if (isinstance(left, LLS_eventually_geometric) and isinstance(right, LLS_eventually_geometric)): + R = P._laurent_poly_ring c = left._constant + right._constant - p = left._laurent_polynomial + right._laurent_polynomial + pl = left._laurent_polynomial + pr = right._laurent_polynomial + d = max(left._degree, right._degree) + pl += R([left._constant]*(d-left._degree)).shift(left._degree) + pr += R([right._constant]*(d-right._degree)).shift(right._degree) + p = pl + pr if not p and not c: return P.zero() - d = max(left._degree, right._degree) return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) return P.element_class(P, LLS_add(self._aux, other._aux)) + def _sub_(self, other): + """ + Return the series of this series minus ``other`` series. + + INPUT: + + - ``other`` -- other series + + TESTS:: + + sage: L. = LLSRing(ZZ) + sage: z - z + 0 + sage: 3*z - 2*z + z + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M - N; P + -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... + + sage: A = L(1, constant=2, degree=3) + sage: B = L(2, constant=3, degree=5) + sage: A - B + -1 + 2*z^3 + 2*z^4 - z^5 - z^6 - z^7 + ... + + sage: A = L(1, constant=2, degree=3) + sage: B = L([1,0,0,2,2], constant=2) + sage: X = A - B; X + 0 + sage: type(X._aux) + + + sage: L. = LLSRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M - N; P + -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... + """ + P = self.parent() + left = self._aux + right = other._aux + if (isinstance(left, LLS_eventually_geometric) and isinstance(right, LLS_eventually_geometric)): + R = P._laurent_poly_ring + c = left._constant - right._constant + pl = left._laurent_polynomial + pr = right._laurent_polynomial + d = max(left._degree, right._degree) + pl += R([left._constant]*(d-left._degree)).shift(left._degree) + pr += R([right._constant]*(d-right._degree)).shift(right._degree) + p = pl - pr + if not p and not c: + return P.zero() + return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) + if left == right: + return P.zero() + return P.element_class(P, LLS_sub(self._aux, other._aux)) + def _div_(self, other): """ Return ``self`` divided by ``other``. @@ -348,50 +420,6 @@ def _rmul_(self, scalar): return P.element_class(P, LLS_scalar(self._aux, scalar)) - def _sub_(self, other): - """ - Return the series of this series minus ``other`` series. - - INPUT: - - - ``other`` -- other series - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: z - z - 0 - sage: 3*z - 2*z - z - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M - N; P - -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... - - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M - N; P - -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... - """ - P = self.parent() - left = self._aux - right = other._aux - if (isinstance(left, LLS_eventually_geometric) and isinstance(right, LLS_eventually_geometric)): - c = left._constant - right._constant - p = left._laurent_polynomial - right._laurent_polynomial - if not p and not c: - return P.zero() - d = max(left._degree, right._degree) - return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) - if left == right: - return P.zero() - return P.element_class(P, LLS_sub(self._aux, other._aux)) - def _neg_(self): """ Return the negative of this series. @@ -560,12 +588,12 @@ def change_ring(self, ring): Lazy Laurent Series Ring in z over Integer Ring sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n, True, 0); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M.parent() Lazy Laurent Series Ring in z over Integer Ring - sage: N = M.change_ring(QQ) - sage: N.parent() + sage: N = M.change_ring(QQ) + sage: N.parent() Lazy Laurent Series Ring in z over Rational Field sage: M ^-1 z^-1 - 2 + z + ... @@ -1184,17 +1212,19 @@ def __hash__(self): return 0 class LLS_coefficient_function(LLS_inexact): - def __init__(self, coefficient_function, is_sparse, approximate_valuation): + def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation): self._coefficient_function = coefficient_function + self._ring = ring super().__init__(is_sparse, approximate_valuation) def get_coefficient(self, n): - return self._coefficient_function(n) + return self._ring(self._coefficient_function(n)) def iterate_coefficients(self): n = self._offset + ring = self._ring while True: - yield self._coefficient_function(n) + yield ring(self._coefficient_function(n)) n += 1 class LLS_uninitialized(LLS_inexact): diff --git a/src/sage/rings/lazy_laurent_series_ring_new.py b/src/sage/rings/lazy_laurent_series_ring_new.py index 741c7e0821b..a0f5d98f082 100644 --- a/src/sage/rings/lazy_laurent_series_ring_new.py +++ b/src/sage/rings/lazy_laurent_series_ring_new.py @@ -142,7 +142,7 @@ def gen(self, n=0): if n != 0: raise IndexError("there is only one generator") R = self._laurent_poly_ring - aux = LLS_eventually_geometric(R.gen(n), self._sparse, R.zero(), 2) + aux = LLS_eventually_geometric(R.gen(n), self._sparse, ZZ.zero(), 2) return self.element_class(self, aux) def ngens(self): @@ -272,13 +272,13 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if callable(x): if valuation is None: valuation = 0 - if degree: + if degree is not None: if constant is None: - constant = self.base_ring().zero() + constant = ZZ.zero() z = R.gen() p = R.sum(x(i) * z**i for i in range(valuation, degree)) - self.element_class(self, LLS_eventually_geometric(p, self._sparse, constant, degree)) - return self.element_class(self, LLS_coefficient_function(x, self._sparse, valuation)) + return self.element_class(self, LLS_eventually_geometric(p, self._sparse, constant, degree)) + return self.element_class(self, LLS_coefficient_function(x, self.base_ring(), self._sparse, valuation)) raise ValueError(f"unable to convert {x} into a lazy Laurent series") def _an_element_(self): @@ -307,7 +307,7 @@ def one(self): 1 """ R = self._laurent_poly_ring - return self.element_class(self, LLS_eventually_geometric(R.one(), self._sparse, 0, 1)) + return self.element_class(self, LLS_eventually_geometric(R.one(), self._sparse, ZZ.zero(), 1)) @cached_method def zero(self): From 49586f5ad7e551881de8e1ca170e600b99d18229 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 15 Jul 2021 10:45:36 +0200 Subject: [PATCH 60/98] remove trailing whitespace --- src/sage/rings/lazy_laurent_series_new.py | 207 +++++++++--------- .../rings/lazy_laurent_series_ring_new.py | 1 - 2 files changed, 103 insertions(+), 105 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 207fc7eb52e..e66583cf7f4 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -14,8 +14,8 @@ This defines the generating function of Fibonacci sequence:: - sage: f = 1 / (1 - z - z^2) - sage: f + sage: f = 1 / (1 - z - z^2) + sage: f 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... The 100th element of Fibonacci sequence can be obtained from the generating @@ -157,8 +157,8 @@ def __getitem__(self, n): sage: f = z/(1 - 2*z^3) sage: [f[n] for n in range(20)] [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] - sage: M = L(lambda n: n) - sage: [M[n] for n in range(20)] + sage: M = L(lambda n: n) + sage: [M[n] for n in range(20)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n) @@ -182,18 +182,18 @@ def _mul_(self, other): 1 - 2*z + z^2 sage: (1 - z)*(1 - z)*(1 - z) 1 - 3*z + 3*z^2 - z^3 - sage: M = L(lambda n: n) - sage: M + sage: M = L(lambda n: n) + sage: M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M * (1 - M) - sage: N + sage: N = M * (1 - M) + sage: N z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 + ... sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: M * N + sage: M * N z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + ... """ P = self.parent() @@ -208,7 +208,7 @@ def _mul_(self, other): c = left._constant return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c)) return P.element_class(P, LLS_mul(self._aux, other._aux)) - + def _add_(self, other): """ Return the sum of this series with ``other``. @@ -224,9 +224,9 @@ def _add_(self, other): 1 - 2*z + z^2 sage: (1 - z)*(1 - z)*(1 - z) 1 - 3*z + 3*z^2 - z^3 - sage: z + z + sage: z + z 2*z - sage: z^2 + 3*z^2 + sage: z^2 + 3*z^2 4*z^2 sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... @@ -241,11 +241,11 @@ def _add_(self, other): 3 + 2*z^3 + 2*z^4 sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M + N; P + sage: P = M + N; P 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... """ P = self.parent() @@ -279,13 +279,13 @@ def _sub_(self, other): sage: L. = LLSRing(ZZ) sage: z - z 0 - sage: 3*z - 2*z + sage: 3*z - 2*z z - sage: M = L(lambda n: n); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M - N; P + sage: P = M - N; P -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... sage: A = L(1, constant=2, degree=3) @@ -305,7 +305,7 @@ def _sub_(self, other): z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M - N; P + sage: P = M - N; P -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... """ P = self.parent() @@ -340,19 +340,19 @@ def _div_(self, other): sage: L. = LLSRing(ZZ) sage: z/(1 - z) z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... - sage: M = L(lambda n: n); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M / N; P + sage: P = M / N; P z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N + sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M / N; P + sage: P = M / N; P z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... """ if isinstance(other._aux, LLS_zero): @@ -376,7 +376,7 @@ def _div_(self, other): return P.element_class(P, LLS_mul(left, LLS_inv(right))) # return P.element_class(P, LLS_div(left, right)) - + def _rmul_(self, scalar): """ Return the scalar multiplication of this series by ``scalar``. @@ -395,19 +395,19 @@ def _rmul_(self, scalar): sage: 0*z 0 sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M * 3 - 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... + 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M * 3 - 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... + 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: N * 4 - 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... + 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... """ P = self.parent() if not scalar: @@ -419,7 +419,7 @@ def _rmul_(self, scalar): return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, self._aux._degree)) return P.element_class(P, LLS_scalar(self._aux, scalar)) - + def _neg_(self): """ Return the negative of this series. @@ -430,17 +430,17 @@ def _neg_(self): sage: z = L.gen() sage: -(1 - z) -1 + z - sage: M = L(lambda n: n); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = -M; P + sage: P = -M; P -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = -M; P + sage: P = -M; P -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... - sage: -(z^2 + 3*z - 4*z^3) + sage: -(z^2 + 3*z - 4*z^3) -3*z - z^2 + 4*z^3 """ P = self.parent() @@ -450,7 +450,7 @@ def _neg_(self): d = self._aux._degree return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) return P.element_class(P, LLS_neg(self._aux)) - + def __invert__(self): """ Return the multiplicative inverse of the element. @@ -467,12 +467,12 @@ def __invert__(self): sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = ~M; P + sage: P = ~M; P z^-1 - 2 + z + ... """ P = self.parent() return P.element_class(P, LLS_inv(self._aux)) - + def coefficient(self, n): """ Return the coefficient of the term with exponent ``n`` of the series. @@ -483,8 +483,8 @@ def coefficient(self, n): EXAMPLES:: - sage: L. = LLSRing(ZZ) - sage: F = L(None) + sage: L. = LLSRing(ZZ) + sage: F = L(None) sage: F.define(1 + z*F^2) sage: F.coefficient(10) 16796 @@ -493,8 +493,8 @@ def coefficient(self, n): TESTS:: - sage: L. = LLSRing(ZZ, sparse=False) - sage: e = L(None) + sage: L. = LLSRing(ZZ, sparse=False) + sage: e = L(None) sage: e.define(1 + z*e^2) sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... @@ -506,25 +506,25 @@ def coefficient(self, n): [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796] sage: M = L(lambda n: n^2); M z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... - sage: M._aux._cache + sage: M._aux._cache [0, 1, 4, 9, 16, 25, 36] - sage: M.coefficient(9) + sage: M.coefficient(9) 81 - sage: M._aux._cache + sage: M._aux._cache [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] sage: L = LLSRing(ZZ, 'z', sparse=True) - sage: M = L(lambda n: n^2); M + sage: M = L(lambda n: n^2); M z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... - sage: M._aux._cache + sage: M._aux._cache {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36} - sage: M.coefficient(10) + sage: M.coefficient(10) 100 - sage: M._aux._cache + sage: M._aux._cache {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 10: 100} """ return self.__getitem__(n) - + def map_coefficients(self, func, ring=None): """ Return the series with ``func`` applied to each coefficient of this series. @@ -543,13 +543,13 @@ def map_coefficients(self, func, ring=None): z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... sage: t 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... - sage: M = L(lambda n: n); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = M.map_coefficients(lambda c: c + 1); N 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = M.map_coefficients(lambda c: c + 1); N 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... @@ -563,7 +563,7 @@ def map_coefficients(self, func, ring=None): return P.zero() return P.element_class(P, LLS_eventually_geometric(p, self._aux._is_sparse, c, d)) return P.element_class(P, LLS_apply_coeff(self._aux, func, R)) - + def change_ring(self, ring): """ Return this series with coefficients converted to elements of ``ring``. @@ -581,21 +581,21 @@ def change_ring(self, ring): 1/2 - 1/4*z + 1/8*z^2 - 1/16*z^3 + 1/32*z^4 - 1/64*z^5 + 1/128*z^6 + ... sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.change_ring(QQ) - sage: N.parent() + sage: N = M.change_ring(QQ) + sage: N.parent() Lazy Laurent Series Ring in z over Rational Field - sage: M.parent() + sage: M.parent() Lazy Laurent Series Ring in z over Integer Ring sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M + sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M.parent() + sage: M.parent() Lazy Laurent Series Ring in z over Integer Ring sage: N = M.change_ring(QQ) sage: N.parent() Lazy Laurent Series Ring in z over Rational Field - sage: M ^-1 + sage: M ^-1 z^-1 - 2 + z + ... """ from .lazy_laurent_series_ring_new import LLSRing @@ -623,13 +623,13 @@ def truncate(self, d): z^5 + z^6 + ... sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M.truncate(4) + sage: M.truncate(4) z + 2*z^2 + 3*z^3 sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M.truncate(4) + sage: M.truncate(4) z + 2*z^2 + 3*z^3 """ P = self.parent() @@ -655,20 +655,20 @@ def __pow__(self, n): 1 + 3*z + 6*z^2 + 10*z^3 + 15*z^4 + 21*z^5 + 28*z^6 + ... sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M ^ 2 + sage: M ^ 2 z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M ^ 2 + sage: M ^ 2 z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... """ if n == 0: return self.parent().one() - return generic_power(self, n) - + return generic_power(self, n) + def approximate_series(self, prec, name=None): """ Return the Laurent series with absolute precision ``prec`` approximated @@ -728,7 +728,7 @@ def prec(self): +Infinity """ return infinity - + def polynomial(self, degree=None, name=None): """ Return the polynomial or Laurent polynomial if the series is actually so. @@ -772,11 +772,11 @@ def polynomial(self, degree=None, name=None): z^-3 + z^-2 + z^-1 + 1 sage: f.polynomial(-5) 0 - sage: M = L(lambda n: n^2, 0) - sage: M.polynomial(3) + sage: M = L(lambda n: n^2, 0) + sage: M.polynomial(3) 9*z^3 + 4*z^2 + z - sage: M = L(lambda n: n^2, 0) - sage: M.polynomial(5) + sage: M = L(lambda n: n^2, 0) + sage: M.polynomial(5) 25*z^5 + 16*z^4 + 9*z^3 + 4*z^2 + z """ if degree is None: @@ -804,7 +804,7 @@ def polynomial(self, degree=None, name=None): from sage.rings.all import PolynomialRing R = PolynomialRing(S.base_ring(), name=name) return R([self[i] for i in range(m)]) - + def valuation(self): """ Return the valuation of the series. @@ -822,11 +822,11 @@ def valuation(self): sage: t = z - z sage: t.valuation() +Infinity - sage: M = L(lambda n: n^2, 0) - sage: M.valuation() + sage: M = L(lambda n: n^2, 0) + sage: M.valuation() 1 - sage: M = L(lambda n: n^2, 0) - sage: M.valuation() + sage: M = L(lambda n: n^2, 0) + sage: M.valuation() 1 """ return self._aux.valuation() @@ -863,7 +863,7 @@ def _repr_(self): ret = repr(R([self._aux[i] for i in range(v, m)]).shift(v)) # TODO: Better handling when ret == 0 but we have not checked up to the constant term return ret + ' + ...' - + def _richcmp_(self, other, op): """ Compare ``self` with ``other`` with respect to the comparison operator ``op``. @@ -913,7 +913,7 @@ def _richcmp_(self, other, op): return not (self == other) return False - + def __hash__(self): """ Return the hash of ``self`` @@ -927,7 +927,7 @@ def __hash__(self): {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} """ return hash(self._aux) - + def __bool__(self): """ Test whether ``self`` is not zero. @@ -940,9 +940,9 @@ def __bool__(self): sage: f = 1/(1 - z) sage: f.is_zero() False - sage: M = L(lambda n: n, 0); M + sage: M = L(lambda n: n, 0); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M.is_zero() + sage: M.is_zero() False """ if isinstance(self._aux, LLS_zero): @@ -981,7 +981,7 @@ class LLS_inexact(LLS_aux): """ def __init__(self, is_sparse, approximate_valuation): super().__init__(is_sparse, approximate_valuation) - + if self._is_sparse: self._cache = dict() # cache of known coefficients else: @@ -1010,7 +1010,7 @@ def __getitem__(self, n): c = self._cache[i] return c - + def valuation(self): if self._is_sparse: n = self._approximate_valuation @@ -1100,7 +1100,7 @@ def __eq__(self, other): class LLS_binary(LLS_inexact): - + def __init__(self, left, right, *args, **kwargs): """ Initialize. @@ -1118,7 +1118,7 @@ def __init__(self, left, right, *args, **kwargs): self._left = left self._right = right super().__init__(*args, **kwargs) - + def __hash__(self): """ Return the hash of ``self``. @@ -1131,7 +1131,7 @@ def __hash__(self): {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} """ return hash((type(self), self._left, self._right)) - + def __eq__(self, other): """ Test equality. @@ -1180,12 +1180,12 @@ def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): self._degree = degree self._laurent_polynomial = laurent_polynomial super().__init__(is_sparse, self._laurent_polynomial.valuation()) - + def __getitem__(self, n): if n >= self._degree: return self._constant return self._laurent_polynomial[n] - + def valuation(self): return self._approximate_valuation @@ -1251,10 +1251,10 @@ class LLS_add(LLS_binary): def __init__(self, left, right): if left._is_sparse != right._is_sparse: raise NotImplementedError - + a = min(left._approximate_valuation, right._approximate_valuation) super().__init__(left, right, left._is_sparse, a) - + def get_coefficient(self, n): return self._left[n] + self._right[n] @@ -1274,7 +1274,7 @@ def __init__(self, left, right): a = min(left._approximate_valuation, right._approximate_valuation) super().__init__(left, right, left._is_sparse, a) - + def get_coefficient(self, n): """ Return the `n`-th coefficient of the series ``s``. @@ -1298,7 +1298,7 @@ class LLS_mul(LLS_binary): """ Operator for multiplication. - We are assuming commutativity of the coefficient ring here. + We are assuming commutativity of the coefficient ring here. """ def __init__(self, left, right): if left._is_sparse != right._is_sparse: @@ -1339,7 +1339,7 @@ def __init__(self, left, right): self._rv = rv self._ainv = ~right[rv] super().__init__(left, right, left._is_sparse, lv - rv) - + def get_coefficient(self, n): lv = self._lv rv = self._rv @@ -1349,7 +1349,7 @@ def get_coefficient(self, n): for k in range(lv - rv, n): c -= self[k] * self._right[n + rv - k] return c * self._ainv - + def iterate_coefficients(self): n = self._offset lv = self._lv @@ -1376,7 +1376,7 @@ def __init__(self, series, scalar): self._scalar = scalar super().__init__(series, series._is_sparse, series._approximate_valuation) - + def get_coefficient(self, n): return self._series[n] * self._scalar @@ -1392,7 +1392,7 @@ class LLS_neg(LLS_unary): """ def __init__(self, series): super().__init__(series, series._is_sparse, series._approximate_valuation) - + def get_coefficient(self, n): return -self._series[n] @@ -1412,7 +1412,7 @@ def __init__(self, series): self._ainv = ~series[v] self._zero = ZZ.zero() - + def get_coefficient(self, n): v = self._approximate_valuation if n == v: @@ -1448,11 +1448,10 @@ def __init__(self, series, function, ring): def get_coefficient(self, n): c = self._ring(self._function(self._series[n])) return c - + def iterate_coefficients(self): n = self._offset while True: c = self._ring(self._function(self._series[n])) yield c n += 1 - diff --git a/src/sage/rings/lazy_laurent_series_ring_new.py b/src/sage/rings/lazy_laurent_series_ring_new.py index a0f5d98f082..44a0d918af5 100644 --- a/src/sage/rings/lazy_laurent_series_ring_new.py +++ b/src/sage/rings/lazy_laurent_series_ring_new.py @@ -321,4 +321,3 @@ def zero(self): 0 """ return self.element_class(self, LLS_zero(self._sparse)) - From 7114a49e334019d62665bf3769d3b3577fde4884 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 16 Jul 2021 10:04:04 +1000 Subject: [PATCH 61/98] Fix bug in valuation of LLS_eventually_geometric and better checking for define(). --- src/sage/rings/lazy_laurent_series_new.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index e66583cf7f4..23cc107274e 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -962,7 +962,7 @@ def define(self, s): """ Define an equation by ``self = s``. """ - if not isinstance(self._aux, LLS_uninitialized): + if not isinstance(self._aux, LLS_uninitialized) or self._aux._target is not None: raise ValueError("series already defined") self._aux._target = s._aux @@ -1179,7 +1179,11 @@ def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): self._constant = constant self._degree = degree self._laurent_polynomial = laurent_polynomial - super().__init__(is_sparse, self._laurent_polynomial.valuation()) + if not laurent_polynomial: + valuation = degree + else: + valuation = laurent_polynomial.valuation() + super().__init__(is_sparse, valuation) def __getitem__(self, n): if n >= self._degree: From 337b0402baadad12feb804c9d7d3031873708311 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 16 Jul 2021 10:58:14 +1000 Subject: [PATCH 62/98] Implementing slices. Fixing bugs in _element_constructor_. Addine define() doctests. --- src/sage/rings/lazy_laurent_series_new.py | 107 +++++++++++++++++- .../rings/lazy_laurent_series_ring_new.py | 19 ++-- 2 files changed, 116 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 23cc107274e..5792ac6c930 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -157,6 +157,9 @@ def __getitem__(self, n): sage: f = z/(1 - 2*z^3) sage: [f[n] for n in range(20)] [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] + sage: f[0:20] + [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] + sage: M = L(lambda n: n) sage: [M[n] for n in range(20)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] @@ -165,7 +168,14 @@ def __getitem__(self, n): sage: [M[n] for n in range(20)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] """ - return self.base_ring()(self._aux[n]) + R = self.base_ring() + if isinstance(n, slice): + if n.stop is None: + raise NotImplementedError("cannot list an infinite set") + start = n.start if n.start is not None else self._aux.valuation() + step = n.step if n.step is not None else 1 + return [R(self._aux[k]) for k in range(start, n.stop, step)] + return R(self._aux[n]) def _mul_(self, other): """ @@ -941,7 +951,7 @@ def __bool__(self): sage: f.is_zero() False sage: M = L(lambda n: n, 0); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + z + z^3 + z^5 + ... sage: M.is_zero() False """ @@ -959,8 +969,84 @@ def __bool__(self): raise ValueError("undecidable as lazy Laurent series") def define(self, s): - """ + r""" Define an equation by ``self = s``. + + EXAMPLES: + + We begin by constructing the Catalan numbers:: + + sage: L. = LLSRing(ZZ) + sage: C = L(None) + sage: C.define(1 + z*C^2) + sage: C + 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + + The Catalan numbers but with a valuation 1:: + + sage: B = L(None, 1) + sage: B.define(z + B^2) + sage: B + z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + ... + + We can define multiple series that are linked:: + + sage: s = L(None) + sage: t = L(None) + sage: s.define(1 + z*t^3) + sage: t.define(1 + z*s^2) + sage: s[:9] + [1, 1, 3, 9, 34, 132, 546, 2327, 10191] + sage: t[:9] + [1, 1, 2, 7, 24, 95, 386, 1641, 7150] + + An bigger example:: + + sage: L. = LLSRing(ZZ) + sage: A = L(None, 5) + sage: B = L(None) + sage: C = L(None, 2) + sage: A.define(z^5 + B^2) + sage: B.define(z^5 + C^2) + sage: C.define(z^2 + C^2 + A^2) + sage: A[0:15] + [0, 0, 0, 0, 0, 1, 0, 0, 1, 2, 5, 4, 14, 10, 48] + sage: B[0:15] + [0, 0, 0, 0, 1, 1, 2, 0, 5, 0, 14, 0, 44, 0, 138] + sage: C[0:15] + [0, 0, 1, 0, 1, 0, 2, 0, 5, 0, 15, 0, 44, 2, 142] + + We count unlabeled ordered trees by total number of nodes + and number of internal nodes:: + + sage: R. = QQ[] + sage: Q. = LLSRing(R) + sage: leaf = z + sage: internal_node = q * z + sage: L = Q(constant=1, degree=1) + sage: T = Q(None, 1) + sage: T.define(leaf + internal_node * L(T)) # not tested - composition need + sage: [T[i] for i in range(7)] # not tested - composition need + [0, 1, q, q^2 + q, q^3 + 3*q^2 + q, q^4 + 6*q^3 + 6*q^2 + q] + + TESTS:: + + sage: L. = LLSRing(ZZ, sparse=True) + sage: s = L(None) + sage: s.define(1 + z*s^3) + sage: s[:10] + [1, 1, 3, 12, 55, 273, 1428, 7752, 43263, 246675] + + sage: e = L(None) + sage: e.define(1 + z*e) + sage: e.define(1 + z*e) + Traceback (most recent call last): + ... + ValueError: series already defined + sage: z.define(1 + z^2) + Traceback (most recent call last): + ... + ValueError: series already defined """ if not isinstance(self._aux, LLS_uninitialized) or self._aux._target is not None: raise ValueError("series already defined") @@ -989,6 +1075,21 @@ def __init__(self, is_sparse, approximate_valuation): self._offset = approximate_valuation self._iter = self.iterate_coefficients() + def __getstate__(self): + d = dict(self.__dict__) + if not self._is_sparse: + # We cannot pickle a generator object, so we remove it and + # the cache from the pickle information. + del d["_iter"] + del d["_cache"] + return d + + def __setstate__(self, d): + self.__dict__ = d + if not self._is_sparse: + self._iter = self.iterate_coefficients() + self._cache = [] + def __getitem__(self, n): if n < self._approximate_valuation: return ZZ.zero() diff --git a/src/sage/rings/lazy_laurent_series_ring_new.py b/src/sage/rings/lazy_laurent_series_ring_new.py index 44a0d918af5..22759512e2e 100644 --- a/src/sage/rings/lazy_laurent_series_ring_new.py +++ b/src/sage/rings/lazy_laurent_series_ring_new.py @@ -211,23 +211,28 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: L(lambda i: i, 5, 1, 10) 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... - sage: L(lambda i: i, 5, (1,10)) + sage: L(lambda i: i, 5, (1, 10)) 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... - sage: def g(s, i): + sage: X = L(constant=5, degree=2); X + 5*z^2 + 5*z^3 + 5*z^4 + ... + sage: X.valuation() + 2 + + sage: def g(i): ....: if i < 0: ....: return 1 ....: else: - ....: return s.coefficient(i - 1) + i + ....: return 1 + sum(k for k in range(i+1)) sage: e = L(g, -5); e z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... sage: f = e^-1; f z^5 - z^6 - z^11 + ... sage: f.coefficient(10) 0 - sage: f.coefficient(20) + sage: f[20] 9 - sage: f.coefficient(30) + sage: f[30] -219 Alternatively, the ``coefficient_function`` can be a list of elements of the @@ -257,8 +262,8 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if isinstance(constant, (tuple, list)): constant, degree = constant if x in R: - if not x: - aux = LLS_zero() + if not x and not constant: + aux = LLS_zero(self._sparse) else: if valuation: x = x.shift(valuation - x.valuation()) From 12f0e5e64cd4a43e02e5c6c33eb96925923f7f7c Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sat, 17 Jul 2021 02:50:36 +0530 Subject: [PATCH 63/98] Added recursive tests. --- src/sage/rings/lazy_laurent_series_new.py | 93 ++++++++++++++++++- .../rings/lazy_laurent_series_ring_new.py | 13 ++- 2 files changed, 99 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 5792ac6c930..e2a252d55e5 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -73,7 +73,7 @@ # **************************************************************************** -from .infinity import Infinity, infinity +from .infinity import infinity from sage.structure.element import ModuleElement from .integer_ring import ZZ from sage.structure.richcmp import op_EQ, op_NE @@ -355,7 +355,7 @@ def _div_(self, other): sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: P = M / N; P - z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: L. = LLSRing(ZZ, sparse=True) sage: M = L(lambda n: n); M @@ -363,7 +363,7 @@ def _div_(self, other): sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: P = M / N; P - z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... """ if isinstance(other._aux, LLS_zero): raise ZeroDivisionError("cannot divide by 0") @@ -1028,6 +1028,91 @@ def define(self, s): sage: T.define(leaf + internal_node * L(T)) # not tested - composition need sage: [T[i] for i in range(7)] # not tested - composition need [0, 1, q, q^2 + q, q^3 + 3*q^2 + q, q^4 + 6*q^3 + 6*q^2 + q] + + Test Recursive 0 + + :: + + sage: L = LLSRing(QQ, 'z') + sage: one = L(1) + sage: monom = L.gen() + sage: s = L(None) + sage: s._name = 's' + sage: s.define(one+monom*s) + sage: s.aorder # not tested + 0 + sage: s.order # not tested + Unknown series order + sage: [s.coefficient(i) for i in range(6)] + [1, 1, 1, 1, 1, 1] + + Test Recursive 1 + + :: + + sage: s = L(None) + sage: s._name = 's' + sage: s.define(one+monom*s*s) + sage: s.aorder # not tested + 0 + sage: s.order # not tested + Unknown series order + sage: [s.coefficient(i) for i in range(6)] + [1, 1, 2, 5, 14, 42] + + Test Recursive 1b + + :: + + sage: s = L(None) + sage: s._name = 's' + sage: s.define(monom + s*s) + sage: s.aorder # not tested + 1 + sage: s.order # not tested + Unknown series order + sage: [s.coefficient(i) for i in range(7)] + [0, 1, 1, 2, 5, 14, 42] + + Test Recursive 2 + + :: + + sage: s = L(None) + sage: s._name = 's' + sage: t = L(None) + sage: t._name = 't' + sage: s.define(one+monom*t*t*t) + sage: t.define(one+monom*s*s) + sage: [s.coefficient(i) for i in range(9)] + [1, 1, 3, 9, 34, 132, 546, 2327, 10191] + sage: [t.coefficient(i) for i in range(9)] + [1, 1, 2, 7, 24, 95, 386, 1641, 7150] + + Test Recursive 2b + + :: + + sage: s = L(None) + sage: s._name = 's' + sage: t = L(None) + sage: t._name = 't' + sage: s.define(monom + t*t*t) + sage: t.define(monom + s*s) + sage: [s.coefficient(i) for i in range(9)] + [0, 1, 0, 1, 3, 3, 7, 30, 63] + sage: [t.coefficient(i) for i in range(9)] + [0, 1, 1, 0, 2, 6, 7, 20, 75] + + Test Recursive 3 + + :: + + sage: s = L(None) + sage: s._name = 's' + sage: s.define(one+monom*s*s*s) + sage: [s.coefficient(i) for i in range(10)] + [1, 1, 3, 12, 55, 273, 1428, 7752, 43263, 246675] TESTS:: @@ -1311,7 +1396,7 @@ def __getitem__(self, n): return ZZ.zero() def valuation(self): - return Infinity + return infinity def __hash__(self): return 0 diff --git a/src/sage/rings/lazy_laurent_series_ring_new.py b/src/sage/rings/lazy_laurent_series_ring_new.py index 22759512e2e..8574cce3951 100644 --- a/src/sage/rings/lazy_laurent_series_ring_new.py +++ b/src/sage/rings/lazy_laurent_series_ring_new.py @@ -38,8 +38,16 @@ Power series can be defined recursively:: - sage: L. = LLSRing(ZZ) - sage: L.series(lambda s,n: (1 + z*s^2)[n], True, approximate_valuation=0) # not tested + sage: L. = LLSRing(ZZ, sparse=True) + sage: L._sparse + True + sage: s = L(None) + sage: s._aux._is_sparse + True + sage: s._aux._approximate_valuation + 0 + sage: s.define(1 + z*s^2) + sage: s 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... AUTHORS: @@ -96,7 +104,6 @@ class LLSRing(UniqueRepresentation, Parent): sage: LLSRing(ZZ, 't') Lazy Laurent Series Ring in t over Integer Ring """ - # Element = LLS Element = LLS def __init__(self, base_ring, names, sparse=False, category=None): From 09a1a55100a82d0443b36822742ddf964e8507c5 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sat, 17 Jul 2021 03:11:34 +0530 Subject: [PATCH 64/98] Removed recursive tests. --- src/sage/rings/lazy_laurent_series_new.py | 85 ----------------------- 1 file changed, 85 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index e2a252d55e5..3c21201f7fc 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -1028,91 +1028,6 @@ def define(self, s): sage: T.define(leaf + internal_node * L(T)) # not tested - composition need sage: [T[i] for i in range(7)] # not tested - composition need [0, 1, q, q^2 + q, q^3 + 3*q^2 + q, q^4 + 6*q^3 + 6*q^2 + q] - - Test Recursive 0 - - :: - - sage: L = LLSRing(QQ, 'z') - sage: one = L(1) - sage: monom = L.gen() - sage: s = L(None) - sage: s._name = 's' - sage: s.define(one+monom*s) - sage: s.aorder # not tested - 0 - sage: s.order # not tested - Unknown series order - sage: [s.coefficient(i) for i in range(6)] - [1, 1, 1, 1, 1, 1] - - Test Recursive 1 - - :: - - sage: s = L(None) - sage: s._name = 's' - sage: s.define(one+monom*s*s) - sage: s.aorder # not tested - 0 - sage: s.order # not tested - Unknown series order - sage: [s.coefficient(i) for i in range(6)] - [1, 1, 2, 5, 14, 42] - - Test Recursive 1b - - :: - - sage: s = L(None) - sage: s._name = 's' - sage: s.define(monom + s*s) - sage: s.aorder # not tested - 1 - sage: s.order # not tested - Unknown series order - sage: [s.coefficient(i) for i in range(7)] - [0, 1, 1, 2, 5, 14, 42] - - Test Recursive 2 - - :: - - sage: s = L(None) - sage: s._name = 's' - sage: t = L(None) - sage: t._name = 't' - sage: s.define(one+monom*t*t*t) - sage: t.define(one+monom*s*s) - sage: [s.coefficient(i) for i in range(9)] - [1, 1, 3, 9, 34, 132, 546, 2327, 10191] - sage: [t.coefficient(i) for i in range(9)] - [1, 1, 2, 7, 24, 95, 386, 1641, 7150] - - Test Recursive 2b - - :: - - sage: s = L(None) - sage: s._name = 's' - sage: t = L(None) - sage: t._name = 't' - sage: s.define(monom + t*t*t) - sage: t.define(monom + s*s) - sage: [s.coefficient(i) for i in range(9)] - [0, 1, 0, 1, 3, 3, 7, 30, 63] - sage: [t.coefficient(i) for i in range(9)] - [0, 1, 1, 0, 2, 6, 7, 20, 75] - - Test Recursive 3 - - :: - - sage: s = L(None) - sage: s._name = 's' - sage: s.define(one+monom*s*s*s) - sage: [s.coefficient(i) for i in range(10)] - [1, 1, 3, 12, 55, 273, 1428, 7752, 43263, 246675] TESTS:: From 544e58d1077c503abe34beb97de75e4d1405a89c Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 18 Jul 2021 20:21:42 +0200 Subject: [PATCH 65/98] remove modifications from original files --- src/sage/rings/lazy_laurent_series.py | 117 ++++++--------------- src/sage/rings/lazy_laurent_series_ring.py | 5 +- 2 files changed, 34 insertions(+), 88 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index c784cc9d21d..23e72dd8084 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -95,6 +95,7 @@ LazyLaurentSeriesOperator_truncate ) + class LazyLaurentSeries(ModuleElement): r""" Return a lazy Laurent series. @@ -165,15 +166,8 @@ def __init__(self, parent, coefficient=None, valuation=0, constant=None): self._coefficient_function = coefficient self._approximate_valuation = valuation self._constant = constant - self._implementation = parent._implementation - if self._implementation == 'sparse': - self._cache = dict() # cache of known coefficients - elif self._implementation == 'dense': - self._cache = list() - self._offset = valuation - else: - raise ValueError + self._cache = dict() # cache of known coefficients def _richcmp_(self, other, op): """ @@ -300,7 +294,7 @@ def _repr_(self): n = self.valuation() if self._constant is None: - m = n + 7 # long enough + m = n + 7 # long enough elif self._constant[0] != 0: m = self._constant[1] + 3 else: @@ -316,16 +310,16 @@ def _repr_(self): if not atomic_repr and n > 0 and (x[1:].find('+') != -1 or x[1:].find('-') != -1): x = '({})'.format(x) if n > 1 or n < 0: - var = '*{}^{}'.format(X, n) + var = '*{}^{}'.format(X,n) elif n == 1: var = '*{}'.format(X) else: # n == 0 var = '' - s += '{}{}'.format(x, var) + s += '{}{}'.format(x,var) first = False n += 1 - s = s.replace(" + -", " - ").replace(" 1*", " ").replace(" -1*", " -")[1:] + s = s.replace(" + -", " - ").replace(" 1*"," ").replace(" -1*", " -")[1:] if not s: # zero series s = '0' @@ -373,51 +367,21 @@ def coefficient(self, n): 16796 sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - - TESTS:: - - sage: def g(s, i): - ....: if i == 0: - ....: return 1 - ....: else: - ....: return sum(s.coefficient(j)*s.coefficient(i - 1 - j) for j in [0..i-1]) - ....: - sage: L = LazyLaurentSeriesRing(ZZ, 'z', implementation='dense') - sage: e = L.series(g, valuation = 0) - sage: e - 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - sage: e._cache - [1, 1, 2, 5, 14, 42, 132] - sage: e.coefficient(10) - 16796 - sage: e._cache - [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796] - """ R = self.base_ring() - if self._approximate_valuation is infinity: + + if self._approximate_valuation == infinity: return R.zero() elif n < self._approximate_valuation: return R.zero() elif self._constant is not None and n >= self._constant[1]: return self._constant[0] - if self._implementation == 'sparse': - try: - c = self._cache[n] - except KeyError: - c = R(self._coefficient_function(self, n)) - self._cache[n] = c - - else: - i = n - self._offset - if i >= len(self._cache): - a = len(self._cache) + self._offset - # it is important to extend by generator: - # self._coefficient_function might recurse, and - # thereby extend the cache itself, too - self._cache.extend(R(self._coefficient_function(self, j)) for j in range(a, n+1)) - c = self._cache[i] + try: + c = self._cache[n] + except KeyError: + c = R(self._coefficient_function(self, n)) + self._cache[n] = c return c @@ -439,7 +403,21 @@ def valuation(self): sage: t.valuation() +Infinity """ - if self._constant is not None: + if self._constant is None: + n = self._approximate_valuation + cache = self._cache + while True: + if n in cache: + if cache[n]: + self._approximate_valuation = n + return n + n += 1 + else: + if self.coefficient(n) != 0: + self._approximate_valuation = n + return n + n += 1 + else: n = self._approximate_valuation m = self._constant[1] while n <= m: @@ -449,38 +427,6 @@ def valuation(self): n += 1 return infinity - elif self._implementation == 'sparse': - if self._constant is None: - n = self._approximate_valuation - cache = self._cache - while True: - if n in cache: - if cache[n]: - self._approximate_valuation = n - return n - n += 1 - else: - if self.coefficient(n) != 0: - self._approximate_valuation = n - return n - n += 1 - - else: - if self._constant is None: - n = self._approximate_valuation - cache = self._cache - while True: - if n - self._offset < len(cache): - if cache[n - self._offset]: - self._approximate_valuation = n - return n - n += 1 - else: - if self.coefficient(n) != 0: - self._approximate_valuation = n - return n - n += 1 - def prec(self): """ Return the precision of the series, which is infinity. @@ -557,7 +503,7 @@ def polynomial(self, degree=None, name=None): from sage.rings.all import LaurentPolynomialRing R = LaurentPolynomialRing(S.base_ring(), name=name) n = self.valuation() - return R([self.coefficient(i) for i in range(n, m)]).shift(n) + return R([self.coefficient(i) for i in range(n,m)]).shift(n) else: from sage.rings.all import PolynomialRing R = PolynomialRing(S.base_ring(), name=name) @@ -607,7 +553,7 @@ def approximate_series(self, prec, name=None): from sage.rings.all import LaurentSeriesRing R = LaurentSeriesRing(S.base_ring(), name=name) n = self.valuation() - return R([self.coefficient(i) for i in range(n, prec)], n).add_bigoh(prec) + return R([self.coefficient(i) for i in range(n,prec)], n).add_bigoh(prec) else: from sage.rings.all import PowerSeriesRing R = PowerSeriesRing(S.base_ring(), name=name) @@ -635,6 +581,7 @@ def _mul_(self, other): op = LazyLaurentSeriesOperator_mul(self, other) a = self._approximate_valuation + other._approximate_valuation + c = None if self._constant is not None and other._constant is not None: if self._constant[0] == 0 and other._constant[0] == 0: @@ -770,7 +717,7 @@ def __invert__(self): """ v = self.valuation() - if v is infinity: + if v == infinity: raise ZeroDivisionError('cannot invert zero') R = self.parent() diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 9886307a440..ffa6b4575c7 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -98,7 +98,7 @@ class LazyLaurentSeriesRing(UniqueRepresentation, Parent): """ Element = LazyLaurentSeries - def __init__(self, base_ring, names, category=None, implementation='sparse'): + def __init__(self, base_ring, names, category=None): """ Initialize. @@ -109,7 +109,6 @@ def __init__(self, base_ring, names, category=None, implementation='sparse'): """ Parent.__init__(self, base=base_ring, names=names, category=MagmasAndAdditiveMagmas().or_subcategory(category)) - self._implementation = implementation def _repr_(self): """ @@ -343,4 +342,4 @@ def series(self, coefficient, valuation, constant=None): constant = (self.base_ring()(c), m) - return self.element_class(self, coefficient=coefficient, valuation=valuation, constant=constant) \ No newline at end of file + return self.element_class(self, coefficient=coefficient, valuation=valuation, constant=constant) From d5a051a08a4a9a690b7e90b38ddc4b0badfa5cef Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Mon, 19 Jul 2021 02:34:57 +0530 Subject: [PATCH 66/98] Composition 1/3 done. --- src/sage/rings/lazy_laurent_series_new.py | 68 +++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 3c21201f7fc..96b52bdf117 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -79,6 +79,7 @@ from sage.structure.richcmp import op_EQ, op_NE from sage.arith.power import generic_power from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing +from sage.rings.integer import Integer class LLS(ModuleElement): r""" @@ -177,6 +178,47 @@ def __getitem__(self, n): return [R(self._aux[k]) for k in range(start, n.stop, step)] return R(self._aux[n]) + + def __call__(self, other): + """ + Return the composition of the series with ``other``. + + INPUT: + + - ``other`` -- other series + + TESTS:: + + sage: L. = LLSRing(QQ) + sage: LS. = LLSRing(QQ) + sage: f = z^2 + 1 + z + sage: g = 0 + sage: f(g) + 1 + sage: g = L(0) + sage: f(g) + 1 + sage: g = y - y + sage: f(g) + 1 + sage: f = L(lambda n: n, 0); f + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: f(g) + 0 + """ + # g = 0, val(f) >= 0 + try: + if isinstance(other._aux, LLS_zero) and self.valuation() >= 0: + return self[0] + except AttributeError: + if other == 0: + return self[0] + raise ValueError() + + # g != 0, val(g) > 0 and g != 0, f has finitely many non-zero coefficients + P = self.parent() + return P.element_class(P, LLS_com(self._aux, other._aux, P.base_ring())) + def _mul_(self, other): """ Return the product of this series with ``other``. @@ -1470,6 +1512,32 @@ def iterate_coefficients(self): yield c * self._ainv n += 1 + +class LLS_com(LLS_binary): + """ + Return ``left`` composed by ``right``. + """ + def __init__(self, left, right, ring): + lv = left.valuation() + rv = right.valuation() + self._ring = ring + self._lv = lv + self._rv = rv + self._ainv = ~right[rv] + super().__init__(left, right, left._is_sparse, min(lv, rv)) + + def iterate_coefficients(self): + # g != 0, val(g) > 0 + if self._rv > 0: + yield self._left[0] + z = self._ring(lambda n: self._left[n - 1], self._lv + 1)(self._right) * self._right + z.coefficient(1) + n = 1 + while True: + yield z._stream[n] + n += 1 + + ##################################################################### ## Unary operations From 78dcdf4b0b8e4a3083ac4d49752b77ab9e6f49b9 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Tue, 20 Jul 2021 15:48:10 +0530 Subject: [PATCH 67/98] Composition 2/3 done. --- src/sage/rings/lazy_laurent_series_new.py | 40 ++++++++++++++++++++--- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 96b52bdf117..9532f1085b9 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -205,6 +205,25 @@ def __call__(self, other): z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: f(g) 0 + sage: L. = LLSRing(QQ) + sage: LS. = LLSRing(QQ, sparse=True) + sage: f = L(lambda n : 1); f + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: g = LS(lambda n: 0 if n != 2 else 1, 0); g + y^2 + ... + sage: f(g) + 1 + z^2 + z^4 + z^6 + ... + sage: g = LS(lambda n: 1 if n > 1 else 0, 0); g + y^2 + y^3 + y^4 + y^5 + y^6 + ... + sage: f(g) + 1 + z^2 + z^3 + 2*z^4 + 3*z^5 + 5*z^6 + ... + sage: L. = LLSRing(QQ, sparse=True) + sage: f = L(lambda n : 1); f + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: g = LS(lambda n: 1, 1); g + y + y^2 + y^3 + y^4 + y^5 + y^6 + y^7 + ... + sage: f(g) + 1 + z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + ... """ # g = 0, val(f) >= 0 try: @@ -217,7 +236,7 @@ def __call__(self, other): # g != 0, val(g) > 0 and g != 0, f has finitely many non-zero coefficients P = self.parent() - return P.element_class(P, LLS_com(self._aux, other._aux, P.base_ring())) + return P.element_class(P, LLS_com(self._aux, other._aux, P)) def _mul_(self, other): """ @@ -1524,17 +1543,30 @@ def __init__(self, left, right, ring): self._lv = lv self._rv = rv self._ainv = ~right[rv] - super().__init__(left, right, left._is_sparse, min(lv, rv)) + super().__init__(left, right, left._is_sparse, lv * rv) + + def get_coefficient(self, n): + # g != 0, val(g) > 0 + if self._rv > 0: + if n == 0: + return self._left[0] + tail = self._ring(lambda n: self._left[n + 1], 0) + right_series = self._ring(lambda n: self._right[n], self._rv) + z = tail(right_series) * right_series + z.coefficient(1) + return z[n] def iterate_coefficients(self): # g != 0, val(g) > 0 if self._rv > 0: yield self._left[0] - z = self._ring(lambda n: self._left[n - 1], self._lv + 1)(self._right) * self._right + tail = self._ring(lambda n: self._left[n + 1], 0) + right_series = self._ring(lambda n: self._right[n], self._rv) + z = tail(right_series) * right_series z.coefficient(1) n = 1 while True: - yield z._stream[n] + yield z[n] n += 1 From 2eee97d4ae24997978ec3c2965edca4f7416126b Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 21 Jul 2021 02:29:29 +0530 Subject: [PATCH 68/98] Composition done, minor edit left. --- src/sage/rings/lazy_laurent_series_new.py | 62 ++++++++++++++++++----- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 9532f1085b9..c1008ff2a20 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -178,7 +178,6 @@ def __getitem__(self, n): return [R(self._aux[k]) for k in range(start, n.stop, step)] return R(self._aux[n]) - def __call__(self, other): """ Return the composition of the series with ``other``. @@ -224,18 +223,43 @@ def __call__(self, other): y + y^2 + y^3 + y^4 + y^5 + y^6 + y^7 + ... sage: f(g) 1 + z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + ... + sage: L. = LLSRing(QQ) + sage: LS. = LLSRing(QQ) + sage: f = z^-2 + 1 + z + sage: g = 1/(y*(1-y)); g + y^-1 + 1 + y + y^2 + y^3 + y^4 + y^5 + ... + sage: f(g) + z^-1 + 2 + z + 2*z^2 - z^3 + 2*z^4 + z^5 + ... + sage: f = z^-3 + z^-2 + 1 + sage: g = 1/(y^2*(1-y)); g + y^-2 + y^-1 + 1 + y + y^2 + y^3 + y^4 + ... + sage: f(g) + 1 + z^4 - 2*z^5 + 2*z^6 + ... """ # g = 0, val(f) >= 0 + P = self.parent() try: - if isinstance(other._aux, LLS_zero) and self.valuation() >= 0: + if isinstance(other._aux, LLS_zero) and self._aux._approximate_valuation >= 0: return self[0] except AttributeError: if other == 0: return self[0] raise ValueError() - + + if isinstance(self._aux, LLS_eventually_geometric): + R = P._laurent_poly_ring + z = R.gen() + p = R.sum(self._aux[i] * z**(i - self._aux._approximate_valuation) for i in range(self._aux._approximate_valuation, self._aux._degree)) + u = LLS_eventually_geometric(p, self._aux._is_sparse, self._aux._degree) + x = P(lambda n: other[n], other._aux._approximate_valuation) + new_x = x ** self._aux._approximate_valuation + m = P(0) + for i in range(u._degree): + if u[i] != 0: + m = m + (x ** i) * u[i] + return m * new_x + # g != 0, val(g) > 0 and g != 0, f has finitely many non-zero coefficients - P = self.parent() return P.element_class(P, LLS_com(self._aux, other._aux, P)) def _mul_(self, other): @@ -446,7 +470,6 @@ def _div_(self, other): pass return P.element_class(P, LLS_mul(left, LLS_inv(right))) - # return P.element_class(P, LLS_div(left, right)) def _rmul_(self, scalar): """ @@ -542,6 +565,9 @@ def __invert__(self): z^-1 - 2 + z + ... """ P = self.parent() + if isinstance(self._aux, LLS_eventually_geometric) and self._aux._laurent_polynomial == P.gen(): + ret = 1 / self._aux._laurent_polynomial + return P.element_class(P, LLS_eventually_geometric(ret, P._sparse, self._aux._constant)) return P.element_class(P, LLS_inv(self._aux)) def coefficient(self, n): @@ -1113,6 +1139,7 @@ def define(self, s): raise ValueError("series already defined") self._aux._target = s._aux + class LLS_aux(): """ Abstract base class for all auxillary LLS. @@ -1121,6 +1148,7 @@ def __init__(self, sparse, approximate_valuation): self._is_sparse = sparse self._approximate_valuation = approximate_valuation + class LLS_inexact(LLS_aux): """ LLS aux class when it is not or we do not know if it is @@ -1537,38 +1565,46 @@ class LLS_com(LLS_binary): Return ``left`` composed by ``right``. """ def __init__(self, left, right, ring): - lv = left.valuation() + lv = left._approximate_valuation rv = right.valuation() self._ring = ring self._lv = lv self._rv = rv - self._ainv = ~right[rv] - super().__init__(left, right, left._is_sparse, lv * rv) + if isinstance(left, LLS_eventually_geometric): + val = rv * (left._degree - 1 - lv) + rv * lv + super().__init__(left, right, left._is_sparse, val) + else: + super().__init__(left, right, left._is_sparse, lv * rv) def get_coefficient(self, n): # g != 0, val(g) > 0 if self._rv > 0: if n == 0: - return self._left[0] + return self._left[self._lv] tail = self._ring(lambda n: self._left[n + 1], 0) + # tail = LLS_coefficient_function(lambda n: self._left[n + 1], self._ring, self._ring._sparse, 0) right_series = self._ring(lambda n: self._right[n], self._rv) + # right_series = LLS_coefficient_function(lambda n: self._right[n], self._ring, self._ring._sparse, self._rv) z = tail(right_series) * right_series - z.coefficient(1) + # z = LLS_mul(LLS_com(tail, right_series, self._ring), right_series) return z[n] + else: + # return u[n] + raise ValueError('Cannot compose series') def iterate_coefficients(self): # g != 0, val(g) > 0 if self._rv > 0: - yield self._left[0] + yield self._left[self._lv] tail = self._ring(lambda n: self._left[n + 1], 0) right_series = self._ring(lambda n: self._right[n], self._rv) z = tail(right_series) * right_series - z.coefficient(1) n = 1 while True: yield z[n] n += 1 - + else: + raise ValueError('Cannot compose series') ##################################################################### ## Unary operations From a4c8b7434f18eb47a9b7a259c2fc65c6c47f19c0 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 21 Jul 2021 02:31:02 +0530 Subject: [PATCH 69/98] Composition done, minor edit left. --- src/sage/rings/lazy_laurent_series_new.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index c1008ff2a20..0148411a1c0 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -235,6 +235,12 @@ def __call__(self, other): y^-2 + y^-1 + 1 + y + y^2 + y^3 + y^4 + ... sage: f(g) 1 + z^4 - 2*z^5 + 2*z^6 + ... + sage: z(z) + z + ... + sage: f = L(lambda n : n); f + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: f(z^2) + z^2 + 2*z^4 + 3*z^6 + ... """ # g = 0, val(f) >= 0 P = self.parent() From 7b6f9537df30aee7d7de8df71e9d63950f98bc01 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 22 Jul 2021 17:26:45 +1000 Subject: [PATCH 70/98] Overhauling LLS composition. --- src/sage/rings/lazy_laurent_series_new.py | 410 +++++++++++++----- .../rings/lazy_laurent_series_ring_new.py | 14 +- 2 files changed, 311 insertions(+), 113 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 0148411a1c0..82c8e899a2f 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -74,7 +74,7 @@ from .infinity import infinity -from sage.structure.element import ModuleElement +from sage.structure.element import ModuleElement, parent from .integer_ring import ZZ from sage.structure.richcmp import op_EQ, op_NE from sage.arith.power import generic_power @@ -178,95 +178,234 @@ def __getitem__(self, n): return [R(self._aux[k]) for k in range(start, n.stop, step)] return R(self._aux[n]) - def __call__(self, other): - """ - Return the composition of the series with ``other``. + def __call__(self, g): + r""" + Return the composition of the series with ``g``. INPUT: - - ``other`` -- other series + - ``g`` -- other series TESTS:: sage: L. = LLSRing(QQ) - sage: LS. = LLSRing(QQ) sage: f = z^2 + 1 + z - sage: g = 0 - sage: f(g) + sage: f(0) 1 - sage: g = L(0) - sage: f(g) + sage: f(L(0)) 1 + sage: f(f) + 3 + 3*z + 4*z^2 + 2*z^3 + z^4 + sage: g = z^-3/(1-2*z); g + z^-3 + 2*z^-2 + 4*z^-1 + 8 + 16*z + 32*z^2 + 64*z^3 + ... + sage: f(g) + z^-6 + 4*z^-5 + 12*z^-4 + 33*z^-3 + 82*z^-2 + 196*z^-1 + 457 + ... + sage: g^2 + 1 + g + z^-6 + 4*z^-5 + 12*z^-4 + 33*z^-3 + 82*z^-2 + 196*z^-1 + 457 + ... + + sage: f = z^-2 + z + 4*z^3 + sage: f(f) + 4*z^-6 + 12*z^-3 + z^-2 + 48*z^-1 + 12 + ... + sage: f^-2 + f + 4*f^3 + 4*z^-6 + 12*z^-3 + z^-2 + 48*z^-1 + 12 + ... + sage: f(g) + 4*z^-9 + 24*z^-8 + 96*z^-7 + 320*z^-6 + 960*z^-5 + 2688*z^-4 + 7169*z^-3 + ... + sage: g^-2 + g + 4*g^3 + 4*z^-9 + 24*z^-8 + 96*z^-7 + 320*z^-6 + 960*z^-5 + 2688*z^-4 + 7169*z^-3 + ... + + sage: f = z^-3 + z^-2 + 1 / (1 + z^2); f + z^-3 + z^-2 + 1 - z^2 + ... + sage: g = z^3 / (1 + z - z^3); g + z^3 - z^4 + z^5 - z^7 + 2*z^8 - 2*z^9 + ... + sage: f(g) + z^-9 + 3*z^-8 + 3*z^-7 - z^-6 - 4*z^-5 - 2*z^-4 + z^-3 + ... + sage: g^-3 + g^-2 + 1 / (1 + g^2) + z^-9 + 3*z^-8 + 3*z^-7 - z^-6 - 4*z^-5 - 2*z^-4 + z^-3 + ... + + sage: f = L(lambda n: n); f + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: f(z^2) + z^2 + 2*z^4 + 3*z^6 + ... + + sage: f = L(lambda n: n, -2); f + -2*z^-2 - z^-1 + z + 2*z^2 + 3*z^3 + 4*z^4 + ... + sage: f3 = f(z^3); f3 + -2*z^-6 - z^-3 + ... + sage: [f3[i] for i in range(-6,13)] + [-2, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4] + + We compose a Laurent polynomial with a generic element:: + + sage: R. = QQ[] + sage: f = z^2 + 1 + z^-1 + sage: g = x^2 + x + 3 + sage: f(g) + (x^6 + 3*x^5 + 12*x^4 + 19*x^3 + 37*x^2 + 28*x + 31)/(x^2 + x + 3) + sage: f(g) == g^2 + 1 + g^-1 + True + + We compose with another lazy Laurent series:: + + sage: LS. = LLSRing(QQ) + sage: f = z^2 + 1 + z^-1 + sage: fy = f(y); fy + y^-1 + 1 + y^2 + sage: fy.parent() is LS + True sage: g = y - y - sage: f(g) - 1 - sage: f = L(lambda n: n, 0); f + sage: f(g) + Traceback (most recent call last): + ... + ZeroDivisionError: the valuation of the series must be nonnegative + + sage: g = 1 - y + sage: f(g) + 3 - y + 2*y^2 + y^3 + y^4 + y^5 + y^6 + ... + sage: g^2 + 1 + g^-1 + 3 - y + 2*y^2 + y^3 + y^4 + y^5 + y^6 + ... + + sage: f = L(lambda n: n, 0); f z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: f(g) + sage: f(0) 0 - sage: L. = LLSRing(QQ) - sage: LS. = LLSRing(QQ, sparse=True) - sage: f = L(lambda n : 1); f + sage: f(y) + y + 2*y^2 + 3*y^3 + 4*y^4 + 5*y^5 + 6*y^6 + ... + sage: fp = f(y - y) + sage: fp == 0 + True + sage: fp.parent() is LS + True + + sage: f = z^2 + 3 + z + sage: f(y - y) + 3 + + With both of them sparse:: + + sage: L. = LLSRing(QQ, sparse=True) + sage: LS. = LLSRing(QQ, sparse=True) + sage: f = L(lambda n: 1); f 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: g = LS(lambda n: 0 if n != 2 else 1, 0); g - y^2 + ... - sage: f(g) - 1 + z^2 + z^4 + z^6 + ... - sage: g = LS(lambda n: 1 if n > 1 else 0, 0); g - y^2 + y^3 + y^4 + y^5 + y^6 + ... - sage: f(g) - 1 + z^2 + z^3 + 2*z^4 + 3*z^5 + 5*z^6 + ... - sage: L. = LLSRing(QQ, sparse=True) - sage: f = L(lambda n : 1); f + sage: f(y^2) + 1 + y^2 + y^4 + y^6 + ... + + sage: fp = f - 1 + z^-2; fp + z^-2 + z + z^2 + z^3 + z^4 + ... + sage: fpy = fp(y^2); fpy + y^-4 + y^2 + ... + sage: [fpy[i] for i in range(-4,11)] + [1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1] + + sage: g = LS(valuation=2, constant=1); g + y^2 + y^3 + y^4 + ... + sage: fg = f(g); fg + 1 + y^2 + y^3 + 2*y^4 + 3*y^5 + 5*y^6 + ... + sage: 1 + g + g^2 + g^3 + g^4 + g^5 + g^6 + 1 + y^2 + y^3 + 2*y^4 + 3*y^5 + 5*y^6 + ... + + sage: h = LS(lambda n: 1 if n % 2 else 0, 2); h + y^3 + y^5 + y^7 + ... + sage: fgh = fg(h); fgh + 1 + y^6 + ... + sage: [fgh[i] for i in range(0, 15)] + [1, 0, 0, 0, 0, 0, 1, 0, 2, 1, 3, 3, 6, 6, 13] + sage: t = 1 + h^2 + h^3 + 2*h^4 + 3*h^5 + 5*h^6 + sage: [t[i] for i in range(0, 15)] + [1, 0, 0, 0, 0, 0, 1, 0, 2, 1, 3, 3, 6, 6, 13] + + We look at mixing the sparse and the dense:: + + sage: L. = LLSRing(QQ) + sage: f = L(lambda n: 1); f 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: g = LS(lambda n: 1, 1); g + sage: g = LS(lambda n: 1, 1); g y + y^2 + y^3 + y^4 + y^5 + y^6 + y^7 + ... - sage: f(g) - 1 + z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + ... - sage: L. = LLSRing(QQ) - sage: LS. = LLSRing(QQ) + sage: f(g) + 1 + y + 2*y^2 + 4*y^3 + 8*y^4 + 16*y^5 + 32*y^6 + ... + sage: f = z^-2 + 1 + z sage: g = 1/(y*(1-y)); g y^-1 + 1 + y + y^2 + y^3 + y^4 + y^5 + ... sage: f(g) - z^-1 + 2 + z + 2*z^2 - z^3 + 2*z^4 + z^5 + ... + y^-1 + 2 + y + 2*y^2 - y^3 + 2*y^4 + y^5 + ... + sage: g^-2 + 1 + g + y^-1 + 2 + y + 2*y^2 - y^3 + 2*y^4 + y^5 + ... + sage: f = z^-3 + z^-2 + 1 sage: g = 1/(y^2*(1-y)); g y^-2 + y^-1 + 1 + y + y^2 + y^3 + y^4 + ... sage: f(g) - 1 + z^4 - 2*z^5 + 2*z^6 + ... - sage: z(z) - z + ... - sage: f = L(lambda n : n); f - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: f(z^2) - z^2 + 2*z^4 + 3*z^6 + ... + 1 + y^4 - 2*y^5 + 2*y^6 + ... + sage: g^-3 + g^-2 + 1 + 1 + y^4 - 2*y^5 + 2*y^6 + ... + sage: z(y) + y """ - # g = 0, val(f) >= 0 - P = self.parent() - try: - if isinstance(other._aux, LLS_zero) and self._aux._approximate_valuation >= 0: - return self[0] - except AttributeError: - if other == 0: - return self[0] - raise ValueError() + # f = self and compute f(g) + P = g.parent() + + # g = 0 case + if (not isinstance(g, LLS) and not g) or (isinstance(g, LLS) and isinstance(g._aux, LLS_zero)): + if self._aux._approximate_valuation >= 0: + return P(self[0]) + # Perhaps we just don't yet know if the valuation is non-negative + if any(self._aux[i] for i in range(self._aux._approximate_valuation, 0)): + raise ZeroDivisionError("the valuation of the series must be nonnegative") + self._aux._approximate_valuation = 0 + return P(self[0]) + + # f has finite length + if isinstance(self._aux, LLS_zero): # constant 0 + return self + if isinstance(self._aux, LLS_eventually_geometric) and not self._aux._constant: + # constant polynomial + if self._aux._laurent_polynomial.is_constant(): + return self + if not isinstance(g, LLS): + return self._aux._laurent_polynomial(g) + # g also has finite length, compose the polynomials + if isinstance(g._aux, LLS_eventually_geometric) and not g._aux._constant: + R = P._laurent_poly_ring + try: + ret = self._aux._laurent_polynomial(g._aux._laurent_polynomial) + if ret.parent() is R: + return P.element_class(P, LLS_eventually_geometric(ret, self._aux._is_sparse, 0)) + except TypeError: # the result is not a Laurent polynomial + pass - if isinstance(self._aux, LLS_eventually_geometric): - R = P._laurent_poly_ring - z = R.gen() - p = R.sum(self._aux[i] * z**(i - self._aux._approximate_valuation) for i in range(self._aux._approximate_valuation, self._aux._degree)) - u = LLS_eventually_geometric(p, self._aux._is_sparse, self._aux._degree) - x = P(lambda n: other[n], other._aux._approximate_valuation) - new_x = x ** self._aux._approximate_valuation - m = P(0) - for i in range(u._degree): - if u[i] != 0: - m = m + (x ** i) * u[i] - return m * new_x - - # g != 0, val(g) > 0 and g != 0, f has finitely many non-zero coefficients - return P.element_class(P, LLS_com(self._aux, other._aux, P)) + # Return the sum since g is not known to be finite or we do not get a Laurent polynomial + # TODO: Optimize when f has positive valuation + poly = self._aux._laurent_polynomial + ret = P.zero() + gp = P.one() + # We build this iteratively so each power can benefit from the caching + # Equivalent to P.sum(poly[i] * g**i for i in range(poly.valuation(), poly.degree()+1)) + # We could just do "return poly(g)" if we don't care about speed + deg = poly.degree() + for i in range(deg): + ret += poly[i] * gp + gp *= g + ret += poly[deg] * gp + gi = ~g + gp = P.one() + for i in range(-1, poly.valuation()-1, -1): + gp *= gi + ret += poly[i] * gp + return ret + + # g != 0 and val(g) > 0 + if not isinstance(g, LLS): + try: + g = self.parent()(g) + except (TypeError, ValueError): + raise NotImplementedError("can only compose with a lazy Laurent series") + # Perhaps we just don't yet know if the valuation is positive + if g._aux._approximate_valuation <= 0: + if any(g._aux[i] for i in range(self._aux._approximate_valuation)): + raise ValueError("can only compose with a positive valuation series") + g._aux._approximate_valuation = 1 + + return P.element_class(P, LLS_composition(self._aux, g._aux)) def _mul_(self, other): """ @@ -296,6 +435,11 @@ def _mul_(self, other): 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: M * N z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + ... + + sage: L.one() * M is M + True + sage: M * L.one() is M + True """ P = self.parent() left = self._aux @@ -303,11 +447,18 @@ def _mul_(self, other): if isinstance(left, LLS_zero) or isinstance(right, LLS_zero): return P.zero() - if (isinstance(left, LLS_eventually_geometric) and isinstance(right, LLS_eventually_geometric)): - if not left._constant and not right._constant: - p = left._laurent_polynomial * right._laurent_polynomial - c = left._constant - return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c)) + R = P._laurent_poly_ring + if isinstance(left, LLS_eventually_geometric): + if not left._constant: + if left._laurent_polynomial == R.one(): # self == 1 + return other + if isinstance(right, LLS_eventually_geometric): + if not right._constant: + p = left._laurent_polynomial * right._laurent_polynomial + c = left._constant + return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c)) + elif isinstance(right, LLS_eventually_geometric) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 + return self return P.element_class(P, LLS_mul(self._aux, other._aux)) def _add_(self, other): @@ -508,10 +659,17 @@ def _rmul_(self, scalar): 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: N * 4 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... + + sage: 1 * M is M + True + sage: M * 1 is M + True """ P = self.parent() if not scalar: return P.zero() + if scalar == 1: + return self if isinstance(self._aux, LLS_eventually_geometric): c = scalar * self._aux._constant @@ -549,6 +707,9 @@ def _neg_(self): c = -self._aux._constant d = self._aux._degree return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) + # -(-f) = f + if isinstance(self._aux, LLS_neg): + return P.element_class(P, self._aux._series) return P.element_class(P, LLS_neg(self._aux)) def __invert__(self): @@ -569,11 +730,17 @@ def __invert__(self): z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: P = ~M; P z^-1 - 2 + z + ... + + sage: ~(~(1 - z)) + 1 - z """ P = self.parent() if isinstance(self._aux, LLS_eventually_geometric) and self._aux._laurent_polynomial == P.gen(): ret = 1 / self._aux._laurent_polynomial return P.element_class(P, LLS_eventually_geometric(ret, P._sparse, self._aux._constant)) + # (f^-1)^-1 = f + if isinstance(self._aux, LLS_inv): + return P.element_class(P, self._aux._series) return P.element_class(P, LLS_inv(self._aux)) def coefficient(self, n): @@ -1109,6 +1276,27 @@ def define(self, s): sage: C[0:15] [0, 0, 1, 0, 1, 0, 2, 0, 5, 0, 15, 0, 44, 2, 142] + Counting binary trees:: + + sage: L. = LLSRing(QQ) + sage: s = L(None, valuation=1) + sage: s.define(z + (s^2+s(z^2))/2) + sage: [s[i] for i in range(9)] + [0, 1, 1, 1, 2, 3, 6, 11, 23] + + The `q`-Catalan numbers:: + + sage: R. = ZZ[] + sage: L. = LLSRing(R) + sage: s = L(None) + sage: s.define(1+z*s*s(q*z)) + sage: s + 1 + z + (q + 1)*z^2 + (q^3 + q^2 + 2*q + 1)*z^3 + + (q^6 + q^5 + 2*q^4 + 3*q^3 + 3*q^2 + 3*q + 1)*z^4 + + (q^10 + q^9 + 2*q^8 + 3*q^7 + 5*q^6 + 5*q^5 + 7*q^4 + 7*q^3 + 6*q^2 + 4*q + 1)*z^5 + + (q^15 + q^14 + 2*q^13 + 3*q^12 + 5*q^11 + 7*q^10 + 9*q^9 + 11*q^8 + + 14*q^7 + 16*q^6 + 16*q^5 + 17*q^4 + 14*q^3 + 10*q^2 + 5*q + 1)*z^6 + ... + We count unlabeled ordered trees by total number of nodes and number of internal nodes:: @@ -1118,8 +1306,8 @@ def define(self, s): sage: internal_node = q * z sage: L = Q(constant=1, degree=1) sage: T = Q(None, 1) - sage: T.define(leaf + internal_node * L(T)) # not tested - composition need - sage: [T[i] for i in range(7)] # not tested - composition need + sage: T.define(leaf + internal_node * L(T)) + sage: [T[i] for i in range(6)] [0, 1, q, q^2 + q, q^3 + 3*q^2 + q, q^4 + 6*q^3 + 6*q^2 + q] TESTS:: @@ -1566,51 +1754,49 @@ def iterate_coefficients(self): n += 1 -class LLS_com(LLS_binary): - """ - Return ``left`` composed by ``right``. +class LLS_composition(LLS_binary): + r""" + Return ``f`` composed by ``g``. + + This is the composition `(f \circ g)(z) = f(g(z))`. + + INPUT: + + - ``f`` -- a :class:`LLS_aux` + - ``g`` -- a :class:`LLS_aux` with positive valuation """ - def __init__(self, left, right, ring): - lv = left._approximate_valuation - rv = right.valuation() - self._ring = ring - self._lv = lv - self._rv = rv - if isinstance(left, LLS_eventually_geometric): - val = rv * (left._degree - 1 - lv) + rv * lv - super().__init__(left, right, left._is_sparse, val) - else: - super().__init__(left, right, left._is_sparse, lv * rv) + def __init__(self, f, g): + assert g._approximate_valuation > 0 + self._fv = f._approximate_valuation + self._gv = g._approximate_valuation + if self._fv < 0: + ginv = LLS_inv(g) + # the constant part makes no contribution to the negative + # we need this for the case so self._neg_powers[0][n] => 0 + self._neg_powers = [LLS_zero(f._is_sparse), ginv] + for i in range(1, -self._fv): + self._neg_powers.append(LLS_mul(self._neg_powers[-1], ginv)) + # Placeholder None to make this 1-based + self._pos_powers = [None, g] + val = self._fv * self._gv + super().__init__(f, g, f._is_sparse, val) def get_coefficient(self, n): - # g != 0, val(g) > 0 - if self._rv > 0: - if n == 0: - return self._left[self._lv] - tail = self._ring(lambda n: self._left[n + 1], 0) - # tail = LLS_coefficient_function(lambda n: self._left[n + 1], self._ring, self._ring._sparse, 0) - right_series = self._ring(lambda n: self._right[n], self._rv) - # right_series = LLS_coefficient_function(lambda n: self._right[n], self._ring, self._ring._sparse, self._rv) - z = tail(right_series) * right_series - # z = LLS_mul(LLS_com(tail, right_series, self._ring), right_series) - return z[n] - else: - # return u[n] - raise ValueError('Cannot compose series') - + if n < 0: + return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) + # n > 0 + while len(self._pos_powers) <= n // self._gv: + self._pos_powers.append(LLS_mul(self._pos_powers[-1], self._right)) + ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, 0)) + if n == 0: + ret += self._left[0] + return ret + sum(self._left[i] * self._pos_powers[i][n] for i in range(1, n // self._gv+1)) + def iterate_coefficients(self): - # g != 0, val(g) > 0 - if self._rv > 0: - yield self._left[self._lv] - tail = self._ring(lambda n: self._left[n + 1], 0) - right_series = self._ring(lambda n: self._right[n], self._rv) - z = tail(right_series) * right_series - n = 1 - while True: - yield z[n] - n += 1 - else: - raise ValueError('Cannot compose series') + n = self._approximate_valuation + while True: + yield self.get_coefficient(n) + n += 1 ##################################################################### ## Unary operations diff --git a/src/sage/rings/lazy_laurent_series_ring_new.py b/src/sage/rings/lazy_laurent_series_ring_new.py index 8574cce3951..b1dec903b78 100644 --- a/src/sage/rings/lazy_laurent_series_ring_new.py +++ b/src/sage/rings/lazy_laurent_series_ring_new.py @@ -242,6 +242,13 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: f[30] -219 + sage: L(valuation=2, constant=1) + y^2 + y^3 + y^4 + ... + sage: L(constant=1) + Traceback (most recent call last): + ... + ValueError: you must specify the degree for the polynomial 0 + Alternatively, the ``coefficient_function`` can be a list of elements of the base ring. Then these elements are read as coefficients of the terms of degrees starting from the ``valuation``. In this case, ``constant`` @@ -261,6 +268,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No return self.element_class(self, LLS_uninitialized(self._sparse, valuation)) R = self._laurent_poly_ring + BR = self.base_ring() try: # Try to build stuff using the polynomial ring constructor x = R(x) @@ -268,12 +276,16 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No pass if isinstance(constant, (tuple, list)): constant, degree = constant + if constant is not None: + constant = BR(constant) if x in R: if not x and not constant: aux = LLS_zero(self._sparse) else: - if valuation: + if x and valuation: x = x.shift(valuation - x.valuation()) + if degree is None and not x: + degree = valuation aux = LLS_eventually_geometric(R(x), self._sparse, constant, degree) return self.element_class(self, aux) if isinstance(x, LLS): From 3a8ff9e00cac8697a68b0980ee541f6450bc372f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 22 Jul 2021 17:36:51 +1000 Subject: [PATCH 71/98] Fix little bug in polynomial(). --- src/sage/rings/lazy_laurent_series_new.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py index 82c8e899a2f..e25896246c6 100644 --- a/src/sage/rings/lazy_laurent_series_new.py +++ b/src/sage/rings/lazy_laurent_series_new.py @@ -1020,7 +1020,7 @@ def polynomial(self, degree=None, name=None): EXAMPLES:: - sage: L = LLSRing(ZZ, 'z') + sage: L. = LLSRing(ZZ) sage: f = L([1,0,0,2,0,0,0,3], 5); f z^5 + 2*z^8 + 3*z^12 sage: f.polynomial() @@ -1048,12 +1048,18 @@ def polynomial(self, degree=None, name=None): sage: M = L(lambda n: n^2, 0) sage: M.polynomial(5) 25*z^5 + 16*z^4 + 9*z^3 + 4*z^2 + z + + sage: f = 1/(1 + z) + sage: f.polynomial() + Traceback (most recent call last): + ... + ValueError: not a polynomial """ if degree is None: if isinstance(self._aux, LLS_zero): from sage.rings.all import PolynomialRing return PolynomialRing(S.base_ring(), name=name).zero() - elif isinstance(self._aux, LLS_eventually_geometric) and self._aux._constant: + elif isinstance(self._aux, LLS_eventually_geometric) and not self._aux._constant: m = self._aux._degree else: raise ValueError("not a polynomial") From 2d59d81c43dc986774db1f3ea26789d8abcf759e Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 23 Jul 2021 00:02:32 +0530 Subject: [PATCH 72/98] Moved the files, and changed LLS to Lazy Laurent Series. --- src/sage/rings/all.py | 1 - src/sage/rings/lazy_laurent_series.py | 2020 +++++++++++++---- src/sage/rings/lazy_laurent_series_new.py | 1896 ---------------- .../rings/lazy_laurent_series_operator.py | 987 -------- src/sage/rings/lazy_laurent_series_ring.py | 258 +-- .../rings/lazy_laurent_series_ring_new.py | 347 --- 6 files changed, 1654 insertions(+), 3855 deletions(-) delete mode 100644 src/sage/rings/lazy_laurent_series_new.py delete mode 100644 src/sage/rings/lazy_laurent_series_operator.py delete mode 100644 src/sage/rings/lazy_laurent_series_ring_new.py diff --git a/src/sage/rings/all.py b/src/sage/rings/all.py index 9acbcffd881..50f0a4e190c 100644 --- a/src/sage/rings/all.py +++ b/src/sage/rings/all.py @@ -120,7 +120,6 @@ from .laurent_series_ring_element import LaurentSeries # Lazy Laurent series ring -lazy_import('sage.rings.lazy_laurent_series_ring_new', 'LLSRing') lazy_import('sage.rings.lazy_laurent_series_ring', 'LazyLaurentSeriesRing') # Tate algebras diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 23e72dd8084..1bd78d8686a 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -14,12 +14,8 @@ This defines the generating function of Fibonacci sequence:: - sage: def coeff(s, i): - ....: if i in [0, 1]: - ....: return 1 - ....: else: - ....: return s.coefficient(i - 1) + s.coefficient(i - 2) - sage: f = L.series(coeff, valuation=0); f + sage: f = 1 / (1 - z - z^2) + sage: f 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... The 100th element of Fibonacci sequence can be obtained from the generating @@ -30,12 +26,12 @@ Coefficients are computed and cached only when necessary:: - sage: f._cache[100] + sage: f._aux._cache[100] 573147844013817084101 - sage: f._cache[101] + sage: f._aux._cache[101] Traceback (most recent call last): ... - KeyError: 101 + IndexError: list index out of range You can do arithmetic with lazy power series:: @@ -75,36 +71,27 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from .infinity import infinity -from sage.structure.element import ModuleElement -from sage.structure.richcmp import op_EQ, op_NE +from .infinity import infinity +from sage.structure.element import ModuleElement, parent +from .integer_ring import ZZ +from sage.structure.richcmp import op_EQ, op_NE from sage.arith.power import generic_power - -from .lazy_laurent_series_operator import ( - LazyLaurentSeriesOperator_mul, - LazyLaurentSeriesOperator_div, - LazyLaurentSeriesOperator_add, - LazyLaurentSeriesOperator_sub, - LazyLaurentSeriesOperator_neg, - LazyLaurentSeriesOperator_inv, - LazyLaurentSeriesOperator_scale, - LazyLaurentSeriesOperator_apply, - LazyLaurentSeriesOperator_change_ring, - LazyLaurentSeriesOperator_truncate -) - +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing +from sage.rings.integer import Integer class LazyLaurentSeries(ModuleElement): r""" - Return a lazy Laurent series. + A Laurent series where the coefficients are computed lazily. INPUT: - - ``coefficient`` -- Python function that computes coefficients + - ``coefficient_function`` -- Python function that computes coefficients + + - ``issparse`` -- Boolean that determines whether the implementation is sparse or dense - - ``valuation`` -- integer; approximate valuation of the series + - ``approximate_valuation`` -- integer; approximate valuation of the series - ``constant`` -- either ``None`` or pair of an element of the base ring and an integer @@ -112,9 +99,9 @@ class LazyLaurentSeries(ModuleElement): series with exponent `i`. Python function ``coefficient`` returns the value of the coefficient of - index `i` from input `s` and `i` where `s` is the series itself. + index `i` from input. - Let ``valuation`` be `n`. All coefficients of index below `n` are zero. If + Let ``approximate_valuation`` be `n`. All coefficients of index below `n` are zero. If ``constant`` is ``None``, then the ``coefficient`` function is responsible to compute the values of all coefficients of index `\ge n`. If ``constant`` is a pair `(c,m)`, then the ``coefficient`` function is responsible to @@ -123,37 +110,30 @@ class LazyLaurentSeries(ModuleElement): EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: L.series(lambda s, i: i, valuation=-3, constant=(-1,3)) + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L(lambda i: i, valuation=-3, constant=(-1,3)) + -3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ... + sage: L(lambda i: i, valuation=-3, constant=-1, degree=3) -3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ... :: - sage: def coeff(s, i): - ....: if i in [0, 1]: - ....: return 1 - ....: else: - ....: return s.coefficient(i - 1) + s.coefficient(i - 2) - sage: f = L.series(coeff, valuation=0); f + sage: f = 1 / (1 - z - z^2); f 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... sage: f.coefficient(100) 573147844013817084101 Lazy Laurent series is picklable:: - sage: z = L.gen() - sage: f = 1/(1 - z - z^2) - sage: f - 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... sage: g = loads(dumps(f)) sage: g 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... sage: g == f True """ - def __init__(self, parent, coefficient=None, valuation=0, constant=None): + def __init__(self, parent, aux): """ - Initialize. + Initialize the series. TESTS:: @@ -162,352 +142,802 @@ def __init__(self, parent, coefficient=None, valuation=0, constant=None): sage: TestSuite(z).run() """ ModuleElement.__init__(self, parent) + self._aux = aux - self._coefficient_function = coefficient - self._approximate_valuation = valuation - self._constant = constant + def __getitem__(self, n): + """ + Return the coefficient of the term with exponent ``n`` of the series. + + INPUT: - self._cache = dict() # cache of known coefficients + - ``n`` -- integer - def _richcmp_(self, other, op): + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = z/(1 - 2*z^3) + sage: [f[n] for n in range(20)] + [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] + sage: f[0:20] + [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] + + sage: M = L(lambda n: n) + sage: [M[n] for n in range(20)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n) + sage: [M[n] for n in range(20)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] """ - Compare ``self` with ``other`` with respect to the comparison operator ``op``. + R = self.base_ring() + if isinstance(n, slice): + if n.stop is None: + raise NotImplementedError("cannot list an infinite set") + start = n.start if n.start is not None else self._aux.valuation() + step = n.step if n.step is not None else 1 + return [R(self._aux[k]) for k in range(start, n.stop, step)] + return R(self._aux[n]) + + def __call__(self, g): + r""" + Return the composition of the series with ``g``. - Equality is verified if the corresponding coefficients of both series - can be checked for equality without computing coefficients - indefinitely. Otherwise an exception is raised to declare that - equality is not decidable. + INPUT: - Inequality is not defined for lazy Laurent series. + - ``g`` -- other series TESTS:: sage: L. = LazyLaurentSeriesRing(QQ) - sage: z + z^2 == z^2 + z + sage: f = z^2 + 1 + z + sage: f(0) + 1 + sage: f(L(0)) + 1 + sage: f(f) + 3 + 3*z + 4*z^2 + 2*z^3 + z^4 + sage: g = z^-3/(1-2*z); g + z^-3 + 2*z^-2 + 4*z^-1 + 8 + 16*z + 32*z^2 + 64*z^3 + ... + sage: f(g) + z^-6 + 4*z^-5 + 12*z^-4 + 33*z^-3 + 82*z^-2 + 196*z^-1 + 457 + ... + sage: g^2 + 1 + g + z^-6 + 4*z^-5 + 12*z^-4 + 33*z^-3 + 82*z^-2 + 196*z^-1 + 457 + ... + + sage: f = z^-2 + z + 4*z^3 + sage: f(f) + 4*z^-6 + 12*z^-3 + z^-2 + 48*z^-1 + 12 + ... + sage: f^-2 + f + 4*f^3 + 4*z^-6 + 12*z^-3 + z^-2 + 48*z^-1 + 12 + ... + sage: f(g) + 4*z^-9 + 24*z^-8 + 96*z^-7 + 320*z^-6 + 960*z^-5 + 2688*z^-4 + 7169*z^-3 + ... + sage: g^-2 + g + 4*g^3 + 4*z^-9 + 24*z^-8 + 96*z^-7 + 320*z^-6 + 960*z^-5 + 2688*z^-4 + 7169*z^-3 + ... + + sage: f = z^-3 + z^-2 + 1 / (1 + z^2); f + z^-3 + z^-2 + 1 - z^2 + ... + sage: g = z^3 / (1 + z - z^3); g + z^3 - z^4 + z^5 - z^7 + 2*z^8 - 2*z^9 + ... + sage: f(g) + z^-9 + 3*z^-8 + 3*z^-7 - z^-6 - 4*z^-5 - 2*z^-4 + z^-3 + ... + sage: g^-3 + g^-2 + 1 / (1 + g^2) + z^-9 + 3*z^-8 + 3*z^-7 - z^-6 - 4*z^-5 - 2*z^-4 + z^-3 + ... + + sage: f = L(lambda n: n); f + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: f(z^2) + z^2 + 2*z^4 + 3*z^6 + ... + + sage: f = L(lambda n: n, -2); f + -2*z^-2 - z^-1 + z + 2*z^2 + 3*z^3 + 4*z^4 + ... + sage: f3 = f(z^3); f3 + -2*z^-6 - z^-3 + ... + sage: [f3[i] for i in range(-6,13)] + [-2, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4] + + We compose a Laurent polynomial with a generic element:: + + sage: R. = QQ[] + sage: f = z^2 + 1 + z^-1 + sage: g = x^2 + x + 3 + sage: f(g) + (x^6 + 3*x^5 + 12*x^4 + 19*x^3 + 37*x^2 + 28*x + 31)/(x^2 + x + 3) + sage: f(g) == g^2 + 1 + g^-1 True - sage: z + z^2 != z^2 + z - False - sage: z + z^2 > z^2 + z - False - sage: z + z^2 < z^2 + z - False - """ - if op is op_EQ: - if self._constant is None: - if other._constant is None: - n = min(self._approximate_valuation, other._approximate_valuation) - m = max(self._approximate_valuation, other._approximate_valuation) - for i in range(n, m): - if self.coefficient(i) != other.coefficient(i): - return False - if self._coefficient_function == other._coefficient_function: - return True - raise ValueError("undecidable as lazy Laurent series") - else: - raise ValueError("undecidable as lazy Laurent series") - elif other._constant is None: - raise ValueError("undecidable as lazy Laurent series") - sc, sm = self._constant - oc, om = other._constant + We compose with another lazy Laurent series:: - if sc != oc: - return False - - n = self._approximate_valuation - m = max(sm, om) + sage: LS. = LazyLaurentSeriesRing(QQ) + sage: f = z^2 + 1 + z^-1 + sage: fy = f(y); fy + y^-1 + 1 + y^2 + sage: fy.parent() is LS + True + sage: g = y - y + sage: f(g) + Traceback (most recent call last): + ... + ZeroDivisionError: the valuation of the series must be nonnegative + + sage: g = 1 - y + sage: f(g) + 3 - y + 2*y^2 + y^3 + y^4 + y^5 + y^6 + ... + sage: g^2 + 1 + g^-1 + 3 - y + 2*y^2 + y^3 + y^4 + y^5 + y^6 + ... + + sage: f = L(lambda n: n, 0); f + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: f(0) + 0 + sage: f(y) + y + 2*y^2 + 3*y^3 + 4*y^4 + 5*y^5 + 6*y^6 + ... + sage: fp = f(y - y) + sage: fp == 0 + True + sage: fp.parent() is LS + True - for i in range(n, m): - if self.coefficient(i) != other.coefficient(i): - return False + sage: f = z^2 + 3 + z + sage: f(y - y) + 3 - return True + With both of them sparse:: - if op is op_NE: - return not (self == other) + sage: L. = LazyLaurentSeriesRing(QQ, sparse=True) + sage: LS. = LazyLaurentSeriesRing(QQ, sparse=True) + sage: f = L(lambda n: 1); f + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: f(y^2) + 1 + y^2 + y^4 + y^6 + ... + + sage: fp = f - 1 + z^-2; fp + z^-2 + z + z^2 + z^3 + z^4 + ... + sage: fpy = fp(y^2); fpy + y^-4 + y^2 + ... + sage: [fpy[i] for i in range(-4,11)] + [1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1] + + sage: g = LS(valuation=2, constant=1); g + y^2 + y^3 + y^4 + ... + sage: fg = f(g); fg + 1 + y^2 + y^3 + 2*y^4 + 3*y^5 + 5*y^6 + ... + sage: 1 + g + g^2 + g^3 + g^4 + g^5 + g^6 + 1 + y^2 + y^3 + 2*y^4 + 3*y^5 + 5*y^6 + ... + + sage: h = LS(lambda n: 1 if n % 2 else 0, 2); h + y^3 + y^5 + y^7 + ... + sage: fgh = fg(h); fgh + 1 + y^6 + ... + sage: [fgh[i] for i in range(0, 15)] + [1, 0, 0, 0, 0, 0, 1, 0, 2, 1, 3, 3, 6, 6, 13] + sage: t = 1 + h^2 + h^3 + 2*h^4 + 3*h^5 + 5*h^6 + sage: [t[i] for i in range(0, 15)] + [1, 0, 0, 0, 0, 0, 1, 0, 2, 1, 3, 3, 6, 6, 13] + + We look at mixing the sparse and the dense:: - return False + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = L(lambda n: 1); f + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: g = LS(lambda n: 1, 1); g + y + y^2 + y^3 + y^4 + y^5 + y^6 + y^7 + ... + sage: f(g) + 1 + y + 2*y^2 + 4*y^3 + 8*y^4 + 16*y^5 + 32*y^6 + ... + + sage: f = z^-2 + 1 + z + sage: g = 1/(y*(1-y)); g + y^-1 + 1 + y + y^2 + y^3 + y^4 + y^5 + ... + sage: f(g) + y^-1 + 2 + y + 2*y^2 - y^3 + 2*y^4 + y^5 + ... + sage: g^-2 + 1 + g + y^-1 + 2 + y + 2*y^2 - y^3 + 2*y^4 + y^5 + ... + + sage: f = z^-3 + z^-2 + 1 + sage: g = 1/(y^2*(1-y)); g + y^-2 + y^-1 + 1 + y + y^2 + y^3 + y^4 + ... + sage: f(g) + 1 + y^4 - 2*y^5 + 2*y^6 + ... + sage: g^-3 + g^-2 + 1 + 1 + y^4 - 2*y^5 + 2*y^6 + ... + sage: z(y) + y + """ + # f = self and compute f(g) + P = g.parent() + + # g = 0 case + if (not isinstance(g, LazyLaurentSeries) and not g) or (isinstance(g, LazyLaurentSeries) and isinstance(g._aux, LazyLaurentSeries_zero)): + if self._aux._approximate_valuation >= 0: + return P(self[0]) + # Perhaps we just don't yet know if the valuation is non-negative + if any(self._aux[i] for i in range(self._aux._approximate_valuation, 0)): + raise ZeroDivisionError("the valuation of the series must be nonnegative") + self._aux._approximate_valuation = 0 + return P(self[0]) + + # f has finite length + if isinstance(self._aux, LazyLaurentSeries_zero): # constant 0 + return self + if isinstance(self._aux, LazyLaurentSeries_eventually_geometric) and not self._aux._constant: + # constant polynomial + if self._aux._laurent_polynomial.is_constant(): + return self + if not isinstance(g, LazyLaurentSeries): + return self._aux._laurent_polynomial(g) + # g also has finite length, compose the polynomials + if isinstance(g._aux, LazyLaurentSeries_eventually_geometric) and not g._aux._constant: + R = P._laurent_poly_ring + try: + ret = self._aux._laurent_polynomial(g._aux._laurent_polynomial) + if ret.parent() is R: + return P.element_class(P, LazyLaurentSeries_eventually_geometric(ret, self._aux._is_sparse, 0)) + except TypeError: # the result is not a Laurent polynomial + pass + + # Return the sum since g is not known to be finite or we do not get a Laurent polynomial + # TODO: Optimize when f has positive valuation + poly = self._aux._laurent_polynomial + ret = P.zero() + gp = P.one() + # We build this iteratively so each power can benefit from the caching + # Equivalent to P.sum(poly[i] * g**i for i in range(poly.valuation(), poly.degree()+1)) + # We could just do "return poly(g)" if we don't care about speed + deg = poly.degree() + for i in range(deg): + ret += poly[i] * gp + gp *= g + ret += poly[deg] * gp + gi = ~g + gp = P.one() + for i in range(-1, poly.valuation()-1, -1): + gp *= gi + ret += poly[i] * gp + return ret + + # g != 0 and val(g) > 0 + if not isinstance(g, LazyLaurentSeries): + try: + g = self.parent()(g) + except (TypeError, ValueError): + raise NotImplementedError("can only compose with a lazy Laurent series") + # Perhaps we just don't yet know if the valuation is positive + if g._aux._approximate_valuation <= 0: + if any(g._aux[i] for i in range(self._aux._approximate_valuation)): + raise ValueError("can only compose with a positive valuation series") + g._aux._approximate_valuation = 1 + + return P.element_class(P, LLS_composition(self._aux, g._aux)) - def __hash__(self): + def _mul_(self, other): """ - Return the hash of ``self`` + Return the product of this series with ``other``. - TESTS:: + INPUT: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], -5) - sage: g = (1 + f)/(1 - f)^2 - sage: {g: 1} - {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} - """ - return hash((type(self), self._coefficient_function, - self._approximate_valuation, self._constant)) + - ``other`` -- other series - def __bool__(self): - """ - Test whether ``self`` is not zero. + TESTS:: - EXAMPLES:: + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: (1 - z)*(1 - z) + 1 - 2*z + z^2 + sage: (1 - z)*(1 - z)*(1 - z) + 1 - 3*z + 3*z^2 - z^3 + sage: M = L(lambda n: n) + sage: M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M * (1 - M) + sage: N + z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 + ... + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: M * N + z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + ... - sage: L. = LazyLaurentSeriesRing(GF(2)) - sage: (z-z).is_zero() + sage: L.one() * M is M + True + sage: M * L.one() is M True - sage: f = 1/(1 - z) - sage: f.is_zero() - False """ - if self._constant is None: - for a in self._cache: - if a: - return True - if self.coefficient(self._approximate_valuation): - return True - raise ValueError("undecidable as lazy Laurent series") + P = self.parent() + left = self._aux + right = other._aux + if isinstance(left, LazyLaurentSeries_zero) or isinstance(right, LazyLaurentSeries_zero): + return P.zero() + + R = P._laurent_poly_ring + if isinstance(left, LazyLaurentSeries_eventually_geometric): + if not left._constant: + if left._laurent_polynomial == R.one(): # self == 1 + return other + if isinstance(right, LazyLaurentSeries_eventually_geometric): + if not right._constant: + p = left._laurent_polynomial * right._laurent_polynomial + c = left._constant + return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c)) + elif isinstance(right, LazyLaurentSeries_eventually_geometric) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 + return self + return P.element_class(P, LLS_mul(self._aux, other._aux)) - sc, sm = self._constant + def _add_(self, other): + """ + Return the sum of this series with ``other``. - if sc: - return True + INPUT: - for i in range(self._approximate_valuation, sm): - if self.coefficient(i): - return True + - ``other`` -- other series - return False + TESTS:: - # for Python 2 compatibility - __nonzero__ = __bool__ + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: (1 - z)*(1 - z) + 1 - 2*z + z^2 + sage: (1 - z)*(1 - z)*(1 - z) + 1 - 3*z + 3*z^2 - z^3 + sage: z + z + 2*z + sage: z^2 + 3*z^2 + 4*z^2 + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M + N; P + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + + sage: A = L(1, constant=2, degree=3) + sage: B = L(2, constant=-2, degree=5) + sage: A + B + 3 + 2*z^3 + 2*z^4 + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M + N; P + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + """ + P = self.parent() + left = self._aux + right = other._aux + if (isinstance(left, LazyLaurentSeries_eventually_geometric) + and isinstance(right, LazyLaurentSeries_eventually_geometric)): + R = P._laurent_poly_ring + c = left._constant + right._constant + pl = left._laurent_polynomial + pr = right._laurent_polynomial + d = max(left._degree, right._degree) + pl += R([left._constant]*(d-left._degree)).shift(left._degree) + pr += R([right._constant]*(d-right._degree)).shift(right._degree) + p = pl + pr + if not p and not c: + return P.zero() + return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, d)) + return P.element_class(P, LLS_add(self._aux, other._aux)) - def _repr_(self): + def _sub_(self, other): """ - Return the string representation of this Laurent series. + Return the series of this series minus ``other`` series. - EXAMPLES:: + INPUT: + + - ``other`` -- other series + + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: -1/(1 + 2*z) - -1 + 2*z - 4*z^2 + 8*z^3 - 16*z^4 + 32*z^5 - 64*z^6 + ... + sage: z - z + 0 + sage: 3*z - 2*z + z + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M - N; P + -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... + + sage: A = L(1, constant=2, degree=3) + sage: B = L(2, constant=3, degree=5) + sage: A - B + -1 + 2*z^3 + 2*z^4 - z^5 - z^6 - z^7 + ... + + sage: A = L(1, constant=2, degree=3) + sage: B = L([1,0,0,2,2], constant=2) + sage: X = A - B; X + 0 + sage: type(X._aux) + + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M - N; P + -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... """ - atomic_repr = self.base_ring()._repr_option('element_is_atomic') - X = self.parent().variable_name() + P = self.parent() + left = self._aux + right = other._aux + if (isinstance(left, LazyLaurentSeries_eventually_geometric) and isinstance(right, LazyLaurentSeries_eventually_geometric)): + R = P._laurent_poly_ring + c = left._constant - right._constant + pl = left._laurent_polynomial + pr = right._laurent_polynomial + d = max(left._degree, right._degree) + pl += R([left._constant]*(d-left._degree)).shift(left._degree) + pr += R([right._constant]*(d-right._degree)).shift(right._degree) + p = pl - pr + if not p and not c: + return P.zero() + return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, d)) + if left == right: + return P.zero() + return P.element_class(P, LLS_sub(self._aux, other._aux)) - n = self.valuation() + def _div_(self, other): + """ + Return ``self`` divided by ``other``. - if self._constant is None: - m = n + 7 # long enough - elif self._constant[0] != 0: - m = self._constant[1] + 3 - else: - m = self._constant[1] - - s = ' ' - first = True - while n < m: - x = repr(self.coefficient(n)) - if x != '0': - if not first: - s += ' + ' - if not atomic_repr and n > 0 and (x[1:].find('+') != -1 or x[1:].find('-') != -1): - x = '({})'.format(x) - if n > 1 or n < 0: - var = '*{}^{}'.format(X,n) - elif n == 1: - var = '*{}'.format(X) - else: # n == 0 - var = '' - s += '{}{}'.format(x,var) - first = False - n += 1 + INPUT: - s = s.replace(" + -", " - ").replace(" 1*"," ").replace(" -1*", " -")[1:] + - ``other`` -- nonzero series - if not s: # zero series - s = '0' + TESTS:: - if self._constant is None or self._constant[1] > m or self._constant[0] != 0: - s += ' + {}'.format('...') + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: z/(1 - z) + z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M / N; P + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - return s + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M / N; P + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + """ + if isinstance(other._aux, LazyLaurentSeries_zero): + raise ZeroDivisionError("cannot divide by 0") + + P = self.parent() + left = self._aux + if isinstance(left, LazyLaurentSeries_zero): + return P.zero() + right = other._aux + if (isinstance(left, LazyLaurentSeries_eventually_geometric) + and isinstance(right, LazyLaurentSeries_eventually_geometric)): + if not left._constant and not right._constant: + ret = left._laurent_polynomial / right._laurent_polynomial + try: + ret = P._laurent_poly_ring(ret) + return P.element_class(P, LazyLaurentSeries_eventually_geometric(ret, P._sparse, left._constant)) + except (TypeError, ValueError): + # We cannot divide the polynomials, so the result must be a series + pass + + return P.element_class(P, LLS_mul(left, LLS_inv(right))) - def __getitem__(self, n): + def _rmul_(self, scalar): """ - Return the coefficient of the term with exponent ``n`` of the series. + Return the scalar multiplication of this series by ``scalar``. INPUT: - - ``n`` -- integer + - ``scalar`` -- an element of the base ring - EXAMPLES:: + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = z/(1 - 2*z^3) - sage: [f[n] for n in range(20)] - [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] - """ - return self.coefficient(n) + sage: 2*z + 2*z + sage: -1*z + -z + sage: 0*z + 0 + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M * 3 + 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M * 3 + 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: N * 4 + 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... - def coefficient(self, n): + sage: 1 * M is M + True + sage: M * 1 is M + True """ - Return the coefficient of the term with exponent ``n`` of the series. + P = self.parent() + if not scalar: + return P.zero() + if scalar == 1: + return self - INPUT: + if isinstance(self._aux, LazyLaurentSeries_eventually_geometric): + c = scalar * self._aux._constant + p = scalar * self._aux._laurent_polynomial + return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, self._aux._degree)) - - ``n`` -- integer + return P.element_class(P, LLS_scalar(self._aux, scalar)) - EXAMPLES:: + def _neg_(self): + """ + Return the negative of this series. + + TESTS:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: def g(s, i): - ....: if i == 0: - ....: return 1 - ....: else: - ....: return sum(s.coefficient(j)*s.coefficient(i - 1 -j) for j in [0..i-1]) - sage: e = L.series(g, valuation=0) - sage: e.coefficient(10) - 16796 - sage: e - 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + sage: z = L.gen() + sage: -(1 - z) + -1 + z + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: P = -M; P + -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: P = -M; P + -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... + sage: -(z^2 + 3*z - 4*z^3) + -3*z - z^2 + 4*z^3 """ - R = self.base_ring() + P = self.parent() + if isinstance(self._aux, LazyLaurentSeries_eventually_geometric): + p = -self._aux._laurent_polynomial + c = -self._aux._constant + d = self._aux._degree + return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, d)) + # -(-f) = f + if isinstance(self._aux, LLS_neg): + return P.element_class(P, self._aux._series) + return P.element_class(P, LLS_neg(self._aux)) - if self._approximate_valuation == infinity: - return R.zero() - elif n < self._approximate_valuation: - return R.zero() - elif self._constant is not None and n >= self._constant[1]: - return self._constant[0] + def __invert__(self): + """ + Return the multiplicative inverse of the element. - try: - c = self._cache[n] - except KeyError: - c = R(self._coefficient_function(self, n)) - self._cache[n] = c + TESTS:: - return c + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) + sage: ~(1 - z) + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: P = ~M; P + z^-1 - 2 + z + ... + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: P = ~M; P + z^-1 - 2 + z + ... + + sage: ~(~(1 - z)) + 1 - z + """ + P = self.parent() + if isinstance(self._aux, LazyLaurentSeries_eventually_geometric) and self._aux._laurent_polynomial == P.gen(): + ret = 1 / self._aux._laurent_polynomial + return P.element_class(P, LazyLaurentSeries_eventually_geometric(ret, P._sparse, self._aux._constant)) + # (f^-1)^-1 = f + if isinstance(self._aux, LLS_inv): + return P.element_class(P, self._aux._series) + return P.element_class(P, LLS_inv(self._aux)) - def valuation(self): + def coefficient(self, n): """ - Return the valuation of the series. + Return the coefficient of the term with exponent ``n`` of the series. - This method determines the valuation of the series by looking for a - nonzero coefficient. Hence if the series happens to be zero, then it - may run forever. + INPUT: + + - ``n`` -- integer EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: s = 1/(1 - z) - 1/(1 - 2*z) - sage: s.valuation() - 1 - sage: t = z - z - sage: t.valuation() - +Infinity + sage: F = L(None) + sage: F.define(1 + z*F^2) + sage: F.coefficient(10) + 16796 + sage: F + 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) + sage: e = L(None) + sage: e.define(1 + z*e^2) + sage: e + 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + sage: e._aux._cache + [1, 1, 2, 5, 14, 42, 132] + sage: e.coefficient(10) + 16796 + sage: e._aux._cache + [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796] + sage: M = L(lambda n: n^2); M + z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... + sage: M._aux._cache + [0, 1, 4, 9, 16, 25, 36] + sage: M.coefficient(9) + 81 + sage: M._aux._cache + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + + sage: L = LazyLaurentSeriesRing(ZZ, 'z', sparse=True) + sage: M = L(lambda n: n^2); M + z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... + sage: M._aux._cache + {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36} + sage: M.coefficient(10) + 100 + sage: M._aux._cache + {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 10: 100} """ - if self._constant is None: - n = self._approximate_valuation - cache = self._cache - while True: - if n in cache: - if cache[n]: - self._approximate_valuation = n - return n - n += 1 - else: - if self.coefficient(n) != 0: - self._approximate_valuation = n - return n - n += 1 - else: - n = self._approximate_valuation - m = self._constant[1] - while n <= m: - if self.coefficient(n) != 0: - self._approximate_valuation = n - return n - n += 1 - return infinity + return self.__getitem__(n) - def prec(self): + def map_coefficients(self, func, ring=None): """ - Return the precision of the series, which is infinity. + Return the series with ``func`` applied to each coefficient of this series. - EXAMPLES:: + INPUT: + + - ``func`` -- Python function that takes in a coefficient and returns + a new coefficient + + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) - sage: f.prec() - +Infinity + sage: s = z/(1 - 2*z) + sage: t = s.map_coefficients(lambda c: c + 1) + sage: s + z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... + sage: t + 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M.map_coefficients(lambda c: c + 1); N + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M.map_coefficients(lambda c: c + 1); N + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... """ - return infinity + P = self.parent() + R = P.base_ring() + if isinstance(self._aux, LazyLaurentSeries_eventually_geometric): + p = p.map_coefficients(func) + c = func(c) + if not p and not c: + return P.zero() + return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, self._aux._is_sparse, c, d)) + return P.element_class(P, LLS_apply_coeff(self._aux, func, R)) - def polynomial(self, degree=None, name=None): + def change_ring(self, ring): """ - Return the polynomial or Laurent polynomial if the series is actually so. + Return this series with coefficients converted to elements of ``ring``. INPUT: - - ``degree`` -- ``None`` or an integer - - - ``name`` -- name of the variable; if it is ``None``, the name of the variable - of the series is used + - ``ring`` -- a ring - OUTPUT: a Laurent polynomial if the valuation of the series is negative or - a polynomial otherwise. + TESTS:: - If ``degree`` is not ``None``, the terms of the series of degree - greater than ``degree`` are truncated first. If ``degree`` is ``None`` - and the series is not a polynomial or a Laurent polynomial, a - ``ValueError`` is raised. + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) + sage: s = 2 + z + sage: t = s.change_ring(QQ) + sage: t^-1 + 1/2 - 1/4*z + 1/8*z^2 - 1/16*z^3 + 1/32*z^4 - 1/64*z^5 + 1/128*z^6 + ... + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M.change_ring(QQ) + sage: N.parent() + Lazy Laurent Series Ring in z over Rational Field + sage: M.parent() + Lazy Laurent Series Ring in z over Integer Ring + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M.parent() + Lazy Laurent Series Ring in z over Integer Ring + sage: N = M.change_ring(QQ) + sage: N.parent() + Lazy Laurent Series Ring in z over Rational Field + sage: M ^-1 + z^-1 - 2 + z + ... + """ + from .lazy_laurent_series_ring import LazyLaurentSeriesRing + Q = LazyLaurentSeriesRing(ring, names=self.parent().variable_names()) + return Q.element_class(Q, self._aux) - EXAMPLES:: + def truncate(self, d): + """ + Return this series with its terms of degree >= ``d`` truncated. - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,0,0,2,0,0,0,3], 5); f - z^5 + 2*z^8 + 3*z^12 - sage: f.polynomial() - 3*z^12 + 2*z^8 + z^5 + INPUT: - :: + - ``d`` -- integer - sage: g = L.series([1,0,0,2,0,0,0,3], -5); g - z^-5 + 2*z^-2 + 3*z^2 - sage: g.polynomial() - z^-5 + 2*z^-2 + 3*z^2 + TESTS:: - :: + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) + sage: alpha = 1/(1-z) + sage: alpha + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: beta = alpha.truncate(5) + sage: beta + 1 + z + z^2 + z^3 + z^4 + sage: alpha - beta + z^5 + z^6 + ... + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M.truncate(4) + z + 2*z^2 + 3*z^3 + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M.truncate(4) + z + 2*z^2 + 3*z^3 + """ + P = self.parent() + R = P._laurent_poly_ring + z = R.gen() + p = R.sum(self[i] * z**i for i in range(self._aux._approximate_valuation, d)) + return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, ZZ.zero(), d)) - sage: z = L.gen() - sage: f = (1 + z)/(z^3 - z^5) - sage: f - z^-3 + z^-2 + z^-1 + 1 + z + z^2 + z^3 + ... - sage: f.polynomial(5) - z^-3 + z^-2 + z^-1 + 1 + z + z^2 + z^3 + z^4 + z^5 - sage: f.polynomial(0) - z^-3 + z^-2 + z^-1 + 1 - sage: f.polynomial(-5) - 0 + def __pow__(self, n): """ - if degree is None: - if self._constant is None or not self._constant[0].is_zero(): - raise ValueError("not a polynomial") - m = self._constant[1] - else: - m = degree + 1 + Return the ``n``-th power of the series. - S = self.parent() + TESTS:: - if name is None: - name = S.variable_name() + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: (1 - z)^-1 + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: (1 - z)^0 + 1 + sage: (1 - z)^3 + 1 - 3*z + 3*z^2 - z^3 + sage: (1 - z)^-3 + 1 + 3*z + 6*z^2 + 10*z^3 + 15*z^4 + 21*z^5 + 28*z^6 + ... + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M ^ 2 + z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M ^ 2 + z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... + """ + if n == 0: + return self.parent().one() - if self.valuation() < 0: - from sage.rings.all import LaurentPolynomialRing - R = LaurentPolynomialRing(S.base_ring(), name=name) - n = self.valuation() - return R([self.coefficient(i) for i in range(n,m)]).shift(n) - else: - from sage.rings.all import PolynomialRing - R = PolynomialRing(S.base_ring(), name=name) - return R([self.coefficient(i) for i in range(m)]) + return generic_power(self, n) def approximate_series(self, prec, name=None): """ @@ -523,7 +953,7 @@ def approximate_series(self, prec, name=None): OUTPUT: a Laurent series with absolute precision ``prec`` - EXAMPLES:: + TESTS:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') sage: z = L.gen() @@ -535,9 +965,6 @@ def approximate_series(self, prec, name=None): z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + O(z^10) sage: g.parent() Power Series Ring in z over Integer Ring - - :: - sage: h = (f^-1).approximate_series(3) sage: h z^-5 - 2*z^-4 + 10*z^-3 - 20*z^-2 + 60*z^-1 - 120 + 280*z - 560*z^2 + O(z^3) @@ -553,316 +980,917 @@ def approximate_series(self, prec, name=None): from sage.rings.all import LaurentSeriesRing R = LaurentSeriesRing(S.base_ring(), name=name) n = self.valuation() - return R([self.coefficient(i) for i in range(n,prec)], n).add_bigoh(prec) + return R([self[i] for i in range(n, prec)], n).add_bigoh(prec) else: from sage.rings.all import PowerSeriesRing R = PowerSeriesRing(S.base_ring(), name=name) - return R([self.coefficient(i) for i in range(prec)]).add_bigoh(prec) + return R([self[i] for i in range(prec)]).add_bigoh(prec) - def _mul_(self, other): + def prec(self): """ - Return the product of this series with ``other``. - - INPUT: - - - ``other`` -- other series + Return the precision of the series, which is infinity. - EXAMPLES:: + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: (1 - z)*(1 - z) - 1 - 2*z + z^2 - sage: (1 - z)*(1 - z)*(1 - z) - 1 - 3*z + 3*z^2 - z^3 + sage: f = 1/(1 - z) + sage: f.prec() + +Infinity """ + return infinity - R = self.parent() - - op = LazyLaurentSeriesOperator_mul(self, other) - - a = self._approximate_valuation + other._approximate_valuation + def polynomial(self, degree=None, name=None): + """ + Return the polynomial or Laurent polynomial if the series is actually so. - c = None - if self._constant is not None and other._constant is not None: - if self._constant[0] == 0 and other._constant[0] == 0: - c = (self._constant[0], self._constant[1] + other._constant[1] - 1) + INPUT: - return R.element_class(R, coefficient=op, valuation=a, constant=c) + - ``degree`` -- ``None`` or an integer - def _rmul_(self, scalar): - """ - Return the scalar multiplication of this series by ``scalar``. + - ``name`` -- name of the variable; if it is ``None``, the name of the variable + of the series is used - INPUT: + OUTPUT: a Laurent polynomial if the valuation of the series is negative or + a polynomial otherwise. - - ``scalar`` -- an element of the base ring + If ``degree`` is not ``None``, the terms of the series of degree + greater than ``degree`` are truncated first. If ``degree`` is ``None`` + and the series is not a polynomial or a Laurent polynomial, a + ``ValueError`` is raised. EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: 2*z - 2*z - sage: -1*z - -z - sage: 0*z + sage: f = L([1,0,0,2,0,0,0,3], 5); f + z^5 + 2*z^8 + 3*z^12 + sage: f.polynomial() + 3*z^12 + 2*z^8 + z^5 + + TESTS:: + + sage: g = L([1,0,0,2,0,0,0,3], -5); g + z^-5 + 2*z^-2 + 3*z^2 + sage: g.polynomial() + z^-5 + 2*z^-2 + 3*z^2 + sage: z = L.gen() + sage: f = (1 + z)/(z^3 - z^5) + sage: f + z^-3 + z^-2 + z^-1 + 1 + z + z^2 + z^3 + ... + sage: f.polynomial(5) + z^-3 + z^-2 + z^-1 + 1 + z + z^2 + z^3 + z^4 + z^5 + sage: f.polynomial(0) + z^-3 + z^-2 + z^-1 + 1 + sage: f.polynomial(-5) 0 + sage: M = L(lambda n: n^2, 0) + sage: M.polynomial(3) + 9*z^3 + 4*z^2 + z + sage: M = L(lambda n: n^2, 0) + sage: M.polynomial(5) + 25*z^5 + 16*z^4 + 9*z^3 + 4*z^2 + z + + sage: f = 1/(1 + z) + sage: f.polynomial() + Traceback (most recent call last): + ... + ValueError: not a polynomial """ - R = self.parent() - - if scalar.is_zero(): - return R.zero() + if degree is None: + if isinstance(self._aux, LazyLaurentSeries_zero): + from sage.rings.all import PolynomialRing + return PolynomialRing(S.base_ring(), name=name).zero() + elif isinstance(self._aux, LazyLaurentSeries_eventually_geometric) and not self._aux._constant: + m = self._aux._degree + else: + raise ValueError("not a polynomial") + else: + m = degree + 1 - op = LazyLaurentSeriesOperator_scale(self, scalar) + S = self.parent() - a = self._approximate_valuation + if name is None: + name = S.variable_name() - if self._constant is not None: - c = (scalar * self._constant[0], self._constant[1]) + if self.valuation() < 0: + from sage.rings.all import LaurentPolynomialRing + R = LaurentPolynomialRing(S.base_ring(), name=name) + n = self.valuation() + return R([self[i] for i in range(n, m)]).shift(n) else: - c = None - - return R.element_class(R, coefficient=op, valuation=a, constant=c) + from sage.rings.all import PolynomialRing + R = PolynomialRing(S.base_ring(), name=name) + return R([self[i] for i in range(m)]) - def _add_(self, other): + def valuation(self): """ - Return the sum of this series with ``other``. - - INPUT: + Return the valuation of the series. - - ``other`` -- other series + This method determines the valuation of the series by looking for a + nonzero coefficient. Hence if the series happens to be zero, then it + may run forever. - EXAMPLES:: + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: (1 - z)*(1 - z) - 1 - 2*z + z^2 - sage: (1 - z)*(1 - z)*(1 - z) - 1 - 3*z + 3*z^2 - z^3 + sage: s = 1/(1 - z) - 1/(1 - 2*z) + sage: s.valuation() + 1 + sage: t = z - z + sage: t.valuation() + +Infinity + sage: M = L(lambda n: n^2, 0) + sage: M.valuation() + 1 + sage: M = L(lambda n: n^2, 0) + sage: M.valuation() + 1 + """ + return self._aux.valuation() + + def _repr_(self): """ - R = self.parent() + Return the string representation of this Laurent series. - op = LazyLaurentSeriesOperator_add(self, other) + TESTS:: - a = min(self._approximate_valuation, other._approximate_valuation) + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: -1/(1 + 2*z) + -1 + 2*z - 4*z^2 + 8*z^3 - 16*z^4 + 32*z^5 - 64*z^6 + ... + """ + if isinstance(self._aux, LazyLaurentSeries_zero): + return '0' + if isinstance(self._aux, LazyLaurentSeries_uninitialized) and self._aux._target is None: + return 'Uninitialized LazyLaurentSeries' + + atomic_repr = self.base_ring()._repr_option('element_is_atomic') + X = self.parent().variable_name() + v = self._aux._approximate_valuation - if self._constant is not None and other._constant is not None: - c = (self._constant[0] + other._constant[0], - max(self._constant[1], other._constant[1])) + if not isinstance(self._aux, LazyLaurentSeries_eventually_geometric): + m = v + 7 # long enough + elif not self._aux._constant: + # Just a polynonial, so let that print itself + return repr(self._aux._laurent_polynomial) else: - c = None + m = self._aux._degree + 3 - return R.element_class(R, coefficient=op, valuation=a, constant=c) + # Use the polynomial printing + R = self.parent()._laurent_poly_ring + ret = repr(R([self._aux[i] for i in range(v, m)]).shift(v)) + # TODO: Better handling when ret == 0 but we have not checked up to the constant term + return ret + ' + ...' - def _sub_(self, other): + def _richcmp_(self, other, op): """ - Return the series of this series minus ``other`` series. + Compare ``self` with ``other`` with respect to the comparison operator ``op``. - INPUT: + Equality is verified if the corresponding coefficients of both series + can be checked for equality without computing coefficients + indefinitely. Otherwise an exception is raised to declare that + equality is not decidable. - - ``other`` -- other series + Inequality is not defined for lazy Laurent series. - EXAMPLES:: + TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: z - z - 0 + sage: L. = LazyLaurentSeriesRing(QQ) + sage: z + z^2 == z^2 + z + True + sage: z + z^2 != z^2 + z + False + sage: z + z^2 > z^2 + z + False + sage: z + z^2 < z^2 + z + False """ - R = self.parent() + if op is op_EQ: + if isinstance(self._aux, LazyLaurentSeries_zero): # self == 0 + return isinstance(other._aux, LazyLaurentSeries_zero) + if isinstance(other._aux, LazyLaurentSeries_zero): # self != 0 but other == 0 + return False - op = LazyLaurentSeriesOperator_sub(self, other) + if (not isinstance(self._aux, LazyLaurentSeries_eventually_geometric) + or not isinstance(other._aux, LazyLaurentSeries_eventually_geometric)): + # One of the lazy laurent series is not known to eventually be constant + # Implement the checking of the caches here. + n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) + m = max(self._aux._approximate_valuation, other._aux._approximate_valuation) + for i in range(n, m): + if self[i] != other[i]: + return False + if self._aux == other._aux: + return True + raise ValueError("undecidable as lazy Laurent series") - a = min(self._approximate_valuation, other._approximate_valuation) + # Both are LazyLaurentSeries_eventually_geometric, which implements a full check + return self._aux == other._aux - if self._constant is not None and other._constant is not None: - c = (self._constant[0] - other._constant[0], - max(self._constant[1], other._constant[1])) - else: - c = None + if op is op_NE: + return not (self == other) - return R.element_class(R, coefficient=op, valuation=a, constant=c) + return False - def _neg_(self): + def __hash__(self): """ - Return the negative of this series. + Return the hash of ``self`` - EXAMPLES:: + TESTS:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: z = L.gen() - sage: -(1 - z) - -1 + z + sage: f = L([1,2,3,4], -5) + sage: g = (1 + f)/(1 - f)^2 + sage: {g: 1} + {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} """ - R = self.parent() + return hash(self._aux) - op = LazyLaurentSeriesOperator_neg(self) + def __bool__(self): + """ + Test whether ``self`` is not zero. - a = self._approximate_valuation + TESTS:: - if self._constant is not None: - c = (-self._constant[0], self._constant[1]) - else: - c = None + sage: L. = LazyLaurentSeriesRing(GF(2)) + sage: (z-z).is_zero() + True + sage: f = 1/(1 - z) + sage: f.is_zero() + False + sage: M = L(lambda n: n, 0); M + z + z^3 + z^5 + ... + sage: M.is_zero() + False + """ + if isinstance(self._aux, LazyLaurentSeries_zero): + return False + if isinstance(self._aux, LazyLaurentSeries_eventually_geometric): + # This should always end up being True, but let's be careful about it for now... + return self._aux._laurent_polynomial or self._aux._constant + + for a in self._aux._cache: + if a: + return True + if self[self._aux._approximate_valuation]: + return True + raise ValueError("undecidable as lazy Laurent series") - return R.element_class(R, coefficient=op, valuation=a, constant=c) + def define(self, s): + r""" + Define an equation by ``self = s``. - def __invert__(self): - """ - Return the multiplicative inverse of the element. + EXAMPLES: - EXAMPLES:: + We begin by constructing the Catalan numbers:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: ~(1 - z) - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: C = L(None) + sage: C.define(1 + z*C^2) + sage: C + 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + + The Catalan numbers but with a valuation 1:: + + sage: B = L(None, 1) + sage: B.define(z + B^2) + sage: B + z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + ... + + We can define multiple series that are linked:: + + sage: s = L(None) + sage: t = L(None) + sage: s.define(1 + z*t^3) + sage: t.define(1 + z*s^2) + sage: s[:9] + [1, 1, 3, 9, 34, 132, 546, 2327, 10191] + sage: t[:9] + [1, 1, 2, 7, 24, 95, 386, 1641, 7150] + + An bigger example:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: A = L(None, 5) + sage: B = L(None) + sage: C = L(None, 2) + sage: A.define(z^5 + B^2) + sage: B.define(z^5 + C^2) + sage: C.define(z^2 + C^2 + A^2) + sage: A[0:15] + [0, 0, 0, 0, 0, 1, 0, 0, 1, 2, 5, 4, 14, 10, 48] + sage: B[0:15] + [0, 0, 0, 0, 1, 1, 2, 0, 5, 0, 14, 0, 44, 0, 138] + sage: C[0:15] + [0, 0, 1, 0, 1, 0, 2, 0, 5, 0, 15, 0, 44, 2, 142] + + Counting binary trees:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: s = L(None, valuation=1) + sage: s.define(z + (s^2+s(z^2))/2) + sage: [s[i] for i in range(9)] + [0, 1, 1, 1, 2, 3, 6, 11, 23] + + The `q`-Catalan numbers:: + + sage: R. = ZZ[] + sage: L. = LazyLaurentSeriesRing(R) + sage: s = L(None) + sage: s.define(1+z*s*s(q*z)) + sage: s + 1 + z + (q + 1)*z^2 + (q^3 + q^2 + 2*q + 1)*z^3 + + (q^6 + q^5 + 2*q^4 + 3*q^3 + 3*q^2 + 3*q + 1)*z^4 + + (q^10 + q^9 + 2*q^8 + 3*q^7 + 5*q^6 + 5*q^5 + 7*q^4 + 7*q^3 + 6*q^2 + 4*q + 1)*z^5 + + (q^15 + q^14 + 2*q^13 + 3*q^12 + 5*q^11 + 7*q^10 + 9*q^9 + 11*q^8 + + 14*q^7 + 16*q^6 + 16*q^5 + 17*q^4 + 14*q^3 + 10*q^2 + 5*q + 1)*z^6 + ... + + We count unlabeled ordered trees by total number of nodes + and number of internal nodes:: + + sage: R. = QQ[] + sage: Q. = LazyLaurentSeriesRing(R) + sage: leaf = z + sage: internal_node = q * z + sage: L = Q(constant=1, degree=1) + sage: T = Q(None, 1) + sage: T.define(leaf + internal_node * L(T)) + sage: [T[i] for i in range(6)] + [0, 1, q, q^2 + q, q^3 + 3*q^2 + q, q^4 + 6*q^3 + 6*q^2 + q] + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: s = L(None) + sage: s.define(1 + z*s^3) + sage: s[:10] + [1, 1, 3, 12, 55, 273, 1428, 7752, 43263, 246675] + + sage: e = L(None) + sage: e.define(1 + z*e) + sage: e.define(1 + z*e) + Traceback (most recent call last): + ... + ValueError: series already defined + sage: z.define(1 + z^2) + Traceback (most recent call last): + ... + ValueError: series already defined """ - v = self.valuation() + if not isinstance(self._aux, LazyLaurentSeries_uninitialized) or self._aux._target is not None: + raise ValueError("series already defined") + self._aux._target = s._aux - if v == infinity: - raise ZeroDivisionError('cannot invert zero') - R = self.parent() +class LazyLaurentSeries_aux(): + """ + Abstract base class for all auxillary LazyLaurentSeries. + """ + def __init__(self, sparse, approximate_valuation): + self._is_sparse = sparse + self._approximate_valuation = approximate_valuation - op = LazyLaurentSeriesOperator_inv(self) - return R.element_class(R, coefficient=op, valuation=-v, constant=None) +class LazyLaurentSeries_inexact(LazyLaurentSeries_aux): + """ + LazyLaurentSeries aux class when it is not or we do not know if it is + eventually geometric. + """ + def __init__(self, is_sparse, approximate_valuation): + super().__init__(is_sparse, approximate_valuation) - def __pow__(self, n): + if self._is_sparse: + self._cache = dict() # cache of known coefficients + else: + self._cache = list() + self._offset = approximate_valuation + self._iter = self.iterate_coefficients() + + def __getstate__(self): + d = dict(self.__dict__) + if not self._is_sparse: + # We cannot pickle a generator object, so we remove it and + # the cache from the pickle information. + del d["_iter"] + del d["_cache"] + return d + + def __setstate__(self, d): + self.__dict__ = d + if not self._is_sparse: + self._iter = self.iterate_coefficients() + self._cache = [] + + def __getitem__(self, n): + if n < self._approximate_valuation: + return ZZ.zero() + + if self._is_sparse: + try: + c = self._cache[n] + except KeyError: + c = self.get_coefficient(n) + self._cache[n] = c + else: + i = n - self._offset + if i >= len(self._cache): + a = len(self._cache) + self._offset + # it is important to extend by generator: + # self._coefficient_function might recurse, and + # thereby extend the cache itself, too + self._cache.extend(next(self._iter) for _ in range(a, n+1)) + c = self._cache[i] + + return c + + def valuation(self): + if self._is_sparse: + n = self._approximate_valuation + cache = self._cache + while True: + if n in cache: + if cache[n]: + self._approximate_valuation = n + return n + n += 1 + else: + if self[n] != 0: + self._approximate_valuation = n + return n + n += 1 + else: + n = self._approximate_valuation + cache = self._cache + while True: + if n - self._offset < len(cache): + if cache[n - self._offset]: + self._approximate_valuation = n + return n + n += 1 + else: + if self[n] != 0: + self._approximate_valuation = n + return n + n += 1 + + +class LazyLaurentSeries_unary(LazyLaurentSeries_inexact): + """ + Abstract base class for unary operators. + + INPUT: + + - ``series`` -- series upon which the operator operates + + """ + def __init__(self, series, *args, **kwargs): """ - Return the `n`-th power of the series. + Initialize. - EXAMPLES:: + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: (1 - z)^-1 - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: (1 - z)^0 - 1 - sage: (1 - z)^3 - 1 - 3*z + 3*z^2 - z^3 - sage: (1 - z)^-3 - 1 + 3*z + 6*z^2 + 10*z^3 + 15*z^4 + 21*z^5 + 28*z^6 + ... + sage: f = -1/(1 - z) + sage: f + -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... + sage: loads(dumps(f)) == f + True """ - if n == 0: - return self.parent().one() - - return generic_power(self, n) + self._series = series + super().__init__(*args, **kwargs) - def _div_(self, other): + def __hash__(self): """ - Return ``self`` divided by ``other``. + Return the hash of ``self``. - INPUT: + TESTS:: - - ``other`` -- nonzero series + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 - z) + sage: {f: 1} + {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} + """ + return hash((type(self), self._series)) - EXAMPLES:: + def __eq__(self, other): + """ + Test equality. + + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: z/(1 - z) - z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... + sage: f = 1/(1 - z) + 1/(1 + z) + sage: g = 1/(1 - z) + 1/(1 + z) + sage: f == g + True + sage: f = ~(1 - z) + sage: g = ~(1 - z) + sage: f == g + True """ - R = self.parent() + return isinstance(other, type(self)) and self._series == other._series - if other.is_zero(): - raise ZeroDivisionError("division by zero series") - if self.is_zero(): - return R.zero() +class LazyLaurentSeries_binary(LazyLaurentSeries_inexact): - op = LazyLaurentSeriesOperator_div(self, other) + def __init__(self, left, right, *args, **kwargs): + """ + Initialize. - a = self.valuation() - other.valuation() + TESTS:: - return R.element_class(R, coefficient=op, valuation=a, constant=None) + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: loads(dumps(f)) == f + True + sage: f = 1/(1 - z) - 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._left = left + self._right = right + super().__init__(*args, **kwargs) - def apply_to_coefficients(self, function): + def __hash__(self): """ - Return the series with ``function`` applied to each coefficient of this series. + Return the hash of ``self``. - INPUT: + TESTS:: - - ``function`` -- Python function + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: {f: 1} + {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} + """ + return hash((type(self), self._left, self._right)) - Python function ``function`` returns a new coefficient for input coefficient. + def __eq__(self, other): + """ + Test equality. - EXAMPLES:: + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: s = z/(1 - 2*z) - sage: t = s.apply_to_coefficients(lambda c: c + 1) - sage: s - z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... - sage: t - 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... + sage: f = 1/(1 - z) + 1/(1 + z) + sage: g = 1/(1 - z) + 1/(1 + z) + sage: f == g + True """ - R = self.parent() + if not isinstance(other, type(self)): + return False + return self._left == other._left and self._right == other._right + + +class LazyLaurentSeries_binary_commutative(LazyLaurentSeries_binary): + + def __hash__(self): + return hash((type(self), frozenset([self._left, self._right]))) - op = LazyLaurentSeriesOperator_apply(self, function) + def __eq__(self, other): + if not isinstance(other, type(self)): + return False + if self._left == other._left and self._right == other._right: + return True + if self._left == other._right and self._right == other._left: + return True + return False - a = self._approximate_valuation - if self._constant: - c = (function(self._constant[0]), self._constant[1]) +class LazyLaurentSeries_eventually_geometric(LazyLaurentSeries_aux): + def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): + if constant is None: + constant = ZZ.zero() + if degree is None: + if not laurent_polynomial: + raise ValueError("you must specify the degree for the polynomial 0") + degree = laurent_polynomial.degree() + 1 else: - c = None + # Consistency check + assert not laurent_polynomial or laurent_polynomial.degree() < degree - return R.element_class(R, coefficient=op, valuation=a, constant=c) + self._constant = constant + self._degree = degree + self._laurent_polynomial = laurent_polynomial + if not laurent_polynomial: + valuation = degree + else: + valuation = laurent_polynomial.valuation() + super().__init__(is_sparse, valuation) - def change_ring(self, ring): - """ - Return this series with coefficients converted to elements of ``ring``. + def __getitem__(self, n): + if n >= self._degree: + return self._constant + return self._laurent_polynomial[n] - INPUT: + def valuation(self): + return self._approximate_valuation - - ``ring`` -- a ring + def __hash__(self): + return hash((self._laurent_polynomial, self._degree, self._constant)) - EXAMPLES:: + def __eq__(self, other): + return (isinstance(other, type(self)) + and self._degree == other._degree + and self._laurent_polynomial == other._laurent_polynomial + and self._constant == other._constant) - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: s = 2 + z - sage: t = s.change_ring(QQ) - sage: t^-1 - 1/2 - 1/4*z + 1/8*z^2 - 1/16*z^3 + 1/32*z^4 - 1/64*z^5 + 1/128*z^6 + ... - """ - R = self.parent() +class LazyLaurentSeries_zero(LazyLaurentSeries_aux): + def __init__(self, sparse): + return super().__init__(sparse, 0) - op = LazyLaurentSeriesOperator_change_ring(self, ring) + def __getitem__(self, n): + return ZZ.zero() - a = self._approximate_valuation + def valuation(self): + return infinity - if self._constant: - c = (ring(self._constant[0]), self._constant[1]) - else: - c = None + def __hash__(self): + return 0 + +class LazyLaurentSeries_coefficient_function(LazyLaurentSeries_inexact): + def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation): + self._coefficient_function = coefficient_function + self._ring = ring + super().__init__(is_sparse, approximate_valuation) + + def get_coefficient(self, n): + return self._ring(self._coefficient_function(n)) + + def iterate_coefficients(self): + n = self._offset + ring = self._ring + while True: + yield ring(self._coefficient_function(n)) + n += 1 - from .lazy_laurent_series_ring import LazyLaurentSeriesRing - Q = LazyLaurentSeriesRing(ring, names=R.variable_name()) - return Q.element_class(Q, coefficient=op, valuation=a, constant=c) +class LazyLaurentSeries_uninitialized(LazyLaurentSeries_inexact): + def __init__(self, is_sparse, approximate_valuation): + self._target = None + super().__init__(is_sparse, approximate_valuation) - def truncate(self, d): - """ - Return this series with its terms of degree >= ``d`` truncated. + def get_coefficient(self, n): + return self._target[n] - INPUT: + def iterate_coefficients(self): + n = self._approximate_valuation + while True: + yield self._target[n] + n += 1 - - ``d`` -- integer +##################################################################### +## Binary operations + +class LLS_add(LazyLaurentSeries_binary): + """ + Operator for addition. + """ + def __init__(self, left, right): + if left._is_sparse != right._is_sparse: + raise NotImplementedError + + a = min(left._approximate_valuation, right._approximate_valuation) + super().__init__(left, right, left._is_sparse, a) + + def get_coefficient(self, n): + return self._left[n] + self._right[n] + + def iterate_coefficients(self): + n = self._offset + while True: + yield self._left[n] + self._right[n] + n += 1 + +class LLS_sub(LazyLaurentSeries_binary): + """ + Operator for subtraction. + """ + def __init__(self, left, right): + if left._is_sparse != right._is_sparse: + raise NotImplementedError + + a = min(left._approximate_valuation, right._approximate_valuation) + super().__init__(left, right, left._is_sparse, a) + + def get_coefficient(self, n): + """ + Return the `n`-th coefficient of the series ``s``. EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: alpha = 1/(1-z) - sage: alpha - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: beta = alpha.truncate(5) - sage: beta - 1 + z + z^2 + z^3 + z^4 - sage: alpha - beta - z^5 + z^6 + z^7 + z^8 + z^9 + z^10 + z^11 + ... + sage: f = (1 + z)*(1 - z) + sage: f.coefficient(2) + -1 """ - R = self.parent() + return self._left[n] - self._right[n] + + def iterate_coefficients(self): + n = self._offset + while True: + yield self._left[n] - self._right[n] + n += 1 + +class LLS_mul(LazyLaurentSeries_binary): + """ + Operator for multiplication. + + We are assuming commutativity of the coefficient ring here. + """ + def __init__(self, left, right): + if left._is_sparse != right._is_sparse: + raise NotImplementedError + + a = left._approximate_valuation + right._approximate_valuation + super().__init__(left, right, left._is_sparse, a) + + def get_coefficient(self, n): + c = ZZ.zero() + for k in range(self._left._approximate_valuation, + n - self._right._approximate_valuation + 1): + l = self._left[k] + if l: + c += l * self._right[n-k] + return c + + def iterate_coefficients(self): + n = self._offset + while True: + c = ZZ.zero() + for k in range(self._left._approximate_valuation, + n - self._right._approximate_valuation + 1): + l = self._left[k] + if l: + c += l * self._right[n-k] + yield c + n += 1 - op = LazyLaurentSeriesOperator_truncate(self, d) - a = self._approximate_valuation - c = (self.base_ring().zero(), d) +class LLS_div(LazyLaurentSeries_binary): + """ + Return ``left`` divided by ``right``. + """ + def __init__(self, left, right): + lv = left.valuation() + rv = right.valuation() + self._lv = lv + self._rv = rv + self._ainv = ~right[rv] + super().__init__(left, right, left._is_sparse, lv - rv) + + def get_coefficient(self, n): + lv = self._lv + rv = self._rv + if n == lv - rv: + return self._left[lv] / self._right[rv] + c = self._left[n + rv] + for k in range(lv - rv, n): + c -= self[k] * self._right[n + rv - k] + return c * self._ainv + + def iterate_coefficients(self): + n = self._offset + lv = self._lv + rv = self._rv + while True: + if n == lv - rv: + yield self._left[lv] / self._right[rv] + n += 1 + continue + c = self._left[n + rv] + for k in range(lv - rv, n): + c -= self[k] * self._right[n + rv - k] + yield c * self._ainv + n += 1 - return R.element_class(R, coefficient=op, valuation=a, constant=c) + +class LLS_composition(LazyLaurentSeries_binary): + r""" + Return ``f`` composed by ``g``. + + This is the composition `(f \circ g)(z) = f(g(z))`. + + INPUT: + + - ``f`` -- a :class:`LazyLaurentSeries_aux` + - ``g`` -- a :class:`LazyLaurentSeries_aux` with positive valuation + """ + def __init__(self, f, g): + assert g._approximate_valuation > 0 + self._fv = f._approximate_valuation + self._gv = g._approximate_valuation + if self._fv < 0: + ginv = LLS_inv(g) + # the constant part makes no contribution to the negative + # we need this for the case so self._neg_powers[0][n] => 0 + self._neg_powers = [LazyLaurentSeries_zero(f._is_sparse), ginv] + for i in range(1, -self._fv): + self._neg_powers.append(LLS_mul(self._neg_powers[-1], ginv)) + # Placeholder None to make this 1-based + self._pos_powers = [None, g] + val = self._fv * self._gv + super().__init__(f, g, f._is_sparse, val) + + def get_coefficient(self, n): + if n < 0: + return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) + # n > 0 + while len(self._pos_powers) <= n // self._gv: + self._pos_powers.append(LLS_mul(self._pos_powers[-1], self._right)) + ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, 0)) + if n == 0: + ret += self._left[0] + return ret + sum(self._left[i] * self._pos_powers[i][n] for i in range(1, n // self._gv+1)) + + def iterate_coefficients(self): + n = self._approximate_valuation + while True: + yield self.get_coefficient(n) + n += 1 + +##################################################################### +## Unary operations + +class LLS_scalar(LazyLaurentSeries_unary): + """ + Operator for multiplying with a scalar. + """ + def __init__(self, series, scalar): + self._scalar = scalar + + super().__init__(series, series._is_sparse, series._approximate_valuation) + + def get_coefficient(self, n): + return self._series[n] * self._scalar + + def iterate_coefficients(self): + n = self._offset + while True: + yield self._series[n] * self._scalar + n += 1 + +class LLS_neg(LazyLaurentSeries_unary): + """ + Operator for negative of the series. + """ + def __init__(self, series): + super().__init__(series, series._is_sparse, series._approximate_valuation) + + def get_coefficient(self, n): + return -self._series[n] + + def iterate_coefficients(self): + n = self._offset + while True: + yield -self._series[n] + n += 1 + +class LLS_inv(LazyLaurentSeries_unary): + """ + Operator for multiplicative inverse of the series. + """ + def __init__(self, series): + v = series.valuation() + super().__init__(series, series._is_sparse, -v) + + self._ainv = ~series[v] + self._zero = ZZ.zero() + + def get_coefficient(self, n): + v = self._approximate_valuation + if n == v: + return self._ainv + c = self._zero + for k in range(v, n): + c += self[k] * self._series[n - v - k] + return -c * self._ainv + + def iterate_coefficients(self): + n = self._offset + while True: + v = self._approximate_valuation + if n == v: + yield self._ainv + n += 1 + continue + c = self._zero + for k in range(v, n): + c += self[k] * self._series[n - v - k] + yield -c * self._ainv + n += 1 + +class LLS_apply_coeff(LazyLaurentSeries_unary): + """ + Return the series with ``function`` applied to each coefficient of this series. + """ + def __init__(self, series, function, ring): + self._function = function + self._ring = ring + super().__init__(series, series._is_sparse, series._approximate_valuation) + + def get_coefficient(self, n): + c = self._ring(self._function(self._series[n])) + return c + + def iterate_coefficients(self): + n = self._offset + while True: + c = self._ring(self._function(self._series[n])) + yield c + n += 1 diff --git a/src/sage/rings/lazy_laurent_series_new.py b/src/sage/rings/lazy_laurent_series_new.py deleted file mode 100644 index e25896246c6..00000000000 --- a/src/sage/rings/lazy_laurent_series_new.py +++ /dev/null @@ -1,1896 +0,0 @@ -r""" -Lazy Laurent Series - -A lazy Laurent series is a Laurent series whose coefficients are computed as -demanded or needed. Unlike the usual Laurent series in Sage, lazy Laurent -series do not have precisions because a lazy Laurent series knows (can be -computed, lazily) all its coefficients. - -EXAMPLES: - -Generating functions are Laurent series over the integer ring:: - - sage: L. = LLSRing(ZZ) - -This defines the generating function of Fibonacci sequence:: - - sage: f = 1 / (1 - z - z^2) - sage: f - 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... - -The 100th element of Fibonacci sequence can be obtained from the generating -function:: - - sage: f.coefficient(100) - 573147844013817084101 - -Coefficients are computed and cached only when necessary:: - - sage: f._aux._cache[100] - 573147844013817084101 - sage: f._aux._cache[101] - Traceback (most recent call last): - ... - IndexError: list index out of range - -You can do arithmetic with lazy power series:: - - sage: f - 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... - sage: f^-1 - 1 - z - z^2 + ... - sage: f + f^-1 - 2 + z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... - sage: g = (f + f^-1)*(f - f^-1); g - 4*z + 6*z^2 + 8*z^3 + 19*z^4 + 38*z^5 + 71*z^6 + ... - -You may need to change the base ring:: - - sage: h = g.change_ring(QQ) - sage: h.parent() - Lazy Laurent Series Ring in z over Rational Field - sage: h - 4*z + 6*z^2 + 8*z^3 + 19*z^4 + 38*z^5 + 71*z^6 + ... - sage: h^-1 - 1/4*z^-1 - 3/8 + 1/16*z - 17/32*z^2 + 5/64*z^3 - 29/128*z^4 + 165/256*z^5 + ... - sage: _.valuation() - -1 - -AUTHORS: - -- Kwankyu Lee (2019-02-24): initial version - -""" - -# **************************************************************************** -# Copyright (C) 2019 Kwankyu Lee -# -# 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 -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** - - -from .infinity import infinity -from sage.structure.element import ModuleElement, parent -from .integer_ring import ZZ -from sage.structure.richcmp import op_EQ, op_NE -from sage.arith.power import generic_power -from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing -from sage.rings.integer import Integer - -class LLS(ModuleElement): - r""" - A Laurent series where the coefficients are computed lazily. - - INPUT: - - - ``coefficient_function`` -- Python function that computes coefficients - - - ``issparse`` -- Boolean that determines whether the implementation is sparse or dense - - - ``approximate_valuation`` -- integer; approximate valuation of the series - - - ``constant`` -- either ``None`` or pair of an element of the base ring and an integer - - Let the coefficient of index `i` mean the coefficient of the term of the - series with exponent `i`. - - Python function ``coefficient`` returns the value of the coefficient of - index `i` from input. - - Let ``approximate_valuation`` be `n`. All coefficients of index below `n` are zero. If - ``constant`` is ``None``, then the ``coefficient`` function is responsible - to compute the values of all coefficients of index `\ge n`. If ``constant`` - is a pair `(c,m)`, then the ``coefficient`` function is responsible to - compute the values of all coefficients of index `\ge n` and `< m` and all - the coefficients of index `\ge m` is the constant `c`. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: L(lambda i: i, valuation=-3, constant=(-1,3)) - -3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ... - sage: L(lambda i: i, valuation=-3, constant=-1, degree=3) - -3*z^-3 - 2*z^-2 - z^-1 + z + 2*z^2 - z^3 - z^4 - z^5 + ... - - :: - - sage: f = 1 / (1 - z - z^2); f - 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... - sage: f.coefficient(100) - 573147844013817084101 - - Lazy Laurent series is picklable:: - - sage: g = loads(dumps(f)) - sage: g - 1 + z + 2*z^2 + 3*z^3 + 5*z^4 + 8*z^5 + 13*z^6 + ... - sage: g == f - True - """ - def __init__(self, parent, aux): - """ - Initialize the series. - - TESTS:: - - sage: L = LLSRing(GF(2), 'z') - sage: z = L.gen() - sage: TestSuite(z).run() - """ - ModuleElement.__init__(self, parent) - self._aux = aux - - def __getitem__(self, n): - """ - Return the coefficient of the term with exponent ``n`` of the series. - - INPUT: - - - ``n`` -- integer - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = z/(1 - 2*z^3) - sage: [f[n] for n in range(20)] - [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] - sage: f[0:20] - [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] - - sage: M = L(lambda n: n) - sage: [M[n] for n in range(20)] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n) - sage: [M[n] for n in range(20)] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] - """ - R = self.base_ring() - if isinstance(n, slice): - if n.stop is None: - raise NotImplementedError("cannot list an infinite set") - start = n.start if n.start is not None else self._aux.valuation() - step = n.step if n.step is not None else 1 - return [R(self._aux[k]) for k in range(start, n.stop, step)] - return R(self._aux[n]) - - def __call__(self, g): - r""" - Return the composition of the series with ``g``. - - INPUT: - - - ``g`` -- other series - - TESTS:: - - sage: L. = LLSRing(QQ) - sage: f = z^2 + 1 + z - sage: f(0) - 1 - sage: f(L(0)) - 1 - sage: f(f) - 3 + 3*z + 4*z^2 + 2*z^3 + z^4 - sage: g = z^-3/(1-2*z); g - z^-3 + 2*z^-2 + 4*z^-1 + 8 + 16*z + 32*z^2 + 64*z^3 + ... - sage: f(g) - z^-6 + 4*z^-5 + 12*z^-4 + 33*z^-3 + 82*z^-2 + 196*z^-1 + 457 + ... - sage: g^2 + 1 + g - z^-6 + 4*z^-5 + 12*z^-4 + 33*z^-3 + 82*z^-2 + 196*z^-1 + 457 + ... - - sage: f = z^-2 + z + 4*z^3 - sage: f(f) - 4*z^-6 + 12*z^-3 + z^-2 + 48*z^-1 + 12 + ... - sage: f^-2 + f + 4*f^3 - 4*z^-6 + 12*z^-3 + z^-2 + 48*z^-1 + 12 + ... - sage: f(g) - 4*z^-9 + 24*z^-8 + 96*z^-7 + 320*z^-6 + 960*z^-5 + 2688*z^-4 + 7169*z^-3 + ... - sage: g^-2 + g + 4*g^3 - 4*z^-9 + 24*z^-8 + 96*z^-7 + 320*z^-6 + 960*z^-5 + 2688*z^-4 + 7169*z^-3 + ... - - sage: f = z^-3 + z^-2 + 1 / (1 + z^2); f - z^-3 + z^-2 + 1 - z^2 + ... - sage: g = z^3 / (1 + z - z^3); g - z^3 - z^4 + z^5 - z^7 + 2*z^8 - 2*z^9 + ... - sage: f(g) - z^-9 + 3*z^-8 + 3*z^-7 - z^-6 - 4*z^-5 - 2*z^-4 + z^-3 + ... - sage: g^-3 + g^-2 + 1 / (1 + g^2) - z^-9 + 3*z^-8 + 3*z^-7 - z^-6 - 4*z^-5 - 2*z^-4 + z^-3 + ... - - sage: f = L(lambda n: n); f - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: f(z^2) - z^2 + 2*z^4 + 3*z^6 + ... - - sage: f = L(lambda n: n, -2); f - -2*z^-2 - z^-1 + z + 2*z^2 + 3*z^3 + 4*z^4 + ... - sage: f3 = f(z^3); f3 - -2*z^-6 - z^-3 + ... - sage: [f3[i] for i in range(-6,13)] - [-2, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 3, 0, 0, 4] - - We compose a Laurent polynomial with a generic element:: - - sage: R. = QQ[] - sage: f = z^2 + 1 + z^-1 - sage: g = x^2 + x + 3 - sage: f(g) - (x^6 + 3*x^5 + 12*x^4 + 19*x^3 + 37*x^2 + 28*x + 31)/(x^2 + x + 3) - sage: f(g) == g^2 + 1 + g^-1 - True - - We compose with another lazy Laurent series:: - - sage: LS. = LLSRing(QQ) - sage: f = z^2 + 1 + z^-1 - sage: fy = f(y); fy - y^-1 + 1 + y^2 - sage: fy.parent() is LS - True - sage: g = y - y - sage: f(g) - Traceback (most recent call last): - ... - ZeroDivisionError: the valuation of the series must be nonnegative - - sage: g = 1 - y - sage: f(g) - 3 - y + 2*y^2 + y^3 + y^4 + y^5 + y^6 + ... - sage: g^2 + 1 + g^-1 - 3 - y + 2*y^2 + y^3 + y^4 + y^5 + y^6 + ... - - sage: f = L(lambda n: n, 0); f - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: f(0) - 0 - sage: f(y) - y + 2*y^2 + 3*y^3 + 4*y^4 + 5*y^5 + 6*y^6 + ... - sage: fp = f(y - y) - sage: fp == 0 - True - sage: fp.parent() is LS - True - - sage: f = z^2 + 3 + z - sage: f(y - y) - 3 - - With both of them sparse:: - - sage: L. = LLSRing(QQ, sparse=True) - sage: LS. = LLSRing(QQ, sparse=True) - sage: f = L(lambda n: 1); f - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: f(y^2) - 1 + y^2 + y^4 + y^6 + ... - - sage: fp = f - 1 + z^-2; fp - z^-2 + z + z^2 + z^3 + z^4 + ... - sage: fpy = fp(y^2); fpy - y^-4 + y^2 + ... - sage: [fpy[i] for i in range(-4,11)] - [1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1] - - sage: g = LS(valuation=2, constant=1); g - y^2 + y^3 + y^4 + ... - sage: fg = f(g); fg - 1 + y^2 + y^3 + 2*y^4 + 3*y^5 + 5*y^6 + ... - sage: 1 + g + g^2 + g^3 + g^4 + g^5 + g^6 - 1 + y^2 + y^3 + 2*y^4 + 3*y^5 + 5*y^6 + ... - - sage: h = LS(lambda n: 1 if n % 2 else 0, 2); h - y^3 + y^5 + y^7 + ... - sage: fgh = fg(h); fgh - 1 + y^6 + ... - sage: [fgh[i] for i in range(0, 15)] - [1, 0, 0, 0, 0, 0, 1, 0, 2, 1, 3, 3, 6, 6, 13] - sage: t = 1 + h^2 + h^3 + 2*h^4 + 3*h^5 + 5*h^6 - sage: [t[i] for i in range(0, 15)] - [1, 0, 0, 0, 0, 0, 1, 0, 2, 1, 3, 3, 6, 6, 13] - - We look at mixing the sparse and the dense:: - - sage: L. = LLSRing(QQ) - sage: f = L(lambda n: 1); f - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: g = LS(lambda n: 1, 1); g - y + y^2 + y^3 + y^4 + y^5 + y^6 + y^7 + ... - sage: f(g) - 1 + y + 2*y^2 + 4*y^3 + 8*y^4 + 16*y^5 + 32*y^6 + ... - - sage: f = z^-2 + 1 + z - sage: g = 1/(y*(1-y)); g - y^-1 + 1 + y + y^2 + y^3 + y^4 + y^5 + ... - sage: f(g) - y^-1 + 2 + y + 2*y^2 - y^3 + 2*y^4 + y^5 + ... - sage: g^-2 + 1 + g - y^-1 + 2 + y + 2*y^2 - y^3 + 2*y^4 + y^5 + ... - - sage: f = z^-3 + z^-2 + 1 - sage: g = 1/(y^2*(1-y)); g - y^-2 + y^-1 + 1 + y + y^2 + y^3 + y^4 + ... - sage: f(g) - 1 + y^4 - 2*y^5 + 2*y^6 + ... - sage: g^-3 + g^-2 + 1 - 1 + y^4 - 2*y^5 + 2*y^6 + ... - sage: z(y) - y - """ - # f = self and compute f(g) - P = g.parent() - - # g = 0 case - if (not isinstance(g, LLS) and not g) or (isinstance(g, LLS) and isinstance(g._aux, LLS_zero)): - if self._aux._approximate_valuation >= 0: - return P(self[0]) - # Perhaps we just don't yet know if the valuation is non-negative - if any(self._aux[i] for i in range(self._aux._approximate_valuation, 0)): - raise ZeroDivisionError("the valuation of the series must be nonnegative") - self._aux._approximate_valuation = 0 - return P(self[0]) - - # f has finite length - if isinstance(self._aux, LLS_zero): # constant 0 - return self - if isinstance(self._aux, LLS_eventually_geometric) and not self._aux._constant: - # constant polynomial - if self._aux._laurent_polynomial.is_constant(): - return self - if not isinstance(g, LLS): - return self._aux._laurent_polynomial(g) - # g also has finite length, compose the polynomials - if isinstance(g._aux, LLS_eventually_geometric) and not g._aux._constant: - R = P._laurent_poly_ring - try: - ret = self._aux._laurent_polynomial(g._aux._laurent_polynomial) - if ret.parent() is R: - return P.element_class(P, LLS_eventually_geometric(ret, self._aux._is_sparse, 0)) - except TypeError: # the result is not a Laurent polynomial - pass - - # Return the sum since g is not known to be finite or we do not get a Laurent polynomial - # TODO: Optimize when f has positive valuation - poly = self._aux._laurent_polynomial - ret = P.zero() - gp = P.one() - # We build this iteratively so each power can benefit from the caching - # Equivalent to P.sum(poly[i] * g**i for i in range(poly.valuation(), poly.degree()+1)) - # We could just do "return poly(g)" if we don't care about speed - deg = poly.degree() - for i in range(deg): - ret += poly[i] * gp - gp *= g - ret += poly[deg] * gp - gi = ~g - gp = P.one() - for i in range(-1, poly.valuation()-1, -1): - gp *= gi - ret += poly[i] * gp - return ret - - # g != 0 and val(g) > 0 - if not isinstance(g, LLS): - try: - g = self.parent()(g) - except (TypeError, ValueError): - raise NotImplementedError("can only compose with a lazy Laurent series") - # Perhaps we just don't yet know if the valuation is positive - if g._aux._approximate_valuation <= 0: - if any(g._aux[i] for i in range(self._aux._approximate_valuation)): - raise ValueError("can only compose with a positive valuation series") - g._aux._approximate_valuation = 1 - - return P.element_class(P, LLS_composition(self._aux, g._aux)) - - def _mul_(self, other): - """ - Return the product of this series with ``other``. - - INPUT: - - - ``other`` -- other series - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: (1 - z)*(1 - z) - 1 - 2*z + z^2 - sage: (1 - z)*(1 - z)*(1 - z) - 1 - 3*z + 3*z^2 - z^3 - sage: M = L(lambda n: n) - sage: M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M * (1 - M) - sage: N - z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 + ... - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: M * N - z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + ... - - sage: L.one() * M is M - True - sage: M * L.one() is M - True - """ - P = self.parent() - left = self._aux - right = other._aux - if isinstance(left, LLS_zero) or isinstance(right, LLS_zero): - return P.zero() - - R = P._laurent_poly_ring - if isinstance(left, LLS_eventually_geometric): - if not left._constant: - if left._laurent_polynomial == R.one(): # self == 1 - return other - if isinstance(right, LLS_eventually_geometric): - if not right._constant: - p = left._laurent_polynomial * right._laurent_polynomial - c = left._constant - return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c)) - elif isinstance(right, LLS_eventually_geometric) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 - return self - return P.element_class(P, LLS_mul(self._aux, other._aux)) - - def _add_(self, other): - """ - Return the sum of this series with ``other``. - - INPUT: - - - ``other`` -- other series - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: (1 - z)*(1 - z) - 1 - 2*z + z^2 - sage: (1 - z)*(1 - z)*(1 - z) - 1 - 3*z + 3*z^2 - z^3 - sage: z + z - 2*z - sage: z^2 + 3*z^2 - 4*z^2 - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M + N; P - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - - sage: A = L(1, constant=2, degree=3) - sage: B = L(2, constant=-2, degree=5) - sage: A + B - 3 + 2*z^3 + 2*z^4 - - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M + N; P - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - """ - P = self.parent() - left = self._aux - right = other._aux - if (isinstance(left, LLS_eventually_geometric) - and isinstance(right, LLS_eventually_geometric)): - R = P._laurent_poly_ring - c = left._constant + right._constant - pl = left._laurent_polynomial - pr = right._laurent_polynomial - d = max(left._degree, right._degree) - pl += R([left._constant]*(d-left._degree)).shift(left._degree) - pr += R([right._constant]*(d-right._degree)).shift(right._degree) - p = pl + pr - if not p and not c: - return P.zero() - return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) - return P.element_class(P, LLS_add(self._aux, other._aux)) - - def _sub_(self, other): - """ - Return the series of this series minus ``other`` series. - - INPUT: - - - ``other`` -- other series - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: z - z - 0 - sage: 3*z - 2*z - z - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M - N; P - -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... - - sage: A = L(1, constant=2, degree=3) - sage: B = L(2, constant=3, degree=5) - sage: A - B - -1 + 2*z^3 + 2*z^4 - z^5 - z^6 - z^7 + ... - - sage: A = L(1, constant=2, degree=3) - sage: B = L([1,0,0,2,2], constant=2) - sage: X = A - B; X - 0 - sage: type(X._aux) - - - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M - N; P - -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... - """ - P = self.parent() - left = self._aux - right = other._aux - if (isinstance(left, LLS_eventually_geometric) and isinstance(right, LLS_eventually_geometric)): - R = P._laurent_poly_ring - c = left._constant - right._constant - pl = left._laurent_polynomial - pr = right._laurent_polynomial - d = max(left._degree, right._degree) - pl += R([left._constant]*(d-left._degree)).shift(left._degree) - pr += R([right._constant]*(d-right._degree)).shift(right._degree) - p = pl - pr - if not p and not c: - return P.zero() - return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) - if left == right: - return P.zero() - return P.element_class(P, LLS_sub(self._aux, other._aux)) - - def _div_(self, other): - """ - Return ``self`` divided by ``other``. - - INPUT: - - - ``other`` -- nonzero series - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: z/(1 - z) - z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M / N; P - z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M / N; P - z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - """ - if isinstance(other._aux, LLS_zero): - raise ZeroDivisionError("cannot divide by 0") - - P = self.parent() - left = self._aux - if isinstance(left, LLS_zero): - return P.zero() - right = other._aux - if (isinstance(left, LLS_eventually_geometric) - and isinstance(right, LLS_eventually_geometric)): - if not left._constant and not right._constant: - ret = left._laurent_polynomial / right._laurent_polynomial - try: - ret = P._laurent_poly_ring(ret) - return P.element_class(P, LLS_eventually_geometric(ret, P._sparse, left._constant)) - except (TypeError, ValueError): - # We cannot divide the polynomials, so the result must be a series - pass - - return P.element_class(P, LLS_mul(left, LLS_inv(right))) - - def _rmul_(self, scalar): - """ - Return the scalar multiplication of this series by ``scalar``. - - INPUT: - - - ``scalar`` -- an element of the base ring - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: 2*z - 2*z - sage: -1*z - -z - sage: 0*z - 0 - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M * 3 - 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... - - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M * 3 - 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: N * 4 - 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... - - sage: 1 * M is M - True - sage: M * 1 is M - True - """ - P = self.parent() - if not scalar: - return P.zero() - if scalar == 1: - return self - - if isinstance(self._aux, LLS_eventually_geometric): - c = scalar * self._aux._constant - p = scalar * self._aux._laurent_polynomial - return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, self._aux._degree)) - - return P.element_class(P, LLS_scalar(self._aux, scalar)) - - def _neg_(self): - """ - Return the negative of this series. - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: z = L.gen() - sage: -(1 - z) - -1 + z - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = -M; P - -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... - - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = -M; P - -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... - sage: -(z^2 + 3*z - 4*z^3) - -3*z - z^2 + 4*z^3 - """ - P = self.parent() - if isinstance(self._aux, LLS_eventually_geometric): - p = -self._aux._laurent_polynomial - c = -self._aux._constant - d = self._aux._degree - return P.element_class(P, LLS_eventually_geometric(p, P._sparse, c, d)) - # -(-f) = f - if isinstance(self._aux, LLS_neg): - return P.element_class(P, self._aux._series) - return P.element_class(P, LLS_neg(self._aux)) - - def __invert__(self): - """ - Return the multiplicative inverse of the element. - - TESTS:: - - sage: L. = LLSRing(ZZ, sparse=False) - sage: ~(1 - z) - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = ~M; P - z^-1 - 2 + z + ... - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = ~M; P - z^-1 - 2 + z + ... - - sage: ~(~(1 - z)) - 1 - z - """ - P = self.parent() - if isinstance(self._aux, LLS_eventually_geometric) and self._aux._laurent_polynomial == P.gen(): - ret = 1 / self._aux._laurent_polynomial - return P.element_class(P, LLS_eventually_geometric(ret, P._sparse, self._aux._constant)) - # (f^-1)^-1 = f - if isinstance(self._aux, LLS_inv): - return P.element_class(P, self._aux._series) - return P.element_class(P, LLS_inv(self._aux)) - - def coefficient(self, n): - """ - Return the coefficient of the term with exponent ``n`` of the series. - - INPUT: - - - ``n`` -- integer - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: F = L(None) - sage: F.define(1 + z*F^2) - sage: F.coefficient(10) - 16796 - sage: F - 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - - TESTS:: - - sage: L. = LLSRing(ZZ, sparse=False) - sage: e = L(None) - sage: e.define(1 + z*e^2) - sage: e - 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - sage: e._aux._cache - [1, 1, 2, 5, 14, 42, 132] - sage: e.coefficient(10) - 16796 - sage: e._aux._cache - [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796] - sage: M = L(lambda n: n^2); M - z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... - sage: M._aux._cache - [0, 1, 4, 9, 16, 25, 36] - sage: M.coefficient(9) - 81 - sage: M._aux._cache - [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] - - sage: L = LLSRing(ZZ, 'z', sparse=True) - sage: M = L(lambda n: n^2); M - z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... - sage: M._aux._cache - {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36} - sage: M.coefficient(10) - 100 - sage: M._aux._cache - {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 10: 100} - """ - return self.__getitem__(n) - - def map_coefficients(self, func, ring=None): - """ - Return the series with ``func`` applied to each coefficient of this series. - - INPUT: - - - ``func`` -- Python function that takes in a coefficient and returns - a new coefficient - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: s = z/(1 - 2*z) - sage: t = s.map_coefficients(lambda c: c + 1) - sage: s - z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... - sage: t - 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.map_coefficients(lambda c: c + 1); N - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.map_coefficients(lambda c: c + 1); N - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - """ - P = self.parent() - R = P.base_ring() - if isinstance(self._aux, LLS_eventually_geometric): - p = p.map_coefficients(func) - c = func(c) - if not p and not c: - return P.zero() - return P.element_class(P, LLS_eventually_geometric(p, self._aux._is_sparse, c, d)) - return P.element_class(P, LLS_apply_coeff(self._aux, func, R)) - - def change_ring(self, ring): - """ - Return this series with coefficients converted to elements of ``ring``. - - INPUT: - - - ``ring`` -- a ring - - TESTS:: - - sage: L. = LLSRing(ZZ, sparse=False) - sage: s = 2 + z - sage: t = s.change_ring(QQ) - sage: t^-1 - 1/2 - 1/4*z + 1/8*z^2 - 1/16*z^3 + 1/32*z^4 - 1/64*z^5 + 1/128*z^6 + ... - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.change_ring(QQ) - sage: N.parent() - Lazy Laurent Series Ring in z over Rational Field - sage: M.parent() - Lazy Laurent Series Ring in z over Integer Ring - - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M.parent() - Lazy Laurent Series Ring in z over Integer Ring - sage: N = M.change_ring(QQ) - sage: N.parent() - Lazy Laurent Series Ring in z over Rational Field - sage: M ^-1 - z^-1 - 2 + z + ... - """ - from .lazy_laurent_series_ring_new import LLSRing - Q = LLSRing(ring, names=self.parent().variable_names()) - return Q.element_class(Q, self._aux) - - def truncate(self, d): - """ - Return this series with its terms of degree >= ``d`` truncated. - - INPUT: - - - ``d`` -- integer - - TESTS:: - - sage: L. = LLSRing(ZZ, sparse=False) - sage: alpha = 1/(1-z) - sage: alpha - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: beta = alpha.truncate(5) - sage: beta - 1 + z + z^2 + z^3 + z^4 - sage: alpha - beta - z^5 + z^6 + ... - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M.truncate(4) - z + 2*z^2 + 3*z^3 - - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M.truncate(4) - z + 2*z^2 + 3*z^3 - """ - P = self.parent() - R = P._laurent_poly_ring - z = R.gen() - p = R.sum(self[i] * z**i for i in range(self._aux._approximate_valuation, d)) - return P.element_class(P, LLS_eventually_geometric(p, P._sparse, ZZ.zero(), d)) - - def __pow__(self, n): - """ - Return the ``n``-th power of the series. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: (1 - z)^-1 - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: (1 - z)^0 - 1 - sage: (1 - z)^3 - 1 - 3*z + 3*z^2 - z^3 - sage: (1 - z)^-3 - 1 + 3*z + 6*z^2 + 10*z^3 + 15*z^4 + 21*z^5 + 28*z^6 + ... - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M ^ 2 - z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... - - sage: L. = LLSRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M ^ 2 - z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... - """ - if n == 0: - return self.parent().one() - - return generic_power(self, n) - - def approximate_series(self, prec, name=None): - """ - Return the Laurent series with absolute precision ``prec`` approximated - from this series. - - INPUT: - - - ``prec`` -- an integer - - - ``name`` -- name of the variable; if it is ``None``, the name of the variable - of the series is used - - OUTPUT: a Laurent series with absolute precision ``prec`` - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: z = L.gen() - sage: f = (z - 2*z^3)^5/(1 - 2*z) - sage: f - z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + 32*z^10 - 16*z^11 + ... - sage: g = f.approximate_series(10) - sage: g - z^5 + 2*z^6 - 6*z^7 - 12*z^8 + 16*z^9 + O(z^10) - sage: g.parent() - Power Series Ring in z over Integer Ring - sage: h = (f^-1).approximate_series(3) - sage: h - z^-5 - 2*z^-4 + 10*z^-3 - 20*z^-2 + 60*z^-1 - 120 + 280*z - 560*z^2 + O(z^3) - sage: h.parent() - Laurent Series Ring in z over Integer Ring - """ - S = self.parent() - - if name is None: - name = S.variable_name() - - if self.valuation() < 0: - from sage.rings.all import LaurentSeriesRing - R = LaurentSeriesRing(S.base_ring(), name=name) - n = self.valuation() - return R([self[i] for i in range(n, prec)], n).add_bigoh(prec) - else: - from sage.rings.all import PowerSeriesRing - R = PowerSeriesRing(S.base_ring(), name=name) - return R([self[i] for i in range(prec)]).add_bigoh(prec) - - def prec(self): - """ - Return the precision of the series, which is infinity. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) - sage: f.prec() - +Infinity - """ - return infinity - - def polynomial(self, degree=None, name=None): - """ - Return the polynomial or Laurent polynomial if the series is actually so. - - INPUT: - - - ``degree`` -- ``None`` or an integer - - - ``name`` -- name of the variable; if it is ``None``, the name of the variable - of the series is used - - OUTPUT: a Laurent polynomial if the valuation of the series is negative or - a polynomial otherwise. - - If ``degree`` is not ``None``, the terms of the series of degree - greater than ``degree`` are truncated first. If ``degree`` is ``None`` - and the series is not a polynomial or a Laurent polynomial, a - ``ValueError`` is raised. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: f = L([1,0,0,2,0,0,0,3], 5); f - z^5 + 2*z^8 + 3*z^12 - sage: f.polynomial() - 3*z^12 + 2*z^8 + z^5 - - TESTS:: - - sage: g = L([1,0,0,2,0,0,0,3], -5); g - z^-5 + 2*z^-2 + 3*z^2 - sage: g.polynomial() - z^-5 + 2*z^-2 + 3*z^2 - sage: z = L.gen() - sage: f = (1 + z)/(z^3 - z^5) - sage: f - z^-3 + z^-2 + z^-1 + 1 + z + z^2 + z^3 + ... - sage: f.polynomial(5) - z^-3 + z^-2 + z^-1 + 1 + z + z^2 + z^3 + z^4 + z^5 - sage: f.polynomial(0) - z^-3 + z^-2 + z^-1 + 1 - sage: f.polynomial(-5) - 0 - sage: M = L(lambda n: n^2, 0) - sage: M.polynomial(3) - 9*z^3 + 4*z^2 + z - sage: M = L(lambda n: n^2, 0) - sage: M.polynomial(5) - 25*z^5 + 16*z^4 + 9*z^3 + 4*z^2 + z - - sage: f = 1/(1 + z) - sage: f.polynomial() - Traceback (most recent call last): - ... - ValueError: not a polynomial - """ - if degree is None: - if isinstance(self._aux, LLS_zero): - from sage.rings.all import PolynomialRing - return PolynomialRing(S.base_ring(), name=name).zero() - elif isinstance(self._aux, LLS_eventually_geometric) and not self._aux._constant: - m = self._aux._degree - else: - raise ValueError("not a polynomial") - else: - m = degree + 1 - - S = self.parent() - - if name is None: - name = S.variable_name() - - if self.valuation() < 0: - from sage.rings.all import LaurentPolynomialRing - R = LaurentPolynomialRing(S.base_ring(), name=name) - n = self.valuation() - return R([self[i] for i in range(n, m)]).shift(n) - else: - from sage.rings.all import PolynomialRing - R = PolynomialRing(S.base_ring(), name=name) - return R([self[i] for i in range(m)]) - - def valuation(self): - """ - Return the valuation of the series. - - This method determines the valuation of the series by looking for a - nonzero coefficient. Hence if the series happens to be zero, then it - may run forever. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: s = 1/(1 - z) - 1/(1 - 2*z) - sage: s.valuation() - 1 - sage: t = z - z - sage: t.valuation() - +Infinity - sage: M = L(lambda n: n^2, 0) - sage: M.valuation() - 1 - sage: M = L(lambda n: n^2, 0) - sage: M.valuation() - 1 - """ - return self._aux.valuation() - - def _repr_(self): - """ - Return the string representation of this Laurent series. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: -1/(1 + 2*z) - -1 + 2*z - 4*z^2 + 8*z^3 - 16*z^4 + 32*z^5 - 64*z^6 + ... - """ - if isinstance(self._aux, LLS_zero): - return '0' - if isinstance(self._aux, LLS_uninitialized) and self._aux._target is None: - return 'Uninitialized LLS' - - atomic_repr = self.base_ring()._repr_option('element_is_atomic') - X = self.parent().variable_name() - v = self._aux._approximate_valuation - - if not isinstance(self._aux, LLS_eventually_geometric): - m = v + 7 # long enough - elif not self._aux._constant: - # Just a polynonial, so let that print itself - return repr(self._aux._laurent_polynomial) - else: - m = self._aux._degree + 3 - - # Use the polynomial printing - R = self.parent()._laurent_poly_ring - ret = repr(R([self._aux[i] for i in range(v, m)]).shift(v)) - # TODO: Better handling when ret == 0 but we have not checked up to the constant term - return ret + ' + ...' - - def _richcmp_(self, other, op): - """ - Compare ``self` with ``other`` with respect to the comparison operator ``op``. - - Equality is verified if the corresponding coefficients of both series - can be checked for equality without computing coefficients - indefinitely. Otherwise an exception is raised to declare that - equality is not decidable. - - Inequality is not defined for lazy Laurent series. - - TESTS:: - - sage: L. = LLSRing(QQ) - sage: z + z^2 == z^2 + z - True - sage: z + z^2 != z^2 + z - False - sage: z + z^2 > z^2 + z - False - sage: z + z^2 < z^2 + z - False - """ - if op is op_EQ: - if isinstance(self._aux, LLS_zero): # self == 0 - return isinstance(other._aux, LLS_zero) - if isinstance(other._aux, LLS_zero): # self != 0 but other == 0 - return False - - if (not isinstance(self._aux, LLS_eventually_geometric) - or not isinstance(other._aux, LLS_eventually_geometric)): - # One of the lazy laurent series is not known to eventually be constant - # Implement the checking of the caches here. - n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) - m = max(self._aux._approximate_valuation, other._aux._approximate_valuation) - for i in range(n, m): - if self[i] != other[i]: - return False - if self._aux == other._aux: - return True - raise ValueError("undecidable as lazy Laurent series") - - # Both are LLS_eventually_geometric, which implements a full check - return self._aux == other._aux - - if op is op_NE: - return not (self == other) - - return False - - def __hash__(self): - """ - Return the hash of ``self`` - - TESTS:: - - sage: L = LLSRing(ZZ, 'z') - sage: f = L([1,2,3,4], -5) - sage: g = (1 + f)/(1 - f)^2 - sage: {g: 1} - {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} - """ - return hash(self._aux) - - def __bool__(self): - """ - Test whether ``self`` is not zero. - - TESTS:: - - sage: L. = LLSRing(GF(2)) - sage: (z-z).is_zero() - True - sage: f = 1/(1 - z) - sage: f.is_zero() - False - sage: M = L(lambda n: n, 0); M - z + z^3 + z^5 + ... - sage: M.is_zero() - False - """ - if isinstance(self._aux, LLS_zero): - return False - if isinstance(self._aux, LLS_eventually_geometric): - # This should always end up being True, but let's be careful about it for now... - return self._aux._laurent_polynomial or self._aux._constant - - for a in self._aux._cache: - if a: - return True - if self[self._aux._approximate_valuation]: - return True - raise ValueError("undecidable as lazy Laurent series") - - def define(self, s): - r""" - Define an equation by ``self = s``. - - EXAMPLES: - - We begin by constructing the Catalan numbers:: - - sage: L. = LLSRing(ZZ) - sage: C = L(None) - sage: C.define(1 + z*C^2) - sage: C - 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - - The Catalan numbers but with a valuation 1:: - - sage: B = L(None, 1) - sage: B.define(z + B^2) - sage: B - z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + ... - - We can define multiple series that are linked:: - - sage: s = L(None) - sage: t = L(None) - sage: s.define(1 + z*t^3) - sage: t.define(1 + z*s^2) - sage: s[:9] - [1, 1, 3, 9, 34, 132, 546, 2327, 10191] - sage: t[:9] - [1, 1, 2, 7, 24, 95, 386, 1641, 7150] - - An bigger example:: - - sage: L. = LLSRing(ZZ) - sage: A = L(None, 5) - sage: B = L(None) - sage: C = L(None, 2) - sage: A.define(z^5 + B^2) - sage: B.define(z^5 + C^2) - sage: C.define(z^2 + C^2 + A^2) - sage: A[0:15] - [0, 0, 0, 0, 0, 1, 0, 0, 1, 2, 5, 4, 14, 10, 48] - sage: B[0:15] - [0, 0, 0, 0, 1, 1, 2, 0, 5, 0, 14, 0, 44, 0, 138] - sage: C[0:15] - [0, 0, 1, 0, 1, 0, 2, 0, 5, 0, 15, 0, 44, 2, 142] - - Counting binary trees:: - - sage: L. = LLSRing(QQ) - sage: s = L(None, valuation=1) - sage: s.define(z + (s^2+s(z^2))/2) - sage: [s[i] for i in range(9)] - [0, 1, 1, 1, 2, 3, 6, 11, 23] - - The `q`-Catalan numbers:: - - sage: R. = ZZ[] - sage: L. = LLSRing(R) - sage: s = L(None) - sage: s.define(1+z*s*s(q*z)) - sage: s - 1 + z + (q + 1)*z^2 + (q^3 + q^2 + 2*q + 1)*z^3 - + (q^6 + q^5 + 2*q^4 + 3*q^3 + 3*q^2 + 3*q + 1)*z^4 - + (q^10 + q^9 + 2*q^8 + 3*q^7 + 5*q^6 + 5*q^5 + 7*q^4 + 7*q^3 + 6*q^2 + 4*q + 1)*z^5 - + (q^15 + q^14 + 2*q^13 + 3*q^12 + 5*q^11 + 7*q^10 + 9*q^9 + 11*q^8 - + 14*q^7 + 16*q^6 + 16*q^5 + 17*q^4 + 14*q^3 + 10*q^2 + 5*q + 1)*z^6 + ... - - We count unlabeled ordered trees by total number of nodes - and number of internal nodes:: - - sage: R. = QQ[] - sage: Q. = LLSRing(R) - sage: leaf = z - sage: internal_node = q * z - sage: L = Q(constant=1, degree=1) - sage: T = Q(None, 1) - sage: T.define(leaf + internal_node * L(T)) - sage: [T[i] for i in range(6)] - [0, 1, q, q^2 + q, q^3 + 3*q^2 + q, q^4 + 6*q^3 + 6*q^2 + q] - - TESTS:: - - sage: L. = LLSRing(ZZ, sparse=True) - sage: s = L(None) - sage: s.define(1 + z*s^3) - sage: s[:10] - [1, 1, 3, 12, 55, 273, 1428, 7752, 43263, 246675] - - sage: e = L(None) - sage: e.define(1 + z*e) - sage: e.define(1 + z*e) - Traceback (most recent call last): - ... - ValueError: series already defined - sage: z.define(1 + z^2) - Traceback (most recent call last): - ... - ValueError: series already defined - """ - if not isinstance(self._aux, LLS_uninitialized) or self._aux._target is not None: - raise ValueError("series already defined") - self._aux._target = s._aux - - -class LLS_aux(): - """ - Abstract base class for all auxillary LLS. - """ - def __init__(self, sparse, approximate_valuation): - self._is_sparse = sparse - self._approximate_valuation = approximate_valuation - - -class LLS_inexact(LLS_aux): - """ - LLS aux class when it is not or we do not know if it is - eventually geometric. - """ - def __init__(self, is_sparse, approximate_valuation): - super().__init__(is_sparse, approximate_valuation) - - if self._is_sparse: - self._cache = dict() # cache of known coefficients - else: - self._cache = list() - self._offset = approximate_valuation - self._iter = self.iterate_coefficients() - - def __getstate__(self): - d = dict(self.__dict__) - if not self._is_sparse: - # We cannot pickle a generator object, so we remove it and - # the cache from the pickle information. - del d["_iter"] - del d["_cache"] - return d - - def __setstate__(self, d): - self.__dict__ = d - if not self._is_sparse: - self._iter = self.iterate_coefficients() - self._cache = [] - - def __getitem__(self, n): - if n < self._approximate_valuation: - return ZZ.zero() - - if self._is_sparse: - try: - c = self._cache[n] - except KeyError: - c = self.get_coefficient(n) - self._cache[n] = c - else: - i = n - self._offset - if i >= len(self._cache): - a = len(self._cache) + self._offset - # it is important to extend by generator: - # self._coefficient_function might recurse, and - # thereby extend the cache itself, too - self._cache.extend(next(self._iter) for _ in range(a, n+1)) - c = self._cache[i] - - return c - - def valuation(self): - if self._is_sparse: - n = self._approximate_valuation - cache = self._cache - while True: - if n in cache: - if cache[n]: - self._approximate_valuation = n - return n - n += 1 - else: - if self[n] != 0: - self._approximate_valuation = n - return n - n += 1 - else: - n = self._approximate_valuation - cache = self._cache - while True: - if n - self._offset < len(cache): - if cache[n - self._offset]: - self._approximate_valuation = n - return n - n += 1 - else: - if self[n] != 0: - self._approximate_valuation = n - return n - n += 1 - - -class LLS_unary(LLS_inexact): - """ - Abstract base class for unary operators. - - INPUT: - - - ``series`` -- series upon which the operator operates - - """ - def __init__(self, series, *args, **kwargs): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = -1/(1 - z) - sage: f - -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... - sage: loads(dumps(f)) == f - True - """ - self._series = series - super().__init__(*args, **kwargs) - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = ~(1 - z) - sage: {f: 1} - {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} - """ - return hash((type(self), self._series)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g - True - sage: f = ~(1 - z) - sage: g = ~(1 - z) - sage: f == g - True - """ - return isinstance(other, type(self)) and self._series == other._series - - -class LLS_binary(LLS_inexact): - - def __init__(self, left, right, *args, **kwargs): - """ - Initialize. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: loads(dumps(f)) == f - True - sage: f = 1/(1 - z) - 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ - self._left = left - self._right = right - super().__init__(*args, **kwargs) - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: {f: 1} - {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} - """ - return hash((type(self), self._left, self._right)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LLSRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g - True - """ - if not isinstance(other, type(self)): - return False - return self._left == other._left and self._right == other._right - - -class LLS_binary_commutative(LLS_binary): - - def __hash__(self): - return hash((type(self), frozenset([self._left, self._right]))) - - def __eq__(self, other): - if not isinstance(other, type(self)): - return False - if self._left == other._left and self._right == other._right: - return True - if self._left == other._right and self._right == other._left: - return True - return False - - -class LLS_eventually_geometric(LLS_aux): - def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): - if constant is None: - constant = ZZ.zero() - if degree is None: - if not laurent_polynomial: - raise ValueError("you must specify the degree for the polynomial 0") - degree = laurent_polynomial.degree() + 1 - else: - # Consistency check - assert not laurent_polynomial or laurent_polynomial.degree() < degree - - self._constant = constant - self._degree = degree - self._laurent_polynomial = laurent_polynomial - if not laurent_polynomial: - valuation = degree - else: - valuation = laurent_polynomial.valuation() - super().__init__(is_sparse, valuation) - - def __getitem__(self, n): - if n >= self._degree: - return self._constant - return self._laurent_polynomial[n] - - def valuation(self): - return self._approximate_valuation - - def __hash__(self): - return hash((self._laurent_polynomial, self._degree, self._constant)) - - def __eq__(self, other): - return (isinstance(other, type(self)) - and self._degree == other._degree - and self._laurent_polynomial == other._laurent_polynomial - and self._constant == other._constant) - -class LLS_zero(LLS_aux): - def __init__(self, sparse): - return super().__init__(sparse, 0) - - def __getitem__(self, n): - return ZZ.zero() - - def valuation(self): - return infinity - - def __hash__(self): - return 0 - -class LLS_coefficient_function(LLS_inexact): - def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation): - self._coefficient_function = coefficient_function - self._ring = ring - super().__init__(is_sparse, approximate_valuation) - - def get_coefficient(self, n): - return self._ring(self._coefficient_function(n)) - - def iterate_coefficients(self): - n = self._offset - ring = self._ring - while True: - yield ring(self._coefficient_function(n)) - n += 1 - -class LLS_uninitialized(LLS_inexact): - def __init__(self, is_sparse, approximate_valuation): - self._target = None - super().__init__(is_sparse, approximate_valuation) - - def get_coefficient(self, n): - return self._target[n] - - def iterate_coefficients(self): - n = self._approximate_valuation - while True: - yield self._target[n] - n += 1 - -##################################################################### -## Binary operations - -class LLS_add(LLS_binary): - """ - Operator for addition. - """ - def __init__(self, left, right): - if left._is_sparse != right._is_sparse: - raise NotImplementedError - - a = min(left._approximate_valuation, right._approximate_valuation) - super().__init__(left, right, left._is_sparse, a) - - def get_coefficient(self, n): - return self._left[n] + self._right[n] - - def iterate_coefficients(self): - n = self._offset - while True: - yield self._left[n] + self._right[n] - n += 1 - -class LLS_sub(LLS_binary): - """ - Operator for subtraction. - """ - def __init__(self, left, right): - if left._is_sparse != right._is_sparse: - raise NotImplementedError - - a = min(left._approximate_valuation, right._approximate_valuation) - super().__init__(left, right, left._is_sparse, a) - - def get_coefficient(self, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: f = (1 + z)*(1 - z) - sage: f.coefficient(2) - -1 - """ - return self._left[n] - self._right[n] - - def iterate_coefficients(self): - n = self._offset - while True: - yield self._left[n] - self._right[n] - n += 1 - -class LLS_mul(LLS_binary): - """ - Operator for multiplication. - - We are assuming commutativity of the coefficient ring here. - """ - def __init__(self, left, right): - if left._is_sparse != right._is_sparse: - raise NotImplementedError - - a = left._approximate_valuation + right._approximate_valuation - super().__init__(left, right, left._is_sparse, a) - - def get_coefficient(self, n): - c = ZZ.zero() - for k in range(self._left._approximate_valuation, - n - self._right._approximate_valuation + 1): - l = self._left[k] - if l: - c += l * self._right[n-k] - return c - - def iterate_coefficients(self): - n = self._offset - while True: - c = ZZ.zero() - for k in range(self._left._approximate_valuation, - n - self._right._approximate_valuation + 1): - l = self._left[k] - if l: - c += l * self._right[n-k] - yield c - n += 1 - -class LLS_div(LLS_binary): - """ - Return ``left`` divided by ``right``. - """ - def __init__(self, left, right): - lv = left.valuation() - rv = right.valuation() - self._lv = lv - self._rv = rv - self._ainv = ~right[rv] - super().__init__(left, right, left._is_sparse, lv - rv) - - def get_coefficient(self, n): - lv = self._lv - rv = self._rv - if n == lv - rv: - return self._left[lv] / self._right[rv] - c = self._left[n + rv] - for k in range(lv - rv, n): - c -= self[k] * self._right[n + rv - k] - return c * self._ainv - - def iterate_coefficients(self): - n = self._offset - lv = self._lv - rv = self._rv - while True: - if n == lv - rv: - yield self._left[lv] / self._right[rv] - n += 1 - continue - c = self._left[n + rv] - for k in range(lv - rv, n): - c -= self[k] * self._right[n + rv - k] - yield c * self._ainv - n += 1 - - -class LLS_composition(LLS_binary): - r""" - Return ``f`` composed by ``g``. - - This is the composition `(f \circ g)(z) = f(g(z))`. - - INPUT: - - - ``f`` -- a :class:`LLS_aux` - - ``g`` -- a :class:`LLS_aux` with positive valuation - """ - def __init__(self, f, g): - assert g._approximate_valuation > 0 - self._fv = f._approximate_valuation - self._gv = g._approximate_valuation - if self._fv < 0: - ginv = LLS_inv(g) - # the constant part makes no contribution to the negative - # we need this for the case so self._neg_powers[0][n] => 0 - self._neg_powers = [LLS_zero(f._is_sparse), ginv] - for i in range(1, -self._fv): - self._neg_powers.append(LLS_mul(self._neg_powers[-1], ginv)) - # Placeholder None to make this 1-based - self._pos_powers = [None, g] - val = self._fv * self._gv - super().__init__(f, g, f._is_sparse, val) - - def get_coefficient(self, n): - if n < 0: - return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) - # n > 0 - while len(self._pos_powers) <= n // self._gv: - self._pos_powers.append(LLS_mul(self._pos_powers[-1], self._right)) - ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, 0)) - if n == 0: - ret += self._left[0] - return ret + sum(self._left[i] * self._pos_powers[i][n] for i in range(1, n // self._gv+1)) - - def iterate_coefficients(self): - n = self._approximate_valuation - while True: - yield self.get_coefficient(n) - n += 1 - -##################################################################### -## Unary operations - -class LLS_scalar(LLS_unary): - """ - Operator for multiplying with a scalar. - """ - def __init__(self, series, scalar): - self._scalar = scalar - - super().__init__(series, series._is_sparse, series._approximate_valuation) - - def get_coefficient(self, n): - return self._series[n] * self._scalar - - def iterate_coefficients(self): - n = self._offset - while True: - yield self._series[n] * self._scalar - n += 1 - -class LLS_neg(LLS_unary): - """ - Operator for negative of the series. - """ - def __init__(self, series): - super().__init__(series, series._is_sparse, series._approximate_valuation) - - def get_coefficient(self, n): - return -self._series[n] - - def iterate_coefficients(self): - n = self._offset - while True: - yield -self._series[n] - n += 1 - -class LLS_inv(LLS_unary): - """ - Operator for multiplicative inverse of the series. - """ - def __init__(self, series): - v = series.valuation() - super().__init__(series, series._is_sparse, -v) - - self._ainv = ~series[v] - self._zero = ZZ.zero() - - def get_coefficient(self, n): - v = self._approximate_valuation - if n == v: - return self._ainv - c = self._zero - for k in range(v, n): - c += self[k] * self._series[n - v - k] - return -c * self._ainv - - def iterate_coefficients(self): - n = self._offset - while True: - v = self._approximate_valuation - if n == v: - yield self._ainv - n += 1 - continue - c = self._zero - for k in range(v, n): - c += self[k] * self._series[n - v - k] - yield -c * self._ainv - n += 1 - -class LLS_apply_coeff(LLS_unary): - """ - Return the series with ``function`` applied to each coefficient of this series. - """ - def __init__(self, series, function, ring): - self._function = function - self._ring = ring - super().__init__(series, series._is_sparse, series._approximate_valuation) - - def get_coefficient(self, n): - c = self._ring(self._function(self._series[n])) - return c - - def iterate_coefficients(self): - n = self._offset - while True: - c = self._ring(self._function(self._series[n])) - yield c - n += 1 diff --git a/src/sage/rings/lazy_laurent_series_operator.py b/src/sage/rings/lazy_laurent_series_operator.py deleted file mode 100644 index ed1948fbb60..00000000000 --- a/src/sage/rings/lazy_laurent_series_operator.py +++ /dev/null @@ -1,987 +0,0 @@ -r""" -Lazy Laurent Series Operators - -This module implements operators internally used to construct lazy Laurent -series. The job of an operator attached to a series is to compute the `n`-th -coefficient of the series if `n` is not less than the valuation of the series -and the `n`-th coefficient is not declared to be a constant. - -If a new operator is added to this module, an example of how it is used should be -added below. - -EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - 2*z) - sage: g = 1/(1 + z^2) - -Constructors:: - - sage: L(1) - 1 - -:: - - sage: L.series([1,2,3,4], -10) - z^-10 + 2*z^-9 + 3*z^-8 + 4*z^-7 - -:: - - sage: L.gen() - z - -:: - - sage: P. = LaurentPolynomialRing(ZZ) - sage: p = (1 + 1/x)^3 + (1 + x)^4 - sage: L(p) - z^-3 + 3*z^-2 + 3*z^-1 + 2 + 4*z + 6*z^2 + 4*z^3 + z^4 - -Unary operators:: - - sage: -f - -1 - 2*z - 4*z^2 - 8*z^3 - 16*z^4 - 32*z^5 - 64*z^6 + ... - -:: - - sage: ~f - 1 - 2*z + ... - -Binary operators:: - - sage: f + g - 2 + 2*z + 3*z^2 + 8*z^3 + 17*z^4 + 32*z^5 + 63*z^6 + ... - -:: - - sage: f - g - 2*z + 5*z^2 + 8*z^3 + 15*z^4 + 32*z^5 + 65*z^6 + 128*z^7 + ... - -:: - - sage: f * g - 1 + 2*z + 3*z^2 + 6*z^3 + 13*z^4 + 26*z^5 + 51*z^6 + ... - -:: - - sage: f / g - 1 + 2*z + 5*z^2 + 10*z^3 + 20*z^4 + 40*z^5 + 80*z^6 + ... - -Transformers:: - - sage: 2*f - 2 + 4*z + 8*z^2 + 16*z^3 + 32*z^4 + 64*z^5 + 128*z^6 + ... - -:: - - sage: f.change_ring(GF(3)) - 1 + 2*z + z^2 + 2*z^3 + z^4 + 2*z^5 + z^6 + ... - -:: - - sage: f.apply_to_coefficients(lambda c: c^2) - 1 + 4*z + 16*z^2 + 64*z^3 + 256*z^4 + 1024*z^5 + 4096*z^6 + ... - -:: - - sage: f.truncate(5) - 1 + 2*z + 4*z^2 + 8*z^3 + 16*z^4 - -AUTHORS: - -- Kwankyu Lee (2019-02-24): initial version - -""" - -# **************************************************************************** -# Copyright (C) 2019 Kwankyu Lee -# -# 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 -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** - - -class LazyLaurentSeriesOperator(object): - """ - Base class for operators computing coefficients of a lazy Laurent series. - - Subclasses of this class are used to implement arithmetic operations for - lazy Laurent series. These classes are not to be used directly by the user. - """ - def __ne__(self, other): - """ - Test inequality. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f != g - False - """ - return not (self == other) - - -class LazyLaurentSeriesBinaryOperator(LazyLaurentSeriesOperator): - """ - Abstract base class for binary operators. - - INPUT: - - - ``left`` -- series on the left side of the binary operator - - - ``right`` -- series on the right side of the binary operator - - """ - def __init__(self, left, right): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: loads(dumps(f)) == f - True - sage: f = 1/(1 - z) - 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ - self._left = left - self._right = right - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: {f: 1} - {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} - """ - return hash((type(self), self._left, self._right)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g - True - """ - return (isinstance(other, type(self)) and - self._left == other._left and self._right == other._right) - - -class LazyLaurentSeriesUnaryOperator(LazyLaurentSeriesOperator): - """ - Abstract base class for unary operators. - - INPUT: - - - ``series`` -- series upon which the operator operates - - """ - def __init__(self, series): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = -1/(1 - z) - sage: f - -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... - sage: loads(dumps(f)) == f - True - """ - self._series = series - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 - z) - sage: {f: 1} - {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} - """ - return hash((type(self), self._series)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g - True - sage: f = ~(1 - z) - sage: g = ~(1 - z) - sage: f == g - True - """ - return isinstance(other, type(self)) and self._series == other._series - - -class LazyLaurentSeriesOperator_add(LazyLaurentSeriesBinaryOperator): - """ - Operator for addition. - """ - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = z + z^2 + z - sage: f.coefficient(1) - 2 - """ - return self._left.coefficient(n) + self._right.coefficient(n) - -class LazyLaurentSeriesOperator_sub(LazyLaurentSeriesBinaryOperator): - """ - Operator for subtraction. - """ - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1 + 3*z - z - sage: f.coefficient(1) - 2 - """ - return self._left.coefficient(n) - self._right.coefficient(n) - -class LazyLaurentSeriesOperator_mul(LazyLaurentSeriesBinaryOperator): - """ - Operator for multiplication. - """ - def __init__(self, left, right): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) * 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ - LazyLaurentSeriesBinaryOperator.__init__(self, left, right) - self._zero = left.base_ring().zero() - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = (1 + z)*(1 - z) - sage: f.coefficient(2) - -1 - """ - c = self._zero - for k in range(self._left._approximate_valuation, n - self._right._approximate_valuation + 1): - c += self._left.coefficient(k) * self._right.coefficient(n-k) - return c - -class LazyLaurentSeriesOperator_neg(LazyLaurentSeriesUnaryOperator): - """ - Operator for negation. - - INPUT: - - - ``series`` -- a lazy Laurent series - - """ - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = -(1 + z) - sage: f.coefficient(1) - -1 - """ - return -self._series.coefficient(n) - -class LazyLaurentSeriesOperator_inv(LazyLaurentSeriesUnaryOperator): - """ - Operator for inversion. - - INPUT: - - - ``series`` -- a lazy Laurent series - - """ - def __init__(self, series): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 - z) - sage: f - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: loads(dumps(f)) == f - True - """ - LazyLaurentSeriesUnaryOperator.__init__(self, series) - - self._v = series.valuation() - self._ainv = ~series.coefficient(self._v) - self._zero = series.base_ring().zero() - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 - z) - sage: f.coefficient(2) - 1 - """ - v = self._v - if n == -v: - return self._ainv - c = self._zero - for k in range(-v, n): - c += s.coefficient(k) * self._series.coefficient(n + v - k) - return -c * self._ainv - -class LazyLaurentSeriesOperator_div(LazyLaurentSeriesBinaryOperator): - """ - Operator for division. - """ - def __init__(self, left, right): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = (1 - z)/(1 + z) - sage: loads(dumps(f)) == f - True - """ - LazyLaurentSeriesBinaryOperator.__init__(self, left, right) - - lv = left.valuation() - rv = right.valuation() - - self._lv = lv - self._rv = rv - self._ainv = ~right.coefficient(rv) - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = (1 + z)/(1 - z) - sage: f.coefficient(2) - 2 - """ - lv = self._lv - rv = self._rv - - if n == lv - rv: - return self._left.coefficient(lv)/self._right.coefficient(rv) - c = self._left.coefficient(n + rv) - for k in range(lv - rv, n): - c -= s.coefficient(k) * self._right.coefficient(n + rv - k) - return c * self._ainv - -class LazyLaurentSeriesOperator_scale(LazyLaurentSeriesOperator): - """ - Operator for scalar multiplication of ``series`` with ``scalar``. - - INPUT: - - - ``series`` -- a lazy Laurent series - - - ``scalar`` -- an element of the base ring - - """ - def __init__(self, series, scalar): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: g = 2*z - sage: loads(dumps(g)) == g - True - """ - self._series = series - self._scalar = scalar - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 2*(z + z^2) - sage: f.coefficient(2) - 2 - sage: f - 2*z + 2*z^2 - """ - return self._scalar * self._series.coefficient(n) - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 2*(z + z^2) - sage: {f: 1} - {2*z + 2*z^2: 1} - """ - return hash((type(self), self._series, self._scalar)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 2*z - sage: g = 2*z - sage: f == g - True - """ - return (isinstance(other, LazyLaurentSeriesOperator_scale) - and self._series == other._series and self._scalar == other._scalar) - -class LazyLaurentSeriesOperator_change_ring(LazyLaurentSeriesOperator): - """ - Operator for changing the base ring of the ``series`` to ``ring``. - - INPUT: - - - ``series`` -- a lazy Laurent series - - - ``ring`` -- a ring - - """ - def __init__(self, series, ring): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 - 2*z) - sage: g = f.change_ring(GF(3)) - sage: g - 1 + 2*z + z^2 + 2*z^3 + z^4 + 2*z^5 + z^6 + ... - sage: loads(dumps(g)) == g - True - """ - self._series = series - self._ring = ring - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 - 2*z) - sage: f - 1 + 2*z + 4*z^2 + 8*z^3 + 16*z^4 + 32*z^5 + 64*z^6 + ... - sage: g = f.change_ring(GF(2)) - sage: g.coefficient(3) - 0 - sage: g - 1 + ... - """ - return self._ring(self._series.coefficient(n)) - - def __hash__(self): - """ - Return the hash of ``self``. - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 - 2*z) - sage: g = f.change_ring(GF(2)) - sage: {g: 1} - {1 + ...: 1} - """ - return hash((type(self), self._series, self._ring)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) - sage: g = 1/(1 - z) - sage: f == g - True - """ - return (isinstance(other, LazyLaurentSeriesOperator_change_ring) - and self._series == other._series and self._ring == other._ring) - -class LazyLaurentSeriesOperator_apply(LazyLaurentSeriesOperator): - """ - Operator for applying a function. - - INPUT: - - - ``series`` -- a lazy Laurent series - - - ``function`` -- a Python function to apply to each coefficient of the series - - """ - def __init__(self, series, function): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 + z) - sage: g = f.apply_to_coefficients(abs) - sage: g - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: loads(dumps(g)) - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - """ - self._series = series - self._function = function - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 + z) - sage: f - 1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ... - sage: f.apply_to_coefficients(lambda c: c if c >= 0 else -c) - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - """ - return self._function(self._series.coefficient(n)) - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 + z).apply_to_coefficients(lambda c: c if c >= 0 else -c) - sage: {f: 1} - {1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ...: 1} - """ - return hash((type(self), self._series, self._function)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 + z) - sage: g = f.apply_to_coefficients(abs) - sage: h = f.apply_to_coefficients(abs) - sage: g == h - True - """ - return (isinstance(other, LazyLaurentSeriesOperator_apply) - and self._series == other._series and self._function is other._function) - - -class LazyLaurentSeriesOperator_truncate(LazyLaurentSeriesOperator): - """ - Operator for truncation. - - INPUT: - - - ``series`` -- a lazy Laurent series - - - ``d`` -- an integer; the series is truncated the terms of degree `> d` - - """ - def __init__(self, series, d): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 + z) - sage: g = f.truncate(4) - sage: loads(dumps(g)) == g - True - """ - self._series = series - self._d = d - - self._zero = series.base_ring().zero() - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 + z) - sage: f - 1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ... - sage: f.truncate(4) - 1 - z + z^2 - z^3 - """ - if n <= self._d: - return self._series.coefficient(n) - else: - return self._zero - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 + z) - sage: {f: 1} - {1 - z + z^2 - z^3 + z^4 - z^5 + z^6 + ...: 1} - """ - return hash((type(self), self._series, self._d)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 + z) - sage: g = f.truncate(4) - sage: h = f.truncate(4) - sage: g == h - True - """ - return (isinstance(other, LazyLaurentSeriesOperator_truncate) - and self._series == other._series and self._d == other._d) - -class LazyLaurentSeriesOperator_gen(LazyLaurentSeriesOperator): - """ - Operator for the generator element. - - INPUT: - - - ``ring`` -- a lazy Laurent series ring - - """ - def __init__(self, ring): - """ - Initialize. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: z = L.gen() - sage: loads(dumps(z)) == z - True - """ - self._ring = ring - - self._one = ring.base_ring().one() - self._zero = ring.base_ring().zero() - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: L.gen() - z - """ - return self._one if n == 1 else self._zero - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: {z: 1} - {z: 1} - """ - return hash((type(self), self._ring)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: z1 = L.gen() - sage: z2 = L.gen() - sage: z1 == z2 - True - """ - return (isinstance(other, LazyLaurentSeriesOperator_gen) and self._ring == other._ring) - -class LazyLaurentSeriesOperator_constant(LazyLaurentSeriesOperator): - """ - Operator for the generator element. - - INPUT: - - - ``ring`` -- a lazy Laurent series ring - - - ``constant`` -- a constant of the base ring of ``ring`` - - """ - def __init__(self, ring, constant): - """ - Initialize. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L(10) - sage: loads(dumps(f)) == f - True - """ - self._ring = ring - self._constant = constant - - self._zero = ring.base_ring().zero() - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L(10); f - 10 - sage: f.coefficient(0) - 10 - sage: f.coefficient(1) - 0 - """ - return self._constant if n == 0 else self._zero - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L(10) - sage: {f: 1} - {10: 1} - """ - return hash((type(self), self._ring, self._constant)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: z1 = L(10) - sage: z2 = L(10) - sage: z1 == z2 - True - """ - return (isinstance(other, LazyLaurentSeriesOperator_constant) - and self._ring == other._ring and self._constant == other._constant) - -class LazyLaurentSeriesOperator_list(LazyLaurentSeriesOperator): - """ - Operator for the series defined by a list. - - INPUT: - - - ``l`` -- list - - - ``v`` -- integer - - """ - def __init__(self, ring, l, v): - """ - Initialize. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], -5) - sage: loads(dumps(f)) == f - True - """ - self._ring = ring - self._list = tuple([ring.base_ring()(e) for e in l]) - self._valuation = v - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], -5) - sage: f - z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 - """ - return self._ring.base_ring()(self._list[n - self._valuation]) - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], -5) - sage: {f: 1} - {z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2: 1} - """ - return hash((type(self), self._ring, self._list, self._valuation)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f1 = L.series([1,2,3,4], -5) - sage: f2 = L.series([1,2,3,4,0], -5) - sage: f1 == f2 - True - """ - return (isinstance(other, LazyLaurentSeriesOperator_list) and - self._ring == other._ring and self._list == other._list and - self._valuation == other._valuation) - -class LazyLaurentSeriesOperator_polynomial(LazyLaurentSeriesOperator): - """ - Operator for the series coerced from a polynomial or a Laurent polynomial. - - INPUT: - - - ``ring`` -- a lazy Laurent series ring - - - ``poly`` -- a polynomial or a Laurent polynomial - - """ - def __init__(self, ring, poly): - """ - Initialize. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: P. = ZZ[] - sage: p = 1 + 2*x + x^10 - sage: f = L(p) - sage: loads(dumps(f)) == f - True - """ - self._ring = ring - self._poly = poly - - def __call__(self, s, n): - """ - Return the `n`-th coefficient of the series ``s``. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: P. = ZZ[] - sage: p = (1 + 2*x + 3*x)^3 - sage: f = L(p) - sage: f - 1 + 15*z + 75*z^2 + 125*z^3 - """ - return self._poly[n] - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: P. = ZZ[] - sage: p = (1 + 2*x)^3 - sage: f = L(p) - sage: {f: 1} - {1 + 6*z + 12*z^2 + 8*z^3: 1} - """ - return hash((type(self), self._ring, self._poly)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: P. = ZZ[] - sage: p = (1 + 2*x)^3 - sage: f = L(p) - sage: g = L(p) - sage: f == g - True - """ - return (isinstance(other, LazyLaurentSeriesOperator_polynomial) and - self._ring == other._ring and self._poly == other._poly) diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index ffa6b4575c7..821f0978e81 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -38,8 +38,16 @@ Power series can be defined recursively:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: L.series(lambda s,n: (1 + z*s^2)[n], valuation=0) + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: L._sparse + True + sage: s = L(None) + sage: s._aux._is_sparse + True + sage: s._aux._approximate_valuation + 0 + sage: s.define(1 + z*s^2) + sage: s 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... AUTHORS: @@ -70,17 +78,17 @@ from sage.misc.cachefunc import cached_method from sage.rings.polynomial.polynomial_ring import PolynomialRing_general -from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing_generic - -from .lazy_laurent_series import LazyLaurentSeries -from .lazy_laurent_series_operator import ( - LazyLaurentSeriesOperator_gen, - LazyLaurentSeriesOperator_constant, - LazyLaurentSeriesOperator_list, - LazyLaurentSeriesOperator_polynomial +from .integer_ring import ZZ +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing, LaurentPolynomialRing_generic + +from .lazy_laurent_series import ( + LazyLaurentSeries, + LazyLaurentSeries_coefficient_function, + LazyLaurentSeries_zero, + LazyLaurentSeries_eventually_geometric, + LazyLaurentSeries_uninitialized ) - class LazyLaurentSeriesRing(UniqueRepresentation, Parent): """ Lazy Laurent series ring. @@ -98,7 +106,7 @@ class LazyLaurentSeriesRing(UniqueRepresentation, Parent): """ Element = LazyLaurentSeries - def __init__(self, base_ring, names, category=None): + def __init__(self, base_ring, names, sparse=False, category=None): """ Initialize. @@ -107,6 +115,8 @@ def __init__(self, base_ring, names, category=None): sage: L = LazyLaurentSeriesRing(ZZ, 't') sage: TestSuite(L).run(skip='_test_elements') """ + self._sparse = sparse + self._laurent_poly_ring = LaurentPolynomialRing(base_ring, names, sparse=sparse) Parent.__init__(self, base=base_ring, names=names, category=MagmasAndAdditiveMagmas().or_subcategory(category)) @@ -138,10 +148,9 @@ def gen(self, n=0): """ if n != 0: raise IndexError("there is only one generator") - - op = LazyLaurentSeriesOperator_gen(self) - c = (self.base_ring().zero(), 2) - return self.element_class(self, coefficient=op, valuation=1, constant=c) + R = self._laurent_poly_ring + aux = LazyLaurentSeries_eventually_geometric(R.gen(n), self._sparse, ZZ.zero(), 2) + return self.element_class(self, aux) def ngens(self): """ @@ -157,6 +166,7 @@ def ngens(self): """ return 1 + @cached_method def gens(self): """ Return the tuple of the generator. @@ -167,7 +177,7 @@ def gens(self): sage: 1/(1 - z) 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... """ - return (self.gen(),) + return tuple([self.gen(n) for n in range(self.ngens())]) def _coerce_map_from_(self, S): """ @@ -184,18 +194,15 @@ def _coerce_map_from_(self, S): if self.base_ring().has_coerce_map_from(S): return True - if isinstance(S, (PolynomialRing_general, LaurentPolynomialRing_generic)) and S.ngens() == 1: + R = self._laurent_poly_ring + if R.has_coerce_map_from(S): def make_series_from(poly): - op = LazyLaurentSeriesOperator_polynomial(self, poly) - a = poly.valuation() - c = (self.base_ring().zero(), poly.degree() + 1) - return self.element_class(self, coefficient=op, valuation=a, constant=c) - + return self.element_class(self, LazyLaurentSeries_eventually_geometric(R(poly), self._sparse)) return SetMorphism(Hom(S, self, Sets()), make_series_from) return False - def _element_constructor_(self, x): + def _element_constructor_(self, x=None, valuation=None, constant=None, degree=None): """ Construct a Laurent series from ``x``. @@ -206,12 +213,97 @@ def _element_constructor_(self, x): 0 sage: L(3) 1 - """ - R = self.base_ring() - op = LazyLaurentSeriesOperator_constant(self, R(x)) + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + + sage: L(lambda i: i, 5, 1, 10) + 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... + sage: L(lambda i: i, 5, (1, 10)) + 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... - return self.element_class(self, coefficient=op, valuation=0, constant=(R.zero(), 1)) + sage: X = L(constant=5, degree=2); X + 5*z^2 + 5*z^3 + 5*z^4 + ... + sage: X.valuation() + 2 + + sage: def g(i): + ....: if i < 0: + ....: return 1 + ....: else: + ....: return 1 + sum(k for k in range(i+1)) + sage: e = L(g, -5); e + z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... + sage: f = e^-1; f + z^5 - z^6 - z^11 + ... + sage: f.coefficient(10) + 0 + sage: f[20] + 9 + sage: f[30] + -219 + + sage: L(valuation=2, constant=1) + z^2 + z^3 + z^4 + ... + sage: L(constant=1) + Traceback (most recent call last): + ... + ValueError: you must specify the degree for the polynomial 0 + + Alternatively, the ``coefficient_function`` can be a list of elements of the + base ring. Then these elements are read as coefficients of the terms of + degrees starting from the ``valuation``. In this case, ``constant`` + may be just an element of the base ring instead of a tuple or can be + simply omitted if it is zero:: + + sage: f = L([1,2,3,4], -5) + sage: f + z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 + sage: g = L([1,3,5,7,9], 5, -1) + sage: g + z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... + """ + if x is None: + if valuation is None: + valuation = 0 + return self.element_class(self, LazyLaurentSeries_uninitialized(self._sparse, valuation)) + + R = self._laurent_poly_ring + BR = self.base_ring() + try: + # Try to build stuff using the polynomial ring constructor + x = R(x) + except (TypeError, ValueError): + pass + if isinstance(constant, (tuple, list)): + constant, degree = constant + if constant is not None: + constant = BR(constant) + if x in R: + if not x and not constant: + aux = LazyLaurentSeries_zero(self._sparse) + else: + if x and valuation: + x = x.shift(valuation - x.valuation()) + if degree is None and not x: + degree = valuation + aux = LazyLaurentSeries_eventually_geometric(R(x), self._sparse, constant, degree) + return self.element_class(self, aux) + if isinstance(x, LazyLaurentSeries): + if x._aux._is_sparse is self._sparse: + return self.element_class(self, x._aux) + # TODO: Implement a way to make a self._sparse copy + raise NotImplementedError("cannot convert between sparse and dense") + if callable(x): + if valuation is None: + valuation = 0 + if degree is not None: + if constant is None: + constant = ZZ.zero() + z = R.gen() + p = R.sum(x(i) * z**i for i in range(valuation, degree)) + return self.element_class(self, LazyLaurentSeries_eventually_geometric(p, self._sparse, constant, degree)) + return self.element_class(self, LazyLaurentSeries_coefficient_function(x, self.base_ring(), self._sparse, valuation)) + raise ValueError(f"unable to convert {x} into a lazy Laurent series") def _an_element_(self): """ @@ -220,21 +312,14 @@ def _an_element_(self): EXAMPLES:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: L.an_element() # random - z^-10 + z^-9 + z^-8 + z^-7 + z^-6 + z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + ... + sage: L.an_element() + z^-10 + z^-9 + z^-8 + ... """ - N = 10 - - e = self.base_ring().an_element() - - def r(s, i): - return self.base_ring().an_element() - - n = random.randint(-N,N) - m = random.randint(0,N) - - return self.element_class(self, coefficient=r, valuation=n, constant=(e,n+m)) + c = self.base_ring().an_element() + R = self._laurent_poly_ring + return self.element_class(self, LazyLaurentSeries_eventually_geometric(R.zero(), self._sparse, c, -10)) + @cached_method def one(self): """ Return the constant series `1`. @@ -245,8 +330,10 @@ def one(self): sage: L.one() 1 """ - return self._element_constructor_(1) + R = self._laurent_poly_ring + return self.element_class(self, LazyLaurentSeries_eventually_geometric(R.one(), self._sparse, ZZ.zero(), 1)) + @cached_method def zero(self): """ Return the zero series. @@ -257,89 +344,4 @@ def zero(self): sage: L.zero() 0 """ - return self._element_constructor_(0) - - def series(self, coefficient, valuation, constant=None): - r""" - Return a lazy Laurent series. - - INPUT: - - - ``coefficient`` -- Python function that computes coefficients - - - ``valuation`` -- integer; approximate valuation of the series - - - ``constant`` -- either ``None`` or pair of an element of the base ring and an integer - - Let the coefficient of index `i` mean the coefficient of the term of the - series with exponent `i`. - - Python function ``coefficient`` returns the value of the coefficient of - index `i` from input `s` and `i` where `s` is the series itself. - - Let ``valuation`` be `n`. All coefficients of index below `n` are zero. - If ``constant`` is ``None``, then the ``coefficient`` function is responsible to - compute the values of all coefficients of index `\ge n`. If - ``constant`` is a pair `(c,m)`, then the ``coefficient`` function is responsible - to compute the values of all coefficients of index `\ge n` and `< m` - and all the coefficients of index `\ge m` is the constant `c`. - - EXAMPLES:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: L.series(lambda s, i: i, 5, (1,10)) - 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... - - sage: def g(s, i): - ....: if i < 0: - ....: return 1 - ....: else: - ....: return s.coefficient(i - 1) + i - sage: e = L.series(g, -5); e - z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... - sage: f = e^-1; f - z^5 - z^6 - z^11 + ... - sage: f.coefficient(10) - 0 - sage: f.coefficient(20) - 9 - sage: f.coefficient(30) - -219 - - Alternatively, the ``coefficient`` can be a list of elements of the - base ring. Then these elements are read as coefficients of the terms of - degrees starting from the ``valuation``. In this case, ``constant`` - may be just an element of the base ring instead of a tuple or can be - simply omitted if it is zero. - - EXAMPLES:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L.series([1,2,3,4], -5) - sage: f - z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 - sage: g = L.series([1,3,5,7,9], 5, -1) - sage: g - z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... - """ - if isinstance(coefficient, (tuple, list)): - if isinstance(constant, tuple): - constant = constant[0] - if constant is None: - constant = self.base_ring().zero() - elif constant not in self.base_ring(): - raise ValueError("constant is not an element of the base ring") - constant = (constant, valuation + len(coefficient)) - coefficient = LazyLaurentSeriesOperator_list(self, coefficient, valuation) - elif constant is not None: - try: - c,m = constant - except TypeError: - raise TypeError('not a tuple') - - if valuation > m and c: # weird case - raise ValueError('inappropriate valuation') - - constant = (self.base_ring()(c), m) - - return self.element_class(self, coefficient=coefficient, valuation=valuation, constant=constant) + return self.element_class(self, LazyLaurentSeries_zero(self._sparse)) diff --git a/src/sage/rings/lazy_laurent_series_ring_new.py b/src/sage/rings/lazy_laurent_series_ring_new.py deleted file mode 100644 index b1dec903b78..00000000000 --- a/src/sage/rings/lazy_laurent_series_ring_new.py +++ /dev/null @@ -1,347 +0,0 @@ -r""" -Lazy Laurent Series Rings - -The ring of lazy Laurent series over a ring has usual arithmetic operations, -but it is actually not a ring in the usual sense since every -arithmetic operation gives a new series. - -EXAMPLES: - -The definition of Laurent series rings is not initially imported into the -global namespace. You need to import it explicitly to use it:: - - sage: L. = LLSRing(QQ) - sage: L.category() - Category of magmas and additive magmas - sage: 1/(1 - z) - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: 1/(1 - z) == 1/(1 - z) - True - -Lazy Laurent series ring over a finite field:: - - sage: L. = LLSRing(GF(3)); L - Lazy Laurent Series Ring in z over Finite Field of size 3 - sage: e = 1/(1 + z) - sage: e.coefficient(100) - 1 - sage: e.coefficient(100).parent() - Finite Field of size 3 - -Generating functions of integer sequences are Laurent series over the integer -ring:: - - sage: L. = LLSRing(ZZ); L - Lazy Laurent Series Ring in z over Integer Ring - sage: 1/(1 - 2*z)^3 - 1 + 6*z + 24*z^2 + 80*z^3 + 240*z^4 + 672*z^5 + 1792*z^6 + ... - -Power series can be defined recursively:: - - sage: L. = LLSRing(ZZ, sparse=True) - sage: L._sparse - True - sage: s = L(None) - sage: s._aux._is_sparse - True - sage: s._aux._approximate_valuation - 0 - sage: s.define(1 + z*s^2) - sage: s - 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - -AUTHORS: - -- Kwankyu Lee (2019-02-24): initial version - -""" - -# **************************************************************************** -# Copyright (C) 2019 Kwankyu Lee -# -# 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 -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# https://www.gnu.org/licenses/ -# **************************************************************************** -import random - -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.parent import Parent - -from sage.categories.magmas_and_additive_magmas import MagmasAndAdditiveMagmas -from sage.categories.morphism import SetMorphism -from sage.categories.homset import Hom -from sage.categories.sets_cat import Sets - -from sage.misc.cachefunc import cached_method - -from sage.rings.polynomial.polynomial_ring import PolynomialRing_general -from .integer_ring import ZZ -from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing, LaurentPolynomialRing_generic - -from .lazy_laurent_series_new import ( - LLS, - LLS_coefficient_function, - LLS_zero, - LLS_eventually_geometric, - LLS_uninitialized -) - -class LLSRing(UniqueRepresentation, Parent): - """ - Lazy Laurent series ring. - - INPUT: - - - ``base_ring`` -- base ring of this Laurent series ring - - - ``names`` -- name of the generator of this Laurent series ring - - EXAMPLES:: - - sage: LLSRing(ZZ, 't') - Lazy Laurent Series Ring in t over Integer Ring - """ - Element = LLS - - def __init__(self, base_ring, names, sparse=False, category=None): - """ - Initialize. - - TESTS:: - - sage: L = LLSRing(ZZ, 't') - sage: TestSuite(L).run(skip='_test_elements') - """ - self._sparse = sparse - self._laurent_poly_ring = LaurentPolynomialRing(base_ring, names, sparse=sparse) - Parent.__init__(self, base=base_ring, names=names, - category=MagmasAndAdditiveMagmas().or_subcategory(category)) - - def _repr_(self): - """ - String representation of this Laurent series ring. - - EXAMPLES:: - - sage: LLSRing(GF(2), 'z') - Lazy Laurent Series Ring in z over Finite Field of size 2 - """ - return "Lazy Laurent Series Ring in {} over {}".format(self.variable_name(), self.base_ring()) - - @cached_method - def gen(self, n=0): - """ - Return the generator of this Laurent series ring. - - EXAMPLES:: - - sage: L = LLSRing(ZZ, 'z') - sage: L.gen() - z - sage: L.gen(3) - Traceback (most recent call last): - ... - IndexError: there is only one generator - """ - if n != 0: - raise IndexError("there is only one generator") - R = self._laurent_poly_ring - aux = LLS_eventually_geometric(R.gen(n), self._sparse, ZZ.zero(), 2) - return self.element_class(self, aux) - - def ngens(self): - """ - Return the number of generators of this Laurent series ring. - - This is always 1. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: L.ngens() - 1 - """ - return 1 - - @cached_method - def gens(self): - """ - Return the tuple of the generator. - - EXAMPLES:: - - sage: L. = LLSRing(ZZ) - sage: 1/(1 - z) - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - """ - return tuple([self.gen(n) for n in range(self.ngens())]) - - def _coerce_map_from_(self, S): - """ - Return ``True`` if a coercion from ``S`` exists. - - EXAMPLES:: - - sage: L = LLSRing(GF(2), 'z') - sage: L.has_coerce_map_from(ZZ) - True - sage: L.has_coerce_map_from(GF(2)) - True - """ - if self.base_ring().has_coerce_map_from(S): - return True - - R = self._laurent_poly_ring - if R.has_coerce_map_from(S): - def make_series_from(poly): - return self.element_class(self, LLS_eventually_geometric(R(poly), self._sparse)) - return SetMorphism(Hom(S, self, Sets()), make_series_from) - - return False - - def _element_constructor_(self, x=None, valuation=None, constant=None, degree=None): - """ - Construct a Laurent series from ``x``. - - EXAMPLES:: - - sage: L = LLSRing(GF(2), 'z') - sage: L(2) - 0 - sage: L(3) - 1 - - sage: L = LLSRing(ZZ, 'z') - - sage: L(lambda i: i, 5, 1, 10) - 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... - sage: L(lambda i: i, 5, (1, 10)) - 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + ... - - sage: X = L(constant=5, degree=2); X - 5*z^2 + 5*z^3 + 5*z^4 + ... - sage: X.valuation() - 2 - - sage: def g(i): - ....: if i < 0: - ....: return 1 - ....: else: - ....: return 1 + sum(k for k in range(i+1)) - sage: e = L(g, -5); e - z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... - sage: f = e^-1; f - z^5 - z^6 - z^11 + ... - sage: f.coefficient(10) - 0 - sage: f[20] - 9 - sage: f[30] - -219 - - sage: L(valuation=2, constant=1) - y^2 + y^3 + y^4 + ... - sage: L(constant=1) - Traceback (most recent call last): - ... - ValueError: you must specify the degree for the polynomial 0 - - Alternatively, the ``coefficient_function`` can be a list of elements of the - base ring. Then these elements are read as coefficients of the terms of - degrees starting from the ``valuation``. In this case, ``constant`` - may be just an element of the base ring instead of a tuple or can be - simply omitted if it is zero:: - - sage: f = L([1,2,3,4], -5) - sage: f - z^-5 + 2*z^-4 + 3*z^-3 + 4*z^-2 - sage: g = L([1,3,5,7,9], 5, -1) - sage: g - z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... - """ - if x is None: - if valuation is None: - valuation = 0 - return self.element_class(self, LLS_uninitialized(self._sparse, valuation)) - - R = self._laurent_poly_ring - BR = self.base_ring() - try: - # Try to build stuff using the polynomial ring constructor - x = R(x) - except (TypeError, ValueError): - pass - if isinstance(constant, (tuple, list)): - constant, degree = constant - if constant is not None: - constant = BR(constant) - if x in R: - if not x and not constant: - aux = LLS_zero(self._sparse) - else: - if x and valuation: - x = x.shift(valuation - x.valuation()) - if degree is None and not x: - degree = valuation - aux = LLS_eventually_geometric(R(x), self._sparse, constant, degree) - return self.element_class(self, aux) - if isinstance(x, LLS): - if x._aux._is_sparse is self._sparse: - return self.element_class(self, x._aux) - # TODO: Implement a way to make a self._sparse copy - raise NotImplementedError("cannot convert between sparse and dense") - if callable(x): - if valuation is None: - valuation = 0 - if degree is not None: - if constant is None: - constant = ZZ.zero() - z = R.gen() - p = R.sum(x(i) * z**i for i in range(valuation, degree)) - return self.element_class(self, LLS_eventually_geometric(p, self._sparse, constant, degree)) - return self.element_class(self, LLS_coefficient_function(x, self.base_ring(), self._sparse, valuation)) - raise ValueError(f"unable to convert {x} into a lazy Laurent series") - - def _an_element_(self): - """ - Return a Laurent series in this ring. - - EXAMPLES:: - - sage: L = LLSRing(ZZ, 'z') - sage: L.an_element() - z^-10 + z^-9 + z^-8 + ... - """ - c = self.base_ring().an_element() - R = self._laurent_poly_ring - return self.element_class(self, LLS_eventually_geometric(R.zero(), self._sparse, c, -10)) - - @cached_method - def one(self): - """ - Return the constant series `1`. - - EXAMPLES:: - - sage: L = LLSRing(ZZ, 'z') - sage: L.one() - 1 - """ - R = self._laurent_poly_ring - return self.element_class(self, LLS_eventually_geometric(R.one(), self._sparse, ZZ.zero(), 1)) - - @cached_method - def zero(self): - """ - Return the zero series. - - EXAMPLES:: - - sage: L = LLSRing(ZZ, 'z') - sage: L.zero() - 0 - """ - return self.element_class(self, LLS_zero(self._sparse)) From f7dc374cf3d4d6197a46e2485ae2c9b4b1154962 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 23 Jul 2021 00:24:12 +0530 Subject: [PATCH 73/98] Converted into PEP8. --- src/sage/rings/lazy_laurent_series.py | 73 +++++++++++++++------- src/sage/rings/lazy_laurent_series_ring.py | 15 ++--- 2 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 1bd78d8686a..e97963a0358 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -81,6 +81,7 @@ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.integer import Integer + class LazyLaurentSeries(ModuleElement): r""" A Laurent series where the coefficients are computed lazily. @@ -131,6 +132,7 @@ class LazyLaurentSeries(ModuleElement): sage: g == f True """ + def __init__(self, parent, aux): """ Initialize the series. @@ -252,7 +254,7 @@ def __call__(self, g): y^-1 + 1 + y^2 sage: fy.parent() is LS True - sage: g = y - y + sage: g = y - y sage: f(g) Traceback (most recent call last): ... @@ -266,7 +268,7 @@ def __call__(self, g): sage: f = L(lambda n: n, 0); f z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: f(0) + sage: f(0) 0 sage: f(y) y + 2*y^2 + 3*y^3 + 4*y^4 + 5*y^5 + 6*y^6 + ... @@ -322,23 +324,23 @@ def __call__(self, g): y + y^2 + y^3 + y^4 + y^5 + y^6 + y^7 + ... sage: f(g) 1 + y + 2*y^2 + 4*y^3 + 8*y^4 + 16*y^5 + 32*y^6 + ... - - sage: f = z^-2 + 1 + z - sage: g = 1/(y*(1-y)); g + + sage: f = z^-2 + 1 + z + sage: g = 1/(y*(1-y)); g y^-1 + 1 + y + y^2 + y^3 + y^4 + y^5 + ... - sage: f(g) + sage: f(g) y^-1 + 2 + y + 2*y^2 - y^3 + 2*y^4 + y^5 + ... sage: g^-2 + 1 + g y^-1 + 2 + y + 2*y^2 - y^3 + 2*y^4 + y^5 + ... - sage: f = z^-3 + z^-2 + 1 - sage: g = 1/(y^2*(1-y)); g + sage: f = z^-3 + z^-2 + 1 + sage: g = 1/(y^2*(1-y)); g y^-2 + y^-1 + 1 + y + y^2 + y^3 + y^4 + ... - sage: f(g) + sage: f(g) 1 + y^4 - 2*y^5 + 2*y^6 + ... sage: g^-3 + g^-2 + 1 1 + y^4 - 2*y^5 + 2*y^6 + ... - sage: z(y) + sage: z(y) y """ # f = self and compute f(g) @@ -504,7 +506,7 @@ def _add_(self, other): left = self._aux right = other._aux if (isinstance(left, LazyLaurentSeries_eventually_geometric) - and isinstance(right, LazyLaurentSeries_eventually_geometric)): + and isinstance(right, LazyLaurentSeries_eventually_geometric)): R = P._laurent_poly_ring c = left._constant + right._constant pl = left._laurent_polynomial @@ -616,7 +618,7 @@ def _div_(self, other): return P.zero() right = other._aux if (isinstance(left, LazyLaurentSeries_eventually_geometric) - and isinstance(right, LazyLaurentSeries_eventually_geometric)): + and isinstance(right, LazyLaurentSeries_eventually_geometric)): if not left._constant and not right._constant: ret = left._laurent_polynomial / right._laurent_polynomial try: @@ -1170,7 +1172,7 @@ def _richcmp_(self, other, op): return False if (not isinstance(self._aux, LazyLaurentSeries_eventually_geometric) - or not isinstance(other._aux, LazyLaurentSeries_eventually_geometric)): + or not isinstance(other._aux, LazyLaurentSeries_eventually_geometric)): # One of the lazy laurent series is not known to eventually be constant # Implement the checking of the caches here. n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) @@ -1284,9 +1286,9 @@ def define(self, s): Counting binary trees:: - sage: L. = LazyLaurentSeriesRing(QQ) + sage: L. = LazyLaurentSeriesRing(QQ) sage: s = L(None, valuation=1) - sage: s.define(z + (s^2+s(z^2))/2) + sage: s.define(z + (s^2+s(z^2))/2) sage: [s[i] for i in range(9)] [0, 1, 1, 1, 2, 3, 6, 11, 23] @@ -1344,6 +1346,7 @@ class LazyLaurentSeries_aux(): """ Abstract base class for all auxillary LazyLaurentSeries. """ + def __init__(self, sparse, approximate_valuation): self._is_sparse = sparse self._approximate_valuation = approximate_valuation @@ -1354,6 +1357,7 @@ class LazyLaurentSeries_inexact(LazyLaurentSeries_aux): LazyLaurentSeries aux class when it is not or we do not know if it is eventually geometric. """ + def __init__(self, is_sparse, approximate_valuation): super().__init__(is_sparse, approximate_valuation) @@ -1441,6 +1445,7 @@ class LazyLaurentSeries_unary(LazyLaurentSeries_inexact): - ``series`` -- series upon which the operator operates """ + def __init__(self, series, *args, **kwargs): """ Initialize. @@ -1592,6 +1597,7 @@ def __eq__(self, other): and self._laurent_polynomial == other._laurent_polynomial and self._constant == other._constant) + class LazyLaurentSeries_zero(LazyLaurentSeries_aux): def __init__(self, sparse): return super().__init__(sparse, 0) @@ -1605,6 +1611,7 @@ def valuation(self): def __hash__(self): return 0 + class LazyLaurentSeries_coefficient_function(LazyLaurentSeries_inexact): def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation): self._coefficient_function = coefficient_function @@ -1621,6 +1628,7 @@ def iterate_coefficients(self): yield ring(self._coefficient_function(n)) n += 1 + class LazyLaurentSeries_uninitialized(LazyLaurentSeries_inexact): def __init__(self, is_sparse, approximate_valuation): self._target = None @@ -1636,12 +1644,14 @@ def iterate_coefficients(self): n += 1 ##################################################################### -## Binary operations +# Binary operations + class LLS_add(LazyLaurentSeries_binary): """ Operator for addition. """ + def __init__(self, left, right): if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -1658,10 +1668,12 @@ def iterate_coefficients(self): yield self._left[n] + self._right[n] n += 1 + class LLS_sub(LazyLaurentSeries_binary): """ Operator for subtraction. """ + def __init__(self, left, right): if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -1688,12 +1700,14 @@ def iterate_coefficients(self): yield self._left[n] - self._right[n] n += 1 + class LLS_mul(LazyLaurentSeries_binary): """ Operator for multiplication. We are assuming commutativity of the coefficient ring here. """ + def __init__(self, left, right): if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -1705,9 +1719,9 @@ def get_coefficient(self, n): c = ZZ.zero() for k in range(self._left._approximate_valuation, n - self._right._approximate_valuation + 1): - l = self._left[k] - if l: - c += l * self._right[n-k] + val = self._left[k] + if val: + c += val * self._right[n-k] return c def iterate_coefficients(self): @@ -1716,16 +1730,18 @@ def iterate_coefficients(self): c = ZZ.zero() for k in range(self._left._approximate_valuation, n - self._right._approximate_valuation + 1): - l = self._left[k] - if l: - c += l * self._right[n-k] + val = self._left[k] + if val: + c += val * self._right[n-k] yield c n += 1 + class LLS_div(LazyLaurentSeries_binary): """ Return ``left`` divided by ``right``. """ + def __init__(self, left, right): lv = left.valuation() rv = right.valuation() @@ -1771,6 +1787,7 @@ class LLS_composition(LazyLaurentSeries_binary): - ``f`` -- a :class:`LazyLaurentSeries_aux` - ``g`` -- a :class:`LazyLaurentSeries_aux` with positive valuation """ + def __init__(self, f, g): assert g._approximate_valuation > 0 self._fv = f._approximate_valuation @@ -1786,7 +1803,7 @@ def __init__(self, f, g): self._pos_powers = [None, g] val = self._fv * self._gv super().__init__(f, g, f._is_sparse, val) - + def get_coefficient(self, n): if n < 0: return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) @@ -1805,12 +1822,14 @@ def iterate_coefficients(self): n += 1 ##################################################################### -## Unary operations +# Unary operations + class LLS_scalar(LazyLaurentSeries_unary): """ Operator for multiplying with a scalar. """ + def __init__(self, series, scalar): self._scalar = scalar @@ -1825,10 +1844,12 @@ def iterate_coefficients(self): yield self._series[n] * self._scalar n += 1 + class LLS_neg(LazyLaurentSeries_unary): """ Operator for negative of the series. """ + def __init__(self, series): super().__init__(series, series._is_sparse, series._approximate_valuation) @@ -1841,10 +1862,12 @@ def iterate_coefficients(self): yield -self._series[n] n += 1 + class LLS_inv(LazyLaurentSeries_unary): """ Operator for multiplicative inverse of the series. """ + def __init__(self, series): v = series.valuation() super().__init__(series, series._is_sparse, -v) @@ -1875,10 +1898,12 @@ def iterate_coefficients(self): yield -c * self._ainv n += 1 + class LLS_apply_coeff(LazyLaurentSeries_unary): """ Return the series with ``function`` applied to each coefficient of this series. """ + def __init__(self, series, function, ring): self._function = function self._ring = ring diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 821f0978e81..9e168e29ef3 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -38,16 +38,16 @@ Power series can be defined recursively:: - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: L._sparse + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: L._sparse True - sage: s = L(None) - sage: s._aux._is_sparse + sage: s = L(None) + sage: s._aux._is_sparse True - sage: s._aux._approximate_valuation + sage: s._aux._approximate_valuation 0 - sage: s.define(1 + z*s^2) - sage: s + sage: s.define(1 + z*s^2) + sage: s 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... AUTHORS: @@ -89,6 +89,7 @@ LazyLaurentSeries_uninitialized ) + class LazyLaurentSeriesRing(UniqueRepresentation, Parent): """ Lazy Laurent series ring. From e09927280904ebadedefadf641817720b059706b Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 23 Jul 2021 01:08:16 +0530 Subject: [PATCH 74/98] Added the description of each function in the files. --- src/sage/rings/lazy_laurent_series.py | 156 +++++++++++++++++++-- src/sage/rings/lazy_laurent_series_ring.py | 4 +- 2 files changed, 151 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index e97963a0358..0933b274715 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -1348,6 +1348,9 @@ class LazyLaurentSeries_aux(): """ def __init__(self, sparse, approximate_valuation): + """ + Initialize the auxillary class for a LazyLaurentSeries. + """ self._is_sparse = sparse self._approximate_valuation = approximate_valuation @@ -1359,6 +1362,9 @@ class LazyLaurentSeries_inexact(LazyLaurentSeries_aux): """ def __init__(self, is_sparse, approximate_valuation): + """ + Initialize the auxillary class for a LazyLaurentSeries when it is not or it cannot be determined if it is eventually geometric. + """ super().__init__(is_sparse, approximate_valuation) if self._is_sparse: @@ -1369,6 +1375,9 @@ def __init__(self, is_sparse, approximate_valuation): self._iter = self.iterate_coefficients() def __getstate__(self): + """ + Remove the cache from the pickle information so that it can be pickled. + """ d = dict(self.__dict__) if not self._is_sparse: # We cannot pickle a generator object, so we remove it and @@ -1378,12 +1387,18 @@ def __getstate__(self): return d def __setstate__(self, d): + """ + Re-create the cache and the generator object when unpickling. + """ self.__dict__ = d if not self._is_sparse: self._iter = self.iterate_coefficients() self._cache = [] def __getitem__(self, n): + """ + Return the `n`-th coefficient of the series. + """ if n < self._approximate_valuation: return ZZ.zero() @@ -1406,6 +1421,9 @@ def __getitem__(self, n): return c def valuation(self): + """ + Return the valuation of the series. + """ if self._is_sparse: n = self._approximate_valuation cache = self._cache @@ -1547,9 +1565,15 @@ def __eq__(self, other): class LazyLaurentSeries_binary_commutative(LazyLaurentSeries_binary): def __hash__(self): + """ + Return the hash of ``self``. + """ return hash((type(self), frozenset([self._left, self._right]))) def __eq__(self, other): + """ + Test the equality between ``self`` and ``other``. + """ if not isinstance(other, type(self)): return False if self._left == other._left and self._right == other._right: @@ -1561,6 +1585,9 @@ def __eq__(self, other): class LazyLaurentSeries_eventually_geometric(LazyLaurentSeries_aux): def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): + """ + Initialize a series that is known to be eventually geometric. + """ if constant is None: constant = ZZ.zero() if degree is None: @@ -1581,17 +1608,29 @@ def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): super().__init__(is_sparse, valuation) def __getitem__(self, n): + """ + Get the ``n``-th coefficient of the series. + """ if n >= self._degree: return self._constant return self._laurent_polynomial[n] def valuation(self): + """ + Return the valuation of the series. + """ return self._approximate_valuation def __hash__(self): + """ + Return the hash of ``self``. + """ return hash((self._laurent_polynomial, self._degree, self._constant)) def __eq__(self, other): + """ + Test the equality between ``self`` and ``other``. + """ return (isinstance(other, type(self)) and self._degree == other._degree and self._laurent_polynomial == other._laurent_polynomial @@ -1600,28 +1639,49 @@ def __eq__(self, other): class LazyLaurentSeries_zero(LazyLaurentSeries_aux): def __init__(self, sparse): + """ + Initialise a lazy Laurent series which is known to be zero. + """ return super().__init__(sparse, 0) def __getitem__(self, n): + """ + Return the ``n``-th coefficient of the series. + """ return ZZ.zero() def valuation(self): + """ + Return the valuation of the series. + """ return infinity def __hash__(self): + """ + Return the hash of ``self``. + """ return 0 class LazyLaurentSeries_coefficient_function(LazyLaurentSeries_inexact): def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation): + """ + Initialize the coefficient function of a lazy Laurent series. + """ self._coefficient_function = coefficient_function self._ring = ring super().__init__(is_sparse, approximate_valuation) def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series. + """ return self._ring(self._coefficient_function(n)) def iterate_coefficients(self): + """ + Return a generator for the coefficients of the series. + """ n = self._offset ring = self._ring while True: @@ -1631,13 +1691,22 @@ def iterate_coefficients(self): class LazyLaurentSeries_uninitialized(LazyLaurentSeries_inexact): def __init__(self, is_sparse, approximate_valuation): + """ + Initialize an uninitialized lazy laurent series. + """ self._target = None super().__init__(is_sparse, approximate_valuation) def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series. + """ return self._target[n] def iterate_coefficients(self): + """ + Return a generator for the coefficients of the series. + """ n = self._approximate_valuation while True: yield self._target[n] @@ -1653,6 +1722,9 @@ class LLS_add(LazyLaurentSeries_binary): """ def __init__(self, left, right): + """ + Initalize. + """ if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -1660,9 +1732,15 @@ def __init__(self, left, right): super().__init__(left, right, left._is_sparse, a) def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series when ``left`` is added to ``right``. + """ return self._left[n] + self._right[n] def iterate_coefficients(self): + """ + Return a generator for the coefficients of the series when ``left`` is added to ``right``. + """ n = self._offset while True: yield self._left[n] + self._right[n] @@ -1675,6 +1753,9 @@ class LLS_sub(LazyLaurentSeries_binary): """ def __init__(self, left, right): + """ + Initialize. + """ if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -1683,18 +1764,14 @@ def __init__(self, left, right): def get_coefficient(self, n): """ - Return the `n`-th coefficient of the series ``s``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = (1 + z)*(1 - z) - sage: f.coefficient(2) - -1 + Return the ``n``-th coefficient of the series when ``right`` is subtracted from ``left``. """ return self._left[n] - self._right[n] def iterate_coefficients(self): + """ + Return the generator for the coefficients of the series when ``right`` is subtracted from ``left``. + """ n = self._offset while True: yield self._left[n] - self._right[n] @@ -1709,6 +1786,9 @@ class LLS_mul(LazyLaurentSeries_binary): """ def __init__(self, left, right): + """ + Initialize. + """ if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -1716,6 +1796,9 @@ def __init__(self, left, right): super().__init__(left, right, left._is_sparse, a) def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series when ``left`` is multiplied by ``right``. + """ c = ZZ.zero() for k in range(self._left._approximate_valuation, n - self._right._approximate_valuation + 1): @@ -1725,6 +1808,9 @@ def get_coefficient(self, n): return c def iterate_coefficients(self): + """ + Return the generator for the coefficients of the series when ``left`` is multiplied by ``right``. + """ n = self._offset while True: c = ZZ.zero() @@ -1743,6 +1829,9 @@ class LLS_div(LazyLaurentSeries_binary): """ def __init__(self, left, right): + """ + Initialize. + """ lv = left.valuation() rv = right.valuation() self._lv = lv @@ -1751,6 +1840,9 @@ def __init__(self, left, right): super().__init__(left, right, left._is_sparse, lv - rv) def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series when ``left`` is divided by ``right``. + """ lv = self._lv rv = self._rv if n == lv - rv: @@ -1761,6 +1853,9 @@ def get_coefficient(self, n): return c * self._ainv def iterate_coefficients(self): + """ + Return the generator for the coefficients of the series when ``left`` is divided by ``right``. + """ n = self._offset lv = self._lv rv = self._rv @@ -1789,6 +1884,9 @@ class LLS_composition(LazyLaurentSeries_binary): """ def __init__(self, f, g): + """ + Initialize. + """ assert g._approximate_valuation > 0 self._fv = f._approximate_valuation self._gv = g._approximate_valuation @@ -1805,6 +1903,9 @@ def __init__(self, f, g): super().__init__(f, g, f._is_sparse, val) def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series when ``f`` is composed by ``g``. + """ if n < 0: return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) # n > 0 @@ -1816,6 +1917,9 @@ 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)) def iterate_coefficients(self): + """ + Return the generator for the coefficients of the series when ``f`` is composed by ``g``. + """ n = self._approximate_valuation while True: yield self.get_coefficient(n) @@ -1831,14 +1935,23 @@ class LLS_scalar(LazyLaurentSeries_unary): """ def __init__(self, series, scalar): + """ + Initialize. + """ self._scalar = scalar super().__init__(series, series._is_sparse, series._approximate_valuation) def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the ``series`` when multiplied by the ``scalar``. + """ return self._series[n] * self._scalar def iterate_coefficients(self): + """ + Return the generator for the coefficients of the ``series`` when multiplied by the ``scalar``. + """ n = self._offset while True: yield self._series[n] * self._scalar @@ -1851,12 +1964,21 @@ class LLS_neg(LazyLaurentSeries_unary): """ def __init__(self, series): + """ + Initialize. + """ super().__init__(series, series._is_sparse, series._approximate_valuation) def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the ``series`` when negated. + """ return -self._series[n] def iterate_coefficients(self): + """ + Return the generator for the coefficients of the ``series`` when negated. + """ n = self._offset while True: yield -self._series[n] @@ -1869,6 +1991,9 @@ class LLS_inv(LazyLaurentSeries_unary): """ def __init__(self, series): + """ + Initialize. + """ v = series.valuation() super().__init__(series, series._is_sparse, -v) @@ -1876,6 +2001,9 @@ def __init__(self, series): self._zero = ZZ.zero() def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the multiplicative inverse of the ``series``. + """ v = self._approximate_valuation if n == v: return self._ainv @@ -1885,6 +2013,9 @@ def get_coefficient(self, n): return -c * self._ainv def iterate_coefficients(self): + """ + Return the generator for the coefficients of the multiplicative inverse of the ``series``. + """ n = self._offset while True: v = self._approximate_valuation @@ -1905,15 +2036,24 @@ class LLS_apply_coeff(LazyLaurentSeries_unary): """ def __init__(self, series, function, ring): + """ + Initialize. + """ self._function = function self._ring = ring super().__init__(series, series._is_sparse, series._approximate_valuation) def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series with ``function`` applied to each coefficient. + """ c = self._ring(self._function(self._series[n])) return c def iterate_coefficients(self): + """ + Return the generator for the coefficients of the series with ``function`` applied to each coefficient. + """ n = self._offset while True: c = self._ring(self._function(self._series[n])) diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 9e168e29ef3..11e85ef83c2 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -109,7 +109,7 @@ class LazyLaurentSeriesRing(UniqueRepresentation, Parent): def __init__(self, base_ring, names, sparse=False, category=None): """ - Initialize. + Initialize the ring. TESTS:: @@ -175,6 +175,8 @@ def gens(self): EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L.gens() + (z,) sage: 1/(1 - z) 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... """ From 1c213b9e78132d27aa6e8ccf89e2adcb3b6d831b Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sun, 25 Jul 2021 22:56:58 +0530 Subject: [PATCH 75/98] Created a new file coefficient_stream.py and sorted the imports. --- src/sage/rings/coefficient_stream.py | 616 +++++++++++++++++++++++++ src/sage/rings/lazy_laurent_series.py | 634 +------------------------- 2 files changed, 632 insertions(+), 618 deletions(-) create mode 100644 src/sage/rings/coefficient_stream.py diff --git a/src/sage/rings/coefficient_stream.py b/src/sage/rings/coefficient_stream.py new file mode 100644 index 00000000000..99c65ad1a95 --- /dev/null +++ b/src/sage/rings/coefficient_stream.py @@ -0,0 +1,616 @@ +from .integer_ring import ZZ +from .infinity import infinity + +class LazyLaurentSeries_aux(): + """ + Abstract base class for all auxillary LazyLaurentSeries. + """ + + def __init__(self, sparse, approximate_valuation): + """ + Initialize the auxillary class for a LazyLaurentSeries. + """ + self._is_sparse = sparse + self._approximate_valuation = approximate_valuation + + +class LazyLaurentSeries_inexact(LazyLaurentSeries_aux): + """ + LazyLaurentSeries aux class when it is not or we do not know if it is + eventually geometric. + """ + + def __init__(self, is_sparse, approximate_valuation): + """ + Initialize the auxillary class for a LazyLaurentSeries when it is not or it cannot be determined if it is eventually geometric. + """ + super().__init__(is_sparse, approximate_valuation) + + if self._is_sparse: + self._cache = dict() # cache of known coefficients + else: + self._cache = list() + self._offset = approximate_valuation + self._iter = self.iterate_coefficients() + + def __getstate__(self): + """ + Remove the cache from the pickle information so that it can be pickled. + """ + d = dict(self.__dict__) + if not self._is_sparse: + # We cannot pickle a generator object, so we remove it and + # the cache from the pickle information. + del d["_iter"] + del d["_cache"] + return d + + def __setstate__(self, d): + """ + Re-create the cache and the generator object when unpickling. + """ + self.__dict__ = d + if not self._is_sparse: + self._iter = self.iterate_coefficients() + self._cache = [] + + def __getitem__(self, n): + """ + Return the `n`-th coefficient of the series. + """ + if n < self._approximate_valuation: + return ZZ.zero() + + if self._is_sparse: + try: + c = self._cache[n] + except KeyError: + c = self.get_coefficient(n) + self._cache[n] = c + else: + i = n - self._offset + if i >= len(self._cache): + a = len(self._cache) + self._offset + # it is important to extend by generator: + # self._coefficient_function might recurse, and + # thereby extend the cache itself, too + self._cache.extend(next(self._iter) for _ in range(a, n+1)) + c = self._cache[i] + + return c + + def valuation(self): + """ + Return the valuation of the series. + """ + if self._is_sparse: + n = self._approximate_valuation + cache = self._cache + while True: + if n in cache: + if cache[n]: + self._approximate_valuation = n + return n + n += 1 + else: + if self[n] != 0: + self._approximate_valuation = n + return n + n += 1 + else: + n = self._approximate_valuation + cache = self._cache + while True: + if n - self._offset < len(cache): + if cache[n - self._offset]: + self._approximate_valuation = n + return n + n += 1 + else: + if self[n] != 0: + self._approximate_valuation = n + return n + n += 1 + + +class LazyLaurentSeries_unary(LazyLaurentSeries_inexact): + """ + Abstract base class for unary operators. + + INPUT: + + - ``series`` -- series upon which the operator operates + + """ + + def __init__(self, series, *args, **kwargs): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = -1/(1 - z) + sage: f + -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... + sage: loads(dumps(f)) == f + True + """ + self._series = series + super().__init__(*args, **kwargs) + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = ~(1 - z) + sage: {f: 1} + {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} + """ + return hash((type(self), self._series)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: g = 1/(1 - z) + 1/(1 + z) + sage: f == g + True + sage: f = ~(1 - z) + sage: g = ~(1 - z) + sage: f == g + True + """ + return isinstance(other, type(self)) and self._series == other._series + + +class LazyLaurentSeries_binary(LazyLaurentSeries_inexact): + + def __init__(self, left, right, *args, **kwargs): + """ + Initialize. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: loads(dumps(f)) == f + True + sage: f = 1/(1 - z) - 1/(1 + z) + sage: loads(dumps(f)) == f + True + """ + self._left = left + self._right = right + super().__init__(*args, **kwargs) + + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: {f: 1} + {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} + """ + return hash((type(self), self._left, self._right)) + + def __eq__(self, other): + """ + Test equality. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + 1/(1 + z) + sage: g = 1/(1 - z) + 1/(1 + z) + sage: f == g + True + """ + if not isinstance(other, type(self)): + return False + return self._left == other._left and self._right == other._right + + +class LazyLaurentSeries_binary_commutative(LazyLaurentSeries_binary): + + def __hash__(self): + """ + Return the hash of ``self``. + """ + return hash((type(self), frozenset([self._left, self._right]))) + + def __eq__(self, other): + """ + Test the equality between ``self`` and ``other``. + """ + if not isinstance(other, type(self)): + return False + if self._left == other._left and self._right == other._right: + return True + if self._left == other._right and self._right == other._left: + return True + return False + + +class LazyLaurentSeries_zero(LazyLaurentSeries_aux): + def __init__(self, sparse): + """ + Initialise a lazy Laurent series which is known to be zero. + """ + return super().__init__(sparse, 0) + + def __getitem__(self, n): + """ + Return the ``n``-th coefficient of the series. + """ + return ZZ.zero() + + def valuation(self): + """ + Return the valuation of the series. + """ + return infinity + + def __hash__(self): + """ + Return the hash of ``self``. + """ + return 0 + +##################################################################### +# Binary operations + + +class LLS_add(LazyLaurentSeries_binary): + """ + Operator for addition. + """ + + def __init__(self, left, right): + """ + Initalize. + """ + if left._is_sparse != right._is_sparse: + raise NotImplementedError + + a = min(left._approximate_valuation, right._approximate_valuation) + super().__init__(left, right, left._is_sparse, a) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series when ``left`` is added to ``right``. + """ + return self._left[n] + self._right[n] + + def iterate_coefficients(self): + """ + Return a generator for the coefficients of the series when ``left`` is added to ``right``. + """ + n = self._offset + while True: + yield self._left[n] + self._right[n] + n += 1 + + +class LLS_sub(LazyLaurentSeries_binary): + """ + Operator for subtraction. + """ + + def __init__(self, left, right): + """ + Initialize. + """ + if left._is_sparse != right._is_sparse: + raise NotImplementedError + + a = min(left._approximate_valuation, right._approximate_valuation) + super().__init__(left, right, left._is_sparse, a) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series when ``right`` is subtracted from ``left``. + """ + return self._left[n] - self._right[n] + + def iterate_coefficients(self): + """ + Return the generator for the coefficients of the series when ``right`` is subtracted from ``left``. + """ + n = self._offset + while True: + yield self._left[n] - self._right[n] + n += 1 + + +class LLS_mul(LazyLaurentSeries_binary): + """ + Operator for multiplication. + + We are assuming commutativity of the coefficient ring here. + """ + + def __init__(self, left, right): + """ + Initialize. + """ + if left._is_sparse != right._is_sparse: + raise NotImplementedError + + a = left._approximate_valuation + right._approximate_valuation + super().__init__(left, right, left._is_sparse, a) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series when ``left`` is multiplied by ``right``. + """ + c = ZZ.zero() + for k in range(self._left._approximate_valuation, + n - self._right._approximate_valuation + 1): + val = self._left[k] + if val: + c += val * self._right[n-k] + return c + + def iterate_coefficients(self): + """ + Return the generator for the coefficients of the series when ``left`` is multiplied by ``right``. + """ + n = self._offset + while True: + c = ZZ.zero() + for k in range(self._left._approximate_valuation, + n - self._right._approximate_valuation + 1): + val = self._left[k] + if val: + c += val * self._right[n-k] + yield c + n += 1 + + +class LLS_div(LazyLaurentSeries_binary): + """ + Return ``left`` divided by ``right``. + """ + + def __init__(self, left, right): + """ + Initialize. + """ + lv = left.valuation() + rv = right.valuation() + self._lv = lv + self._rv = rv + self._ainv = ~right[rv] + super().__init__(left, right, left._is_sparse, lv - rv) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series when ``left`` is divided by ``right``. + """ + lv = self._lv + rv = self._rv + if n == lv - rv: + return self._left[lv] / self._right[rv] + c = self._left[n + rv] + for k in range(lv - rv, n): + c -= self[k] * self._right[n + rv - k] + return c * self._ainv + + def iterate_coefficients(self): + """ + Return the generator for the coefficients of the series when ``left`` is divided by ``right``. + """ + n = self._offset + lv = self._lv + rv = self._rv + while True: + if n == lv - rv: + yield self._left[lv] / self._right[rv] + n += 1 + continue + c = self._left[n + rv] + for k in range(lv - rv, n): + c -= self[k] * self._right[n + rv - k] + yield c * self._ainv + n += 1 + + +class LLS_composition(LazyLaurentSeries_binary): + r""" + Return ``f`` composed by ``g``. + + This is the composition `(f \circ g)(z) = f(g(z))`. + + INPUT: + + - ``f`` -- a :class:`LazyLaurentSeries_aux` + - ``g`` -- a :class:`LazyLaurentSeries_aux` with positive valuation + """ + + def __init__(self, f, g): + """ + Initialize. + """ + assert g._approximate_valuation > 0 + self._fv = f._approximate_valuation + self._gv = g._approximate_valuation + if self._fv < 0: + ginv = LLS_inv(g) + # the constant part makes no contribution to the negative + # we need this for the case so self._neg_powers[0][n] => 0 + self._neg_powers = [LazyLaurentSeries_zero(f._is_sparse), ginv] + for i in range(1, -self._fv): + self._neg_powers.append(LLS_mul(self._neg_powers[-1], ginv)) + # Placeholder None to make this 1-based + self._pos_powers = [None, g] + val = self._fv * self._gv + super().__init__(f, g, f._is_sparse, val) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series when ``f`` is composed by ``g``. + """ + if n < 0: + return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) + # n > 0 + while len(self._pos_powers) <= n // self._gv: + self._pos_powers.append(LLS_mul(self._pos_powers[-1], self._right)) + ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, 0)) + if n == 0: + ret += self._left[0] + return ret + sum(self._left[i] * self._pos_powers[i][n] for i in range(1, n // self._gv+1)) + + def iterate_coefficients(self): + """ + Return the generator for the coefficients of the series when ``f`` is composed by ``g``. + """ + n = self._approximate_valuation + while True: + yield self.get_coefficient(n) + n += 1 + +##################################################################### +# Unary operations + + +class LLS_scalar(LazyLaurentSeries_unary): + """ + Operator for multiplying with a scalar. + """ + + def __init__(self, series, scalar): + """ + Initialize. + """ + self._scalar = scalar + + super().__init__(series, series._is_sparse, series._approximate_valuation) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the ``series`` when multiplied by the ``scalar``. + """ + return self._series[n] * self._scalar + + def iterate_coefficients(self): + """ + Return the generator for the coefficients of the ``series`` when multiplied by the ``scalar``. + """ + n = self._offset + while True: + yield self._series[n] * self._scalar + n += 1 + + +class LLS_neg(LazyLaurentSeries_unary): + """ + Operator for negative of the series. + """ + + def __init__(self, series): + """ + Initialize. + """ + super().__init__(series, series._is_sparse, series._approximate_valuation) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the ``series`` when negated. + """ + return -self._series[n] + + def iterate_coefficients(self): + """ + Return the generator for the coefficients of the ``series`` when negated. + """ + n = self._offset + while True: + yield -self._series[n] + n += 1 + + +class LLS_inv(LazyLaurentSeries_unary): + """ + Operator for multiplicative inverse of the series. + """ + + def __init__(self, series): + """ + Initialize. + """ + v = series.valuation() + super().__init__(series, series._is_sparse, -v) + + self._ainv = ~series[v] + self._zero = ZZ.zero() + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the multiplicative inverse of the ``series``. + """ + v = self._approximate_valuation + if n == v: + return self._ainv + c = self._zero + for k in range(v, n): + c += self[k] * self._series[n - v - k] + return -c * self._ainv + + def iterate_coefficients(self): + """ + Return the generator for the coefficients of the multiplicative inverse of the ``series``. + """ + n = self._offset + while True: + v = self._approximate_valuation + if n == v: + yield self._ainv + n += 1 + continue + c = self._zero + for k in range(v, n): + c += self[k] * self._series[n - v - k] + yield -c * self._ainv + n += 1 + + +class LLS_apply_coeff(LazyLaurentSeries_unary): + """ + Return the series with ``function`` applied to each coefficient of this series. + """ + + def __init__(self, series, function, ring): + """ + Initialize. + """ + self._function = function + self._ring = ring + super().__init__(series, series._is_sparse, series._approximate_valuation) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series with ``function`` applied to each coefficient. + """ + c = self._ring(self._function(self._series[n])) + return c + + def iterate_coefficients(self): + """ + Return the generator for the coefficients of the series with ``function`` applied to each coefficient. + """ + n = self._offset + while True: + c = self._ring(self._function(self._series[n])) + yield c + n += 1 \ No newline at end of file diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 0933b274715..3905d4c616b 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -79,7 +79,20 @@ from sage.structure.richcmp import op_EQ, op_NE from sage.arith.power import generic_power from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing -from sage.rings.integer import Integer +from sage.rings.coefficient_stream import ( + LLS_add, + LLS_mul, + LLS_sub, + LLS_div, + LLS_composition, + LLS_scalar, + LLS_neg, + LLS_inv, + LLS_apply_coeff, + LazyLaurentSeries_aux, + LazyLaurentSeries_inexact, + LazyLaurentSeries_zero +) class LazyLaurentSeries(ModuleElement): @@ -552,7 +565,7 @@ def _sub_(self, other): sage: X = A - B; X 0 sage: type(X._aux) - + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M @@ -1342,247 +1355,6 @@ def define(self, s): self._aux._target = s._aux -class LazyLaurentSeries_aux(): - """ - Abstract base class for all auxillary LazyLaurentSeries. - """ - - def __init__(self, sparse, approximate_valuation): - """ - Initialize the auxillary class for a LazyLaurentSeries. - """ - self._is_sparse = sparse - self._approximate_valuation = approximate_valuation - - -class LazyLaurentSeries_inexact(LazyLaurentSeries_aux): - """ - LazyLaurentSeries aux class when it is not or we do not know if it is - eventually geometric. - """ - - def __init__(self, is_sparse, approximate_valuation): - """ - Initialize the auxillary class for a LazyLaurentSeries when it is not or it cannot be determined if it is eventually geometric. - """ - super().__init__(is_sparse, approximate_valuation) - - if self._is_sparse: - self._cache = dict() # cache of known coefficients - else: - self._cache = list() - self._offset = approximate_valuation - self._iter = self.iterate_coefficients() - - def __getstate__(self): - """ - Remove the cache from the pickle information so that it can be pickled. - """ - d = dict(self.__dict__) - if not self._is_sparse: - # We cannot pickle a generator object, so we remove it and - # the cache from the pickle information. - del d["_iter"] - del d["_cache"] - return d - - def __setstate__(self, d): - """ - Re-create the cache and the generator object when unpickling. - """ - self.__dict__ = d - if not self._is_sparse: - self._iter = self.iterate_coefficients() - self._cache = [] - - def __getitem__(self, n): - """ - Return the `n`-th coefficient of the series. - """ - if n < self._approximate_valuation: - return ZZ.zero() - - if self._is_sparse: - try: - c = self._cache[n] - except KeyError: - c = self.get_coefficient(n) - self._cache[n] = c - else: - i = n - self._offset - if i >= len(self._cache): - a = len(self._cache) + self._offset - # it is important to extend by generator: - # self._coefficient_function might recurse, and - # thereby extend the cache itself, too - self._cache.extend(next(self._iter) for _ in range(a, n+1)) - c = self._cache[i] - - return c - - def valuation(self): - """ - Return the valuation of the series. - """ - if self._is_sparse: - n = self._approximate_valuation - cache = self._cache - while True: - if n in cache: - if cache[n]: - self._approximate_valuation = n - return n - n += 1 - else: - if self[n] != 0: - self._approximate_valuation = n - return n - n += 1 - else: - n = self._approximate_valuation - cache = self._cache - while True: - if n - self._offset < len(cache): - if cache[n - self._offset]: - self._approximate_valuation = n - return n - n += 1 - else: - if self[n] != 0: - self._approximate_valuation = n - return n - n += 1 - - -class LazyLaurentSeries_unary(LazyLaurentSeries_inexact): - """ - Abstract base class for unary operators. - - INPUT: - - - ``series`` -- series upon which the operator operates - - """ - - def __init__(self, series, *args, **kwargs): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = -1/(1 - z) - sage: f - -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... - sage: loads(dumps(f)) == f - True - """ - self._series = series - super().__init__(*args, **kwargs) - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 - z) - sage: {f: 1} - {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} - """ - return hash((type(self), self._series)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g - True - sage: f = ~(1 - z) - sage: g = ~(1 - z) - sage: f == g - True - """ - return isinstance(other, type(self)) and self._series == other._series - - -class LazyLaurentSeries_binary(LazyLaurentSeries_inexact): - - def __init__(self, left, right, *args, **kwargs): - """ - Initialize. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: loads(dumps(f)) == f - True - sage: f = 1/(1 - z) - 1/(1 + z) - sage: loads(dumps(f)) == f - True - """ - self._left = left - self._right = right - super().__init__(*args, **kwargs) - - def __hash__(self): - """ - Return the hash of ``self``. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: {f: 1} - {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} - """ - return hash((type(self), self._left, self._right)) - - def __eq__(self, other): - """ - Test equality. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g - True - """ - if not isinstance(other, type(self)): - return False - return self._left == other._left and self._right == other._right - - -class LazyLaurentSeries_binary_commutative(LazyLaurentSeries_binary): - - def __hash__(self): - """ - Return the hash of ``self``. - """ - return hash((type(self), frozenset([self._left, self._right]))) - - def __eq__(self, other): - """ - Test the equality between ``self`` and ``other``. - """ - if not isinstance(other, type(self)): - return False - if self._left == other._left and self._right == other._right: - return True - if self._left == other._right and self._right == other._left: - return True - return False - - class LazyLaurentSeries_eventually_geometric(LazyLaurentSeries_aux): def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): """ @@ -1637,32 +1409,6 @@ def __eq__(self, other): and self._constant == other._constant) -class LazyLaurentSeries_zero(LazyLaurentSeries_aux): - def __init__(self, sparse): - """ - Initialise a lazy Laurent series which is known to be zero. - """ - return super().__init__(sparse, 0) - - def __getitem__(self, n): - """ - Return the ``n``-th coefficient of the series. - """ - return ZZ.zero() - - def valuation(self): - """ - Return the valuation of the series. - """ - return infinity - - def __hash__(self): - """ - Return the hash of ``self``. - """ - return 0 - - class LazyLaurentSeries_coefficient_function(LazyLaurentSeries_inexact): def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation): """ @@ -1710,352 +1456,4 @@ def iterate_coefficients(self): n = self._approximate_valuation while True: yield self._target[n] - n += 1 - -##################################################################### -# Binary operations - - -class LLS_add(LazyLaurentSeries_binary): - """ - Operator for addition. - """ - - def __init__(self, left, right): - """ - Initalize. - """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError - - a = min(left._approximate_valuation, right._approximate_valuation) - super().__init__(left, right, left._is_sparse, a) - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of the series when ``left`` is added to ``right``. - """ - return self._left[n] + self._right[n] - - def iterate_coefficients(self): - """ - Return a generator for the coefficients of the series when ``left`` is added to ``right``. - """ - n = self._offset - while True: - yield self._left[n] + self._right[n] - n += 1 - - -class LLS_sub(LazyLaurentSeries_binary): - """ - Operator for subtraction. - """ - - def __init__(self, left, right): - """ - Initialize. - """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError - - a = min(left._approximate_valuation, right._approximate_valuation) - super().__init__(left, right, left._is_sparse, a) - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of the series when ``right`` is subtracted from ``left``. - """ - return self._left[n] - self._right[n] - - def iterate_coefficients(self): - """ - Return the generator for the coefficients of the series when ``right`` is subtracted from ``left``. - """ - n = self._offset - while True: - yield self._left[n] - self._right[n] - n += 1 - - -class LLS_mul(LazyLaurentSeries_binary): - """ - Operator for multiplication. - - We are assuming commutativity of the coefficient ring here. - """ - - def __init__(self, left, right): - """ - Initialize. - """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError - - a = left._approximate_valuation + right._approximate_valuation - super().__init__(left, right, left._is_sparse, a) - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of the series when ``left`` is multiplied by ``right``. - """ - c = ZZ.zero() - for k in range(self._left._approximate_valuation, - n - self._right._approximate_valuation + 1): - val = self._left[k] - if val: - c += val * self._right[n-k] - return c - - def iterate_coefficients(self): - """ - Return the generator for the coefficients of the series when ``left`` is multiplied by ``right``. - """ - n = self._offset - while True: - c = ZZ.zero() - for k in range(self._left._approximate_valuation, - n - self._right._approximate_valuation + 1): - val = self._left[k] - if val: - c += val * self._right[n-k] - yield c - n += 1 - - -class LLS_div(LazyLaurentSeries_binary): - """ - Return ``left`` divided by ``right``. - """ - - def __init__(self, left, right): - """ - Initialize. - """ - lv = left.valuation() - rv = right.valuation() - self._lv = lv - self._rv = rv - self._ainv = ~right[rv] - super().__init__(left, right, left._is_sparse, lv - rv) - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of the series when ``left`` is divided by ``right``. - """ - lv = self._lv - rv = self._rv - if n == lv - rv: - return self._left[lv] / self._right[rv] - c = self._left[n + rv] - for k in range(lv - rv, n): - c -= self[k] * self._right[n + rv - k] - return c * self._ainv - - def iterate_coefficients(self): - """ - Return the generator for the coefficients of the series when ``left`` is divided by ``right``. - """ - n = self._offset - lv = self._lv - rv = self._rv - while True: - if n == lv - rv: - yield self._left[lv] / self._right[rv] - n += 1 - continue - c = self._left[n + rv] - for k in range(lv - rv, n): - c -= self[k] * self._right[n + rv - k] - yield c * self._ainv - n += 1 - - -class LLS_composition(LazyLaurentSeries_binary): - r""" - Return ``f`` composed by ``g``. - - This is the composition `(f \circ g)(z) = f(g(z))`. - - INPUT: - - - ``f`` -- a :class:`LazyLaurentSeries_aux` - - ``g`` -- a :class:`LazyLaurentSeries_aux` with positive valuation - """ - - def __init__(self, f, g): - """ - Initialize. - """ - assert g._approximate_valuation > 0 - self._fv = f._approximate_valuation - self._gv = g._approximate_valuation - if self._fv < 0: - ginv = LLS_inv(g) - # the constant part makes no contribution to the negative - # we need this for the case so self._neg_powers[0][n] => 0 - self._neg_powers = [LazyLaurentSeries_zero(f._is_sparse), ginv] - for i in range(1, -self._fv): - self._neg_powers.append(LLS_mul(self._neg_powers[-1], ginv)) - # Placeholder None to make this 1-based - self._pos_powers = [None, g] - val = self._fv * self._gv - super().__init__(f, g, f._is_sparse, val) - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of the series when ``f`` is composed by ``g``. - """ - if n < 0: - return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) - # n > 0 - while len(self._pos_powers) <= n // self._gv: - self._pos_powers.append(LLS_mul(self._pos_powers[-1], self._right)) - ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, 0)) - if n == 0: - ret += self._left[0] - return ret + sum(self._left[i] * self._pos_powers[i][n] for i in range(1, n // self._gv+1)) - - def iterate_coefficients(self): - """ - Return the generator for the coefficients of the series when ``f`` is composed by ``g``. - """ - n = self._approximate_valuation - while True: - yield self.get_coefficient(n) - n += 1 - -##################################################################### -# Unary operations - - -class LLS_scalar(LazyLaurentSeries_unary): - """ - Operator for multiplying with a scalar. - """ - - def __init__(self, series, scalar): - """ - Initialize. - """ - self._scalar = scalar - - super().__init__(series, series._is_sparse, series._approximate_valuation) - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of the ``series`` when multiplied by the ``scalar``. - """ - return self._series[n] * self._scalar - - def iterate_coefficients(self): - """ - Return the generator for the coefficients of the ``series`` when multiplied by the ``scalar``. - """ - n = self._offset - while True: - yield self._series[n] * self._scalar - n += 1 - - -class LLS_neg(LazyLaurentSeries_unary): - """ - Operator for negative of the series. - """ - - def __init__(self, series): - """ - Initialize. - """ - super().__init__(series, series._is_sparse, series._approximate_valuation) - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of the ``series`` when negated. - """ - return -self._series[n] - - def iterate_coefficients(self): - """ - Return the generator for the coefficients of the ``series`` when negated. - """ - n = self._offset - while True: - yield -self._series[n] - n += 1 - - -class LLS_inv(LazyLaurentSeries_unary): - """ - Operator for multiplicative inverse of the series. - """ - - def __init__(self, series): - """ - Initialize. - """ - v = series.valuation() - super().__init__(series, series._is_sparse, -v) - - self._ainv = ~series[v] - self._zero = ZZ.zero() - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of the multiplicative inverse of the ``series``. - """ - v = self._approximate_valuation - if n == v: - return self._ainv - c = self._zero - for k in range(v, n): - c += self[k] * self._series[n - v - k] - return -c * self._ainv - - def iterate_coefficients(self): - """ - Return the generator for the coefficients of the multiplicative inverse of the ``series``. - """ - n = self._offset - while True: - v = self._approximate_valuation - if n == v: - yield self._ainv - n += 1 - continue - c = self._zero - for k in range(v, n): - c += self[k] * self._series[n - v - k] - yield -c * self._ainv - n += 1 - - -class LLS_apply_coeff(LazyLaurentSeries_unary): - """ - Return the series with ``function`` applied to each coefficient of this series. - """ - - def __init__(self, series, function, ring): - """ - Initialize. - """ - self._function = function - self._ring = ring - super().__init__(series, series._is_sparse, series._approximate_valuation) - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of the series with ``function`` applied to each coefficient. - """ - c = self._ring(self._function(self._series[n])) - return c - - def iterate_coefficients(self): - """ - Return the generator for the coefficients of the series with ``function`` applied to each coefficient. - """ - n = self._offset - while True: - c = self._ring(self._function(self._series[n])) - yield c - n += 1 + n += 1 \ No newline at end of file From c70280100c8446202e39372f6285c2b2aa8ef7f3 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sun, 25 Jul 2021 23:06:49 +0530 Subject: [PATCH 76/98] Chanded aux to coeff_stream. --- src/sage/rings/coefficient_stream.py | 34 ++-- src/sage/rings/lazy_laurent_series.py | 212 ++++++++++----------- src/sage/rings/lazy_laurent_series_ring.py | 18 +- 3 files changed, 132 insertions(+), 132 deletions(-) diff --git a/src/sage/rings/coefficient_stream.py b/src/sage/rings/coefficient_stream.py index 99c65ad1a95..d93e4641b31 100644 --- a/src/sage/rings/coefficient_stream.py +++ b/src/sage/rings/coefficient_stream.py @@ -1,7 +1,7 @@ from .integer_ring import ZZ from .infinity import infinity -class LazyLaurentSeries_aux(): +class CoefficientStream(): """ Abstract base class for all auxillary LazyLaurentSeries. """ @@ -14,7 +14,7 @@ def __init__(self, sparse, approximate_valuation): self._approximate_valuation = approximate_valuation -class LazyLaurentSeries_inexact(LazyLaurentSeries_aux): +class LazyLaurentSeries_inexact(CoefficientStream): """ LazyLaurentSeries aux class when it is not or we do not know if it is eventually geometric. @@ -242,7 +242,7 @@ def __eq__(self, other): return False -class LazyLaurentSeries_zero(LazyLaurentSeries_aux): +class LazyLaurentSeries_zero(CoefficientStream): def __init__(self, sparse): """ Initialise a lazy Laurent series which is known to be zero. @@ -271,7 +271,7 @@ def __hash__(self): # Binary operations -class LLS_add(LazyLaurentSeries_binary): +class CoefficientStream_add(LazyLaurentSeries_binary): """ Operator for addition. """ @@ -302,7 +302,7 @@ def iterate_coefficients(self): n += 1 -class LLS_sub(LazyLaurentSeries_binary): +class CoefficientStream_sub(LazyLaurentSeries_binary): """ Operator for subtraction. """ @@ -333,7 +333,7 @@ def iterate_coefficients(self): n += 1 -class LLS_mul(LazyLaurentSeries_binary): +class CoefficientStream_mul(LazyLaurentSeries_binary): """ Operator for multiplication. @@ -378,7 +378,7 @@ def iterate_coefficients(self): n += 1 -class LLS_div(LazyLaurentSeries_binary): +class CoefficientStream_div(LazyLaurentSeries_binary): """ Return ``left`` divided by ``right``. """ @@ -426,7 +426,7 @@ def iterate_coefficients(self): n += 1 -class LLS_composition(LazyLaurentSeries_binary): +class CoefficientStream_composition(LazyLaurentSeries_binary): r""" Return ``f`` composed by ``g``. @@ -434,8 +434,8 @@ class LLS_composition(LazyLaurentSeries_binary): INPUT: - - ``f`` -- a :class:`LazyLaurentSeries_aux` - - ``g`` -- a :class:`LazyLaurentSeries_aux` with positive valuation + - ``f`` -- a :class:`CoefficientStream` + - ``g`` -- a :class:`CoefficientStream` with positive valuation """ def __init__(self, f, g): @@ -446,12 +446,12 @@ def __init__(self, f, g): self._fv = f._approximate_valuation self._gv = g._approximate_valuation if self._fv < 0: - ginv = LLS_inv(g) + ginv = CoefficientStream_inv(g) # the constant part makes no contribution to the negative # we need this for the case so self._neg_powers[0][n] => 0 self._neg_powers = [LazyLaurentSeries_zero(f._is_sparse), ginv] for i in range(1, -self._fv): - self._neg_powers.append(LLS_mul(self._neg_powers[-1], ginv)) + self._neg_powers.append(CoefficientStream_mul(self._neg_powers[-1], ginv)) # Placeholder None to make this 1-based self._pos_powers = [None, g] val = self._fv * self._gv @@ -465,7 +465,7 @@ def get_coefficient(self, n): return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) # n > 0 while len(self._pos_powers) <= n // self._gv: - self._pos_powers.append(LLS_mul(self._pos_powers[-1], self._right)) + self._pos_powers.append(CoefficientStream_mul(self._pos_powers[-1], self._right)) ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, 0)) if n == 0: ret += self._left[0] @@ -484,7 +484,7 @@ def iterate_coefficients(self): # Unary operations -class LLS_scalar(LazyLaurentSeries_unary): +class CoefficientStream_scalar(LazyLaurentSeries_unary): """ Operator for multiplying with a scalar. """ @@ -513,7 +513,7 @@ def iterate_coefficients(self): n += 1 -class LLS_neg(LazyLaurentSeries_unary): +class CoefficientStream_neg(LazyLaurentSeries_unary): """ Operator for negative of the series. """ @@ -540,7 +540,7 @@ def iterate_coefficients(self): n += 1 -class LLS_inv(LazyLaurentSeries_unary): +class CoefficientStream_inv(LazyLaurentSeries_unary): """ Operator for multiplicative inverse of the series. """ @@ -585,7 +585,7 @@ def iterate_coefficients(self): n += 1 -class LLS_apply_coeff(LazyLaurentSeries_unary): +class CoefficientStream_apply_coeff(LazyLaurentSeries_unary): """ Return the series with ``function`` applied to each coefficient of this series. """ diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 3905d4c616b..a27856645c8 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -26,9 +26,9 @@ Coefficients are computed and cached only when necessary:: - sage: f._aux._cache[100] + sage: f._coeff_stream._cache[100] 573147844013817084101 - sage: f._aux._cache[101] + sage: f._coeff_stream._cache[101] Traceback (most recent call last): ... IndexError: list index out of range @@ -80,16 +80,16 @@ from sage.arith.power import generic_power from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.rings.coefficient_stream import ( - LLS_add, - LLS_mul, - LLS_sub, - LLS_div, - LLS_composition, - LLS_scalar, - LLS_neg, - LLS_inv, - LLS_apply_coeff, - LazyLaurentSeries_aux, + CoefficientStream_add, + CoefficientStream_mul, + CoefficientStream_sub, + CoefficientStream_div, + CoefficientStream_composition, + CoefficientStream_scalar, + CoefficientStream_neg, + CoefficientStream_inv, + CoefficientStream_apply_coeff, + CoefficientStream, LazyLaurentSeries_inexact, LazyLaurentSeries_zero ) @@ -146,7 +146,7 @@ class LazyLaurentSeries(ModuleElement): True """ - def __init__(self, parent, aux): + def __init__(self, parent, coeff_stream): """ Initialize the series. @@ -157,7 +157,7 @@ def __init__(self, parent, aux): sage: TestSuite(z).run() """ ModuleElement.__init__(self, parent) - self._aux = aux + self._coeff_stream = coeff_stream def __getitem__(self, n): """ @@ -188,10 +188,10 @@ def __getitem__(self, n): if isinstance(n, slice): if n.stop is None: raise NotImplementedError("cannot list an infinite set") - start = n.start if n.start is not None else self._aux.valuation() + start = n.start if n.start is not None else self._coeff_stream.valuation() step = n.step if n.step is not None else 1 - return [R(self._aux[k]) for k in range(start, n.stop, step)] - return R(self._aux[n]) + return [R(self._coeff_stream[k]) for k in range(start, n.stop, step)] + return R(self._coeff_stream[n]) def __call__(self, g): r""" @@ -360,37 +360,37 @@ def __call__(self, g): P = g.parent() # g = 0 case - if (not isinstance(g, LazyLaurentSeries) and not g) or (isinstance(g, LazyLaurentSeries) and isinstance(g._aux, LazyLaurentSeries_zero)): - if self._aux._approximate_valuation >= 0: + if (not isinstance(g, LazyLaurentSeries) and not g) or (isinstance(g, LazyLaurentSeries) and isinstance(g._coeff_stream, LazyLaurentSeries_zero)): + if self._coeff_stream._approximate_valuation >= 0: return P(self[0]) # Perhaps we just don't yet know if the valuation is non-negative - if any(self._aux[i] for i in range(self._aux._approximate_valuation, 0)): + if any(self._coeff_stream[i] for i in range(self._coeff_stream._approximate_valuation, 0)): raise ZeroDivisionError("the valuation of the series must be nonnegative") - self._aux._approximate_valuation = 0 + self._coeff_stream._approximate_valuation = 0 return P(self[0]) # f has finite length - if isinstance(self._aux, LazyLaurentSeries_zero): # constant 0 + if isinstance(self._coeff_stream, LazyLaurentSeries_zero): # constant 0 return self - if isinstance(self._aux, LazyLaurentSeries_eventually_geometric) and not self._aux._constant: + if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric) and not self._coeff_stream._constant: # constant polynomial - if self._aux._laurent_polynomial.is_constant(): + if self._coeff_stream._laurent_polynomial.is_constant(): return self if not isinstance(g, LazyLaurentSeries): - return self._aux._laurent_polynomial(g) + return self._coeff_stream._laurent_polynomial(g) # g also has finite length, compose the polynomials - if isinstance(g._aux, LazyLaurentSeries_eventually_geometric) and not g._aux._constant: + if isinstance(g._coeff_stream, LazyLaurentSeries_eventually_geometric) and not g._coeff_stream._constant: R = P._laurent_poly_ring try: - ret = self._aux._laurent_polynomial(g._aux._laurent_polynomial) + ret = self._coeff_stream._laurent_polynomial(g._coeff_stream._laurent_polynomial) if ret.parent() is R: - return P.element_class(P, LazyLaurentSeries_eventually_geometric(ret, self._aux._is_sparse, 0)) + return P.element_class(P, LazyLaurentSeries_eventually_geometric(ret, self._coeff_stream._is_sparse, 0)) except TypeError: # the result is not a Laurent polynomial pass # Return the sum since g is not known to be finite or we do not get a Laurent polynomial # TODO: Optimize when f has positive valuation - poly = self._aux._laurent_polynomial + poly = self._coeff_stream._laurent_polynomial ret = P.zero() gp = P.one() # We build this iteratively so each power can benefit from the caching @@ -415,12 +415,12 @@ def __call__(self, g): except (TypeError, ValueError): raise NotImplementedError("can only compose with a lazy Laurent series") # Perhaps we just don't yet know if the valuation is positive - if g._aux._approximate_valuation <= 0: - if any(g._aux[i] for i in range(self._aux._approximate_valuation)): + if g._coeff_stream._approximate_valuation <= 0: + if any(g._coeff_stream[i] for i in range(self._coeff_stream._approximate_valuation)): raise ValueError("can only compose with a positive valuation series") - g._aux._approximate_valuation = 1 + g._coeff_stream._approximate_valuation = 1 - return P.element_class(P, LLS_composition(self._aux, g._aux)) + return P.element_class(P, CoefficientStream_composition(self._coeff_stream, g._coeff_stream)) def _mul_(self, other): """ @@ -457,8 +457,8 @@ def _mul_(self, other): True """ P = self.parent() - left = self._aux - right = other._aux + left = self._coeff_stream + right = other._coeff_stream if isinstance(left, LazyLaurentSeries_zero) or isinstance(right, LazyLaurentSeries_zero): return P.zero() @@ -474,7 +474,7 @@ def _mul_(self, other): return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c)) elif isinstance(right, LazyLaurentSeries_eventually_geometric) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 return self - return P.element_class(P, LLS_mul(self._aux, other._aux)) + return P.element_class(P, CoefficientStream_mul(self._coeff_stream, other._coeff_stream)) def _add_(self, other): """ @@ -516,8 +516,8 @@ def _add_(self, other): 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... """ P = self.parent() - left = self._aux - right = other._aux + left = self._coeff_stream + right = other._coeff_stream if (isinstance(left, LazyLaurentSeries_eventually_geometric) and isinstance(right, LazyLaurentSeries_eventually_geometric)): R = P._laurent_poly_ring @@ -531,7 +531,7 @@ def _add_(self, other): if not p and not c: return P.zero() return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, d)) - return P.element_class(P, LLS_add(self._aux, other._aux)) + return P.element_class(P, CoefficientStream_add(self._coeff_stream, other._coeff_stream)) def _sub_(self, other): """ @@ -564,7 +564,7 @@ def _sub_(self, other): sage: B = L([1,0,0,2,2], constant=2) sage: X = A - B; X 0 - sage: type(X._aux) + sage: type(X._coeff_stream) sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) @@ -576,8 +576,8 @@ def _sub_(self, other): -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... """ P = self.parent() - left = self._aux - right = other._aux + left = self._coeff_stream + right = other._coeff_stream if (isinstance(left, LazyLaurentSeries_eventually_geometric) and isinstance(right, LazyLaurentSeries_eventually_geometric)): R = P._laurent_poly_ring c = left._constant - right._constant @@ -592,7 +592,7 @@ def _sub_(self, other): return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, d)) if left == right: return P.zero() - return P.element_class(P, LLS_sub(self._aux, other._aux)) + return P.element_class(P, CoefficientStream_sub(self._coeff_stream, other._coeff_stream)) def _div_(self, other): """ @@ -622,14 +622,14 @@ def _div_(self, other): sage: P = M / N; P z + z^2 + z^3 + z^4 + z^5 + z^6 + ... """ - if isinstance(other._aux, LazyLaurentSeries_zero): + if isinstance(other._coeff_stream, LazyLaurentSeries_zero): raise ZeroDivisionError("cannot divide by 0") P = self.parent() - left = self._aux + left = self._coeff_stream if isinstance(left, LazyLaurentSeries_zero): return P.zero() - right = other._aux + right = other._coeff_stream if (isinstance(left, LazyLaurentSeries_eventually_geometric) and isinstance(right, LazyLaurentSeries_eventually_geometric)): if not left._constant and not right._constant: @@ -641,7 +641,7 @@ def _div_(self, other): # We cannot divide the polynomials, so the result must be a series pass - return P.element_class(P, LLS_mul(left, LLS_inv(right))) + return P.element_class(P, CoefficientStream_mul(left, CoefficientStream_inv(right))) def _rmul_(self, scalar): """ @@ -686,12 +686,12 @@ def _rmul_(self, scalar): if scalar == 1: return self - if isinstance(self._aux, LazyLaurentSeries_eventually_geometric): - c = scalar * self._aux._constant - p = scalar * self._aux._laurent_polynomial - return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, self._aux._degree)) + if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric): + c = scalar * self._coeff_stream._constant + p = scalar * self._coeff_stream._laurent_polynomial + return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, self._coeff_stream._degree)) - return P.element_class(P, LLS_scalar(self._aux, scalar)) + return P.element_class(P, CoefficientStream_scalar(self._coeff_stream, scalar)) def _neg_(self): """ @@ -717,15 +717,15 @@ def _neg_(self): -3*z - z^2 + 4*z^3 """ P = self.parent() - if isinstance(self._aux, LazyLaurentSeries_eventually_geometric): - p = -self._aux._laurent_polynomial - c = -self._aux._constant - d = self._aux._degree + if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric): + p = -self._coeff_stream._laurent_polynomial + c = -self._coeff_stream._constant + d = self._coeff_stream._degree return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, d)) # -(-f) = f - if isinstance(self._aux, LLS_neg): - return P.element_class(P, self._aux._series) - return P.element_class(P, LLS_neg(self._aux)) + if isinstance(self._coeff_stream, CoefficientStream_neg): + return P.element_class(P, self._coeff_stream._series) + return P.element_class(P, CoefficientStream_neg(self._coeff_stream)) def __invert__(self): """ @@ -750,13 +750,13 @@ def __invert__(self): 1 - z """ P = self.parent() - if isinstance(self._aux, LazyLaurentSeries_eventually_geometric) and self._aux._laurent_polynomial == P.gen(): - ret = 1 / self._aux._laurent_polynomial - return P.element_class(P, LazyLaurentSeries_eventually_geometric(ret, P._sparse, self._aux._constant)) + if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric) and self._coeff_stream._laurent_polynomial == P.gen(): + ret = 1 / self._coeff_stream._laurent_polynomial + return P.element_class(P, LazyLaurentSeries_eventually_geometric(ret, P._sparse, self._coeff_stream._constant)) # (f^-1)^-1 = f - if isinstance(self._aux, LLS_inv): - return P.element_class(P, self._aux._series) - return P.element_class(P, LLS_inv(self._aux)) + if isinstance(self._coeff_stream, CoefficientStream_inv): + return P.element_class(P, self._coeff_stream._series) + return P.element_class(P, CoefficientStream_inv(self._coeff_stream)) def coefficient(self, n): """ @@ -783,29 +783,29 @@ def coefficient(self, n): sage: e.define(1 + z*e^2) sage: e 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - sage: e._aux._cache + sage: e._coeff_stream._cache [1, 1, 2, 5, 14, 42, 132] sage: e.coefficient(10) 16796 - sage: e._aux._cache + sage: e._coeff_stream._cache [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796] sage: M = L(lambda n: n^2); M z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... - sage: M._aux._cache + sage: M._coeff_stream._cache [0, 1, 4, 9, 16, 25, 36] sage: M.coefficient(9) 81 - sage: M._aux._cache + sage: M._coeff_stream._cache [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] sage: L = LazyLaurentSeriesRing(ZZ, 'z', sparse=True) sage: M = L(lambda n: n^2); M z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... - sage: M._aux._cache + sage: M._coeff_stream._cache {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36} sage: M.coefficient(10) 100 - sage: M._aux._cache + sage: M._coeff_stream._cache {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 10: 100} """ return self.__getitem__(n) @@ -841,13 +841,13 @@ def map_coefficients(self, func, ring=None): """ P = self.parent() R = P.base_ring() - if isinstance(self._aux, LazyLaurentSeries_eventually_geometric): + if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric): p = p.map_coefficients(func) c = func(c) if not p and not c: return P.zero() - return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, self._aux._is_sparse, c, d)) - return P.element_class(P, LLS_apply_coeff(self._aux, func, R)) + return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, self._coeff_stream._is_sparse, c, d)) + return P.element_class(P, CoefficientStream_apply_coeff(self._coeff_stream, func, R)) def change_ring(self, ring): """ @@ -885,7 +885,7 @@ def change_ring(self, ring): """ from .lazy_laurent_series_ring import LazyLaurentSeriesRing Q = LazyLaurentSeriesRing(ring, names=self.parent().variable_names()) - return Q.element_class(Q, self._aux) + return Q.element_class(Q, self._coeff_stream) def truncate(self, d): """ @@ -920,7 +920,7 @@ def truncate(self, d): P = self.parent() R = P._laurent_poly_ring z = R.gen() - p = R.sum(self[i] * z**i for i in range(self._aux._approximate_valuation, d)) + p = R.sum(self[i] * z**i for i in range(self._coeff_stream._approximate_valuation, d)) return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, ZZ.zero(), d)) def __pow__(self, n): @@ -1071,11 +1071,11 @@ def polynomial(self, degree=None, name=None): ValueError: not a polynomial """ if degree is None: - if isinstance(self._aux, LazyLaurentSeries_zero): + if isinstance(self._coeff_stream, LazyLaurentSeries_zero): from sage.rings.all import PolynomialRing return PolynomialRing(S.base_ring(), name=name).zero() - elif isinstance(self._aux, LazyLaurentSeries_eventually_geometric) and not self._aux._constant: - m = self._aux._degree + elif isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric) and not self._coeff_stream._constant: + m = self._coeff_stream._degree else: raise ValueError("not a polynomial") else: @@ -1120,7 +1120,7 @@ def valuation(self): sage: M.valuation() 1 """ - return self._aux.valuation() + return self._coeff_stream.valuation() def _repr_(self): """ @@ -1132,26 +1132,26 @@ def _repr_(self): sage: -1/(1 + 2*z) -1 + 2*z - 4*z^2 + 8*z^3 - 16*z^4 + 32*z^5 - 64*z^6 + ... """ - if isinstance(self._aux, LazyLaurentSeries_zero): + if isinstance(self._coeff_stream, LazyLaurentSeries_zero): return '0' - if isinstance(self._aux, LazyLaurentSeries_uninitialized) and self._aux._target is None: + if isinstance(self._coeff_stream, LazyLaurentSeries_uninitialized) and self._coeff_stream._target is None: return 'Uninitialized LazyLaurentSeries' atomic_repr = self.base_ring()._repr_option('element_is_atomic') X = self.parent().variable_name() - v = self._aux._approximate_valuation + v = self._coeff_stream._approximate_valuation - if not isinstance(self._aux, LazyLaurentSeries_eventually_geometric): + if not isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric): m = v + 7 # long enough - elif not self._aux._constant: + elif not self._coeff_stream._constant: # Just a polynonial, so let that print itself - return repr(self._aux._laurent_polynomial) + return repr(self._coeff_stream._laurent_polynomial) else: - m = self._aux._degree + 3 + m = self._coeff_stream._degree + 3 # Use the polynomial printing R = self.parent()._laurent_poly_ring - ret = repr(R([self._aux[i] for i in range(v, m)]).shift(v)) + ret = repr(R([self._coeff_stream[i] for i in range(v, m)]).shift(v)) # TODO: Better handling when ret == 0 but we have not checked up to the constant term return ret + ' + ...' @@ -1179,26 +1179,26 @@ def _richcmp_(self, other, op): False """ if op is op_EQ: - if isinstance(self._aux, LazyLaurentSeries_zero): # self == 0 - return isinstance(other._aux, LazyLaurentSeries_zero) - if isinstance(other._aux, LazyLaurentSeries_zero): # self != 0 but other == 0 + if isinstance(self._coeff_stream, LazyLaurentSeries_zero): # self == 0 + return isinstance(other._coeff_stream, LazyLaurentSeries_zero) + if isinstance(other._coeff_stream, LazyLaurentSeries_zero): # self != 0 but other == 0 return False - if (not isinstance(self._aux, LazyLaurentSeries_eventually_geometric) - or not isinstance(other._aux, LazyLaurentSeries_eventually_geometric)): + if (not isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric) + or not isinstance(other._coeff_stream, LazyLaurentSeries_eventually_geometric)): # One of the lazy laurent series is not known to eventually be constant # Implement the checking of the caches here. - n = min(self._aux._approximate_valuation, other._aux._approximate_valuation) - m = max(self._aux._approximate_valuation, other._aux._approximate_valuation) + n = min(self._coeff_stream._approximate_valuation, other._coeff_stream._approximate_valuation) + m = max(self._coeff_stream._approximate_valuation, other._coeff_stream._approximate_valuation) for i in range(n, m): if self[i] != other[i]: return False - if self._aux == other._aux: + if self._coeff_stream == other._coeff_stream: return True raise ValueError("undecidable as lazy Laurent series") # Both are LazyLaurentSeries_eventually_geometric, which implements a full check - return self._aux == other._aux + return self._coeff_stream == other._coeff_stream if op is op_NE: return not (self == other) @@ -1217,7 +1217,7 @@ def __hash__(self): sage: {g: 1} {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} """ - return hash(self._aux) + return hash(self._coeff_stream) def __bool__(self): """ @@ -1236,16 +1236,16 @@ def __bool__(self): sage: M.is_zero() False """ - if isinstance(self._aux, LazyLaurentSeries_zero): + if isinstance(self._coeff_stream, LazyLaurentSeries_zero): return False - if isinstance(self._aux, LazyLaurentSeries_eventually_geometric): + if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric): # This should always end up being True, but let's be careful about it for now... - return self._aux._laurent_polynomial or self._aux._constant + return self._coeff_stream._laurent_polynomial or self._coeff_stream._constant - for a in self._aux._cache: + for a in self._coeff_stream._cache: if a: return True - if self[self._aux._approximate_valuation]: + if self[self._coeff_stream._approximate_valuation]: return True raise ValueError("undecidable as lazy Laurent series") @@ -1350,12 +1350,12 @@ def define(self, s): ... ValueError: series already defined """ - if not isinstance(self._aux, LazyLaurentSeries_uninitialized) or self._aux._target is not None: + if not isinstance(self._coeff_stream, LazyLaurentSeries_uninitialized) or self._coeff_stream._target is not None: raise ValueError("series already defined") - self._aux._target = s._aux + self._coeff_stream._target = s._coeff_stream -class LazyLaurentSeries_eventually_geometric(LazyLaurentSeries_aux): +class LazyLaurentSeries_eventually_geometric(CoefficientStream): def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): """ Initialize a series that is known to be eventually geometric. diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 11e85ef83c2..09831ee7eb9 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -42,9 +42,9 @@ sage: L._sparse True sage: s = L(None) - sage: s._aux._is_sparse + sage: s._coeff_stream._is_sparse True - sage: s._aux._approximate_valuation + sage: s._coeff_stream._approximate_valuation 0 sage: s.define(1 + z*s^2) sage: s @@ -150,8 +150,8 @@ def gen(self, n=0): if n != 0: raise IndexError("there is only one generator") R = self._laurent_poly_ring - aux = LazyLaurentSeries_eventually_geometric(R.gen(n), self._sparse, ZZ.zero(), 2) - return self.element_class(self, aux) + coeff_stream = LazyLaurentSeries_eventually_geometric(R.gen(n), self._sparse, ZZ.zero(), 2) + return self.element_class(self, coeff_stream) def ngens(self): """ @@ -283,17 +283,17 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No constant = BR(constant) if x in R: if not x and not constant: - aux = LazyLaurentSeries_zero(self._sparse) + coeff_stream = LazyLaurentSeries_zero(self._sparse) else: if x and valuation: x = x.shift(valuation - x.valuation()) if degree is None and not x: degree = valuation - aux = LazyLaurentSeries_eventually_geometric(R(x), self._sparse, constant, degree) - return self.element_class(self, aux) + coeff_stream = LazyLaurentSeries_eventually_geometric(R(x), self._sparse, constant, degree) + return self.element_class(self, coeff_stream) if isinstance(x, LazyLaurentSeries): - if x._aux._is_sparse is self._sparse: - return self.element_class(self, x._aux) + if x._coeff_stream._is_sparse is self._sparse: + return self.element_class(self, x._coeff_stream) # TODO: Implement a way to make a self._sparse copy raise NotImplementedError("cannot convert between sparse and dense") if callable(x): From 18c443fa2c1c503090bef5b5b40982c0868c7ec7 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sun, 25 Jul 2021 23:17:23 +0530 Subject: [PATCH 77/98] Moved the coefficients stream file into the data structures file. --- src/sage/{rings => data_structures}/coefficient_stream.py | 4 ++-- src/sage/rings/lazy_laurent_series.py | 4 ++-- src/sage/rings/lazy_laurent_series_ring.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/sage/{rings => data_structures}/coefficient_stream.py (99%) diff --git a/src/sage/rings/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py similarity index 99% rename from src/sage/rings/coefficient_stream.py rename to src/sage/data_structures/coefficient_stream.py index d93e4641b31..7102470379b 100644 --- a/src/sage/rings/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -1,5 +1,5 @@ -from .integer_ring import ZZ -from .infinity import infinity +from sage.rings.integer_ring import ZZ +from sage.rings.infinity import infinity class CoefficientStream(): """ diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index a27856645c8..1e5770887be 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -79,7 +79,7 @@ from sage.structure.richcmp import op_EQ, op_NE from sage.arith.power import generic_power from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing -from sage.rings.coefficient_stream import ( +from sage.data_structures.coefficient_stream import ( CoefficientStream_add, CoefficientStream_mul, CoefficientStream_sub, @@ -565,7 +565,7 @@ def _sub_(self, other): sage: X = A - B; X 0 sage: type(X._coeff_stream) - + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 09831ee7eb9..91941e1ec09 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -84,11 +84,11 @@ from .lazy_laurent_series import ( LazyLaurentSeries, LazyLaurentSeries_coefficient_function, - LazyLaurentSeries_zero, LazyLaurentSeries_eventually_geometric, LazyLaurentSeries_uninitialized ) +from sage.data_structures.coefficient_stream import LazyLaurentSeries_zero class LazyLaurentSeriesRing(UniqueRepresentation, Parent): """ From 8d3e45fde3979e1932c28878050a3bcbb61dff10 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sun, 25 Jul 2021 23:23:57 +0530 Subject: [PATCH 78/98] PEP 8 done again. --- src/sage/data_structures/coefficient_stream.py | 3 ++- src/sage/rings/lazy_laurent_series.py | 2 +- src/sage/rings/lazy_laurent_series_ring.py | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 7102470379b..103452c6544 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -1,6 +1,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.infinity import infinity + class CoefficientStream(): """ Abstract base class for all auxillary LazyLaurentSeries. @@ -613,4 +614,4 @@ def iterate_coefficients(self): while True: c = self._ring(self._function(self._series[n])) yield c - n += 1 \ No newline at end of file + n += 1 diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 1e5770887be..8258a80215a 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -1456,4 +1456,4 @@ def iterate_coefficients(self): n = self._approximate_valuation while True: yield self._target[n] - n += 1 \ No newline at end of file + n += 1 diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 91941e1ec09..fdc7f6624b1 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -90,6 +90,7 @@ from sage.data_structures.coefficient_stream import LazyLaurentSeries_zero + class LazyLaurentSeriesRing(UniqueRepresentation, Parent): """ Lazy Laurent series ring. From 76ee5b76e4ca89a6f975f79a45fa0a14e51348bb Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Mon, 26 Jul 2021 01:13:40 +0530 Subject: [PATCH 79/98] Basic documentation done for the rings and series files. --- src/sage/.vscode/settings.json | 4 + src/sage/rings/lazy_laurent_series.py | 222 +++++++++++++++++++-- src/sage/rings/lazy_laurent_series_ring.py | 16 +- 3 files changed, 218 insertions(+), 24 deletions(-) create mode 100644 src/sage/.vscode/settings.json diff --git a/src/sage/.vscode/settings.json b/src/sage/.vscode/settings.json new file mode 100644 index 00000000000..c1d1f0a2469 --- /dev/null +++ b/src/sage/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.linting.pycodestyleEnabled": true, + "python.linting.enabled": true +} \ No newline at end of file diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 8258a80215a..e837f4c017f 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -101,26 +101,9 @@ class LazyLaurentSeries(ModuleElement): INPUT: - - ``coefficient_function`` -- Python function that computes coefficients + - ``parent`` -- The base ring for the series - - ``issparse`` -- Boolean that determines whether the implementation is sparse or dense - - - ``approximate_valuation`` -- integer; approximate valuation of the series - - - ``constant`` -- either ``None`` or pair of an element of the base ring and an integer - - Let the coefficient of index `i` mean the coefficient of the term of the - series with exponent `i`. - - Python function ``coefficient`` returns the value of the coefficient of - index `i` from input. - - Let ``approximate_valuation`` be `n`. All coefficients of index below `n` are zero. If - ``constant`` is ``None``, then the ``coefficient`` function is responsible - to compute the values of all coefficients of index `\ge n`. If ``constant`` - is a pair `(c,m)`, then the ``coefficient`` function is responsible to - compute the values of all coefficients of index `\ge n` and `< m` and all - the coefficients of index `\ge m` is the constant `c`. + - ``coeff_stream`` -- The auxiliary class that handles the coefficient stream EXAMPLES:: @@ -197,6 +180,12 @@ def __call__(self, g): r""" Return the composition of the series with ``g``. + Given two Laurent Series `f` and `g` over the same base ring, the composition of `f` with `g`, + `(f \circ g)(z) = f(g(z))`, is defined if and only if: + - `g = 0` and `val(f) >= 0` + - `g` is non-zero and `f` has only finitely many non-zero coefficients + - `g` is non-zero and `val(g) > 0` + INPUT: - ``g`` -- other series @@ -927,6 +916,10 @@ def __pow__(self, n): """ Return the ``n``-th power of the series. + INPUT: + + - ``n`` -- integer, the power to which to raise the series + TESTS:: sage: L. = LazyLaurentSeriesRing(ZZ) @@ -1135,7 +1128,7 @@ def _repr_(self): if isinstance(self._coeff_stream, LazyLaurentSeries_zero): return '0' if isinstance(self._coeff_stream, LazyLaurentSeries_uninitialized) and self._coeff_stream._target is None: - return 'Uninitialized LazyLaurentSeries' + return 'Uninitialized Lazy Laurent Series' atomic_repr = self.base_ring()._repr_option('element_is_atomic') X = self.parent().variable_name() @@ -1166,6 +1159,12 @@ def _richcmp_(self, other, op): Inequality is not defined for lazy Laurent series. + INPUT: + + - ``other`` -- another Laurent series + + - ``op`` -- comparison operator + TESTS:: sage: L. = LazyLaurentSeriesRing(QQ) @@ -1253,6 +1252,10 @@ def define(self, s): r""" Define an equation by ``self = s``. + INPUT:: + + - ``s`` -- a Laurent polynomial + EXAMPLES: We begin by constructing the Catalan numbers:: @@ -1356,9 +1359,51 @@ def define(self, s): class LazyLaurentSeries_eventually_geometric(CoefficientStream): + r""" + A lazy Laurent series which is known to be eventually geometric + + INPUT: + + - ``laurent_polynomial`` -- a Laurent polynomial + + - ``is_sparse`` -- a boolean, which specifies whether the series is sparse + + - ``constant`` -- either ``None`` (default: ``None``) or pair of an element of the base ring and an integer + + - ``degree`` -- either ``None`` (default: ``None``) or an integer, the degree of the polynomial + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1 + z^2 + sage: f[2] + 1 + + You can do arithmetic operations with eventually geometric series:: + + sage: g = z^3 + 1 + sage: s = f + g + sage: s[2] + 1 + sage: s + 2 + z^2 + z^3 + sage: s = f - g + sage: s[2] + 1 + sage: s + z^2 - z^3 + """ + def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): """ Initialize a series that is known to be eventually geometric. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: M = z^2 + z + sage: type(M._coeff_stream) + """ if constant is None: constant = ZZ.zero() @@ -1381,7 +1426,20 @@ def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): def __getitem__(self, n): """ - Get the ``n``-th coefficient of the series. + Return the coefficient of the term with exponent ``n`` of the series. + + INPUT: + + - ``n`` -- integer, the degree for which the coefficient is required + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = 1 + z + z^2 + z^3 + sage: f[3] + 1 + sage: f[8] + 0 """ if n >= self._degree: return self._constant @@ -1390,18 +1448,44 @@ def __getitem__(self, n): def valuation(self): """ Return the valuation of the series. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = 1 + z + z^2 + z^3 + sage: f.valuation() + 0 """ return self._approximate_valuation def __hash__(self): """ Return the hash of ``self``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = 1 + z + z^2 + z^3 + sage: {f: 1} + {1 + z + z^2 + z^3: 1} """ return hash((self._laurent_polynomial, self._degree, self._constant)) def __eq__(self, other): """ Test the equality between ``self`` and ``other``. + + INPUT: + + - ``other`` -- a lazy Laurent series which is known to be eventaully geometric + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = 1 + z + z^2 + z^3 + sage: m = 1 + z + z^2 + z^3 + sage: f == m + True """ return (isinstance(other, type(self)) and self._degree == other._degree @@ -1410,9 +1494,37 @@ def __eq__(self, other): class LazyLaurentSeries_coefficient_function(LazyLaurentSeries_inexact): + r""" + The coefficient function of a lazy Laurent series. + + INPUT: + + - ``coefficient_function`` -- a python function that generates the coefficients of the series + + - ``ring`` -- the base ring of the series + + - ``is_sparse`` -- a boolean, which specifies whether the series is sparse + + - ``approximate_valuation`` -- the approximate valuation of the series + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: M = L(lambda n: n, True); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + 7*z^7 + ... + sage: M[4] + 4 + """ + def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation): """ Initialize the coefficient function of a lazy Laurent series. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: type(L(lambda n:n, True)._coeff_stream) + """ self._coefficient_function = coefficient_function self._ring = ring @@ -1420,13 +1532,32 @@ def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation) def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the series. + Return the coefficient of the term with exponent ``n`` of the series. + + INPUT: + + - ``n`` -- integer, the degree for which the coefficient is required + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: M = L(lambda n: n, True) + sage: M._coeff_stream.get_coefficient(4) + 4 """ return self._ring(self._coefficient_function(n)) def iterate_coefficients(self): """ Return a generator for the coefficients of the series. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: M = L(lambda n: n, False) + sage: n = M._coeff_stream.iterate_coefficients() + sage: [next(n) for _ in range(10)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] """ n = self._offset ring = self._ring @@ -1436,9 +1567,33 @@ def iterate_coefficients(self): class LazyLaurentSeries_uninitialized(LazyLaurentSeries_inexact): + r""" + An uninitialized lazy Laurent series. + + INPUT: + + - ``is_sparse`` -- a boolean, which specifies whether the series is sparse + + - ``approximate_valuation`` -- the approximate valuation of the series + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: N = L(None) + sage: N + Uninitialized Lazy Laurent Series + """ + def __init__(self, is_sparse, approximate_valuation): """ Initialize an uninitialized lazy laurent series. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: N = L(None, 2) + sage: N + Uninitialized Lazy Laurent Series """ self._target = None super().__init__(is_sparse, approximate_valuation) @@ -1446,12 +1601,35 @@ def __init__(self, is_sparse, approximate_valuation): def get_coefficient(self, n): """ Return the ``n``-th coefficient of the series. + + INPUT: + + - ``n`` -- integer, the degree for which the coefficient is required + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: C = L(None) + sage: C.define(1 + z*C^2) + sage: C._coeff_stream.get_coefficient(4) + 14 + """ return self._target[n] def iterate_coefficients(self): """ Return a generator for the coefficients of the series. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: C = L(None) + sage: C.define(1 + z*C^2) + sage: n = C._coeff_stream.iterate_coefficients() + sage: [next(n) for _ in range(6)] + [1, 1, 2, 5, 14, 42] + """ n = self._approximate_valuation while True: diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index fdc7f6624b1..e95573e5b23 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -210,6 +210,14 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No """ Construct a Laurent series from ``x``. + INPUT: + + - ``x`` -- a Laurent series, a Laurent polynomial, a Python function, or a list of elements in the base ring + + - ``valuation`` -- an integer or ``None`` (default: ``None``), the approximate valuation of the series + + - ``constant`` -- either ``None`` (default: ``None``) or pair of an element of the base ring and an integer + EXAMPLES:: sage: L = LazyLaurentSeriesRing(GF(2), 'z') @@ -253,8 +261,8 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No ... ValueError: you must specify the degree for the polynomial 0 - Alternatively, the ``coefficient_function`` can be a list of elements of the - base ring. Then these elements are read as coefficients of the terms of + Alternatively, ``x`` can be a list of elements of the base ring. + Then these elements are read as coefficients of the terms of degrees starting from the ``valuation``. In this case, ``constant`` may be just an element of the base ring instead of a tuple or can be simply omitted if it is zero:: @@ -265,6 +273,10 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: g = L([1,3,5,7,9], 5, -1) sage: g z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... + + TODO:: + + Add a method to make a copy of self._sparse. """ if x is None: if valuation is None: From efe5a53bd5649e3ff4c4e45ef82b8636155d17ed Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Mon, 26 Jul 2021 01:14:05 +0530 Subject: [PATCH 80/98] Deleted .vscode --- src/sage/.vscode/settings.json | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/sage/.vscode/settings.json diff --git a/src/sage/.vscode/settings.json b/src/sage/.vscode/settings.json deleted file mode 100644 index c1d1f0a2469..00000000000 --- a/src/sage/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "python.linting.pycodestyleEnabled": true, - "python.linting.enabled": true -} \ No newline at end of file From 0e056f5fe9df9d0aeff20c463ceb68ad8f089305 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Mon, 26 Jul 2021 01:25:06 +0530 Subject: [PATCH 81/98] Started the documentation for the coefficient stream file. --- .../data_structures/coefficient_stream.py | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 103452c6544..7f72354ba02 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -1,15 +1,29 @@ +r""" +Coefficient Stream + +This module provides lazy class implementations of basic operators +on coefficient streams. The classes implemented in this module +can be used to build up more complex streams for different kinds of +series (Laurent, Dirichlet, etc). +""" + from sage.rings.integer_ring import ZZ from sage.rings.infinity import infinity class CoefficientStream(): """ - Abstract base class for all auxillary LazyLaurentSeries. + Abstract base class for all streams. + + INPUT: + + - ``sparse`` -- boolean; whether the implementation of the series is sparse + - ``approximate_valuation`` -- the approximate valuation of the series """ def __init__(self, sparse, approximate_valuation): """ - Initialize the auxillary class for a LazyLaurentSeries. + Initialize the auxillary class for any series. """ self._is_sparse = sparse self._approximate_valuation = approximate_valuation @@ -17,13 +31,14 @@ def __init__(self, sparse, approximate_valuation): class LazyLaurentSeries_inexact(CoefficientStream): """ - LazyLaurentSeries aux class when it is not or we do not know if it is + LazyLaurentSeries stream class when it is not or we do not know if it is eventually geometric. """ def __init__(self, is_sparse, approximate_valuation): """ - Initialize the auxillary class for a LazyLaurentSeries when it is not or it cannot be determined if it is eventually geometric. + Initialize the stream class for a LazyLaurentSeries when it is not + or it cannot be determined if it is eventually geometric. """ super().__init__(is_sparse, approximate_valuation) From b136d7031b59c1861bc98da95620bb4695ab2e78 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 26 Jul 2021 11:49:06 +1000 Subject: [PATCH 82/98] Improve speed of inv by using the cache. --- .../data_structures/coefficient_stream.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 7f72354ba02..bdb72be2d4f 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -587,18 +587,19 @@ def iterate_coefficients(self): """ Return the generator for the coefficients of the multiplicative inverse of the ``series``. """ - n = self._offset + v = self._approximate_valuation # shorthand name + n = 0 # Counts the number of places from the valuation + yield self._ainv + # Note that first entry of the cache will correspond to z^v while True: - v = self._approximate_valuation - if n == v: - yield self._ainv - n += 1 - continue + n += 1 c = self._zero - for k in range(v, n): - c += self[k] * self._series[n - v - k] + m = min(len(self._cache), n) + for k in range(m): + c += self._cache[k] * self._series[n - k] + for k in range(m, n): + c += self[v+k] * self._series[n - k] yield -c * self._ainv - n += 1 class CoefficientStream_apply_coeff(LazyLaurentSeries_unary): From 937fc6d9b4bd173fbea84ede9467a44bab07a768 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Tue, 27 Jul 2021 00:07:15 +0530 Subject: [PATCH 83/98] Basic documentation for coefficient stream code. --- .../data_structures/coefficient_stream.py | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 7f72354ba02..94f2ead4c77 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -5,8 +5,83 @@ on coefficient streams. The classes implemented in this module can be used to build up more complex streams for different kinds of series (Laurent, Dirichlet, etc). + +EXAMPLES:: + + The coefficient stream can be used to build up a Lazy laurent series:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = L(lambda n: n, True) + sage: f + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + 7*z^7 + ... + sage: type(f._coeff_stream) + + + There are basic unary and binary operators available for the coefficient streams. + For example, we can add two streams together:: + + sage: g = L(lambda n: -n, True) + sage: f + g + 0 + ... + + Coefficient streams can be subtracted:: + + sage: f - g + 2*z + 4*z^2 + 6*z^3 + 8*z^4 + 10*z^5 + 12*z^6 + 14*z^7 + ... + + Coefficient streams can be multiplied:: + + sage: g = L(lambda n: n^2, True) + sage: f * g + z^2 + 6*z^3 + 20*z^4 + 50*z^5 + 105*z^6 + 196*z^7 + 336*z^8 + ... + + Coefficient streams can be divided:: + + sage: f / g + 1 - 2*z + 2*z^2 - 2*z^3 + 2*z^4 - 2*z^5 + 2*z^6 + ... + + Two coefficient streams can be composed (depending on whether it exists):: + + sage: f(g) + z + 6*z^2 + 28*z^3 + 124*z^4 + 527*z^5 + 2172*z^6 + 8755*z^7 + ... + + We can also use the unary negation operator on a coefficient stream:: + + sage: -f + -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 - 7*z^7 + ... + + Coefficient streams can be multiplied by a scalar:: + + sage: f * 2 + 2*z + 4*z^2 + 6*z^3 + 8*z^4 + 10*z^5 + 12*z^6 + 14*z^7 + ... + + The multiplicative inverse of a series can also be obtained:: + + sage: f = L(lambda n: 1, True, 1); f + z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... + sage: ~f + z^-1 - 1 + ... + + Functions can also be applied to a coefficient stream:: + + sage: f.map_coefficients(lambda n: n*3) + 3*z + 3*z^2 + 3*z^3 + 3*z^4 + 3*z^5 + 3*z^6 + 3*z^7 + ... + +AUTHORS: + +- Kwankyu Lee (2019-02-24): initial version + """ +# **************************************************************************** +# Copyright (C) 2019 Kwankyu Lee +# +# 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 +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.rings.integer_ring import ZZ from sage.rings.infinity import infinity @@ -18,12 +93,22 @@ class CoefficientStream(): INPUT: - ``sparse`` -- boolean; whether the implementation of the series is sparse + - ``approximate_valuation`` -- the approximate valuation of the series + + EXAMPLES:: + """ def __init__(self, sparse, approximate_valuation): """ Initialize the auxillary class for any series. + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream + sage: type(CoefficientStream(True, 1)) + """ self._is_sparse = sparse self._approximate_valuation = approximate_valuation From 5cd5b4d68dd676881e6ebb262c569cda333582dc Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 27 Jul 2021 08:36:21 +1000 Subject: [PATCH 84/98] Fix bug with inverse. --- src/sage/data_structures/coefficient_stream.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index bdb72be2d4f..9d0b79862e1 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -596,9 +596,9 @@ def iterate_coefficients(self): c = self._zero m = min(len(self._cache), n) for k in range(m): - c += self._cache[k] * self._series[n - k] - for k in range(m, n): - c += self[v+k] * self._series[n - k] + c += self._cache[k] * self._series[n - v - k] + for k in range(v+m, v+n): + c += self[k] * self._series[n - k] yield -c * self._ainv From e27da4b321b835328c498e207aac4d4eda38ed5d Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Tue, 27 Jul 2021 14:03:55 +0530 Subject: [PATCH 85/98] Corrected coefficient stream file. --- src/sage/.vscode/settings.json | 4 + .../data_structures/coefficient_stream.py | 315 ++++++++++++++- src/sage/rings/lazy_laurent_series.py | 374 +++--------------- src/sage/rings/lazy_laurent_series_ring.py | 34 +- 4 files changed, 367 insertions(+), 360 deletions(-) create mode 100644 src/sage/.vscode/settings.json diff --git a/src/sage/.vscode/settings.json b/src/sage/.vscode/settings.json new file mode 100644 index 00000000000..c1d1f0a2469 --- /dev/null +++ b/src/sage/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "python.linting.pycodestyleEnabled": true, + "python.linting.enabled": true +} \ No newline at end of file diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 7dca14b47ad..761013c42d8 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -15,7 +15,7 @@ sage: f z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + 7*z^7 + ... sage: type(f._coeff_stream) - + There are basic unary and binary operators available for the coefficient streams. For example, we can add two streams together:: @@ -114,15 +114,15 @@ def __init__(self, sparse, approximate_valuation): self._approximate_valuation = approximate_valuation -class LazyLaurentSeries_inexact(CoefficientStream): +class CoefficientStream_inexact(CoefficientStream): """ - LazyLaurentSeries stream class when it is not or we do not know if it is + CoefficientStream stream class when it is not or we do not know if it is eventually geometric. """ def __init__(self, is_sparse, approximate_valuation): """ - Initialize the stream class for a LazyLaurentSeries when it is not + Initialize the stream class for a CoefficientStream when it is not or it cannot be determined if it is eventually geometric. """ super().__init__(is_sparse, approximate_valuation) @@ -214,7 +214,286 @@ def valuation(self): n += 1 -class LazyLaurentSeries_unary(LazyLaurentSeries_inexact): +class CoefficientStream_eventually_geometric(CoefficientStream): + r""" + A lazy Laurent series which is known to be eventually geometric + + INPUT: + + - ``laurent_polynomial`` -- a Laurent polynomial + + - ``is_sparse`` -- a boolean, which specifies whether the series is sparse + + - ``constant`` -- either ``None`` (default: ``None``) or pair of an element of the base ring and an integer + + - ``degree`` -- either ``None`` (default: ``None``) or an integer, the degree of the polynomial + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1 + z^2 + sage: f[2] + 1 + + You can do arithmetic operations with eventually geometric series:: + + sage: g = z^3 + 1 + sage: s = f + g + sage: s[2] + 1 + sage: s + 2 + z^2 + z^3 + sage: s = f - g + sage: s[2] + 1 + sage: s + z^2 - z^3 + """ + + def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): + """ + Initialize a series that is known to be eventually geometric. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: M = z^2 + z + sage: type(M._coeff_stream) + + """ + if constant is None: + constant = ZZ.zero() + if degree is None: + if not laurent_polynomial: + raise ValueError("you must specify the degree for the polynomial 0") + degree = laurent_polynomial.degree() + 1 + else: + # Consistency check + assert not laurent_polynomial or laurent_polynomial.degree() < degree + + self._constant = constant + self._degree = degree + self._laurent_polynomial = laurent_polynomial + if not laurent_polynomial: + valuation = degree + else: + valuation = laurent_polynomial.valuation() + super().__init__(is_sparse, valuation) + + def __getitem__(self, n): + """ + Return the coefficient of the term with exponent ``n`` of the series. + + INPUT: + + - ``n`` -- integer, the degree for which the coefficient is required + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = 1 + z + z^2 + z^3 + sage: f[3] + 1 + sage: f[8] + 0 + """ + if n >= self._degree: + return self._constant + return self._laurent_polynomial[n] + + def valuation(self): + """ + Return the valuation of the series. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = 1 + z + z^2 + z^3 + sage: f.valuation() + 0 + """ + return self._approximate_valuation + + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = 1 + z + z^2 + z^3 + sage: {f: 1} + {1 + z + z^2 + z^3: 1} + """ + return hash((self._laurent_polynomial, self._degree, self._constant)) + + def __eq__(self, other): + """ + Test the equality between ``self`` and ``other``. + + INPUT: + + - ``other`` -- a lazy Laurent series which is known to be eventaully geometric + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: f = 1 + z + z^2 + z^3 + sage: m = 1 + z + z^2 + z^3 + sage: f == m + True + """ + return (isinstance(other, type(self)) + and self._degree == other._degree + and self._laurent_polynomial == other._laurent_polynomial + and self._constant == other._constant) + + +class CoefficientStream_coefficient_function(CoefficientStream_inexact): + r""" + The coefficient function of a lazy Laurent series. + + INPUT: + + - ``coefficient_function`` -- a python function that generates the coefficients of the series + + - ``ring`` -- the base ring of the series + + - ``is_sparse`` -- a boolean, which specifies whether the series is sparse + + - ``approximate_valuation`` -- the approximate valuation of the series + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: M = L(lambda n: n, True); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + 7*z^7 + ... + sage: M[4] + 4 + """ + + def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation): + """ + Initialize the coefficient function of a lazy Laurent series. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: type(L(lambda n:n, True)._coeff_stream) + + """ + self._coefficient_function = coefficient_function + self._ring = ring + super().__init__(is_sparse, approximate_valuation) + + def get_coefficient(self, n): + """ + Return the coefficient of the term with exponent ``n`` of the series. + + INPUT: + + - ``n`` -- integer, the degree for which the coefficient is required + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: M = L(lambda n: n, True) + sage: M._coeff_stream.get_coefficient(4) + 4 + """ + return self._ring(self._coefficient_function(n)) + + def iterate_coefficients(self): + """ + Return a generator for the coefficients of the series. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: M = L(lambda n: n, False) + sage: n = M._coeff_stream.iterate_coefficients() + sage: [next(n) for _ in range(10)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + """ + n = self._offset + ring = self._ring + while True: + yield ring(self._coefficient_function(n)) + n += 1 + + +class CoefficientStream_uninitialized(CoefficientStream_inexact): + r""" + An uninitialized lazy Laurent series. + + INPUT: + + - ``is_sparse`` -- a boolean, which specifies whether the series is sparse + + - ``approximate_valuation`` -- the approximate valuation of the series + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: N = L(None) + sage: N + Uninitialized Lazy Laurent Series + """ + + def __init__(self, is_sparse, approximate_valuation): + """ + Initialize an uninitialized lazy laurent series. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: N = L(None, 2) + sage: N + Uninitialized Lazy Laurent Series + """ + self._target = None + super().__init__(is_sparse, approximate_valuation) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of the series. + + INPUT: + + - ``n`` -- integer, the degree for which the coefficient is required + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: C = L(None) + sage: C.define(1 + z*C^2) + sage: C._coeff_stream.get_coefficient(4) + 14 + + """ + return self._target[n] + + def iterate_coefficients(self): + """ + Return a generator for the coefficients of the series. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: C = L(None) + sage: C.define(1 + z*C^2) + sage: n = C._coeff_stream.iterate_coefficients() + sage: [next(n) for _ in range(6)] + [1, 1, 2, 5, 14, 42] + + """ + n = self._approximate_valuation + while True: + yield self._target[n] + n += 1 + + +class CoefficientStream_unary(CoefficientStream_inexact): """ Abstract base class for unary operators. @@ -272,7 +551,7 @@ def __eq__(self, other): return isinstance(other, type(self)) and self._series == other._series -class LazyLaurentSeries_binary(LazyLaurentSeries_inexact): +class CoefficientStream_binary(CoefficientStream_inexact): def __init__(self, left, right, *args, **kwargs): """ @@ -322,7 +601,7 @@ def __eq__(self, other): return self._left == other._left and self._right == other._right -class LazyLaurentSeries_binary_commutative(LazyLaurentSeries_binary): +class CoefficientStream_binary_commutative(CoefficientStream_binary): def __hash__(self): """ @@ -343,7 +622,7 @@ def __eq__(self, other): return False -class LazyLaurentSeries_zero(CoefficientStream): +class CoefficientStream_zero(CoefficientStream): def __init__(self, sparse): """ Initialise a lazy Laurent series which is known to be zero. @@ -372,7 +651,7 @@ def __hash__(self): # Binary operations -class CoefficientStream_add(LazyLaurentSeries_binary): +class CoefficientStream_add(CoefficientStream_binary): """ Operator for addition. """ @@ -403,7 +682,7 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_sub(LazyLaurentSeries_binary): +class CoefficientStream_sub(CoefficientStream_binary): """ Operator for subtraction. """ @@ -434,7 +713,7 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_mul(LazyLaurentSeries_binary): +class CoefficientStream_mul(CoefficientStream_binary): """ Operator for multiplication. @@ -479,7 +758,7 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_div(LazyLaurentSeries_binary): +class CoefficientStream_div(CoefficientStream_binary): """ Return ``left`` divided by ``right``. """ @@ -527,7 +806,7 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_composition(LazyLaurentSeries_binary): +class CoefficientStream_composition(CoefficientStream_binary): r""" Return ``f`` composed by ``g``. @@ -550,7 +829,7 @@ def __init__(self, f, g): ginv = CoefficientStream_inv(g) # the constant part makes no contribution to the negative # we need this for the case so self._neg_powers[0][n] => 0 - self._neg_powers = [LazyLaurentSeries_zero(f._is_sparse), ginv] + self._neg_powers = [CoefficientStream_zero(f._is_sparse), ginv] for i in range(1, -self._fv): self._neg_powers.append(CoefficientStream_mul(self._neg_powers[-1], ginv)) # Placeholder None to make this 1-based @@ -585,7 +864,7 @@ def iterate_coefficients(self): # Unary operations -class CoefficientStream_scalar(LazyLaurentSeries_unary): +class CoefficientStream_scalar(CoefficientStream_unary): """ Operator for multiplying with a scalar. """ @@ -614,7 +893,7 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_neg(LazyLaurentSeries_unary): +class CoefficientStream_neg(CoefficientStream_unary): """ Operator for negative of the series. """ @@ -641,7 +920,7 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_inv(LazyLaurentSeries_unary): +class CoefficientStream_inv(CoefficientStream_unary): """ Operator for multiplicative inverse of the series. """ @@ -687,7 +966,7 @@ def iterate_coefficients(self): yield -c * self._ainv -class CoefficientStream_apply_coeff(LazyLaurentSeries_unary): +class CoefficientStream_apply_coeff(CoefficientStream_unary): """ Return the series with ``function`` applied to each coefficient of this series. """ diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index e837f4c017f..3e5e0bba5b9 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -90,8 +90,11 @@ CoefficientStream_inv, CoefficientStream_apply_coeff, CoefficientStream, - LazyLaurentSeries_inexact, - LazyLaurentSeries_zero + CoefficientStream_inexact, + CoefficientStream_zero, + CoefficientStream_eventually_geometric, + CoefficientStream_coefficient_function, + CoefficientStream_uninitialized ) @@ -349,7 +352,7 @@ def __call__(self, g): P = g.parent() # g = 0 case - if (not isinstance(g, LazyLaurentSeries) and not g) or (isinstance(g, LazyLaurentSeries) and isinstance(g._coeff_stream, LazyLaurentSeries_zero)): + if (not isinstance(g, LazyLaurentSeries) and not g) or (isinstance(g, LazyLaurentSeries) and isinstance(g._coeff_stream, CoefficientStream_zero)): if self._coeff_stream._approximate_valuation >= 0: return P(self[0]) # Perhaps we just don't yet know if the valuation is non-negative @@ -359,21 +362,21 @@ def __call__(self, g): return P(self[0]) # f has finite length - if isinstance(self._coeff_stream, LazyLaurentSeries_zero): # constant 0 + if isinstance(self._coeff_stream, CoefficientStream_zero): # constant 0 return self - if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric) and not self._coeff_stream._constant: + if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) and not self._coeff_stream._constant: # constant polynomial if self._coeff_stream._laurent_polynomial.is_constant(): return self if not isinstance(g, LazyLaurentSeries): return self._coeff_stream._laurent_polynomial(g) # g also has finite length, compose the polynomials - if isinstance(g._coeff_stream, LazyLaurentSeries_eventually_geometric) and not g._coeff_stream._constant: + if isinstance(g._coeff_stream, CoefficientStream_eventually_geometric) and not g._coeff_stream._constant: R = P._laurent_poly_ring try: ret = self._coeff_stream._laurent_polynomial(g._coeff_stream._laurent_polynomial) if ret.parent() is R: - return P.element_class(P, LazyLaurentSeries_eventually_geometric(ret, self._coeff_stream._is_sparse, 0)) + return P.element_class(P, CoefficientStream_eventually_geometric(ret, self._coeff_stream._is_sparse, 0)) except TypeError: # the result is not a Laurent polynomial pass @@ -448,20 +451,20 @@ def _mul_(self, other): P = self.parent() left = self._coeff_stream right = other._coeff_stream - if isinstance(left, LazyLaurentSeries_zero) or isinstance(right, LazyLaurentSeries_zero): + if isinstance(left, CoefficientStream_zero) or isinstance(right, CoefficientStream_zero): return P.zero() R = P._laurent_poly_ring - if isinstance(left, LazyLaurentSeries_eventually_geometric): + if isinstance(left, CoefficientStream_eventually_geometric): if not left._constant: if left._laurent_polynomial == R.one(): # self == 1 return other - if isinstance(right, LazyLaurentSeries_eventually_geometric): + if isinstance(right, CoefficientStream_eventually_geometric): if not right._constant: p = left._laurent_polynomial * right._laurent_polynomial c = left._constant - return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c)) - elif isinstance(right, LazyLaurentSeries_eventually_geometric) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 + return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c)) + elif isinstance(right, CoefficientStream_eventually_geometric) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 return self return P.element_class(P, CoefficientStream_mul(self._coeff_stream, other._coeff_stream)) @@ -507,8 +510,8 @@ def _add_(self, other): P = self.parent() left = self._coeff_stream right = other._coeff_stream - if (isinstance(left, LazyLaurentSeries_eventually_geometric) - and isinstance(right, LazyLaurentSeries_eventually_geometric)): + if (isinstance(left, CoefficientStream_eventually_geometric) + and isinstance(right, CoefficientStream_eventually_geometric)): R = P._laurent_poly_ring c = left._constant + right._constant pl = left._laurent_polynomial @@ -519,7 +522,7 @@ def _add_(self, other): p = pl + pr if not p and not c: return P.zero() - return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, d)) + return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, d)) return P.element_class(P, CoefficientStream_add(self._coeff_stream, other._coeff_stream)) def _sub_(self, other): @@ -554,7 +557,7 @@ def _sub_(self, other): sage: X = A - B; X 0 sage: type(X._coeff_stream) - + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M @@ -567,7 +570,7 @@ def _sub_(self, other): P = self.parent() left = self._coeff_stream right = other._coeff_stream - if (isinstance(left, LazyLaurentSeries_eventually_geometric) and isinstance(right, LazyLaurentSeries_eventually_geometric)): + if (isinstance(left, CoefficientStream_eventually_geometric) and isinstance(right, CoefficientStream_eventually_geometric)): R = P._laurent_poly_ring c = left._constant - right._constant pl = left._laurent_polynomial @@ -578,7 +581,7 @@ def _sub_(self, other): p = pl - pr if not p and not c: return P.zero() - return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, d)) + return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, d)) if left == right: return P.zero() return P.element_class(P, CoefficientStream_sub(self._coeff_stream, other._coeff_stream)) @@ -611,21 +614,21 @@ def _div_(self, other): sage: P = M / N; P z + z^2 + z^3 + z^4 + z^5 + z^6 + ... """ - if isinstance(other._coeff_stream, LazyLaurentSeries_zero): + if isinstance(other._coeff_stream, CoefficientStream_zero): raise ZeroDivisionError("cannot divide by 0") P = self.parent() left = self._coeff_stream - if isinstance(left, LazyLaurentSeries_zero): + if isinstance(left, CoefficientStream_zero): return P.zero() right = other._coeff_stream - if (isinstance(left, LazyLaurentSeries_eventually_geometric) - and isinstance(right, LazyLaurentSeries_eventually_geometric)): + if (isinstance(left, CoefficientStream_eventually_geometric) + and isinstance(right, CoefficientStream_eventually_geometric)): if not left._constant and not right._constant: ret = left._laurent_polynomial / right._laurent_polynomial try: ret = P._laurent_poly_ring(ret) - return P.element_class(P, LazyLaurentSeries_eventually_geometric(ret, P._sparse, left._constant)) + return P.element_class(P, CoefficientStream_eventually_geometric(ret, P._sparse, left._constant)) except (TypeError, ValueError): # We cannot divide the polynomials, so the result must be a series pass @@ -675,10 +678,10 @@ def _rmul_(self, scalar): if scalar == 1: return self - if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): c = scalar * self._coeff_stream._constant p = scalar * self._coeff_stream._laurent_polynomial - return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, self._coeff_stream._degree)) + return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, self._coeff_stream._degree)) return P.element_class(P, CoefficientStream_scalar(self._coeff_stream, scalar)) @@ -706,11 +709,11 @@ def _neg_(self): -3*z - z^2 + 4*z^3 """ P = self.parent() - if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): p = -self._coeff_stream._laurent_polynomial c = -self._coeff_stream._constant d = self._coeff_stream._degree - return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, c, d)) + return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, d)) # -(-f) = f if isinstance(self._coeff_stream, CoefficientStream_neg): return P.element_class(P, self._coeff_stream._series) @@ -739,9 +742,9 @@ def __invert__(self): 1 - z """ P = self.parent() - if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric) and self._coeff_stream._laurent_polynomial == P.gen(): + if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) and self._coeff_stream._laurent_polynomial == P.gen(): ret = 1 / self._coeff_stream._laurent_polynomial - return P.element_class(P, LazyLaurentSeries_eventually_geometric(ret, P._sparse, self._coeff_stream._constant)) + return P.element_class(P, CoefficientStream_eventually_geometric(ret, P._sparse, self._coeff_stream._constant)) # (f^-1)^-1 = f if isinstance(self._coeff_stream, CoefficientStream_inv): return P.element_class(P, self._coeff_stream._series) @@ -830,12 +833,12 @@ def map_coefficients(self, func, ring=None): """ P = self.parent() R = P.base_ring() - if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): p = p.map_coefficients(func) c = func(c) if not p and not c: return P.zero() - return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, self._coeff_stream._is_sparse, c, d)) + return P.element_class(P, CoefficientStream_eventually_geometric(p, self._coeff_stream._is_sparse, c, d)) return P.element_class(P, CoefficientStream_apply_coeff(self._coeff_stream, func, R)) def change_ring(self, ring): @@ -910,7 +913,7 @@ def truncate(self, d): R = P._laurent_poly_ring z = R.gen() p = R.sum(self[i] * z**i for i in range(self._coeff_stream._approximate_valuation, d)) - return P.element_class(P, LazyLaurentSeries_eventually_geometric(p, P._sparse, ZZ.zero(), d)) + return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, ZZ.zero(), d)) def __pow__(self, n): """ @@ -1064,10 +1067,10 @@ def polynomial(self, degree=None, name=None): ValueError: not a polynomial """ if degree is None: - if isinstance(self._coeff_stream, LazyLaurentSeries_zero): + if isinstance(self._coeff_stream, CoefficientStream_zero): from sage.rings.all import PolynomialRing return PolynomialRing(S.base_ring(), name=name).zero() - elif isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric) and not self._coeff_stream._constant: + elif isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) and not self._coeff_stream._constant: m = self._coeff_stream._degree else: raise ValueError("not a polynomial") @@ -1125,16 +1128,16 @@ def _repr_(self): sage: -1/(1 + 2*z) -1 + 2*z - 4*z^2 + 8*z^3 - 16*z^4 + 32*z^5 - 64*z^6 + ... """ - if isinstance(self._coeff_stream, LazyLaurentSeries_zero): + if isinstance(self._coeff_stream, CoefficientStream_zero): return '0' - if isinstance(self._coeff_stream, LazyLaurentSeries_uninitialized) and self._coeff_stream._target is None: + if isinstance(self._coeff_stream, CoefficientStream_uninitialized) and self._coeff_stream._target is None: return 'Uninitialized Lazy Laurent Series' atomic_repr = self.base_ring()._repr_option('element_is_atomic') X = self.parent().variable_name() v = self._coeff_stream._approximate_valuation - if not isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric): + if not isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): m = v + 7 # long enough elif not self._coeff_stream._constant: # Just a polynonial, so let that print itself @@ -1178,13 +1181,13 @@ def _richcmp_(self, other, op): False """ if op is op_EQ: - if isinstance(self._coeff_stream, LazyLaurentSeries_zero): # self == 0 - return isinstance(other._coeff_stream, LazyLaurentSeries_zero) - if isinstance(other._coeff_stream, LazyLaurentSeries_zero): # self != 0 but other == 0 + if isinstance(self._coeff_stream, CoefficientStream_zero): # self == 0 + return isinstance(other._coeff_stream, CoefficientStream_zero) + if isinstance(other._coeff_stream, CoefficientStream_zero): # self != 0 but other == 0 return False - if (not isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric) - or not isinstance(other._coeff_stream, LazyLaurentSeries_eventually_geometric)): + if (not isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) + or not isinstance(other._coeff_stream, CoefficientStream_eventually_geometric)): # One of the lazy laurent series is not known to eventually be constant # Implement the checking of the caches here. n = min(self._coeff_stream._approximate_valuation, other._coeff_stream._approximate_valuation) @@ -1196,7 +1199,7 @@ def _richcmp_(self, other, op): return True raise ValueError("undecidable as lazy Laurent series") - # Both are LazyLaurentSeries_eventually_geometric, which implements a full check + # Both are CoefficientStream_eventually_geometric, which implements a full check return self._coeff_stream == other._coeff_stream if op is op_NE: @@ -1235,9 +1238,9 @@ def __bool__(self): sage: M.is_zero() False """ - if isinstance(self._coeff_stream, LazyLaurentSeries_zero): + if isinstance(self._coeff_stream, CoefficientStream_zero): return False - if isinstance(self._coeff_stream, LazyLaurentSeries_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): # This should always end up being True, but let's be careful about it for now... return self._coeff_stream._laurent_polynomial or self._coeff_stream._constant @@ -1353,285 +1356,6 @@ def define(self, s): ... ValueError: series already defined """ - if not isinstance(self._coeff_stream, LazyLaurentSeries_uninitialized) or self._coeff_stream._target is not None: + if not isinstance(self._coeff_stream, CoefficientStream_uninitialized) or self._coeff_stream._target is not None: raise ValueError("series already defined") self._coeff_stream._target = s._coeff_stream - - -class LazyLaurentSeries_eventually_geometric(CoefficientStream): - r""" - A lazy Laurent series which is known to be eventually geometric - - INPUT: - - - ``laurent_polynomial`` -- a Laurent polynomial - - - ``is_sparse`` -- a boolean, which specifies whether the series is sparse - - - ``constant`` -- either ``None`` (default: ``None``) or pair of an element of the base ring and an integer - - - ``degree`` -- either ``None`` (default: ``None``) or an integer, the degree of the polynomial - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1 + z^2 - sage: f[2] - 1 - - You can do arithmetic operations with eventually geometric series:: - - sage: g = z^3 + 1 - sage: s = f + g - sage: s[2] - 1 - sage: s - 2 + z^2 + z^3 - sage: s = f - g - sage: s[2] - 1 - sage: s - z^2 - z^3 - """ - - def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): - """ - Initialize a series that is known to be eventually geometric. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: M = z^2 + z - sage: type(M._coeff_stream) - - """ - if constant is None: - constant = ZZ.zero() - if degree is None: - if not laurent_polynomial: - raise ValueError("you must specify the degree for the polynomial 0") - degree = laurent_polynomial.degree() + 1 - else: - # Consistency check - assert not laurent_polynomial or laurent_polynomial.degree() < degree - - self._constant = constant - self._degree = degree - self._laurent_polynomial = laurent_polynomial - if not laurent_polynomial: - valuation = degree - else: - valuation = laurent_polynomial.valuation() - super().__init__(is_sparse, valuation) - - def __getitem__(self, n): - """ - Return the coefficient of the term with exponent ``n`` of the series. - - INPUT: - - - ``n`` -- integer, the degree for which the coefficient is required - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = 1 + z + z^2 + z^3 - sage: f[3] - 1 - sage: f[8] - 0 - """ - if n >= self._degree: - return self._constant - return self._laurent_polynomial[n] - - def valuation(self): - """ - Return the valuation of the series. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = 1 + z + z^2 + z^3 - sage: f.valuation() - 0 - """ - return self._approximate_valuation - - def __hash__(self): - """ - Return the hash of ``self``. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = 1 + z + z^2 + z^3 - sage: {f: 1} - {1 + z + z^2 + z^3: 1} - """ - return hash((self._laurent_polynomial, self._degree, self._constant)) - - def __eq__(self, other): - """ - Test the equality between ``self`` and ``other``. - - INPUT: - - - ``other`` -- a lazy Laurent series which is known to be eventaully geometric - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = 1 + z + z^2 + z^3 - sage: m = 1 + z + z^2 + z^3 - sage: f == m - True - """ - return (isinstance(other, type(self)) - and self._degree == other._degree - and self._laurent_polynomial == other._laurent_polynomial - and self._constant == other._constant) - - -class LazyLaurentSeries_coefficient_function(LazyLaurentSeries_inexact): - r""" - The coefficient function of a lazy Laurent series. - - INPUT: - - - ``coefficient_function`` -- a python function that generates the coefficients of the series - - - ``ring`` -- the base ring of the series - - - ``is_sparse`` -- a boolean, which specifies whether the series is sparse - - - ``approximate_valuation`` -- the approximate valuation of the series - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: M = L(lambda n: n, True); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + 7*z^7 + ... - sage: M[4] - 4 - """ - - def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation): - """ - Initialize the coefficient function of a lazy Laurent series. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: type(L(lambda n:n, True)._coeff_stream) - - """ - self._coefficient_function = coefficient_function - self._ring = ring - super().__init__(is_sparse, approximate_valuation) - - def get_coefficient(self, n): - """ - Return the coefficient of the term with exponent ``n`` of the series. - - INPUT: - - - ``n`` -- integer, the degree for which the coefficient is required - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: M = L(lambda n: n, True) - sage: M._coeff_stream.get_coefficient(4) - 4 - """ - return self._ring(self._coefficient_function(n)) - - def iterate_coefficients(self): - """ - Return a generator for the coefficients of the series. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: M = L(lambda n: n, False) - sage: n = M._coeff_stream.iterate_coefficients() - sage: [next(n) for _ in range(10)] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - """ - n = self._offset - ring = self._ring - while True: - yield ring(self._coefficient_function(n)) - n += 1 - - -class LazyLaurentSeries_uninitialized(LazyLaurentSeries_inexact): - r""" - An uninitialized lazy Laurent series. - - INPUT: - - - ``is_sparse`` -- a boolean, which specifies whether the series is sparse - - - ``approximate_valuation`` -- the approximate valuation of the series - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: N = L(None) - sage: N - Uninitialized Lazy Laurent Series - """ - - def __init__(self, is_sparse, approximate_valuation): - """ - Initialize an uninitialized lazy laurent series. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: N = L(None, 2) - sage: N - Uninitialized Lazy Laurent Series - """ - self._target = None - super().__init__(is_sparse, approximate_valuation) - - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of the series. - - INPUT: - - - ``n`` -- integer, the degree for which the coefficient is required - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: C = L(None) - sage: C.define(1 + z*C^2) - sage: C._coeff_stream.get_coefficient(4) - 14 - - """ - return self._target[n] - - def iterate_coefficients(self): - """ - Return a generator for the coefficients of the series. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: C = L(None) - sage: C.define(1 + z*C^2) - sage: n = C._coeff_stream.iterate_coefficients() - sage: [next(n) for _ in range(6)] - [1, 1, 2, 5, 14, 42] - - """ - n = self._approximate_valuation - while True: - yield self._target[n] - n += 1 diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index e95573e5b23..d4fb001bae1 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -81,14 +81,14 @@ from .integer_ring import ZZ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing, LaurentPolynomialRing_generic -from .lazy_laurent_series import ( - LazyLaurentSeries, - LazyLaurentSeries_coefficient_function, - LazyLaurentSeries_eventually_geometric, - LazyLaurentSeries_uninitialized -) +from .lazy_laurent_series import LazyLaurentSeries -from sage.data_structures.coefficient_stream import LazyLaurentSeries_zero +from sage.data_structures.coefficient_stream import ( + CoefficientStream_zero, + CoefficientStream_coefficient_function, + CoefficientStream_eventually_geometric, + CoefficientStream_uninitialized +) class LazyLaurentSeriesRing(UniqueRepresentation, Parent): @@ -151,7 +151,7 @@ def gen(self, n=0): if n != 0: raise IndexError("there is only one generator") R = self._laurent_poly_ring - coeff_stream = LazyLaurentSeries_eventually_geometric(R.gen(n), self._sparse, ZZ.zero(), 2) + coeff_stream = CoefficientStream_eventually_geometric(R.gen(n), self._sparse, ZZ.zero(), 2) return self.element_class(self, coeff_stream) def ngens(self): @@ -201,7 +201,7 @@ def _coerce_map_from_(self, S): R = self._laurent_poly_ring if R.has_coerce_map_from(S): def make_series_from(poly): - return self.element_class(self, LazyLaurentSeries_eventually_geometric(R(poly), self._sparse)) + return self.element_class(self, CoefficientStream_eventually_geometric(R(poly), self._sparse)) return SetMorphism(Hom(S, self, Sets()), make_series_from) return False @@ -281,7 +281,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if x is None: if valuation is None: valuation = 0 - return self.element_class(self, LazyLaurentSeries_uninitialized(self._sparse, valuation)) + return self.element_class(self, CoefficientStream_uninitialized(self._sparse, valuation)) R = self._laurent_poly_ring BR = self.base_ring() @@ -296,13 +296,13 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No constant = BR(constant) if x in R: if not x and not constant: - coeff_stream = LazyLaurentSeries_zero(self._sparse) + coeff_stream = CoefficientStream_zero(self._sparse) else: if x and valuation: x = x.shift(valuation - x.valuation()) if degree is None and not x: degree = valuation - coeff_stream = LazyLaurentSeries_eventually_geometric(R(x), self._sparse, constant, degree) + coeff_stream = CoefficientStream_eventually_geometric(R(x), self._sparse, constant, degree) return self.element_class(self, coeff_stream) if isinstance(x, LazyLaurentSeries): if x._coeff_stream._is_sparse is self._sparse: @@ -317,8 +317,8 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No constant = ZZ.zero() z = R.gen() p = R.sum(x(i) * z**i for i in range(valuation, degree)) - return self.element_class(self, LazyLaurentSeries_eventually_geometric(p, self._sparse, constant, degree)) - return self.element_class(self, LazyLaurentSeries_coefficient_function(x, self.base_ring(), self._sparse, valuation)) + return self.element_class(self, CoefficientStream_eventually_geometric(p, self._sparse, constant, degree)) + return self.element_class(self, CoefficientStream_coefficient_function(x, self.base_ring(), self._sparse, valuation)) raise ValueError(f"unable to convert {x} into a lazy Laurent series") def _an_element_(self): @@ -333,7 +333,7 @@ def _an_element_(self): """ c = self.base_ring().an_element() R = self._laurent_poly_ring - return self.element_class(self, LazyLaurentSeries_eventually_geometric(R.zero(), self._sparse, c, -10)) + return self.element_class(self, CoefficientStream_eventually_geometric(R.zero(), self._sparse, c, -10)) @cached_method def one(self): @@ -347,7 +347,7 @@ def one(self): 1 """ R = self._laurent_poly_ring - return self.element_class(self, LazyLaurentSeries_eventually_geometric(R.one(), self._sparse, ZZ.zero(), 1)) + return self.element_class(self, CoefficientStream_eventually_geometric(R.one(), self._sparse, ZZ.zero(), 1)) @cached_method def zero(self): @@ -360,4 +360,4 @@ def zero(self): sage: L.zero() 0 """ - return self.element_class(self, LazyLaurentSeries_zero(self._sparse)) + return self.element_class(self, CoefficientStream_zero(self._sparse)) From 525409bd6be7c3fd13486e11a6cd1043a321ad0b Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 28 Jul 2021 04:03:53 +0530 Subject: [PATCH 86/98] Added documentation for the coefficient_stream file. --- .../data_structures/coefficient_stream.py | 867 ++++++++++++++++-- 1 file changed, 769 insertions(+), 98 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 761013c42d8..2387ba297e6 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -20,52 +20,74 @@ There are basic unary and binary operators available for the coefficient streams. For example, we can add two streams together:: - sage: g = L(lambda n: -n, True) - sage: f + g - 0 + ... + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: from sage.data_structures.coefficient_stream import CoefficientStream_add + sage: f = CoefficientStream_coefficient_function(lambda n: n, QQ, True, 0) + sage: [f[i] for i in range(10)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + sage: g = CoefficientStream_coefficient_function(lambda n: 1, QQ, True, 0) + sage: [g[i] for i in range(10)] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + sage: h = CoefficientStream_add(f, g) + sage: [h[i] for i in range(10)] + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Coefficient streams can be subtracted:: - sage: f - g - 2*z + 4*z^2 + 6*z^3 + 8*z^4 + 10*z^5 + 12*z^6 + 14*z^7 + ... + sage: from sage.data_structures.coefficient_stream import CoefficientStream_sub + sage: h = CoefficientStream_sub(f, g) + sage: [h[i] for i in range(10)] + [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8] Coefficient streams can be multiplied:: - sage: g = L(lambda n: n^2, True) - sage: f * g - z^2 + 6*z^3 + 20*z^4 + 50*z^5 + 105*z^6 + 196*z^7 + 336*z^8 + ... + sage: from sage.data_structures.coefficient_stream import CoefficientStream_mul + sage: h = CoefficientStream_mul(f, g) + sage: [h[i] for i in range(10)] + [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] Coefficient streams can be divided:: - sage: f / g - 1 - 2*z + 2*z^2 - 2*z^3 + 2*z^4 - 2*z^5 + 2*z^6 + ... + sage: from sage.data_structures.coefficient_stream import CoefficientStream_div + sage: h = CoefficientStream_div(f, g) + sage: [h[i] for i in range(10)] + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1] Two coefficient streams can be composed (depending on whether it exists):: - sage: f(g) - z + 6*z^2 + 28*z^3 + 124*z^4 + 527*z^5 + 2172*z^6 + 8755*z^7 + ... + sage: from sage.data_structures.coefficient_stream import CoefficientStream_composition + sage: g = CoefficientStream_coefficient_function(lambda n: n, QQ, True, 1) + sage: h = CoefficientStream_composition(f, g) + sage: [h[i] for i in range(10)] + [0, 1, 4, 14, 46, 145, 444, 1331, 3926, 11434] We can also use the unary negation operator on a coefficient stream:: - sage: -f - -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 - 7*z^7 + ... + sage: from sage.data_structures.coefficient_stream import CoefficientStream_neg + sage: h = CoefficientStream_neg(f) + sage: [h[i] for i in range(10)] + [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] Coefficient streams can be multiplied by a scalar:: - sage: f * 2 - 2*z + 4*z^2 + 6*z^3 + 8*z^4 + 10*z^5 + 12*z^6 + 14*z^7 + ... + sage: from sage.data_structures.coefficient_stream import CoefficientStream_scalar + sage: h = CoefficientStream_scalar(f, 2) + sage: [h[i] for i in range(10)] + [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] The multiplicative inverse of a series can also be obtained:: - sage: f = L(lambda n: 1, True, 1); f - z + z^2 + z^3 + z^4 + z^5 + z^6 + z^7 + ... - sage: ~f - z^-1 - 1 + ... + sage: from sage.data_structures.coefficient_stream import CoefficientStream_inv + sage: h = CoefficientStream_inv(g) + sage: [h[i] for i in range(10)] + [-2, 1, 0, 0, 0, 0, 0, 0, 0, 0] Functions can also be applied to a coefficient stream:: - sage: f.map_coefficients(lambda n: n*3) - 3*z + 3*z^2 + 3*z^3 + 3*z^4 + 3*z^5 + 3*z^6 + 3*z^7 + ... + sage: from sage.data_structures.coefficient_stream import CoefficientStream_apply_coeff + sage: h = CoefficientStream_apply_coeff(f, lambda n: n^2, QQ) + sage: [h[i] for i in range(10)] + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] AUTHORS: @@ -116,14 +138,39 @@ def __init__(self, sparse, approximate_valuation): class CoefficientStream_inexact(CoefficientStream): """ - CoefficientStream stream class when it is not or we do not know if it is + An abstract base class for the stream when it is not or we do not know if it is eventually geometric. + + INPUT: + + - ``sparse`` -- boolean; whether the implementation of the series is sparse + + - ``approximate_valuation`` -- integer; the approximate valuation of the series + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function, CoefficientStream_uninitialized + sage: CoefficientStream_coefficient_function.__base__ + + sage: CoefficientStream_uninitialized.__base__ + """ def __init__(self, is_sparse, approximate_valuation): """ Initialize the stream class for a CoefficientStream when it is not or it cannot be determined if it is eventually geometric. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_inexact + sage: f = CoefficientStream_inexact(True, 0) + sage: f._is_sparse + True + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: g = CoefficientStream_coefficient_function(lambda n: n, QQ, False, 0) + sage: g._offset + 0 """ super().__init__(is_sparse, approximate_valuation) @@ -137,6 +184,21 @@ def __init__(self, is_sparse, approximate_valuation): def __getstate__(self): """ Remove the cache from the pickle information so that it can be pickled. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric + sage: from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing + sage: Y = LaurentPolynomialRing(QQ, 'z') + sage: h = CoefficientStream_eventually_geometric(Y(1), True) + sage: g = CoefficientStream_eventually_geometric(Y([1, -1, -1]), True) + sage: from sage.data_structures.coefficient_stream import CoefficientStream_div + sage: u = CoefficientStream_div(h, g) + sage: [u[i] for i in range(10)] + [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] + sage: m = loads(dumps(u)) + sage: [m[i] for i in range(10)] + [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] """ d = dict(self.__dict__) if not self._is_sparse: @@ -149,6 +211,24 @@ def __getstate__(self): def __setstate__(self, d): """ Re-create the cache and the generator object when unpickling. + + INPUT: + + - ``d`` -- a dictionary that needs to be unpickled + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric + sage: from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing + sage: Y = LaurentPolynomialRing(QQ, 'z') + sage: h = CoefficientStream_eventually_geometric(Y(-1), True) + sage: g = CoefficientStream_eventually_geometric(Y([1, -1]), True) + sage: from sage.data_structures.coefficient_stream import CoefficientStream_div + sage: u = CoefficientStream_div(h, g) + sage: [u[i] for i in range(10)] + [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1] + sage: loads(dumps(u)) == u + True """ self.__dict__ = d if not self._is_sparse: @@ -158,6 +238,19 @@ def __setstate__(self, d): def __getitem__(self, n): """ Return the `n`-th coefficient of the series. + + INPUT: + + - ``n`` -- integer; the index of the coefficient to return + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: f = CoefficientStream_coefficient_function(lambda n: n, QQ, True, 0) + sage: f[3] + 3 + sage: [f[i] for i in range(10)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] """ if n < self._approximate_valuation: return ZZ.zero() @@ -183,6 +276,13 @@ def __getitem__(self, n): def valuation(self): """ Return the valuation of the series. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: f = CoefficientStream_coefficient_function(lambda n: n, QQ, True, 0) + sage: f.valuation() + 1 """ if self._is_sparse: n = self._approximate_valuation @@ -216,7 +316,7 @@ def valuation(self): class CoefficientStream_eventually_geometric(CoefficientStream): r""" - A lazy Laurent series which is known to be eventually geometric + Coefficient Stream for a series which is known to be eventually geometric INPUT: @@ -252,7 +352,7 @@ class CoefficientStream_eventually_geometric(CoefficientStream): def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): """ - Initialize a series that is known to be eventually geometric. + Initialize the stream for a series that is known to be eventually geometric. TESTS:: @@ -333,7 +433,7 @@ def __eq__(self, other): INPUT: - - ``other`` -- a lazy Laurent series which is known to be eventaully geometric + - ``other`` -- a stream for a series which is known to be eventually geometric EXAMPLES:: @@ -351,7 +451,7 @@ def __eq__(self, other): class CoefficientStream_coefficient_function(CoefficientStream_inexact): r""" - The coefficient function of a lazy Laurent series. + Class that returns the elements in the coefficient stream. INPUT: @@ -365,22 +465,22 @@ class CoefficientStream_coefficient_function(CoefficientStream_inexact): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: M = L(lambda n: n, True); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + 7*z^7 + ... - sage: M[4] - 4 + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) + sage: f[3] + 9 + sage: [f[i] for i in range(10)] + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] """ def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation): """ - Initialize the coefficient function of a lazy Laurent series. + Initialize. TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: type(L(lambda n:n, True)._coeff_stream) - + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 1) """ self._coefficient_function = coefficient_function self._ring = ring @@ -388,7 +488,7 @@ def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation) def get_coefficient(self, n): """ - Return the coefficient of the term with exponent ``n`` of the series. + Return the coefficient of the term with exponent ``n`` of the stream. INPUT: @@ -396,24 +496,24 @@ def get_coefficient(self, n): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: M = L(lambda n: n, True) - sage: M._coeff_stream.get_coefficient(4) + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: f = CoefficientStream_coefficient_function(lambda n: n, QQ, True, 0) + sage: f.get_coefficient(4) 4 """ return self._ring(self._coefficient_function(n)) def iterate_coefficients(self): """ - Return a generator for the coefficients of the series. + Return a generator for the coefficients of the stream. EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: M = L(lambda n: n, False) - sage: n = M._coeff_stream.iterate_coefficients() + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: f = CoefficientStream_coefficient_function(lambda n: 1, QQ, False, 0) + sage: n = f.iterate_coefficients() sage: [next(n) for _ in range(10)] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ n = self._offset ring = self._ring @@ -424,7 +524,7 @@ def iterate_coefficients(self): class CoefficientStream_uninitialized(CoefficientStream_inexact): r""" - An uninitialized lazy Laurent series. + Coefficient Stream for an uninitialized series. INPUT: @@ -495,12 +595,26 @@ def iterate_coefficients(self): class CoefficientStream_unary(CoefficientStream_inexact): """ - Abstract base class for unary operators. + Class for unary operators for the coefficient stream. INPUT: - - ``series`` -- series upon which the operator operates + - ``series`` -- stream upon which the operator operates + + - ``*args`` -- optional arguments (non-keyword) + + - ``**kwargs`` -- optional arguments (keyword) + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_inv, CoefficientStream_scalar) + sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, False, 1) + sage: g = CoefficientStream_inv(f) + sage: [g[i] for i in range(10)] + [-1, 1/2, 0, 0, 0, 0, 0, 0, 0, 0] + sage: g = CoefficientStream_scalar(f, 2) + sage: [g[i] for i in range(10)] + [0, 4, 8, 12, 16, 20, 24, 28, 32, 36] """ def __init__(self, series, *args, **kwargs): @@ -509,12 +623,9 @@ def __init__(self, series, *args, **kwargs): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = -1/(1 - z) - sage: f - -1 - z - z^2 - z^3 - z^4 - z^5 - z^6 + ... - sage: loads(dumps(f)) == f - True + sage: from sage.data_structures.coefficient_stream import CoefficientStream_neg + sage: CoefficientStream_neg.__base__ + """ self._series = series super().__init__(*args, **kwargs) @@ -536,22 +647,53 @@ def __eq__(self, other): """ Test equality. + INPUT: + + - ``other`` -- a stream of coefficients + TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_scalar) + sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, False, 1) + sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, False, 1) + sage: h = CoefficientStream_scalar(f, 2) + sage: n = CoefficientStream_scalar(g, 2) + sage: h == n + False + sage: n == n True - sage: f = ~(1 - z) - sage: g = ~(1 - z) - sage: f == g + sage: h == h True """ return isinstance(other, type(self)) and self._series == other._series class CoefficientStream_binary(CoefficientStream_inexact): + """ + Class for binary operators for the coefficient stream. + + INPUT: + + - ``left`` -- stream to the left side of the operator + + - ``right`` -- stream to the right side of the operator + + - ``*args`` -- optional arguments (non-keyword) + + - ``**kwargs`` -- optional arguments (keyword) + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_add, CoefficientStream_sub) + sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: h = CoefficientStream_add(f, g) + sage: [h[i] for i in range(10)] + [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] + sage: h = CoefficientStream_sub(f, g) + sage: [h[i] for i in range(10)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + """ def __init__(self, left, right, *args, **kwargs): """ @@ -559,13 +701,9 @@ def __init__(self, left, right, *args, **kwargs): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: loads(dumps(f)) == f - True - sage: f = 1/(1 - z) - 1/(1 + z) - sage: loads(dumps(f)) == f - True + sage: from sage.data_structures.coefficient_stream import CoefficientStream_mul + sage: (CoefficientStream_mul.__base__).__base__ + """ self._left = left self._right = right @@ -588,13 +726,25 @@ def __eq__(self, other): """ Test equality. + INPUT: + + - ``other`` -- a stream of coefficients + TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: g = 1/(1 - z) + 1/(1 + z) - sage: f == g + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, False, 1) + sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, False, 1) + sage: h = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 1) + sage: t = CoefficientStream_mul(f, g) + sage: u = CoefficientStream_mul(g, h) + sage: v = CoefficientStream_mul(h, f) + sage: t == u + False + sage: t == t True + sage: u == v + False """ if not isinstance(other, type(self)): return False @@ -602,16 +752,61 @@ def __eq__(self, other): class CoefficientStream_binary_commutative(CoefficientStream_binary): + """ + Abstract base class for commutative binary operators for the coefficient stream. + + INPUT: + + - ``other`` -- a stream of coefficients + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_add) + sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: h = CoefficientStream_add(f, g) + sage: [h[i] for i in range(10)] + [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] + sage: u = CoefficientStream_add(g, f) + sage: [u[i] for i in range(10)] + [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] + sage: h == u + True + """ def __hash__(self): """ Return the hash of ``self``. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = z^2 + z + sage: {f: 1} + {z + z^2: 1} """ return hash((type(self), frozenset([self._left, self._right]))) def __eq__(self, other): """ Test the equality between ``self`` and ``other``. + + INPUT: + + - ``other`` -- a stream of coefficients + + TESTS:: + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: h = CoefficientStream_mul(f, g) + sage: [h[i] for i in range(10)] + [0, 0, 2, 8, 20, 40, 70, 112, 168, 240] + sage: u = CoefficientStream_mul(g, f) + sage: [u[i] for i in range(10)] + [0, 0, 2, 8, 20, 40, 70, 112, 168, 240] + sage: h == u + True """ if not isinstance(other, type(self)): return False @@ -623,27 +818,81 @@ def __eq__(self, other): class CoefficientStream_zero(CoefficientStream): + """ + A coefficient Stream that is exactly equal to zero. + + INPUT: + + - ``sparse`` -- boolean; whether the coefficient stream is sparse or not + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_zero + sage: s = CoefficientStream_zero(True) + sage: s[5] + 0 + """ + def __init__(self, sparse): """ - Initialise a lazy Laurent series which is known to be zero. + Initialize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_zero + sage: s = CoefficientStream_zero(False) + sage: [s[i] for i in range(10)] + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] """ return super().__init__(sparse, 0) def __getitem__(self, n): """ Return the ``n``-th coefficient of the series. + + INPUT:: + + - ``n`` -- integer, the index of the coefficient to be returned + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_zero + sage: s = CoefficientStream_zero(True) + sage: s[1] + 0 + sage: sum([s[i] for i in range(10)]) + 0 """ return ZZ.zero() def valuation(self): """ Return the valuation of the series. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_zero + sage: s = CoefficientStream_zero(True) + sage: s.valuation() + +Infinity """ return infinity def __hash__(self): """ Return the hash of ``self``. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_zero + sage: s = CoefficientStream_zero(False) + sage: a = s.__hash__(); a + 0 + sage: t = CoefficientStream_zero(False) + sage: b = t.__hash__(); b + 0 + sage: b == a + True """ return 0 @@ -651,14 +900,39 @@ def __hash__(self): # Binary operations -class CoefficientStream_add(CoefficientStream_binary): +class CoefficientStream_add(CoefficientStream_binary_commutative): """ - Operator for addition. + Operator for addition of two coefficient streams. + + INPUT: + + - ``left`` -- stream of coefficients on the left side of the operator + + - ``right`` -- stream of coefficients on the right side of the operator + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_add, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) + sage: h = CoefficientStream_add(f, g) + sage: [h[i] for i in range(10)] + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + sage: u = CoefficientStream_add(g, f) + sage: [u[i] for i in range(10)] + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] """ def __init__(self, left, right): """ Initalize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_add) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 0) + sage: h = CoefficientStream_add(f, g) """ if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -668,13 +942,38 @@ def __init__(self, left, right): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the series when ``left`` is added to ``right``. + Return the ``n``-th coefficient of the stream when ``left`` is added to ``right``. + + INPUT:: + + - ``n`` -- integer, the index of the coefficient to be returned + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_add) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 0) + sage: h = CoefficientStream_add(f, g) + sage: h.get_coefficient(5) + 30 + sage: [h.get_coefficient(i) for i in range(10)] + [0, 2, 6, 12, 20, 30, 42, 56, 72, 90] """ return self._left[n] + self._right[n] def iterate_coefficients(self): """ - Return a generator for the coefficients of the series when ``left`` is added to ``right``. + Return a generator for the coefficients of the stream when ``left`` is added to ``right``. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_add) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n^3, ZZ, False, 0) + sage: h = CoefficientStream_add(f, g) + sage: n = h.iterate_coefficients() + sage: [next(n) for i in range(10)] + [1, 2, 9, 28, 65, 126, 217, 344, 513, 730] """ n = self._offset while True: @@ -684,12 +983,37 @@ def iterate_coefficients(self): class CoefficientStream_sub(CoefficientStream_binary): """ - Operator for subtraction. + Operator for subtraction of two coefficient streams. + + INPUT: + + - ``left`` -- stream of coefficients on the left side of the operator + + - ``right`` -- stream of coefficients on the right side of the operator + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_sub, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) + sage: h = CoefficientStream_sub(f, g) + sage: [h[i] for i in range(10)] + [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8] + sage: u = CoefficientStream_sub(g, f) + sage: [u[i] for i in range(10)] + [1, 0, -1, -2, -3, -4, -5, -6, -7, -8] """ def __init__(self, left, right): """ - Initialize. + Initalize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_sub) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 0) + sage: h = CoefficientStream_sub(f, g) """ if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -699,13 +1023,38 @@ def __init__(self, left, right): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the series when ``right`` is subtracted from ``left``. + Return the ``n``-th coefficient of the stream when ``right`` is subtracted from ``left``. + + INPUT:: + + - ``n`` -- integer, the index of the coefficient to be returned + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_sub) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 0) + sage: h = CoefficientStream_sub(f, g) + sage: h.get_coefficient(5) + -20 + sage: [h.get_coefficient(i) for i in range(10)] + [0, 0, -2, -6, -12, -20, -30, -42, -56, -72] """ return self._left[n] - self._right[n] def iterate_coefficients(self): """ - Return the generator for the coefficients of the series when ``right`` is subtracted from ``left``. + Return a generator for the coefficients of the stream when ``right`` is subtracted from ``left``. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_sub) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n^3, ZZ, False, 0) + sage: h = CoefficientStream_sub(f, g) + sage: n = h.iterate_coefficients() + sage: [next(n) for i in range(10)] + [1, 0, -7, -26, -63, -124, -215, -342, -511, -728] """ n = self._offset while True: @@ -713,16 +1062,41 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_mul(CoefficientStream_binary): +class CoefficientStream_mul(CoefficientStream_binary_commutative): """ - Operator for multiplication. + Operator for multiplication of two coefficient streams. We are assuming commutativity of the coefficient ring here. + + INPUT: + + - ``left`` -- stream of coefficients on the left side of the operator + + - ``right`` -- stream of coefficients on the right side of the operator + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_mul, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) + sage: h = CoefficientStream_mul(f, g) + sage: [h[i] for i in range(10)] + [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] + sage: u = CoefficientStream_mul(g, f) + sage: [u[i] for i in range(10)] + [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] """ def __init__(self, left, right): """ - Initialize. + Initalize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 0) + sage: h = CoefficientStream_mul(f, g) """ if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -732,7 +1106,22 @@ def __init__(self, left, right): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the series when ``left`` is multiplied by ``right``. + Return the ``n``-th coefficient of the stream when ``right`` is multiplied with ``left``. + + INPUT:: + + - ``n`` -- integer, the index of the coefficient to be returned + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 0) + sage: h = CoefficientStream_mul(f, g) + sage: h.get_coefficient(5) + 50 + sage: [h.get_coefficient(i) for i in range(10)] + [0, 0, 1, 6, 20, 50, 105, 196, 336, 540] """ c = ZZ.zero() for k in range(self._left._approximate_valuation, @@ -744,7 +1133,17 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return the generator for the coefficients of the series when ``left`` is multiplied by ``right``. + Return a generator for the coefficients of the stream when ``right`` is multiplied with ``left``. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n^3, ZZ, False, 0) + sage: h = CoefficientStream_mul(f, g) + sage: n = h.iterate_coefficients() + sage: [next(n) for i in range(10)] + [0, 1, 9, 36, 100, 225, 441, 784, 1296, 2025] """ n = self._offset while True: @@ -760,12 +1159,37 @@ def iterate_coefficients(self): class CoefficientStream_div(CoefficientStream_binary): """ - Return ``left`` divided by ``right``. + Operator for division of two coefficient streams. + + INPUT: + + - ``left`` -- stream of coefficients on the left side of the operator + + - ``right`` -- stream of coefficients on the right side of the operator + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_div, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: g = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 1) + sage: h = CoefficientStream_div(f, g) + sage: [h[i] for i in range(10)] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + sage: u = CoefficientStream_div(g, f) + sage: [u[i] for i in range(10)] + [1, -1, 0, 0, 0, 0, 0, 0, 0, 0] """ def __init__(self, left, right): """ - Initialize. + Initalize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_div) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 1) + sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 1) + sage: h = CoefficientStream_div(f, g) """ lv = left.valuation() rv = right.valuation() @@ -776,7 +1200,22 @@ def __init__(self, left, right): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the series when ``left`` is divided by ``right``. + Return the ``n``-th coefficient of the stream when ``left`` is divided by ``right``. + + INPUT:: + + - ``n`` -- integer, the index of the coefficient to be returned + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_div) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 1) + sage: h = CoefficientStream_div(f, g) + sage: h.get_coefficient(5) + -2 + sage: [h.get_coefficient(i) for i in range(10)] + [1, -2, 2, -2, 2, -2, 2, -2, 2, -2] """ lv = self._lv rv = self._rv @@ -789,7 +1228,17 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return the generator for the coefficients of the series when ``left`` is divided by ``right``. + Return a generator for the coefficients of the stream when ``left`` is divided by ``right``. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_div) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 1) + sage: g = CoefficientStream_coefficient_function(lambda n: n^3, ZZ, False, 1) + sage: h = CoefficientStream_div(f, g) + sage: n = h.iterate_coefficients() + sage: [next(n) for i in range(10)] + [1, -7, 30, -114, 426, -1590, 5934, -22146, 82650, -308454] """ n = self._offset lv = self._lv @@ -807,7 +1256,7 @@ def iterate_coefficients(self): class CoefficientStream_composition(CoefficientStream_binary): - r""" + """ Return ``f`` composed by ``g``. This is the composition `(f \circ g)(z) = f(g(z))`. @@ -816,11 +1265,30 @@ class CoefficientStream_composition(CoefficientStream_binary): - ``f`` -- a :class:`CoefficientStream` - ``g`` -- a :class:`CoefficientStream` with positive valuation + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_composition, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: g = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 1) + sage: h = CoefficientStream_composition(f, g) + sage: [h[i] for i in range(10)] + [0, 1, 3, 8, 20, 48, 112, 256, 576, 1280] + sage: u = CoefficientStream_composition(g, f) + sage: [u[i] for i in range(10)] + [0, 1, 3, 8, 21, 55, 144, 377, 987, 2584] """ def __init__(self, f, g): """ - Initialize. + Initalize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_composition) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 1) + sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 1) + sage: h = CoefficientStream_composition(f, g) """ assert g._approximate_valuation > 0 self._fv = f._approximate_valuation @@ -839,7 +1307,22 @@ def __init__(self, f, g): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the series when ``f`` is composed by ``g``. + Return the ``n``-th coefficient of the stream when ``f`` is composed by ``g``. + + INPUT:: + + - ``n`` -- integer, the index of the coefficient to be returned + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_composition) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 1) + sage: h = CoefficientStream_composition(f, g) + sage: h.get_coefficient(5) + 527 + sage: [h.get_coefficient(i) for i in range(10)] + [0, 1, 6, 28, 124, 527, 2172, 8755, 34704, 135772] """ if n < 0: return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) @@ -853,7 +1336,17 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return the generator for the coefficients of the series when ``f`` is composed by ``g``. + Return a generator for the coefficients of the stream when ``f`` is composed by ``g``. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_composition) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 1) + sage: g = CoefficientStream_coefficient_function(lambda n: n^3, ZZ, False, 1) + sage: h = CoefficientStream_composition(f, g) + sage: n = h.iterate_coefficients() + sage: [next(n) for i in range(10)] + [1, 9, 44, 207, 991, 4752, 22769, 109089, 522676, 2504295] """ n = self._approximate_valuation while True: @@ -866,12 +1359,34 @@ def iterate_coefficients(self): class CoefficientStream_scalar(CoefficientStream_unary): """ - Operator for multiplying with a scalar. + Operator for multiplying a coefficient stream with a scalar. + + INPUT: + + - ``series`` -- a :class:`CoefficientStream` + + - ``scalar`` -- a scalar + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_scalar, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 1) + sage: g = CoefficientStream_scalar(f, 2) + sage: [g[i] for i in range(10)] + [0, 2, 2, 2, 2, 2, 2, 2, 2, 2] """ def __init__(self, series, scalar): """ Initialize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_scalar, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: -1, ZZ, True, 0) + sage: g = CoefficientStream_scalar(f, 3) + sage: [g[i] for i in range(10)] + [-3, -3, -3, -3, -3, -3, -3, -3, -3, -3] """ self._scalar = scalar @@ -880,12 +1395,35 @@ def __init__(self, series, scalar): def get_coefficient(self, n): """ Return the ``n``-th coefficient of the ``series`` when multiplied by the ``scalar``. + + INPUT:: + + - ``n`` -- integer, the index of the coefficient to be returned + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_scalar, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: g = CoefficientStream_scalar(f, 3) + sage: g.get_coefficient(5) + 15 + sage: [g.get_coefficient(i) for i in range(10)] + [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] """ return self._series[n] * self._scalar def iterate_coefficients(self): """ Return the generator for the coefficients of the ``series`` when multiplied by the ``scalar``. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_scalar, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) + sage: g = CoefficientStream_scalar(f, 4) + sage: n = g.iterate_coefficients() + sage: [next(n) for i in range(10)] + [4, 16, 36, 64, 100, 144, 196, 256, 324, 400] """ n = self._offset while True: @@ -895,24 +1433,67 @@ def iterate_coefficients(self): class CoefficientStream_neg(CoefficientStream_unary): """ - Operator for negative of the series. + Operator for negative of the stream. + + INPUT: + + - ``series`` -- a :class:`CoefficientStream` + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_neg, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 1) + sage: g = CoefficientStream_neg(f) + sage: [g[i] for i in range(10)] + [0, -1, -1, -1, -1, -1, -1, -1, -1, -1] """ def __init__(self, series): """ Initialize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_neg, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: -1, ZZ, True, 0) + sage: g = CoefficientStream_neg(f) + sage: [g[i] for i in range(10)] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ super().__init__(series, series._is_sparse, series._approximate_valuation) def get_coefficient(self, n): """ Return the ``n``-th coefficient of the ``series`` when negated. + + INPUT:: + + - ``n`` -- integer, the index of the coefficient to be returned + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_neg, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: g = CoefficientStream_neg(f) + sage: g.get_coefficient(5) + -5 + sage: [g.get_coefficient(i) for i in range(10)] + [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] """ return -self._series[n] def iterate_coefficients(self): """ Return the generator for the coefficients of the ``series`` when negated. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_neg, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) + sage: g = CoefficientStream_neg(f) + sage: n = g.iterate_coefficients() + sage: [next(n) for i in range(10)] + [-1, -4, -9, -16, -25, -36, -49, -64, -81, -100] """ n = self._offset while True: @@ -922,12 +1503,32 @@ def iterate_coefficients(self): class CoefficientStream_inv(CoefficientStream_unary): """ - Operator for multiplicative inverse of the series. + Operator for multiplicative inverse of the stream. + + INPUT: + + - ``series`` -- a :class:`CoefficientStream` + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 1) + sage: g = CoefficientStream_inv(f) + sage: [g[i] for i in range(10)] + [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0] """ def __init__(self, series): """ Initialize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: -1, ZZ, True, 0) + sage: g = CoefficientStream_inv(f) + sage: [g[i] for i in range(10)] + [-1, 1, 0, 0, 0, 0, 0, 0, 0, 0] """ v = series.valuation() super().__init__(series, series._is_sparse, -v) @@ -938,6 +1539,20 @@ def __init__(self, series): def get_coefficient(self, n): """ Return the ``n``-th coefficient of the multiplicative inverse of the ``series``. + + INPUT:: + + - ``n`` -- integer, the index of the coefficient to be returned + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: g = CoefficientStream_inv(f) + sage: g.get_coefficient(5) + 0 + sage: [g.get_coefficient(i) for i in range(10)] + [-2, 1, 0, 0, 0, 0, 0, 0, 0, 0] """ v = self._approximate_valuation if n == v: @@ -950,6 +1565,15 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ Return the generator for the coefficients of the multiplicative inverse of the ``series``. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) + sage: g = CoefficientStream_inv(f) + sage: n = g.iterate_coefficients() + sage: [next(n) for i in range(10)] + [1, -4, 7, -8, 8, -8, 8, -8, 8, -8] """ v = self._approximate_valuation # shorthand name n = 0 # Counts the number of places from the valuation @@ -968,12 +1592,36 @@ def iterate_coefficients(self): class CoefficientStream_apply_coeff(CoefficientStream_unary): """ - Return the series with ``function`` applied to each coefficient of this series. + Return the stream with ``function`` applied to each coefficient of this series. + + INPUT: + + - ``series`` -- a :class:`CoefficientStream` + + - ``function`` -- a python function that modifies the elements of the stream + + - ``ring`` -- the base ring of the stream + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_apply_coeff, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 1) + sage: g = CoefficientStream_apply_coeff(f, lambda n: -n, ZZ) + sage: [g[i] for i in range(10)] + [0, -1, -1, -1, -1, -1, -1, -1, -1, -1] """ def __init__(self, series, function, ring): """ Initialize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_apply_coeff, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: -1, ZZ, True, 0) + sage: g = CoefficientStream_apply_coeff(f, lambda n: n + 1, ZZ) + sage: [g[i] for i in range(10)] + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] """ self._function = function self._ring = ring @@ -982,6 +1630,20 @@ def __init__(self, series, function, ring): def get_coefficient(self, n): """ Return the ``n``-th coefficient of the series with ``function`` applied to each coefficient. + + INPUT:: + + - ``n`` -- integer, the index of the coefficient to be returned + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_apply_coeff, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: g = CoefficientStream_apply_coeff(f, lambda n: n^2, ZZ) + sage: g.get_coefficient(5) + 25 + sage: [g.get_coefficient(i) for i in range(10)] + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] """ c = self._ring(self._function(self._series[n])) return c @@ -989,6 +1651,15 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ Return the generator for the coefficients of the series with ``function`` applied to each coefficient. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_apply_coeff, CoefficientStream_coefficient_function) + sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) + sage: g = CoefficientStream_apply_coeff(f, lambda n: 2*n, ZZ) + sage: n = g.iterate_coefficients() + sage: [next(n) for i in range(10)] + [2, 8, 18, 32, 50, 72, 98, 128, 162, 200] """ n = self._offset while True: From fd6549a976f5e1bf5c3c334717573740ad5c1f2f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 28 Jul 2021 08:48:45 +1000 Subject: [PATCH 87/98] Removing json file that should not be there. --- src/sage/.vscode/settings.json | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 src/sage/.vscode/settings.json diff --git a/src/sage/.vscode/settings.json b/src/sage/.vscode/settings.json deleted file mode 100644 index c1d1f0a2469..00000000000 --- a/src/sage/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "python.linting.pycodestyleEnabled": true, - "python.linting.enabled": true -} \ No newline at end of file From 4b7efa309d011373d52fe5b6c7930213be9cb1c7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 28 Jul 2021 09:31:29 +1000 Subject: [PATCH 88/98] Fixing up some documentation and doctests. --- .../data_structures/coefficient_stream.py | 303 ++++++++---------- src/sage/rings/lazy_laurent_series.py | 72 +++-- src/sage/rings/lazy_laurent_series_ring.py | 46 ++- 3 files changed, 209 insertions(+), 212 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 2387ba297e6..f5da2a09023 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -6,9 +6,9 @@ can be used to build up more complex streams for different kinds of series (Laurent, Dirichlet, etc). -EXAMPLES:: +EXAMPLES: - The coefficient stream can be used to build up a Lazy laurent series:: +The coefficient stream can be used to build up a Lazy laurent series:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: f = L(lambda n: n, True) @@ -32,28 +32,28 @@ sage: [h[i] for i in range(10)] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - Coefficient streams can be subtracted:: +Coefficient streams can be subtracted:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_sub sage: h = CoefficientStream_sub(f, g) sage: [h[i] for i in range(10)] [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8] - Coefficient streams can be multiplied:: +Coefficient streams can be multiplied:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_mul sage: h = CoefficientStream_mul(f, g) sage: [h[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] - Coefficient streams can be divided:: +Coefficient streams can be divided:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_div sage: h = CoefficientStream_div(f, g) sage: [h[i] for i in range(10)] [0, 1, 1, 1, 1, 1, 1, 1, 1, 1] - Two coefficient streams can be composed (depending on whether it exists):: +Two coefficient streams can be composed (depending on whether it exists):: sage: from sage.data_structures.coefficient_stream import CoefficientStream_composition sage: g = CoefficientStream_coefficient_function(lambda n: n, QQ, True, 1) @@ -61,28 +61,28 @@ sage: [h[i] for i in range(10)] [0, 1, 4, 14, 46, 145, 444, 1331, 3926, 11434] - We can also use the unary negation operator on a coefficient stream:: +We can also use the unary negation operator on a coefficient stream:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_neg sage: h = CoefficientStream_neg(f) sage: [h[i] for i in range(10)] [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] - Coefficient streams can be multiplied by a scalar:: +Coefficient streams can be multiplied by a scalar:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_scalar sage: h = CoefficientStream_scalar(f, 2) sage: [h[i] for i in range(10)] [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] - The multiplicative inverse of a series can also be obtained:: +The multiplicative inverse of a series can also be obtained:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_inv sage: h = CoefficientStream_inv(g) sage: [h[i] for i in range(10)] [-2, 1, 0, 0, 0, 0, 0, 0, 0, 0] - Functions can also be applied to a coefficient stream:: +Functions can also be applied to a coefficient stream:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_apply_coeff sage: h = CoefficientStream_apply_coeff(f, lambda n: n^2, QQ) @@ -104,24 +104,20 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** + from sage.rings.integer_ring import ZZ from sage.rings.infinity import infinity class CoefficientStream(): """ - Abstract base class for all streams. + Abstract base class for all coefficient streams. INPUT: - ``sparse`` -- boolean; whether the implementation of the series is sparse - - ``approximate_valuation`` -- the approximate valuation of the series - - EXAMPLES:: - """ - def __init__(self, sparse, approximate_valuation): """ Initialize the auxillary class for any series. @@ -138,24 +134,14 @@ def __init__(self, sparse, approximate_valuation): class CoefficientStream_inexact(CoefficientStream): """ - An abstract base class for the stream when it is not or we do not know if it is + An abstract base class for the stream when we do not know it is eventually geometric. INPUT: - ``sparse`` -- boolean; whether the implementation of the series is sparse - - ``approximate_valuation`` -- integer; the approximate valuation of the series - - EXAMPLES:: - - sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function, CoefficientStream_uninitialized - sage: CoefficientStream_coefficient_function.__base__ - - sage: CoefficientStream_uninitialized.__base__ - """ - def __init__(self, is_sparse, approximate_valuation): """ Initialize the stream class for a CoefficientStream when it is not @@ -243,14 +229,28 @@ def __getitem__(self, n): - ``n`` -- integer; the index of the coefficient to return - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function - sage: f = CoefficientStream_coefficient_function(lambda n: n, QQ, True, 0) + sage: f = CoefficientStream_coefficient_function(lambda n: n^2, QQ, True, 0) sage: f[3] - 3 + 9 + sage: f._cache + {3: 9} sage: [f[i] for i in range(10)] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + sage: f._cache + {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81} + + sage: f = CoefficientStream_coefficient_function(lambda n: n^2, QQ, False, 0) + sage: f[3] + 9 + sage: f._cache + [0, 1, 4, 9] + sage: [f[i] for i in range(10)] + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] + sage: f._cache + [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] """ if n < self._approximate_valuation: return ZZ.zero() @@ -277,7 +277,7 @@ def valuation(self): """ Return the valuation of the series. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function sage: f = CoefficientStream_coefficient_function(lambda n: n, QQ, True, 0) @@ -316,17 +316,15 @@ def valuation(self): class CoefficientStream_eventually_geometric(CoefficientStream): r""" - Coefficient Stream for a series which is known to be eventually geometric + Coefficient stream for a series which is known to be eventually geometric. INPUT: - - ``laurent_polynomial`` -- a Laurent polynomial - - - ``is_sparse`` -- a boolean, which specifies whether the series is sparse - - - ``constant`` -- either ``None`` (default: ``None``) or pair of an element of the base ring and an integer - - - ``degree`` -- either ``None`` (default: ``None``) or an integer, the degree of the polynomial + - ``laurent_polynomial`` -- a Laurent polynomial + - ``is_sparse`` -- boolean; specifies whether the series is sparse + - ``constant`` -- (default: 0) the eventual constant value + - ``degree`` -- (default: the degree of ``laurent_polynomial`` plus 1) + the degree where the coefficient stream becomes ``constant`` EXAMPLES:: @@ -352,14 +350,21 @@ class CoefficientStream_eventually_geometric(CoefficientStream): def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): """ - Initialize the stream for a series that is known to be eventually geometric. + Initialize ``self``. TESTS:: - sage: L. = LazyLaurentSeriesRing(QQ) - sage: M = z^2 + z - sage: type(M._coeff_stream) - + sage: R. = LaurentPolynomialRing(QQ) + sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric + sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False) + sage: [X[i] for i in range(-1,8)] + [1, 0, 0, 1, 0, 0, 0, 0, 0] + sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, True, 5) + sage: [X[i] for i in range(-1,8)] + [1, 0, 0, 1, 5, 5, 5, 5, 5] + sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 5, 4) + sage: [X[i] for i in range(-1,8)] + [1, 0, 0, 1, 0, 5, 5, 5, 5] """ if constant is None: constant = ZZ.zero() @@ -386,16 +391,19 @@ def __getitem__(self, n): INPUT: - - ``n`` -- integer, the degree for which the coefficient is required + - ``n`` -- integer; the degree for which the coefficient is required EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = 1 + z + z^2 + z^3 - sage: f[3] + sage: R. = LaurentPolynomialRing(QQ) + sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric + sage: f = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 3, 10) + sage: f[2] 1 sage: f[8] 0 + sage: f[15] + 3 """ if n >= self._degree: return self._constant @@ -455,12 +463,10 @@ class CoefficientStream_coefficient_function(CoefficientStream_inexact): INPUT: - - ``coefficient_function`` -- a python function that generates the coefficients of the series - + - ``coefficient_function`` -- a python function that generates the + coefficients of the series - ``ring`` -- the base ring of the series - - - ``is_sparse`` -- a boolean, which specifies whether the series is sparse - + - ``is_sparse`` -- boolean; specifies whether the series is sparse - ``approximate_valuation`` -- the approximate valuation of the series EXAMPLES:: @@ -488,11 +494,11 @@ def __init__(self, coefficient_function, ring, is_sparse, approximate_valuation) def get_coefficient(self, n): """ - Return the coefficient of the term with exponent ``n`` of the stream. + Return the ``n``-th coefficient of ``self``. INPUT: - - ``n`` -- integer, the degree for which the coefficient is required + - ``n`` -- integer; the degree for the coefficient EXAMPLES:: @@ -505,7 +511,7 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return a generator for the coefficients of the stream. + A generator for the coefficients of ``self``. EXAMPLES:: @@ -524,12 +530,11 @@ def iterate_coefficients(self): class CoefficientStream_uninitialized(CoefficientStream_inexact): r""" - Coefficient Stream for an uninitialized series. + Coefficient stream for an uninitialized series. INPUT: - - ``is_sparse`` -- a boolean, which specifies whether the series is sparse - + - ``is_sparse`` -- boolean; which specifies whether the series is sparse - ``approximate_valuation`` -- the approximate valuation of the series EXAMPLES:: @@ -539,7 +544,6 @@ class CoefficientStream_uninitialized(CoefficientStream_inexact): sage: N Uninitialized Lazy Laurent Series """ - def __init__(self, is_sparse, approximate_valuation): """ Initialize an uninitialized lazy laurent series. @@ -556,11 +560,11 @@ def __init__(self, is_sparse, approximate_valuation): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the series. + Return the ``n``-th coefficient of ``self``. INPUT: - - ``n`` -- integer, the degree for which the coefficient is required + - ``n`` -- integer; the degree for the coefficient EXAMPLES:: @@ -575,7 +579,7 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return a generator for the coefficients of the series. + A generator for the coefficients of ``self``. EXAMPLES:: @@ -594,16 +598,12 @@ def iterate_coefficients(self): class CoefficientStream_unary(CoefficientStream_inexact): - """ + r""" Class for unary operators for the coefficient stream. INPUT: - - ``series`` -- stream upon which the operator operates - - - ``*args`` -- optional arguments (non-keyword) - - - ``**kwargs`` -- optional arguments (keyword) + - ``series`` -- :class:`CoefficientStream` the operator acts on EXAMPLES:: @@ -619,7 +619,7 @@ class CoefficientStream_unary(CoefficientStream_inexact): def __init__(self, series, *args, **kwargs): """ - Initialize. + Initialize ``self``. TESTS:: @@ -674,13 +674,8 @@ class CoefficientStream_binary(CoefficientStream_inexact): INPUT: - - ``left`` -- stream to the left side of the operator - - - ``right`` -- stream to the right side of the operator - - - ``*args`` -- optional arguments (non-keyword) - - - ``**kwargs`` -- optional arguments (keyword) + - ``left`` -- :class:`CoefficientStream` for the left side of the operator + - ``right`` -- :class:`CoefficientStream` for the right side of the operator EXAMPLES:: @@ -752,12 +747,13 @@ def __eq__(self, other): class CoefficientStream_binary_commutative(CoefficientStream_binary): - """ - Abstract base class for commutative binary operators for the coefficient stream. + r""" + Abstract base class for commutative binary operators for the + coefficient stream. INPUT: - - ``other`` -- a stream of coefficients + - ``other`` -- a :class:`CoefficientStream` EXAMPLES:: @@ -773,7 +769,6 @@ class CoefficientStream_binary_commutative(CoefficientStream_binary): sage: h == u True """ - def __hash__(self): """ Return the hash of ``self``. @@ -852,7 +847,7 @@ def __getitem__(self, n): INPUT:: - - ``n`` -- integer, the index of the coefficient to be returned + - ``n`` -- integer; the index of the coefficient to be returned TESTS:: @@ -869,7 +864,7 @@ def valuation(self): """ Return the valuation of the series. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_zero sage: s = CoefficientStream_zero(True) @@ -896,10 +891,10 @@ def __hash__(self): """ return 0 + ##################################################################### # Binary operations - class CoefficientStream_add(CoefficientStream_binary_commutative): """ Operator for addition of two coefficient streams. @@ -907,7 +902,6 @@ class CoefficientStream_add(CoefficientStream_binary_commutative): INPUT: - ``left`` -- stream of coefficients on the left side of the operator - - ``right`` -- stream of coefficients on the right side of the operator EXAMPLES:: @@ -922,7 +916,6 @@ class CoefficientStream_add(CoefficientStream_binary_commutative): sage: [u[i] for i in range(10)] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] """ - def __init__(self, left, right): """ Initalize. @@ -942,13 +935,13 @@ def __init__(self, left, right): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the stream when ``left`` is added to ``right``. + Return the ``n``-th coefficient of ``self``. - INPUT:: + INPUT: - - ``n`` -- integer, the index of the coefficient to be returned + - ``n`` -- integer; the degree for the coefficient - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_add) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) @@ -963,9 +956,9 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return a generator for the coefficients of the stream when ``left`` is added to ``right``. + A generator for the coefficients of ``self``. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_add) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 0) @@ -988,7 +981,6 @@ class CoefficientStream_sub(CoefficientStream_binary): INPUT: - ``left`` -- stream of coefficients on the left side of the operator - - ``right`` -- stream of coefficients on the right side of the operator EXAMPLES:: @@ -1006,7 +998,7 @@ class CoefficientStream_sub(CoefficientStream_binary): def __init__(self, left, right): """ - Initalize. + Initalize ``self``. TESTS:: @@ -1023,13 +1015,13 @@ def __init__(self, left, right): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the stream when ``right`` is subtracted from ``left``. + Return the ``n``-th coefficient of ``self``. - INPUT:: + INPUT: - - ``n`` -- integer, the index of the coefficient to be returned + - ``n`` -- integer; the degree for the coefficient - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_sub) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) @@ -1044,9 +1036,9 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return a generator for the coefficients of the stream when ``right`` is subtracted from ``left``. + A generator for the coefficients of ``self``. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_sub) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 0) @@ -1071,7 +1063,6 @@ class CoefficientStream_mul(CoefficientStream_binary_commutative): INPUT: - ``left`` -- stream of coefficients on the left side of the operator - - ``right`` -- stream of coefficients on the right side of the operator EXAMPLES:: @@ -1086,10 +1077,9 @@ class CoefficientStream_mul(CoefficientStream_binary_commutative): sage: [u[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] """ - def __init__(self, left, right): """ - Initalize. + Initalize ``self``. TESTS:: @@ -1106,13 +1096,13 @@ def __init__(self, left, right): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the stream when ``right`` is multiplied with ``left``. + Return the ``n``-th coefficient of ``self``. - INPUT:: + INPUT: - - ``n`` -- integer, the index of the coefficient to be returned + - ``n`` -- integer; the degree for the coefficient - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) @@ -1133,9 +1123,9 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return a generator for the coefficients of the stream when ``right`` is multiplied with ``left``. + A generator for the coefficients of ``self``. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 0) @@ -1164,7 +1154,6 @@ class CoefficientStream_div(CoefficientStream_binary): INPUT: - ``left`` -- stream of coefficients on the left side of the operator - - ``right`` -- stream of coefficients on the right side of the operator EXAMPLES:: @@ -1182,7 +1171,7 @@ class CoefficientStream_div(CoefficientStream_binary): def __init__(self, left, right): """ - Initalize. + Initalize ``self``. TESTS:: @@ -1200,13 +1189,13 @@ def __init__(self, left, right): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the stream when ``left`` is divided by ``right``. + Return the ``n``-th coefficient of ``self``. - INPUT:: + INPUT: - - ``n`` -- integer, the index of the coefficient to be returned + - ``n`` -- integer; the degree for the coefficient - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_div) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) @@ -1228,9 +1217,9 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return a generator for the coefficients of the stream when ``left`` is divided by ``right``. + A generator for the coefficients of ``self``. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_div) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 1) @@ -1256,7 +1245,7 @@ def iterate_coefficients(self): class CoefficientStream_composition(CoefficientStream_binary): - """ + r""" Return ``f`` composed by ``g``. This is the composition `(f \circ g)(z) = f(g(z))`. @@ -1278,10 +1267,9 @@ class CoefficientStream_composition(CoefficientStream_binary): sage: [u[i] for i in range(10)] [0, 1, 3, 8, 21, 55, 144, 377, 987, 2584] """ - def __init__(self, f, g): """ - Initalize. + Initalize ``self``. TESTS:: @@ -1307,13 +1295,13 @@ def __init__(self, f, g): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the stream when ``f`` is composed by ``g``. + Return the ``n``-th coefficient of ``self``. - INPUT:: + INPUT: - - ``n`` -- integer, the index of the coefficient to be returned + - ``n`` -- integer; the degree for the coefficient - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_composition) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) @@ -1336,9 +1324,9 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return a generator for the coefficients of the stream when ``f`` is composed by ``g``. + A generator for the coefficients of ``self``. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_composition) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 1) @@ -1353,10 +1341,10 @@ def iterate_coefficients(self): yield self.get_coefficient(n) n += 1 + ##################################################################### # Unary operations - class CoefficientStream_scalar(CoefficientStream_unary): """ Operator for multiplying a coefficient stream with a scalar. @@ -1364,7 +1352,6 @@ class CoefficientStream_scalar(CoefficientStream_unary): INPUT: - ``series`` -- a :class:`CoefficientStream` - - ``scalar`` -- a scalar EXAMPLES:: @@ -1375,7 +1362,6 @@ class CoefficientStream_scalar(CoefficientStream_unary): sage: [g[i] for i in range(10)] [0, 2, 2, 2, 2, 2, 2, 2, 2, 2] """ - def __init__(self, series, scalar): """ Initialize. @@ -1389,18 +1375,17 @@ def __init__(self, series, scalar): [-3, -3, -3, -3, -3, -3, -3, -3, -3, -3] """ self._scalar = scalar - super().__init__(series, series._is_sparse, series._approximate_valuation) def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the ``series`` when multiplied by the ``scalar``. + Return the ``n``-th coefficient of ``self``. - INPUT:: + INPUT: - - ``n`` -- integer, the index of the coefficient to be returned + - ``n`` -- integer; the degree for the coefficient - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_scalar, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) @@ -1414,9 +1399,9 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return the generator for the coefficients of the ``series`` when multiplied by the ``scalar``. + A generator for the coefficients of ``self``. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_scalar, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) @@ -1430,7 +1415,6 @@ def iterate_coefficients(self): yield self._series[n] * self._scalar n += 1 - class CoefficientStream_neg(CoefficientStream_unary): """ Operator for negative of the stream. @@ -1464,13 +1448,13 @@ def __init__(self, series): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the ``series`` when negated. + Return the ``n``-th coefficient of ``self``. - INPUT:: + INPUT: - - ``n`` -- integer, the index of the coefficient to be returned + - ``n`` -- integer; the degree for the coefficient - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_neg, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) @@ -1484,9 +1468,9 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return the generator for the coefficients of the ``series`` when negated. + A generator for the coefficients of ``self``. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_neg, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) @@ -1517,7 +1501,6 @@ class CoefficientStream_inv(CoefficientStream_unary): sage: [g[i] for i in range(10)] [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0] """ - def __init__(self, series): """ Initialize. @@ -1538,13 +1521,13 @@ def __init__(self, series): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the multiplicative inverse of the ``series``. + Return the ``n``-th coefficient of ``self``. - INPUT:: + INPUT: - - ``n`` -- integer, the index of the coefficient to be returned + - ``n`` -- integer; the degree for the coefficient - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) @@ -1564,9 +1547,9 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return the generator for the coefficients of the multiplicative inverse of the ``series``. + A generator for the coefficients of ``self``. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) @@ -1597,9 +1580,7 @@ class CoefficientStream_apply_coeff(CoefficientStream_unary): INPUT: - ``series`` -- a :class:`CoefficientStream` - - - ``function`` -- a python function that modifies the elements of the stream - + - ``function`` -- a function that modifies the elements of the stream - ``ring`` -- the base ring of the stream EXAMPLES:: @@ -1610,7 +1591,6 @@ class CoefficientStream_apply_coeff(CoefficientStream_unary): sage: [g[i] for i in range(10)] [0, -1, -1, -1, -1, -1, -1, -1, -1, -1] """ - def __init__(self, series, function, ring): """ Initialize. @@ -1629,13 +1609,13 @@ def __init__(self, series, function, ring): def get_coefficient(self, n): """ - Return the ``n``-th coefficient of the series with ``function`` applied to each coefficient. + Return the ``n``-th coefficient of ``self``. - INPUT:: + INPUT: - - ``n`` -- integer, the index of the coefficient to be returned + - ``n`` -- integer; the degree for the coefficient - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_apply_coeff, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) @@ -1650,9 +1630,9 @@ def get_coefficient(self, n): def iterate_coefficients(self): """ - Return the generator for the coefficients of the series with ``function`` applied to each coefficient. + A generator for the coefficients of ``self``. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_apply_coeff, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) @@ -1666,3 +1646,4 @@ def iterate_coefficients(self): c = self._ring(self._function(self._series[n])) yield c n += 1 + diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 3e5e0bba5b9..8e0c2801b22 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -102,12 +102,6 @@ class LazyLaurentSeries(ModuleElement): r""" A Laurent series where the coefficients are computed lazily. - INPUT: - - - ``parent`` -- The base ring for the series - - - ``coeff_stream`` -- The auxiliary class that handles the coefficient stream - EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) @@ -134,7 +128,12 @@ class LazyLaurentSeries(ModuleElement): def __init__(self, parent, coeff_stream): """ - Initialize the series. + Initialize ``self``. + + INPUT: + + - ``parent`` -- the base ring for the series + - ``coeff_stream`` -- the coefficient stream defining the series TESTS:: @@ -153,7 +152,7 @@ def __getitem__(self, n): - ``n`` -- integer - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: f = z/(1 - 2*z^3) @@ -181,19 +180,20 @@ def __getitem__(self, n): def __call__(self, g): r""" - Return the composition of the series with ``g``. + Return the composition of ``self`` with ``g``. - Given two Laurent Series `f` and `g` over the same base ring, the composition of `f` with `g`, - `(f \circ g)(z) = f(g(z))`, is defined if and only if: - - `g = 0` and `val(f) >= 0` - - `g` is non-zero and `f` has only finitely many non-zero coefficients - - `g` is non-zero and `val(g) > 0` + Given two Laurent Series `f` and `g` over the same base ring, the + composition `(f \circ g)(z) = f(g(z))` is defined if and only if: + + - `g = 0` and `val(f) >= 0`, + - `g` is non-zero and `f` has only finitely many non-zero coefficients, + - `g` is non-zero and `val(g) > 0`. INPUT: - ``g`` -- other series - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(QQ) sage: f = z^2 + 1 + z @@ -804,14 +804,14 @@ def coefficient(self, n): def map_coefficients(self, func, ring=None): """ - Return the series with ``func`` applied to each coefficient of this series. + Return the series with ``func`` applied to each coefficient of ``self``. INPUT: - - ``func`` -- Python function that takes in a coefficient and returns + - ``func`` -- function that takes in a coefficient and returns a new coefficient - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: s = z/(1 - 2*z) @@ -849,7 +849,7 @@ def change_ring(self, ring): - ``ring`` -- a ring - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) sage: s = 2 + z @@ -887,7 +887,7 @@ def truncate(self, d): - ``d`` -- integer - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) sage: alpha = 1/(1-z) @@ -921,9 +921,9 @@ def __pow__(self, n): INPUT: - - ``n`` -- integer, the power to which to raise the series + - ``n`` -- integer; the power to which to raise the series - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: (1 - z)^-1 @@ -959,12 +959,12 @@ def approximate_series(self, prec, name=None): - ``prec`` -- an integer - - ``name`` -- name of the variable; if it is ``None``, the name of the variable - of the series is used + - ``name`` -- name of the variable; if it is ``None``, the name of + the variable of the series is used OUTPUT: a Laurent series with absolute precision ``prec`` - TESTS:: + EXAMPLES:: sage: L = LazyLaurentSeriesRing(ZZ, 'z') sage: z = L.gen() @@ -1001,7 +1001,7 @@ def prec(self): """ Return the precision of the series, which is infinity. - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: f = 1/(1 - z) @@ -1011,7 +1011,7 @@ def prec(self): return infinity def polynomial(self, degree=None, name=None): - """ + r""" Return the polynomial or Laurent polynomial if the series is actually so. INPUT: @@ -1021,7 +1021,9 @@ def polynomial(self, degree=None, name=None): - ``name`` -- name of the variable; if it is ``None``, the name of the variable of the series is used - OUTPUT: a Laurent polynomial if the valuation of the series is negative or + OUTPUT: + + A Laurent polynomial if the valuation of the series is negative or a polynomial otherwise. If ``degree`` is not ``None``, the terms of the series of degree @@ -1093,14 +1095,14 @@ def polynomial(self, degree=None, name=None): return R([self[i] for i in range(m)]) def valuation(self): - """ - Return the valuation of the series. + r""" + Return the valuation of ``self``. This method determines the valuation of the series by looking for a nonzero coefficient. Hence if the series happens to be zero, then it may run forever. - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: s = 1/(1 - z) - 1/(1 - 2*z) @@ -1152,8 +1154,9 @@ def _repr_(self): return ret + ' + ...' def _richcmp_(self, other, op): - """ - Compare ``self` with ``other`` with respect to the comparison operator ``op``. + r""" + Compare ``self` with ``other`` with respect to the comparison + operator ``op``. Equality is verified if the corresponding coefficients of both series can be checked for equality without computing coefficients @@ -1168,7 +1171,7 @@ def _richcmp_(self, other, op): - ``op`` -- comparison operator - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(QQ) sage: z + z^2 == z^2 + z @@ -1359,3 +1362,4 @@ def define(self, s): if not isinstance(self._coeff_stream, CoefficientStream_uninitialized) or self._coeff_stream._target is not None: raise ValueError("series already defined") self._coeff_stream._target = s._coeff_stream + diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index d4fb001bae1..54a381026c7 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -65,7 +65,6 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -import random from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -98,8 +97,8 @@ class LazyLaurentSeriesRing(UniqueRepresentation, Parent): INPUT: - ``base_ring`` -- base ring of this Laurent series ring - - ``names`` -- name of the generator of this Laurent series ring + - ``sparse`` -- (default: ``False``) whether this series is sparse or not EXAMPLES:: @@ -110,7 +109,7 @@ class LazyLaurentSeriesRing(UniqueRepresentation, Parent): def __init__(self, base_ring, names, sparse=False, category=None): """ - Initialize the ring. + Initialize ``self``. TESTS:: @@ -133,10 +132,23 @@ def _repr_(self): """ return "Lazy Laurent Series Ring in {} over {}".format(self.variable_name(), self.base_ring()) + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: L = LazyLaurentSeriesRing(GF(2), 'z') + sage: latex(L) + \Bold{F}_{2} [\![z]\!] + """ + from sage.misc.latex import latex + return latex(self.base_ring()) + r"[\![{}]\!]".format(self.variable_name()) + @cached_method def gen(self, n=0): """ - Return the generator of this Laurent series ring. + Return the ``n``-th generator of ``self``. EXAMPLES:: @@ -155,8 +167,8 @@ def gen(self, n=0): return self.element_class(self, coeff_stream) def ngens(self): - """ - Return the number of generators of this Laurent series ring. + r""" + Return the number of generators of ``self``. This is always 1. @@ -171,7 +183,7 @@ def ngens(self): @cached_method def gens(self): """ - Return the tuple of the generator. + Return the generators of ``self``. EXAMPLES:: @@ -212,11 +224,10 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No INPUT: - - ``x`` -- a Laurent series, a Laurent polynomial, a Python function, or a list of elements in the base ring - - - ``valuation`` -- an integer or ``None`` (default: ``None``), the approximate valuation of the series - - - ``constant`` -- either ``None`` (default: ``None``) or pair of an element of the base ring and an integer + - ``x`` -- data used to the define a Laurent series + - ``valuation`` -- integer (optional); the valuation of the series + - ``constant`` -- (optional) the eventual constant of the series + - ``degree`` -- (optional) the degree when the series is ``constant`` EXAMPLES:: @@ -274,9 +285,9 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: g z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... - TODO:: + .. TODO:: - Add a method to make a copy of self._sparse. + Add a method to change the sparse/dense implementation. """ if x is None: if valuation is None: @@ -323,7 +334,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No def _an_element_(self): """ - Return a Laurent series in this ring. + Return a Laurent series in ``self``. EXAMPLES:: @@ -337,7 +348,7 @@ def _an_element_(self): @cached_method def one(self): - """ + r""" Return the constant series `1`. EXAMPLES:: @@ -351,7 +362,7 @@ def one(self): @cached_method def zero(self): - """ + r""" Return the zero series. EXAMPLES:: @@ -361,3 +372,4 @@ def zero(self): 0 """ return self.element_class(self, CoefficientStream_zero(self._sparse)) + From e5c89c9675bfed04fa99384aa8e7cc07e1702cd8 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Fri, 30 Jul 2021 17:25:26 +0530 Subject: [PATCH 89/98] Started moving eventually_geometric to exact. There are still many issues left. --- .../data_structures/coefficient_stream.py | 391 ++++++++++++------ src/sage/rings/lazy_laurent_series.py | 83 ++-- src/sage/rings/lazy_laurent_series_ring.py | 19 +- 3 files changed, 319 insertions(+), 174 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index f5da2a09023..4ec569317de 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -41,8 +41,8 @@ Coefficient streams can be multiplied:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_mul - sage: h = CoefficientStream_mul(f, g) + sage: from sage.data_structures.coefficient_stream import CoefficientStream_cauchy_product + sage: h = CoefficientStream_cauchy_product(f, g) sage: [h[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] @@ -77,8 +77,8 @@ The multiplicative inverse of a series can also be obtained:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_inv - sage: h = CoefficientStream_inv(g) + sage: from sage.data_structures.coefficient_stream import CoefficientStream_cauchy_inverse + sage: h = CoefficientStream_cauchy_inverse(g) sage: [h[i] for i in range(10)] [-2, 1, 0, 0, 0, 0, 0, 0, 0, 0] @@ -116,7 +116,7 @@ class CoefficientStream(): INPUT: - ``sparse`` -- boolean; whether the implementation of the series is sparse - - ``approximate_valuation`` -- the approximate valuation of the series + - ``approximate_valuation`` -- integer; a lower bound for the valuation of the series """ def __init__(self, sparse, approximate_valuation): """ @@ -140,7 +140,7 @@ class CoefficientStream_inexact(CoefficientStream): INPUT: - ``sparse`` -- boolean; whether the implementation of the series is sparse - - ``approximate_valuation`` -- integer; the approximate valuation of the series + - ``approximate_valuation`` -- integer; a lower bound for the valuation of the series """ def __init__(self, is_sparse, approximate_valuation): """ @@ -173,11 +173,11 @@ def __getstate__(self): TESTS:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact sage: from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing sage: Y = LaurentPolynomialRing(QQ, 'z') - sage: h = CoefficientStream_eventually_geometric(Y(1), True) - sage: g = CoefficientStream_eventually_geometric(Y([1, -1, -1]), True) + sage: h = CoefficientStream_exact(Y(1), True) + sage: g = CoefficientStream_exact(Y([1, -1, -1]), True) sage: from sage.data_structures.coefficient_stream import CoefficientStream_div sage: u = CoefficientStream_div(h, g) sage: [u[i] for i in range(10)] @@ -204,11 +204,11 @@ def __setstate__(self, d): TESTS:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact sage: from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing sage: Y = LaurentPolynomialRing(QQ, 'z') - sage: h = CoefficientStream_eventually_geometric(Y(-1), True) - sage: g = CoefficientStream_eventually_geometric(Y([1, -1]), True) + sage: h = CoefficientStream_exact(Y(-1), True) + sage: g = CoefficientStream_exact(Y([1, -1]), True) sage: from sage.data_structures.coefficient_stream import CoefficientStream_div sage: u = CoefficientStream_div(h, g) sage: [u[i] for i in range(10)] @@ -314,75 +314,67 @@ def valuation(self): n += 1 -class CoefficientStream_eventually_geometric(CoefficientStream): +class CoefficientStream_exact(CoefficientStream): r""" - Coefficient stream for a series which is known to be eventually geometric. + A stream of eventually constant coefficients. INPUT: - - ``laurent_polynomial`` -- a Laurent polynomial - - ``is_sparse`` -- boolean; specifies whether the series is sparse - - ``constant`` -- (default: 0) the eventual constant value - - ``degree`` -- (default: the degree of ``laurent_polynomial`` plus 1) - the degree where the coefficient stream becomes ``constant`` + - ``initial_values`` -- a list of initial values - EXAMPLES:: + - ``is_sparse`` -- a boolean, which specifies whether the + series is sparse - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1 + z^2 - sage: f[2] - 1 - - You can do arithmetic operations with eventually geometric series:: - - sage: g = z^3 + 1 - sage: s = f + g - sage: s[2] - 1 - sage: s - 2 + z^2 + z^3 - sage: s = f - g - sage: s[2] - 1 - sage: s - z^2 - z^3 - """ + - ``valuation`` -- (default: 0), an integer, determining the + degree of the first element of ``initial_values`` - def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): + - ``degree`` -- (default: None), an integer, determining the + degree of the first element which is known to be equal to + ``constant`` + + - ``constant`` -- (default: 0), an integer, the coefficient + of every index larger than or equal to ``degree`` + """ + def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None, valuation=None): """ - Initialize ``self``. + Initialize a series that is known to be eventually geometric. TESTS:: - sage: R. = LaurentPolynomialRing(QQ) - sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric - sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False) - sage: [X[i] for i in range(-1,8)] - [1, 0, 0, 1, 0, 0, 0, 0, 0] - sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, True, 5) - sage: [X[i] for i in range(-1,8)] - [1, 0, 0, 1, 5, 5, 5, 5, 5] - sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 5, 4) - sage: [X[i] for i in range(-1,8)] - [1, 0, 0, 1, 0, 5, 5, 5, 5] + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: CoefficientStream_exact([], False)[0] + 0 + """ if constant is None: - constant = ZZ.zero() + self._constant = ZZ.zero() + else: + self._constant = constant + if valuation is None: + valuation = 0 if degree is None: - if not laurent_polynomial: - raise ValueError("you must specify the degree for the polynomial 0") - degree = laurent_polynomial.degree() + 1 + self._degree = valuation + len(initial_coefficients) else: - # Consistency check - assert not laurent_polynomial or laurent_polynomial.degree() < degree - - self._constant = constant - self._degree = degree - self._laurent_polynomial = laurent_polynomial - if not laurent_polynomial: - valuation = degree + self._degree = degree + + assert valuation + len(initial_coefficients) <= self._degree + + for i, v in enumerate(initial_coefficients): + if v: + valuation += i + initial_coefficients = initial_coefficients[i:] + for j, w in enumerate(reversed(initial_coefficients)): + if w: + break + initial_coefficients.pop() + self._initial_coefficients = tuple(initial_coefficients) + break else: - valuation = laurent_polynomial.valuation() + valuation = self._degree + self._initial_coefficients = tuple() + + assert self._initial_coefficients or self._constant, "CoefficientStream_exact should only be used for non-zero streams" + super().__init__(is_sparse, valuation) def __getitem__(self, n): @@ -391,35 +383,43 @@ def __getitem__(self, n): INPUT: - - ``n`` -- integer; the degree for which the coefficient is required + - ``n`` -- integer, the degree for which the coefficient is required EXAMPLES:: - sage: R. = LaurentPolynomialRing(QQ) - sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric - sage: f = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 3, 10) - sage: f[2] - 1 - sage: f[8] - 0 - sage: f[15] - 3 + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: s = CoefficientStream_exact([], False) + sage: [s[i] for i in range(-2, 5)] + [0, 0, 0, 0, 0, 0, 0] + + sage: s = CoefficientStream_exact([], False, constant=1) + sage: [s[i] for i in range(-2, 5)] + [0, 0, 1, 1, 1, 1, 1] + + sage: s = CoefficientStream_exact([2], False, constant=1) + sage: [s[i] for i in range(-2, 5)] + [0, 0, 2, 1, 1, 1, 1] + + sage: s = CoefficientStream_exact([2], False, valuation=-1, constant=1) + sage: [s[i] for i in range(-2, 5)] + [0, 2, 1, 1, 1, 1, 1] + + sage: s = CoefficientStream_exact([2], False, valuation=-1, degree=2, constant=1) + sage: [s[i] for i in range(-2, 5)] + [0, 2, 0, 0, 1, 1, 1] + + sage: t = CoefficientStream_exact([0, 2, 0], False, valuation=-2, degree=2, constant=1) + sage: t == s + True """ if n >= self._degree: return self._constant - return self._laurent_polynomial[n] + i = n - self._approximate_valuation + if i < 0 or i >= len(self._initial_coefficients): + return 0 + return self._initial_coefficients[i] def valuation(self): - """ - Return the valuation of the series. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = 1 + z + z^2 + z^3 - sage: f.valuation() - 0 - """ return self._approximate_valuation def __hash__(self): @@ -433,7 +433,7 @@ def __hash__(self): sage: {f: 1} {1 + z + z^2 + z^3: 1} """ - return hash((self._laurent_polynomial, self._degree, self._constant)) + return hash((self._initial_coefficients, self._degree, self._constant)) def __eq__(self, other): """ @@ -441,7 +441,7 @@ def __eq__(self, other): INPUT: - - ``other`` -- a stream for a series which is known to be eventually geometric + - ``other`` -- a lazy Laurent series which is known to be eventaully geometric EXAMPLES:: @@ -453,10 +453,153 @@ def __eq__(self, other): """ return (isinstance(other, type(self)) and self._degree == other._degree - and self._laurent_polynomial == other._laurent_polynomial + and self._initial_coefficients == other._initial_coefficients and self._constant == other._constant) +# class CoefficientStream_exact(CoefficientStream): +# r""" +# Coefficient stream for a series which is known to be eventually geometric. + +# INPUT: + +# - ``laurent_polynomial`` -- a Laurent polynomial +# - ``is_sparse`` -- boolean; specifies whether the series is sparse +# - ``constant`` -- (default: 0) the eventual constant value +# - ``degree`` -- (default: the degree of ``laurent_polynomial`` plus 1) +# the degree where the coefficient stream becomes ``constant`` + +# EXAMPLES:: + +# sage: L. = LazyLaurentSeriesRing(ZZ) +# sage: f = 1 + z^2 +# sage: f[2] +# 1 + +# You can do arithmetic operations with eventually geometric series:: + +# sage: g = z^3 + 1 +# sage: s = f + g +# sage: s[2] +# 1 +# sage: s +# 2 + z^2 + z^3 +# sage: s = f - g +# sage: s[2] +# 1 +# sage: s +# z^2 - z^3 +# """ + +# def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): +# """ +# Initialize ``self``. + +# TESTS:: + +# sage: R. = LaurentPolynomialRing(QQ) +# sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric +# sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False) +# sage: [X[i] for i in range(-1,8)] +# [1, 0, 0, 1, 0, 0, 0, 0, 0] +# sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, True, 5) +# sage: [X[i] for i in range(-1,8)] +# [1, 0, 0, 1, 5, 5, 5, 5, 5] +# sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 5, 4) +# sage: [X[i] for i in range(-1,8)] +# [1, 0, 0, 1, 0, 5, 5, 5, 5] +# """ +# if constant is None: +# constant = ZZ.zero() +# if degree is None: +# if not laurent_polynomial: +# raise ValueError("you must specify the degree for the polynomial 0") +# degree = laurent_polynomial.degree() + 1 +# else: +# # Consistency check +# assert not laurent_polynomial or laurent_polynomial.degree() < degree + +# self._constant = constant +# self._degree = degree +# self._laurent_polynomial = laurent_polynomial +# if not laurent_polynomial: +# valuation = degree +# else: +# valuation = laurent_polynomial.valuation() +# super().__init__(is_sparse, valuation) + +# def __getitem__(self, n): +# """ +# Return the coefficient of the term with exponent ``n`` of the series. + +# INPUT: + +# - ``n`` -- integer; the degree for which the coefficient is required + +# EXAMPLES:: + +# sage: R. = LaurentPolynomialRing(QQ) +# sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric +# sage: f = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 3, 10) +# sage: f[2] +# 1 +# sage: f[8] +# 0 +# sage: f[15] +# 3 +# """ +# if n >= self._degree: +# return self._constant +# return self._laurent_polynomial[n] + +# def valuation(self): +# """ +# Return the valuation of the series. + +# EXAMPLES:: + +# sage: L. = LazyLaurentSeriesRing(QQ) +# sage: f = 1 + z + z^2 + z^3 +# sage: f.valuation() +# 0 +# """ +# return self._approximate_valuation + +# def __hash__(self): +# """ +# Return the hash of ``self``. + +# EXAMPLES:: + +# sage: L. = LazyLaurentSeriesRing(QQ) +# sage: f = 1 + z + z^2 + z^3 +# sage: {f: 1} +# {1 + z + z^2 + z^3: 1} +# """ +# return hash((self._laurent_polynomial, self._degree, self._constant)) + +# def __eq__(self, other): +# """ +# Test the equality between ``self`` and ``other``. + +# INPUT: + +# - ``other`` -- a stream for a series which is known to be eventually geometric + +# EXAMPLES:: + +# sage: L. = LazyLaurentSeriesRing(QQ) +# sage: f = 1 + z + z^2 + z^3 +# sage: m = 1 + z + z^2 + z^3 +# sage: f == m +# True +# """ +# return (isinstance(other, type(self)) +# and self._degree == other._degree +# and self._laurent_polynomial == other._laurent_polynomial +# and self._constant == other._constant) + + class CoefficientStream_coefficient_function(CoefficientStream_inexact): r""" Class that returns the elements in the coefficient stream. @@ -467,7 +610,7 @@ class CoefficientStream_coefficient_function(CoefficientStream_inexact): coefficients of the series - ``ring`` -- the base ring of the series - ``is_sparse`` -- boolean; specifies whether the series is sparse - - ``approximate_valuation`` -- the approximate valuation of the series + - ``approximate_valuation`` -- integer; a lower bound for the valuation of the series EXAMPLES:: @@ -535,7 +678,7 @@ class CoefficientStream_uninitialized(CoefficientStream_inexact): INPUT: - ``is_sparse`` -- boolean; which specifies whether the series is sparse - - ``approximate_valuation`` -- the approximate valuation of the series + - ``approximate_valuation`` -- integer; a lower bound for the valuation of the series EXAMPLES:: @@ -607,9 +750,9 @@ class CoefficientStream_unary(CoefficientStream_inexact): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_inv, CoefficientStream_scalar) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_inverse, CoefficientStream_scalar) sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, False, 1) - sage: g = CoefficientStream_inv(f) + sage: g = CoefficientStream_cauchy_inverse(f) sage: [g[i] for i in range(10)] [-1, 1/2, 0, 0, 0, 0, 0, 0, 0, 0] sage: g = CoefficientStream_scalar(f, 2) @@ -696,8 +839,8 @@ def __init__(self, left, right, *args, **kwargs): TESTS:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_mul - sage: (CoefficientStream_mul.__base__).__base__ + sage: from sage.data_structures.coefficient_stream import CoefficientStream_cauchy_product + sage: (CoefficientStream_cauchy_product.__base__).__base__ """ self._left = left @@ -727,13 +870,13 @@ def __eq__(self, other): TESTS:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, False, 1) sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, False, 1) sage: h = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 1) - sage: t = CoefficientStream_mul(f, g) - sage: u = CoefficientStream_mul(g, h) - sage: v = CoefficientStream_mul(h, f) + sage: t = CoefficientStream_cauchy_product(f, g) + sage: u = CoefficientStream_cauchy_product(g, h) + sage: v = CoefficientStream_cauchy_product(h, f) sage: t == u False sage: t == t @@ -791,13 +934,13 @@ def __eq__(self, other): - ``other`` -- a stream of coefficients TESTS:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, True, 0) sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) - sage: h = CoefficientStream_mul(f, g) + sage: h = CoefficientStream_cauchy_product(f, g) sage: [h[i] for i in range(10)] [0, 0, 2, 8, 20, 40, 70, 112, 168, 240] - sage: u = CoefficientStream_mul(g, f) + sage: u = CoefficientStream_cauchy_product(g, f) sage: [u[i] for i in range(10)] [0, 0, 2, 8, 20, 40, 70, 112, 168, 240] sage: h == u @@ -1054,7 +1197,7 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_mul(CoefficientStream_binary_commutative): +class CoefficientStream_cauchy_product(CoefficientStream_binary_commutative): """ Operator for multiplication of two coefficient streams. @@ -1067,13 +1210,13 @@ class CoefficientStream_mul(CoefficientStream_binary_commutative): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_mul, CoefficientStream_coefficient_function) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_cauchy_product, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) sage: g = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) - sage: h = CoefficientStream_mul(f, g) + sage: h = CoefficientStream_cauchy_product(f, g) sage: [h[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] - sage: u = CoefficientStream_mul(g, f) + sage: u = CoefficientStream_cauchy_product(g, f) sage: [u[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] """ @@ -1083,10 +1226,10 @@ def __init__(self, left, right): TESTS:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 0) - sage: h = CoefficientStream_mul(f, g) + sage: h = CoefficientStream_cauchy_product(f, g) """ if left._is_sparse != right._is_sparse: raise NotImplementedError @@ -1104,10 +1247,10 @@ def get_coefficient(self, n): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) sage: g = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, True, 0) - sage: h = CoefficientStream_mul(f, g) + sage: h = CoefficientStream_cauchy_product(f, g) sage: h.get_coefficient(5) 50 sage: [h.get_coefficient(i) for i in range(10)] @@ -1127,10 +1270,10 @@ def iterate_coefficients(self): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_mul) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 0) sage: g = CoefficientStream_coefficient_function(lambda n: n^3, ZZ, False, 0) - sage: h = CoefficientStream_mul(f, g) + sage: h = CoefficientStream_cauchy_product(f, g) sage: n = h.iterate_coefficients() sage: [next(n) for i in range(10)] [0, 1, 9, 36, 100, 225, 441, 784, 1296, 2025] @@ -1282,12 +1425,12 @@ def __init__(self, f, g): self._fv = f._approximate_valuation self._gv = g._approximate_valuation if self._fv < 0: - ginv = CoefficientStream_inv(g) + ginv = CoefficientStream_cauchy_inverse(g) # the constant part makes no contribution to the negative # we need this for the case so self._neg_powers[0][n] => 0 self._neg_powers = [CoefficientStream_zero(f._is_sparse), ginv] for i in range(1, -self._fv): - self._neg_powers.append(CoefficientStream_mul(self._neg_powers[-1], ginv)) + self._neg_powers.append(CoefficientStream_cauchy_product(self._neg_powers[-1], ginv)) # Placeholder None to make this 1-based self._pos_powers = [None, g] val = self._fv * self._gv @@ -1316,7 +1459,7 @@ def get_coefficient(self, n): return sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, n // self._gv + 1)) # n > 0 while len(self._pos_powers) <= n // self._gv: - self._pos_powers.append(CoefficientStream_mul(self._pos_powers[-1], self._right)) + self._pos_powers.append(CoefficientStream_cauchy_product(self._pos_powers[-1], self._right)) ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(self._fv, 0)) if n == 0: ret += self._left[0] @@ -1485,7 +1628,7 @@ def iterate_coefficients(self): n += 1 -class CoefficientStream_inv(CoefficientStream_unary): +class CoefficientStream_cauchy_inverse(CoefficientStream_unary): """ Operator for multiplicative inverse of the stream. @@ -1495,9 +1638,9 @@ class CoefficientStream_inv(CoefficientStream_unary): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_cauchy_inverse, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 1) - sage: g = CoefficientStream_inv(f) + sage: g = CoefficientStream_cauchy_inverse(f) sage: [g[i] for i in range(10)] [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0] """ @@ -1507,9 +1650,9 @@ def __init__(self, series): TESTS:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_cauchy_inverse, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: -1, ZZ, True, 0) - sage: g = CoefficientStream_inv(f) + sage: g = CoefficientStream_cauchy_inverse(f) sage: [g[i] for i in range(10)] [-1, 1, 0, 0, 0, 0, 0, 0, 0, 0] """ @@ -1529,9 +1672,9 @@ def get_coefficient(self, n): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_cauchy_inverse, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) - sage: g = CoefficientStream_inv(f) + sage: g = CoefficientStream_cauchy_inverse(f) sage: g.get_coefficient(5) 0 sage: [g.get_coefficient(i) for i in range(10)] @@ -1551,9 +1694,9 @@ def iterate_coefficients(self): EXAMPLES:: - sage: from sage.data_structures.coefficient_stream import (CoefficientStream_inv, CoefficientStream_coefficient_function) + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_cauchy_inverse, CoefficientStream_coefficient_function) sage: f = CoefficientStream_coefficient_function(lambda n: n^2, ZZ, False, 1) - sage: g = CoefficientStream_inv(f) + sage: g = CoefficientStream_cauchy_inverse(f) sage: n = g.iterate_coefficients() sage: [next(n) for i in range(10)] [1, -4, 7, -8, 8, -8, 8, -8, 8, -8] diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 8e0c2801b22..97e61234dcc 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -81,18 +81,18 @@ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.data_structures.coefficient_stream import ( CoefficientStream_add, - CoefficientStream_mul, + CoefficientStream_cauchy_product, CoefficientStream_sub, CoefficientStream_div, CoefficientStream_composition, CoefficientStream_scalar, CoefficientStream_neg, - CoefficientStream_inv, + CoefficientStream_cauchy_inverse, CoefficientStream_apply_coeff, CoefficientStream, CoefficientStream_inexact, CoefficientStream_zero, - CoefficientStream_eventually_geometric, + CoefficientStream_exact, CoefficientStream_coefficient_function, CoefficientStream_uninitialized ) @@ -364,19 +364,19 @@ def __call__(self, g): # f has finite length if isinstance(self._coeff_stream, CoefficientStream_zero): # constant 0 return self - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) and not self._coeff_stream._constant: + if isinstance(self._coeff_stream, CoefficientStream_exact) and not self._coeff_stream._constant: # constant polynomial if self._coeff_stream._laurent_polynomial.is_constant(): return self if not isinstance(g, LazyLaurentSeries): return self._coeff_stream._laurent_polynomial(g) # g also has finite length, compose the polynomials - if isinstance(g._coeff_stream, CoefficientStream_eventually_geometric) and not g._coeff_stream._constant: + if isinstance(g._coeff_stream, CoefficientStream_exact) and not g._coeff_stream._constant: R = P._laurent_poly_ring try: ret = self._coeff_stream._laurent_polynomial(g._coeff_stream._laurent_polynomial) if ret.parent() is R: - return P.element_class(P, CoefficientStream_eventually_geometric(ret, self._coeff_stream._is_sparse, 0)) + return P.element_class(P, CoefficientStream_exact([ret], self._coeff_stream._is_sparse, 0)) except TypeError: # the result is not a Laurent polynomial pass @@ -414,7 +414,7 @@ def __call__(self, g): return P.element_class(P, CoefficientStream_composition(self._coeff_stream, g._coeff_stream)) - def _mul_(self, other): + def _cauchy_product_(self, other): """ Return the product of this series with ``other``. @@ -455,18 +455,18 @@ def _mul_(self, other): return P.zero() R = P._laurent_poly_ring - if isinstance(left, CoefficientStream_eventually_geometric): + if isinstance(left, CoefficientStream_exact): if not left._constant: if left._laurent_polynomial == R.one(): # self == 1 return other - if isinstance(right, CoefficientStream_eventually_geometric): + if isinstance(right, CoefficientStream_exact): if not right._constant: p = left._laurent_polynomial * right._laurent_polynomial c = left._constant - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c)) - elif isinstance(right, CoefficientStream_eventually_geometric) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 + return P.element_class(P, CoefficientStream_exact([p], P._sparse, c)) + elif isinstance(right, CoefficientStream_exact) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 return self - return P.element_class(P, CoefficientStream_mul(self._coeff_stream, other._coeff_stream)) + return P.element_class(P, CoefficientStream_cauchy_product(self._coeff_stream, other._coeff_stream)) def _add_(self, other): """ @@ -510,8 +510,8 @@ def _add_(self, other): P = self.parent() left = self._coeff_stream right = other._coeff_stream - if (isinstance(left, CoefficientStream_eventually_geometric) - and isinstance(right, CoefficientStream_eventually_geometric)): + if (isinstance(left, CoefficientStream_exact) + and isinstance(right, CoefficientStream_exact)): R = P._laurent_poly_ring c = left._constant + right._constant pl = left._laurent_polynomial @@ -522,7 +522,7 @@ def _add_(self, other): p = pl + pr if not p and not c: return P.zero() - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, d)) + return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, d)) return P.element_class(P, CoefficientStream_add(self._coeff_stream, other._coeff_stream)) def _sub_(self, other): @@ -570,18 +570,18 @@ def _sub_(self, other): P = self.parent() left = self._coeff_stream right = other._coeff_stream - if (isinstance(left, CoefficientStream_eventually_geometric) and isinstance(right, CoefficientStream_eventually_geometric)): + if (isinstance(left, CoefficientStream_exact) and isinstance(right, CoefficientStream_exact)): R = P._laurent_poly_ring c = left._constant - right._constant - pl = left._laurent_polynomial - pr = right._laurent_polynomial + pl = R(left._initial_coefficients) + pr = R(right._initial_coefficients) d = max(left._degree, right._degree) pl += R([left._constant]*(d-left._degree)).shift(left._degree) pr += R([right._constant]*(d-right._degree)).shift(right._degree) p = pl - pr if not p and not c: return P.zero() - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, d)) + return P.element_class(P, CoefficientStream_exact(p, P._sparse, c, d)) if left == right: return P.zero() return P.element_class(P, CoefficientStream_sub(self._coeff_stream, other._coeff_stream)) @@ -622,18 +622,18 @@ def _div_(self, other): if isinstance(left, CoefficientStream_zero): return P.zero() right = other._coeff_stream - if (isinstance(left, CoefficientStream_eventually_geometric) - and isinstance(right, CoefficientStream_eventually_geometric)): + if (isinstance(left, CoefficientStream_exact) + and isinstance(right, CoefficientStream_exact)): if not left._constant and not right._constant: ret = left._laurent_polynomial / right._laurent_polynomial try: ret = P._laurent_poly_ring(ret) - return P.element_class(P, CoefficientStream_eventually_geometric(ret, P._sparse, left._constant)) + return P.element_class(P, CoefficientStream_exact([ret], P._sparse, left._constant)) except (TypeError, ValueError): # We cannot divide the polynomials, so the result must be a series pass - return P.element_class(P, CoefficientStream_mul(left, CoefficientStream_inv(right))) + return P.element_class(P, CoefficientStream_cauchy_product(left, CoefficientStream_cauchy_inverse(right))) def _rmul_(self, scalar): """ @@ -678,10 +678,10 @@ def _rmul_(self, scalar): if scalar == 1: return self - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_exact): c = scalar * self._coeff_stream._constant p = scalar * self._coeff_stream._laurent_polynomial - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, self._coeff_stream._degree)) + return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, self._coeff_stream._degree)) return P.element_class(P, CoefficientStream_scalar(self._coeff_stream, scalar)) @@ -709,11 +709,11 @@ def _neg_(self): -3*z - z^2 + 4*z^3 """ P = self.parent() - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_exact): p = -self._coeff_stream._laurent_polynomial c = -self._coeff_stream._constant d = self._coeff_stream._degree - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, c, d)) + return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, d)) # -(-f) = f if isinstance(self._coeff_stream, CoefficientStream_neg): return P.element_class(P, self._coeff_stream._series) @@ -742,13 +742,13 @@ def __invert__(self): 1 - z """ P = self.parent() - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) and self._coeff_stream._laurent_polynomial == P.gen(): + if isinstance(self._coeff_stream, CoefficientStream_exact) and self._coeff_stream._laurent_polynomial == P.gen(): ret = 1 / self._coeff_stream._laurent_polynomial - return P.element_class(P, CoefficientStream_eventually_geometric(ret, P._sparse, self._coeff_stream._constant)) + return P.element_class(P, CoefficientStream_exact([ret], P._sparse, self._coeff_stream._constant)) # (f^-1)^-1 = f - if isinstance(self._coeff_stream, CoefficientStream_inv): + if isinstance(self._coeff_stream, CoefficientStream_cauchy_inverse): return P.element_class(P, self._coeff_stream._series) - return P.element_class(P, CoefficientStream_inv(self._coeff_stream)) + return P.element_class(P, CoefficientStream_cauchy_inverse(self._coeff_stream)) def coefficient(self, n): """ @@ -833,12 +833,12 @@ def map_coefficients(self, func, ring=None): """ P = self.parent() R = P.base_ring() - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_exact): p = p.map_coefficients(func) c = func(c) if not p and not c: return P.zero() - return P.element_class(P, CoefficientStream_eventually_geometric(p, self._coeff_stream._is_sparse, c, d)) + return P.element_class(P, CoefficientStream_exact([p], self._coeff_stream._is_sparse, c, d)) return P.element_class(P, CoefficientStream_apply_coeff(self._coeff_stream, func, R)) def change_ring(self, ring): @@ -913,7 +913,7 @@ def truncate(self, d): R = P._laurent_poly_ring z = R.gen() p = R.sum(self[i] * z**i for i in range(self._coeff_stream._approximate_valuation, d)) - return P.element_class(P, CoefficientStream_eventually_geometric(p, P._sparse, ZZ.zero(), d)) + return P.element_class(P, CoefficientStream_exact([p], P._sparse, ZZ.zero(), d)) def __pow__(self, n): """ @@ -1072,7 +1072,7 @@ def polynomial(self, degree=None, name=None): if isinstance(self._coeff_stream, CoefficientStream_zero): from sage.rings.all import PolynomialRing return PolynomialRing(S.base_ring(), name=name).zero() - elif isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) and not self._coeff_stream._constant: + elif isinstance(self._coeff_stream, CoefficientStream_exact) and not self._coeff_stream._constant: m = self._coeff_stream._degree else: raise ValueError("not a polynomial") @@ -1139,11 +1139,12 @@ def _repr_(self): X = self.parent().variable_name() v = self._coeff_stream._approximate_valuation - if not isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): + if not isinstance(self._coeff_stream, CoefficientStream_exact): m = v + 7 # long enough elif not self._coeff_stream._constant: # Just a polynonial, so let that print itself - return repr(self._coeff_stream._laurent_polynomial) + R = self.parent()._laurent_poly_ring + return repr(R([self._coeff_stream[i] for i in range(self._coeff_stream._constant, self._coeff_stream._degree)])) else: m = self._coeff_stream._degree + 3 @@ -1189,8 +1190,8 @@ def _richcmp_(self, other, op): if isinstance(other._coeff_stream, CoefficientStream_zero): # self != 0 but other == 0 return False - if (not isinstance(self._coeff_stream, CoefficientStream_eventually_geometric) - or not isinstance(other._coeff_stream, CoefficientStream_eventually_geometric)): + if (not isinstance(self._coeff_stream, CoefficientStream_exact) + or not isinstance(other._coeff_stream, CoefficientStream_exact)): # One of the lazy laurent series is not known to eventually be constant # Implement the checking of the caches here. n = min(self._coeff_stream._approximate_valuation, other._coeff_stream._approximate_valuation) @@ -1202,7 +1203,7 @@ def _richcmp_(self, other, op): return True raise ValueError("undecidable as lazy Laurent series") - # Both are CoefficientStream_eventually_geometric, which implements a full check + # Both are CoefficientStream_exact, which implements a full check return self._coeff_stream == other._coeff_stream if op is op_NE: @@ -1243,7 +1244,7 @@ def __bool__(self): """ if isinstance(self._coeff_stream, CoefficientStream_zero): return False - if isinstance(self._coeff_stream, CoefficientStream_eventually_geometric): + if isinstance(self._coeff_stream, CoefficientStream_exact): # This should always end up being True, but let's be careful about it for now... return self._coeff_stream._laurent_polynomial or self._coeff_stream._constant diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 54a381026c7..65856831d8a 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -85,7 +85,7 @@ from sage.data_structures.coefficient_stream import ( CoefficientStream_zero, CoefficientStream_coefficient_function, - CoefficientStream_eventually_geometric, + CoefficientStream_exact, CoefficientStream_uninitialized ) @@ -163,7 +163,7 @@ def gen(self, n=0): if n != 0: raise IndexError("there is only one generator") R = self._laurent_poly_ring - coeff_stream = CoefficientStream_eventually_geometric(R.gen(n), self._sparse, ZZ.zero(), 2) + coeff_stream = CoefficientStream_exact([R.gen(n)[1]], self._sparse, constant=ZZ.zero(), degree=2) return self.element_class(self, coeff_stream) def ngens(self): @@ -213,7 +213,7 @@ def _coerce_map_from_(self, S): R = self._laurent_poly_ring if R.has_coerce_map_from(S): def make_series_from(poly): - return self.element_class(self, CoefficientStream_eventually_geometric(R(poly), self._sparse)) + return self.element_class(self, CoefficientStream_exact([R(poly)], self._sparse)) return SetMorphism(Hom(S, self, Sets()), make_series_from) return False @@ -225,7 +225,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No INPUT: - ``x`` -- data used to the define a Laurent series - - ``valuation`` -- integer (optional); the valuation of the series + - ``valuation`` -- integer (optional); integer; a lower bound for the valuation of the series - ``constant`` -- (optional) the eventual constant of the series - ``degree`` -- (optional) the degree when the series is ``constant`` @@ -313,7 +313,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No x = x.shift(valuation - x.valuation()) if degree is None and not x: degree = valuation - coeff_stream = CoefficientStream_eventually_geometric(R(x), self._sparse, constant, degree) + coeff_stream = CoefficientStream_exact([x], self._sparse, constant=constant, degree=degree) return self.element_class(self, coeff_stream) if isinstance(x, LazyLaurentSeries): if x._coeff_stream._is_sparse is self._sparse: @@ -327,8 +327,9 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if constant is None: constant = ZZ.zero() z = R.gen() - p = R.sum(x(i) * z**i for i in range(valuation, degree)) - return self.element_class(self, CoefficientStream_eventually_geometric(p, self._sparse, constant, degree)) + # p = R.sum(x(i) * z**i for i in range(valuation, degree)) + p = [x(i) for i in range(valuation, degree)] + return self.element_class(self, CoefficientStream_exact(p, self._sparse, constant=constant, degree=degree)) return self.element_class(self, CoefficientStream_coefficient_function(x, self.base_ring(), self._sparse, valuation)) raise ValueError(f"unable to convert {x} into a lazy Laurent series") @@ -344,7 +345,7 @@ def _an_element_(self): """ c = self.base_ring().an_element() R = self._laurent_poly_ring - return self.element_class(self, CoefficientStream_eventually_geometric(R.zero(), self._sparse, c, -10)) + return self.element_class(self, CoefficientStream_exact([R.zero()], self._sparse, c, -10)) @cached_method def one(self): @@ -358,7 +359,7 @@ def one(self): 1 """ R = self._laurent_poly_ring - return self.element_class(self, CoefficientStream_eventually_geometric(R.one(), self._sparse, ZZ.zero(), 1)) + return self.element_class(self, CoefficientStream_exact([R.one()], self._sparse, constant=ZZ.zero(), degree=1)) @cached_method def zero(self): From 799e22cfe7a4ef35b58f58fdeab13858de04ded2 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Sat, 31 Jul 2021 19:23:12 +0530 Subject: [PATCH 90/98] CS_eventually_geometric is now CS_exact --- .../data_structures/coefficient_stream.py | 155 +----------------- src/sage/rings/lazy_laurent_series.py | 110 +++++++++---- src/sage/rings/lazy_laurent_series_ring.py | 30 +++- 3 files changed, 102 insertions(+), 193 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 4ec569317de..994c64d2a20 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -174,10 +174,8 @@ def __getstate__(self): TESTS:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact - sage: from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing - sage: Y = LaurentPolynomialRing(QQ, 'z') - sage: h = CoefficientStream_exact(Y(1), True) - sage: g = CoefficientStream_exact(Y([1, -1, -1]), True) + sage: h = CoefficientStream_exact([1], True) + sage: g = CoefficientStream_exact([1, -1, -1], True) sage: from sage.data_structures.coefficient_stream import CoefficientStream_div sage: u = CoefficientStream_div(h, g) sage: [u[i] for i in range(10)] @@ -205,10 +203,8 @@ def __setstate__(self, d): TESTS:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact - sage: from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing - sage: Y = LaurentPolynomialRing(QQ, 'z') - sage: h = CoefficientStream_exact(Y(-1), True) - sage: g = CoefficientStream_exact(Y([1, -1]), True) + sage: h = CoefficientStream_exact([-1], True) + sage: g = CoefficientStream_exact([1, -1], True) sage: from sage.data_structures.coefficient_stream import CoefficientStream_div sage: u = CoefficientStream_div(h, g) sage: [u[i] for i in range(10)] @@ -457,149 +453,6 @@ def __eq__(self, other): and self._constant == other._constant) -# class CoefficientStream_exact(CoefficientStream): -# r""" -# Coefficient stream for a series which is known to be eventually geometric. - -# INPUT: - -# - ``laurent_polynomial`` -- a Laurent polynomial -# - ``is_sparse`` -- boolean; specifies whether the series is sparse -# - ``constant`` -- (default: 0) the eventual constant value -# - ``degree`` -- (default: the degree of ``laurent_polynomial`` plus 1) -# the degree where the coefficient stream becomes ``constant`` - -# EXAMPLES:: - -# sage: L. = LazyLaurentSeriesRing(ZZ) -# sage: f = 1 + z^2 -# sage: f[2] -# 1 - -# You can do arithmetic operations with eventually geometric series:: - -# sage: g = z^3 + 1 -# sage: s = f + g -# sage: s[2] -# 1 -# sage: s -# 2 + z^2 + z^3 -# sage: s = f - g -# sage: s[2] -# 1 -# sage: s -# z^2 - z^3 -# """ - -# def __init__(self, laurent_polynomial, is_sparse, constant=None, degree=None): -# """ -# Initialize ``self``. - -# TESTS:: - -# sage: R. = LaurentPolynomialRing(QQ) -# sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric -# sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False) -# sage: [X[i] for i in range(-1,8)] -# [1, 0, 0, 1, 0, 0, 0, 0, 0] -# sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, True, 5) -# sage: [X[i] for i in range(-1,8)] -# [1, 0, 0, 1, 5, 5, 5, 5, 5] -# sage: X = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 5, 4) -# sage: [X[i] for i in range(-1,8)] -# [1, 0, 0, 1, 0, 5, 5, 5, 5] -# """ -# if constant is None: -# constant = ZZ.zero() -# if degree is None: -# if not laurent_polynomial: -# raise ValueError("you must specify the degree for the polynomial 0") -# degree = laurent_polynomial.degree() + 1 -# else: -# # Consistency check -# assert not laurent_polynomial or laurent_polynomial.degree() < degree - -# self._constant = constant -# self._degree = degree -# self._laurent_polynomial = laurent_polynomial -# if not laurent_polynomial: -# valuation = degree -# else: -# valuation = laurent_polynomial.valuation() -# super().__init__(is_sparse, valuation) - -# def __getitem__(self, n): -# """ -# Return the coefficient of the term with exponent ``n`` of the series. - -# INPUT: - -# - ``n`` -- integer; the degree for which the coefficient is required - -# EXAMPLES:: - -# sage: R. = LaurentPolynomialRing(QQ) -# sage: from sage.data_structures.coefficient_stream import CoefficientStream_eventually_geometric -# sage: f = CoefficientStream_eventually_geometric(z^2 + z^-1, False, 3, 10) -# sage: f[2] -# 1 -# sage: f[8] -# 0 -# sage: f[15] -# 3 -# """ -# if n >= self._degree: -# return self._constant -# return self._laurent_polynomial[n] - -# def valuation(self): -# """ -# Return the valuation of the series. - -# EXAMPLES:: - -# sage: L. = LazyLaurentSeriesRing(QQ) -# sage: f = 1 + z + z^2 + z^3 -# sage: f.valuation() -# 0 -# """ -# return self._approximate_valuation - -# def __hash__(self): -# """ -# Return the hash of ``self``. - -# EXAMPLES:: - -# sage: L. = LazyLaurentSeriesRing(QQ) -# sage: f = 1 + z + z^2 + z^3 -# sage: {f: 1} -# {1 + z + z^2 + z^3: 1} -# """ -# return hash((self._laurent_polynomial, self._degree, self._constant)) - -# def __eq__(self, other): -# """ -# Test the equality between ``self`` and ``other``. - -# INPUT: - -# - ``other`` -- a stream for a series which is known to be eventually geometric - -# EXAMPLES:: - -# sage: L. = LazyLaurentSeriesRing(QQ) -# sage: f = 1 + z + z^2 + z^3 -# sage: m = 1 + z + z^2 + z^3 -# sage: f == m -# True -# """ -# return (isinstance(other, type(self)) -# and self._degree == other._degree -# and self._laurent_polynomial == other._laurent_polynomial -# and self._constant == other._constant) - - class CoefficientStream_coefficient_function(CoefficientStream_inexact): r""" Class that returns the elements in the coefficient stream. diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 97e61234dcc..95ab0068eac 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -366,23 +366,27 @@ def __call__(self, g): return self if isinstance(self._coeff_stream, CoefficientStream_exact) and not self._coeff_stream._constant: # constant polynomial - if self._coeff_stream._laurent_polynomial.is_constant(): + R = self.parent()._laurent_poly_ring + z = R.gen() + poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) + if poly.is_constant(): return self if not isinstance(g, LazyLaurentSeries): - return self._coeff_stream._laurent_polynomial(g) + return poly(g) # g also has finite length, compose the polynomials if isinstance(g._coeff_stream, CoefficientStream_exact) and not g._coeff_stream._constant: - R = P._laurent_poly_ring try: - ret = self._coeff_stream._laurent_polynomial(g._coeff_stream._laurent_polynomial) + R = P._laurent_poly_ring + g_poly = R(sum([g._coeff_stream[i] * z**i for i in range(g._coeff_stream._approximate_valuation, g._coeff_stream._degree)])) + ret = poly(g_poly) if ret.parent() is R: - return P.element_class(P, CoefficientStream_exact([ret], self._coeff_stream._is_sparse, 0)) + p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, self._coeff_stream._is_sparse, valuation=ret.valuation())) except TypeError: # the result is not a Laurent polynomial pass # Return the sum since g is not known to be finite or we do not get a Laurent polynomial # TODO: Optimize when f has positive valuation - poly = self._coeff_stream._laurent_polynomial ret = P.zero() gp = P.one() # We build this iteratively so each power can benefit from the caching @@ -414,7 +418,7 @@ def __call__(self, g): return P.element_class(P, CoefficientStream_composition(self._coeff_stream, g._coeff_stream)) - def _cauchy_product_(self, other): + def _mul_(self, other): """ Return the product of this series with ``other``. @@ -455,17 +459,24 @@ def _cauchy_product_(self, other): return P.zero() R = P._laurent_poly_ring + z = R.gen() if isinstance(left, CoefficientStream_exact): if not left._constant: - if left._laurent_polynomial == R.one(): # self == 1 + pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) + if pl == R.one(): # self == 1 return other if isinstance(right, CoefficientStream_exact): if not right._constant: - p = left._laurent_polynomial * right._laurent_polynomial + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) + p = pl * pr c = left._constant - return P.element_class(P, CoefficientStream_exact([p], P._sparse, c)) - elif isinstance(right, CoefficientStream_exact) and not right._constant and right._laurent_polynomial == R.one(): # other == 1 - return self + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c)) + elif isinstance(right, CoefficientStream_exact): + if not right._constant: + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) + if pr == R.one(): # other == 1 + return self return P.element_class(P, CoefficientStream_cauchy_product(self._coeff_stream, other._coeff_stream)) def _add_(self, other): @@ -513,16 +524,18 @@ def _add_(self, other): if (isinstance(left, CoefficientStream_exact) and isinstance(right, CoefficientStream_exact)): R = P._laurent_poly_ring + z = R.gen() + pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) c = left._constant + right._constant - pl = left._laurent_polynomial - pr = right._laurent_polynomial d = max(left._degree, right._degree) pl += R([left._constant]*(d-left._degree)).shift(left._degree) pr += R([right._constant]*(d-right._degree)).shift(right._degree) p = pl + pr if not p and not c: return P.zero() - return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, d)) + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=d)) return P.element_class(P, CoefficientStream_add(self._coeff_stream, other._coeff_stream)) def _sub_(self, other): @@ -572,16 +585,18 @@ def _sub_(self, other): right = other._coeff_stream if (isinstance(left, CoefficientStream_exact) and isinstance(right, CoefficientStream_exact)): R = P._laurent_poly_ring + z = R.gen() c = left._constant - right._constant - pl = R(left._initial_coefficients) - pr = R(right._initial_coefficients) + pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) d = max(left._degree, right._degree) pl += R([left._constant]*(d-left._degree)).shift(left._degree) pr += R([right._constant]*(d-right._degree)).shift(right._degree) p = pl - pr if not p and not c: return P.zero() - return P.element_class(P, CoefficientStream_exact(p, P._sparse, c, d)) + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=d)) if left == right: return P.zero() return P.element_class(P, CoefficientStream_sub(self._coeff_stream, other._coeff_stream)) @@ -625,10 +640,15 @@ def _div_(self, other): if (isinstance(left, CoefficientStream_exact) and isinstance(right, CoefficientStream_exact)): if not left._constant and not right._constant: - ret = left._laurent_polynomial / right._laurent_polynomial + R = P._laurent_poly_ring + z = R.gen() + pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) + ret = pl / pr try: ret = P._laurent_poly_ring(ret) - return P.element_class(P, CoefficientStream_exact([ret], P._sparse, left._constant)) + p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=ret.valuation(), constant=left._constant)) except (TypeError, ValueError): # We cannot divide the polynomials, so the result must be a series pass @@ -679,9 +699,13 @@ def _rmul_(self, scalar): return self if isinstance(self._coeff_stream, CoefficientStream_exact): + R = P._laurent_poly_ring + z = R.gen() c = scalar * self._coeff_stream._constant - p = scalar * self._coeff_stream._laurent_polynomial - return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, self._coeff_stream._degree)) + pl = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) + p = scalar * pl + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=self._coeff_stream._degree)) return P.element_class(P, CoefficientStream_scalar(self._coeff_stream, scalar)) @@ -710,10 +734,14 @@ def _neg_(self): """ P = self.parent() if isinstance(self._coeff_stream, CoefficientStream_exact): - p = -self._coeff_stream._laurent_polynomial + R = P._laurent_poly_ring + z = R.gen() + poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) + p = -poly c = -self._coeff_stream._constant d = self._coeff_stream._degree - return P.element_class(P, CoefficientStream_exact([p], P._sparse, c, d)) + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=d)) # -(-f) = f if isinstance(self._coeff_stream, CoefficientStream_neg): return P.element_class(P, self._coeff_stream._series) @@ -742,9 +770,14 @@ def __invert__(self): 1 - z """ P = self.parent() - if isinstance(self._coeff_stream, CoefficientStream_exact) and self._coeff_stream._laurent_polynomial == P.gen(): - ret = 1 / self._coeff_stream._laurent_polynomial - return P.element_class(P, CoefficientStream_exact([ret], P._sparse, self._coeff_stream._constant)) + R = P._laurent_poly_ring + z = R.gen() + if isinstance(self._coeff_stream, CoefficientStream_exact): + poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) + if poly == R.gen(): + ret = 1 / poly + p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=ret.valuation(), constant=self._coeff_stream._constant)) # (f^-1)^-1 = f if isinstance(self._coeff_stream, CoefficientStream_cauchy_inverse): return P.element_class(P, self._coeff_stream._series) @@ -832,14 +865,17 @@ def map_coefficients(self, func, ring=None): 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... """ P = self.parent() - R = P.base_ring() if isinstance(self._coeff_stream, CoefficientStream_exact): + R = P._laurent_poly_ring + z = R.gen() + p = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) p = p.map_coefficients(func) - c = func(c) + c = func(self._coeff_stream._constant) if not p and not c: return P.zero() - return P.element_class(P, CoefficientStream_exact([p], self._coeff_stream._is_sparse, c, d)) - return P.element_class(P, CoefficientStream_apply_coeff(self._coeff_stream, func, R)) + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, self._coeff_stream._is_sparse, valuation=p.valuation(), constant=c)) + return P.element_class(P, CoefficientStream_apply_coeff(self._coeff_stream, func, P.base_ring())) def change_ring(self, ring): """ @@ -913,7 +949,8 @@ def truncate(self, d): R = P._laurent_poly_ring z = R.gen() p = R.sum(self[i] * z**i for i in range(self._coeff_stream._approximate_valuation, d)) - return P.element_class(P, CoefficientStream_exact([p], P._sparse, ZZ.zero(), d)) + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=ZZ.zero(), degree=d)) def __pow__(self, n): """ @@ -1144,7 +1181,8 @@ def _repr_(self): elif not self._coeff_stream._constant: # Just a polynonial, so let that print itself R = self.parent()._laurent_poly_ring - return repr(R([self._coeff_stream[i] for i in range(self._coeff_stream._constant, self._coeff_stream._degree)])) + z = R.gen() + return repr(R.sum(self._coeff_stream[i] * z**i for i in range(v, self._coeff_stream._degree))) else: m = self._coeff_stream._degree + 3 @@ -1246,7 +1284,11 @@ def __bool__(self): return False if isinstance(self._coeff_stream, CoefficientStream_exact): # This should always end up being True, but let's be careful about it for now... - return self._coeff_stream._laurent_polynomial or self._coeff_stream._constant + P = self.paren() + R = P._laurent_poly_ring + z = R.gen() + poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) + return poly or self._coeff_stream._constant for a in self._coeff_stream._cache: if a: diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 65856831d8a..49c5a266e96 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -163,7 +163,7 @@ def gen(self, n=0): if n != 0: raise IndexError("there is only one generator") R = self._laurent_poly_ring - coeff_stream = CoefficientStream_exact([R.gen(n)[1]], self._sparse, constant=ZZ.zero(), degree=2) + coeff_stream = CoefficientStream_exact([R.gen(n)[1]], self._sparse, constant=ZZ.zero(), valuation=1, degree=2) return self.element_class(self, coeff_stream) def ngens(self): @@ -213,7 +213,8 @@ def _coerce_map_from_(self, S): R = self._laurent_poly_ring if R.has_coerce_map_from(S): def make_series_from(poly): - return self.element_class(self, CoefficientStream_exact([R(poly)], self._sparse)) + p_list = [poly[i] for i in range(poly.valuation(), poly.degree() + 1)] + return self.element_class(self, CoefficientStream_exact(p_list, self._sparse, valuation=poly.valuation())) return SetMorphism(Hom(S, self, Sets()), make_series_from) return False @@ -293,7 +294,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if valuation is None: valuation = 0 return self.element_class(self, CoefficientStream_uninitialized(self._sparse, valuation)) - + R = self._laurent_poly_ring BR = self.base_ring() try: @@ -312,8 +313,22 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if x and valuation: x = x.shift(valuation - x.valuation()) if degree is None and not x: + if valuation is None: + raise ValueError("you must specify the degree for the polynomial 0") degree = valuation - coeff_stream = CoefficientStream_exact([x], self._sparse, constant=constant, degree=degree) + # print('it breaks here') + # print(x) + # x = R(x) + # print(x) + # x = [R(x)] + # print(x) + # print(x) + # print(type(x)) + if x == R.zero(): + coeff_stream = CoefficientStream_exact([x], self._sparse, valuation=degree-1, constant=constant) + return self.element_class(self, coeff_stream) + p_list = [x[i] for i in range(x.valuation(), x.degree() + 1)] + coeff_stream = CoefficientStream_exact(p_list, self._sparse, valuation=x.valuation(), constant=constant, degree=degree) return self.element_class(self, coeff_stream) if isinstance(x, LazyLaurentSeries): if x._coeff_stream._is_sparse is self._sparse: @@ -327,9 +342,8 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if constant is None: constant = ZZ.zero() z = R.gen() - # p = R.sum(x(i) * z**i for i in range(valuation, degree)) p = [x(i) for i in range(valuation, degree)] - return self.element_class(self, CoefficientStream_exact(p, self._sparse, constant=constant, degree=degree)) + return self.element_class(self, CoefficientStream_exact(p, self._sparse, valuation=valuation, constant=constant, degree=degree)) return self.element_class(self, CoefficientStream_coefficient_function(x, self.base_ring(), self._sparse, valuation)) raise ValueError(f"unable to convert {x} into a lazy Laurent series") @@ -343,9 +357,9 @@ def _an_element_(self): sage: L.an_element() z^-10 + z^-9 + z^-8 + ... """ - c = self.base_ring().an_element() + c = self.base_ring()(1) R = self._laurent_poly_ring - return self.element_class(self, CoefficientStream_exact([R.zero()], self._sparse, c, -10)) + return self.element_class(self, CoefficientStream_exact([R.zero()], self._sparse, valuation=-11, constant=c)) @cached_method def one(self): From 97bb021a21189b20402c457d9b7b6a9a8bf2f2bd Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 31 Jul 2021 21:35:45 +0200 Subject: [PATCH 91/98] fix doctests --- src/sage/data_structures/coefficient_stream.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 994c64d2a20..f0452132b5f 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -338,8 +338,10 @@ def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None, TESTS:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact - sage: CoefficientStream_exact([], False)[0] - 0 + sage: CoefficientStream_exact([], False) + Traceback (most recent call last): + ... + AssertionError: CoefficientStream_exact should only be used for non-zero streams """ if constant is None: @@ -384,9 +386,9 @@ def __getitem__(self, n): EXAMPLES:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact - sage: s = CoefficientStream_exact([], False) + sage: s = CoefficientStream_exact([1], False) sage: [s[i] for i in range(-2, 5)] - [0, 0, 0, 0, 0, 0, 0] + [0, 0, 1, 0, 0, 0, 0] sage: s = CoefficientStream_exact([], False, constant=1) sage: [s[i] for i in range(-2, 5)] @@ -1411,6 +1413,7 @@ def iterate_coefficients(self): yield self._series[n] * self._scalar n += 1 + class CoefficientStream_neg(CoefficientStream_unary): """ Operator for negative of the stream. @@ -1642,4 +1645,3 @@ def iterate_coefficients(self): c = self._ring(self._function(self._series[n])) yield c n += 1 - From 12e127cac36332e181ff40548bd0434bdc2a2196 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 1 Aug 2021 16:09:32 +0200 Subject: [PATCH 92/98] backport code factorization from #32309 --- src/sage/rings/lazy_laurent_series.py | 1411 ++++++++++++------------- 1 file changed, 688 insertions(+), 723 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 95ab0068eac..5289284adc0 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -97,8 +97,607 @@ CoefficientStream_uninitialized ) +class LazySequenceElement(ModuleElement): + def __getitem__(self, n): + """ + Return the coefficient of the term with exponent ``n`` of the series. + + INPUT: + + - ``n`` -- integer + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = z/(1 - 2*z^3) + sage: [f[n] for n in range(20)] + [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] + sage: f[0:20] + [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] + + sage: M = L(lambda n: n) + sage: [M[n] for n in range(20)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n) + sage: [M[n] for n in range(20)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + + """ + R = self.base_ring() + if isinstance(n, slice): + if n.stop is None: + raise NotImplementedError("cannot list an infinite set") + start = n.start if n.start is not None else self._coeff_stream.valuation() + step = n.step if n.step is not None else 1 + return [R(self._coeff_stream[k]) for k in range(start, n.stop, step)] + return R(self._coeff_stream[n]) + + coefficient = __getitem__ + + def map_coefficients(self, func, ring=None): + """ + Return the series with ``func`` applied to each nonzero coefficient of ``self``. + + INPUT: + + - ``func`` -- function that takes in a coefficient and returns + a new coefficient + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: s = z/(1 - 2*z^2) + sage: t = s.map_coefficients(lambda c: c + 1) + sage: s + z + 2*z^3 + 4*z^5 + 8*z^7 + ... + sage: t + 2*z + 3*z^3 + 5*z^5 + 9*z^7 + ... + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M.map_coefficients(lambda c: c + 1); N + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M.map_coefficients(lambda c: c + 1); N + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + + """ + P = self.parent() + coeff_stream = self._coeff_stream + if isinstance(coeff_stream, CoefficientStream_exact): + initial_values = [func(i) if i else 0 for i in coeff_stream._initial_values] + c = func(coeffs._constant) if coeffs._constant else 0 + if not any(initial_values) and not c: + return P.zero() + coeff_stream = CoefficientStream_exact(p_list, self._coeff_stream._is_sparse, + valuation=coeff_stream._approximate_valuation, + degree=coeff_stream._degree, + constant=c) + return P.element_class(P, coeff_stream) + return P.element_class(P, CoefficientStream_apply_coeff(self._coeff_stream, func, P.base_ring())) + + def truncate(self, d): + """ + Return this series with its terms of degree >= ``d`` truncated. + + INPUT: + + - ``d`` -- integer + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) + sage: alpha = 1/(1-z) + sage: alpha + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: beta = alpha.truncate(5) + sage: beta + 1 + z + z^2 + z^3 + z^4 + sage: alpha - beta + z^5 + z^6 + ... + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M.truncate(4) + z + 2*z^2 + 3*z^3 + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M.truncate(4) + z + 2*z^2 + 3*z^3 + """ + P = self.parent() + coeff_stream = self._coeff_stream + initial_values = [coeff_stream[i] for i in range(coeff_stream._approximate_valuation, d)] + return P.element_class(P, CoefficientStream_exact(initial_values, P._sparse, + valuation=coeff_stream._approximate_valuation)) + + def prec(self): + """ + Return the precision of the series, which is infinity. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + sage: f.prec() + +Infinity + """ + return infinity + + def valuation(self): + r""" + Return the valuation of ``self``. + + This method determines the valuation of the series by looking for a + nonzero coefficient. Hence if the series happens to be zero, then it + may run forever. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: s = 1/(1 - z) - 1/(1 - 2*z) + sage: s.valuation() + 1 + sage: t = z - z + sage: t.valuation() + +Infinity + sage: M = L(lambda n: n^2, 0) + sage: M.valuation() + 1 + sage: (M - M).valuation() + +Infinity + + """ + return self._coeff_stream.valuation() + + def _richcmp_(self, other, op): + r""" + Compare ``self` with ``other`` with respect to the comparison + operator ``op``. + + Equality is verified if the corresponding coefficients of both series + can be checked for equality without computing coefficients + indefinitely. Otherwise an exception is raised to declare that + equality is not decidable. + + Inequality is not defined for lazy Laurent series. + + INPUT: + + - ``other`` -- another Laurent series + + - ``op`` -- comparison operator + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: z + z^2 == z^2 + z + True + sage: z + z^2 != z^2 + z + False + sage: z + z^2 > z^2 + z + False + sage: z + z^2 < z^2 + z + False + """ + if op is op_EQ: + if isinstance(self._coeff_stream, CoefficientStream_zero): # self == 0 + return isinstance(other._coeff_stream, CoefficientStream_zero) + if isinstance(other._coeff_stream, CoefficientStream_zero): # self != 0 but other == 0 + return False + + if (not isinstance(self._coeff_stream, CoefficientStream_exact) + or not isinstance(other._coeff_stream, CoefficientStream_exact)): + # One of the lazy laurent series is not known to eventually be constant + # Implement the checking of the caches here. + n = min(self._coeff_stream._approximate_valuation, other._coeff_stream._approximate_valuation) + m = max(self._coeff_stream._approximate_valuation, other._coeff_stream._approximate_valuation) + for i in range(n, m): + if self[i] != other[i]: + return False + if self._coeff_stream == other._coeff_stream: + return True + raise ValueError("undecidable") + + # Both are CoefficientStream_exact, which implements a full check + return self._coeff_stream == other._coeff_stream + + if op is op_NE: + return not (self == other) + + return False + + def __hash__(self): + """ + Return the hash of ``self`` + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: f = L([1,2,3,4], -5) + sage: g = (1 + f)/(1 - f)^2 + sage: {g: 1} + {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} + """ + return hash(self._coeff_stream) + + def __bool__(self): + """ + Test whether ``self`` is not zero. + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(GF(2)) + sage: (z-z).is_zero() + True + sage: f = 1/(1 - z) + sage: f.is_zero() + False + sage: M = L(lambda n: n, 0); M + z + z^3 + z^5 + ... + sage: M.is_zero() + False + """ + if isinstance(self._coeff_stream, CoefficientStream_zero): + return False + if isinstance(self._coeff_stream, CoefficientStream_exact): + return True + for a in self._coeff_stream._cache: + if a: + return True + if self[self._coeff_stream._approximate_valuation]: + return True + raise ValueError("undecidable as lazy Laurent series") + + def define(self, s): + r""" + Define an equation by ``self = s``. + + INPUT:: + + - ``s`` -- a Laurent polynomial + + EXAMPLES: + + We begin by constructing the Catalan numbers:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: C = L(None) + sage: C.define(1 + z*C^2) + sage: C + 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... + + The Catalan numbers but with a valuation 1:: + + sage: B = L(None, 1) + sage: B.define(z + B^2) + sage: B + z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + ... + + We can define multiple series that are linked:: + + sage: s = L(None) + sage: t = L(None) + sage: s.define(1 + z*t^3) + sage: t.define(1 + z*s^2) + sage: s[:9] + [1, 1, 3, 9, 34, 132, 546, 2327, 10191] + sage: t[:9] + [1, 1, 2, 7, 24, 95, 386, 1641, 7150] + + An bigger example:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: A = L(None, 5) + sage: B = L(None) + sage: C = L(None, 2) + sage: A.define(z^5 + B^2) + sage: B.define(z^5 + C^2) + sage: C.define(z^2 + C^2 + A^2) + sage: A[0:15] + [0, 0, 0, 0, 0, 1, 0, 0, 1, 2, 5, 4, 14, 10, 48] + sage: B[0:15] + [0, 0, 0, 0, 1, 1, 2, 0, 5, 0, 14, 0, 44, 0, 138] + sage: C[0:15] + [0, 0, 1, 0, 1, 0, 2, 0, 5, 0, 15, 0, 44, 2, 142] + + Counting binary trees:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: s = L(None, valuation=1) + sage: s.define(z + (s^2+s(z^2))/2) + sage: [s[i] for i in range(9)] + [0, 1, 1, 1, 2, 3, 6, 11, 23] + + The `q`-Catalan numbers:: + + sage: R. = ZZ[] + sage: L. = LazyLaurentSeriesRing(R) + sage: s = L(None) + sage: s.define(1+z*s*s(q*z)) + sage: s + 1 + z + (q + 1)*z^2 + (q^3 + q^2 + 2*q + 1)*z^3 + + (q^6 + q^5 + 2*q^4 + 3*q^3 + 3*q^2 + 3*q + 1)*z^4 + + (q^10 + q^9 + 2*q^8 + 3*q^7 + 5*q^6 + 5*q^5 + 7*q^4 + 7*q^3 + 6*q^2 + 4*q + 1)*z^5 + + (q^15 + q^14 + 2*q^13 + 3*q^12 + 5*q^11 + 7*q^10 + 9*q^9 + 11*q^8 + + 14*q^7 + 16*q^6 + 16*q^5 + 17*q^4 + 14*q^3 + 10*q^2 + 5*q + 1)*z^6 + ... + + We count unlabeled ordered trees by total number of nodes + and number of internal nodes:: + + sage: R. = QQ[] + sage: Q. = LazyLaurentSeriesRing(R) + sage: leaf = z + sage: internal_node = q * z + sage: L = Q(constant=1, degree=1) + sage: T = Q(None, 1) + sage: T.define(leaf + internal_node * L(T)) + sage: [T[i] for i in range(6)] + [0, 1, q, q^2 + q, q^3 + 3*q^2 + q, q^4 + 6*q^3 + 6*q^2 + q] + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: s = L(None) + sage: s.define(1 + z*s^3) + sage: s[:10] + [1, 1, 3, 12, 55, 273, 1428, 7752, 43263, 246675] + + sage: e = L(None) + sage: e.define(1 + z*e) + sage: e.define(1 + z*e) + Traceback (most recent call last): + ... + ValueError: series already defined + sage: z.define(1 + z^2) + Traceback (most recent call last): + ... + ValueError: series already defined + + """ + if not isinstance(self._coeff_stream, CoefficientStream_uninitialized) or self._coeff_stream._target is not None: + raise ValueError("series already defined") + self._coeff_stream._target = s._coeff_stream + + +class LazySequencesModuleElement(LazySequenceElement): + def __init__(self, parent, coeff_stream): + """ + Initialize the series. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(GF(2), 'z') + sage: z = L.gen() + sage: TestSuite(z).run() + + """ + ModuleElement.__init__(self, parent) + self._coeff_stream = coeff_stream + + def _add_(self, other): + """ + Return the sum of ``self`` and ``other``. + + INPUT: + + - ``other`` -- other series + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: (1 - z)*(1 - z) + 1 - 2*z + z^2 + sage: (1 - z)*(1 - z)*(1 - z) + 1 - 3*z + 3*z^2 - z^3 + sage: z + z + 2*z + sage: z^2 + 3*z^2 + 4*z^2 + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M + N; P + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + + sage: A = L(1, constant=2, degree=3) + sage: B = L(2, constant=-2, degree=5) + sage: A + B + 3 + 2*z^3 + 2*z^4 + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M + N; P + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + + """ + P = self.parent() + left = self._coeff_stream + right = other._coeff_stream + if (isinstance(left, CoefficientStream_exact) + and isinstance(right, CoefficientStream_exact)): + approximate_valuation = min(left.valuation(), right.valuation()) + degree = max(left._degree, right._degree) + initial_coefficients = [left[i] + right[i] for i in range(approximate_valuation, degree)] + constant = left._constant + right._constant + if not any(initial_coefficients) and not constant: + return P.zero() + coeff_stream = CoefficientStream_exact(initial_coefficients, P._sparse, + constant=constant, + degree=degree, + valuation=approximate_valuation) + return P.element_class(P, coeff_stream) + return P.element_class(P, CoefficientStream_add(self._coeff_stream, other._coeff_stream)) + + def _sub_(self, other): + """ + Return the series of this series minus ``other`` series. + + INPUT: + + - ``other`` -- other series + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: z - z + 0 + sage: 3*z - 2*z + z + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M - N; P + -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... + + sage: A = L(1, constant=2, degree=3) + sage: B = L(2, constant=3, degree=5) + sage: A - B + -1 + 2*z^3 + 2*z^4 - z^5 - z^6 - z^7 + ... + + sage: A = L(1, constant=2, degree=3) + sage: B = L([1,0,0,2,2], constant=2) + sage: X = A - B; X + 0 + sage: type(X._coeff_stream) + + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: P = M - N; P + -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... + """ + P = self.parent() + left = self._coeff_stream + right = other._coeff_stream + if (isinstance(left, CoefficientStream_exact) and isinstance(right, CoefficientStream_exact)): + approximate_valuation = min(left.valuation(), right.valuation()) + degree = max(left._degree, right._degree) + initial_coefficients = [left[i] - right[i] for i in range(approximate_valuation, degree)] + constant = left._constant - right._constant + if not any(initial_coefficients) and not constant: + return P.zero() + coeff_stream = CoefficientStream_exact(initial_coefficients, P._sparse, + constant=constant, + degree=degree, + valuation=approximate_valuation) + return P.element_class(P, coeff_stream) + if left == right: + return P.zero() + return P.element_class(P, CoefficientStream_sub(self._coeff_stream, other._coeff_stream)) + + def _lmul_(self, scalar): + """ + Scalar multiplication for module elements with the module + element on the left and the scalar on the right. + + INPUT: + + - ``scalar`` -- an element of the base ring + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: 2*z + 2*z + sage: -1*z + -z + sage: 0*z + 0 + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M * 3 + 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M * 3 + 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... + sage: N = L(lambda n: 1); N + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + sage: N * 4 + 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... + + sage: 1 * M is M + True + sage: M * 1 is M + True + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_scalar + sage: isinstance((M*3)._coeff_stream, CoefficientStream_scalar) + True + sage: isinstance((3*M)._coeff_stream, CoefficientStream_scalar) + True + + """ + P = self.parent() + if not scalar: + return P.zero() + if scalar == 1: + return self + + if isinstance(self._coeff_stream, CoefficientStream_exact): + c = scalar * self._coeff_stream._constant + v = self._coeff_stream.valuation() + initial_coefficients = [scalar * v for v in self._coeff_stream._initial_coefficients] + return P.element_class(P, CoefficientStream_exact(initial_coefficients, P._sparse, + valuation=v, constant=c, + degree=self._coeff_stream._degree)) + + return P.element_class(P, CoefficientStream_scalar(self._coeff_stream, scalar)) + + def _neg_(self): + """ + Return the negative of this series. + + TESTS:: + + sage: L = LazyLaurentSeriesRing(ZZ, 'z') + sage: z = L.gen() + sage: -(1 - z) + -1 + z + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: P = -M; P + -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... -class LazyLaurentSeries(ModuleElement): + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: P = -M; P + -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... + sage: -(z^2 + 3*z - 4*z^3) + -3*z - z^2 + 4*z^3 + """ + P = self.parent() + coeff_stream = self._coeff_stream + if isinstance(self._coeff_stream, CoefficientStream_exact): + initial_coefficients = [-v for v in coeff_stream._initial_coefficients] + constant = -coeff_stream._constant + coeff_stream = CoefficientStream_exact(initial_coefficients, P._sparse, + constant=constant, + valuation=coeff_stream.valuation()) + return P.element_class(P, coeff_stream) + # -(-f) = f + if isinstance(coeff_stream, CoefficientStream_neg): + return P.element_class(P, coeff_stream._series) + return P.element_class(P, CoefficientStream_neg(coeff_stream)) + + +class LazyLaurentSeries(LazySequencesModuleElement): r""" A Laurent series where the coefficients are computed lazily. @@ -125,58 +724,43 @@ class LazyLaurentSeries(ModuleElement): sage: g == f True """ - - def __init__(self, parent, coeff_stream): - """ - Initialize ``self``. - - INPUT: - - - ``parent`` -- the base ring for the series - - ``coeff_stream`` -- the coefficient stream defining the series - - TESTS:: - - sage: L = LazyLaurentSeriesRing(GF(2), 'z') - sage: z = L.gen() - sage: TestSuite(z).run() - """ - ModuleElement.__init__(self, parent) - self._coeff_stream = coeff_stream - - def __getitem__(self, n): + def change_ring(self, ring): """ - Return the coefficient of the term with exponent ``n`` of the series. + Return this series with coefficients converted to elements of ``ring``. INPUT: - - ``n`` -- integer + - ``ring`` -- a ring EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = z/(1 - 2*z^3) - sage: [f[n] for n in range(20)] - [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] - sage: f[0:20] - [0, 1, 0, 0, 2, 0, 0, 4, 0, 0, 8, 0, 0, 16, 0, 0, 32, 0, 0, 64] + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) + sage: s = 2 + z + sage: t = s.change_ring(QQ) + sage: t^-1 + 1/2 - 1/4*z + 1/8*z^2 - 1/16*z^3 + 1/32*z^4 - 1/64*z^5 + 1/128*z^6 + ... + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M.change_ring(QQ) + sage: N.parent() + Lazy Laurent Series Ring in z over Rational Field + sage: M.parent() + Lazy Laurent Series Ring in z over Integer Ring - sage: M = L(lambda n: n) - sage: [M[n] for n in range(20)] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n) - sage: [M[n] for n in range(20)] - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + sage: M = L(lambda n: n); M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: M.parent() + Lazy Laurent Series Ring in z over Integer Ring + sage: N = M.change_ring(QQ) + sage: N.parent() + Lazy Laurent Series Ring in z over Rational Field + sage: M ^-1 + z^-1 - 2 + z + ... """ - R = self.base_ring() - if isinstance(n, slice): - if n.stop is None: - raise NotImplementedError("cannot list an infinite set") - start = n.start if n.start is not None else self._coeff_stream.valuation() - step = n.step if n.step is not None else 1 - return [R(self._coeff_stream[k]) for k in range(start, n.stop, step)] - return R(self._coeff_stream[n]) + from .lazy_laurent_series_ring import LazyLaurentSeriesRing + Q = LazyLaurentSeriesRing(ring, names=self.parent().variable_names()) + return Q.element_class(Q, self._coeff_stream) def __call__(self, g): r""" @@ -352,7 +936,9 @@ def __call__(self, g): P = g.parent() # g = 0 case - if (not isinstance(g, LazyLaurentSeries) and not g) or (isinstance(g, LazyLaurentSeries) and isinstance(g._coeff_stream, CoefficientStream_zero)): + if ((not isinstance(g, LazyLaurentSeries) and not g) + or (isinstance(g, LazyLaurentSeries) + and isinstance(g._coeff_stream, CoefficientStream_zero))): if self._coeff_stream._approximate_valuation >= 0: return P(self[0]) # Perhaps we just don't yet know if the valuation is non-negative @@ -432,174 +1018,67 @@ def _mul_(self, other): sage: (1 - z)*(1 - z) 1 - 2*z + z^2 sage: (1 - z)*(1 - z)*(1 - z) - 1 - 3*z + 3*z^2 - z^3 - sage: M = L(lambda n: n) - sage: M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M * (1 - M) - sage: N - z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 + ... - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: M * N - z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + ... - - sage: L.one() * M is M - True - sage: M * L.one() is M - True - """ - P = self.parent() - left = self._coeff_stream - right = other._coeff_stream - if isinstance(left, CoefficientStream_zero) or isinstance(right, CoefficientStream_zero): - return P.zero() - - R = P._laurent_poly_ring - z = R.gen() - if isinstance(left, CoefficientStream_exact): - if not left._constant: - pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) - if pl == R.one(): # self == 1 - return other - if isinstance(right, CoefficientStream_exact): - if not right._constant: - pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) - p = pl * pr - c = left._constant - p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c)) - elif isinstance(right, CoefficientStream_exact): - if not right._constant: - pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) - if pr == R.one(): # other == 1 - return self - return P.element_class(P, CoefficientStream_cauchy_product(self._coeff_stream, other._coeff_stream)) - - def _add_(self, other): - """ - Return the sum of this series with ``other``. - - INPUT: - - - ``other`` -- other series - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: (1 - z)*(1 - z) - 1 - 2*z + z^2 - sage: (1 - z)*(1 - z)*(1 - z) - 1 - 3*z + 3*z^2 - z^3 - sage: z + z - 2*z - sage: z^2 + 3*z^2 - 4*z^2 - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M + N; P - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - - sage: A = L(1, constant=2, degree=3) - sage: B = L(2, constant=-2, degree=5) - sage: A + B - 3 + 2*z^3 + 2*z^4 - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M + N; P - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - """ - P = self.parent() - left = self._coeff_stream - right = other._coeff_stream - if (isinstance(left, CoefficientStream_exact) - and isinstance(right, CoefficientStream_exact)): - R = P._laurent_poly_ring - z = R.gen() - pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) - pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) - c = left._constant + right._constant - d = max(left._degree, right._degree) - pl += R([left._constant]*(d-left._degree)).shift(left._degree) - pr += R([right._constant]*(d-right._degree)).shift(right._degree) - p = pl + pr - if not p and not c: - return P.zero() - p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=d)) - return P.element_class(P, CoefficientStream_add(self._coeff_stream, other._coeff_stream)) - - def _sub_(self, other): - """ - Return the series of this series minus ``other`` series. - - INPUT: - - - ``other`` -- other series - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: z - z - 0 - sage: 3*z - 2*z - z - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M - N; P - -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... - - sage: A = L(1, constant=2, degree=3) - sage: B = L(2, constant=3, degree=5) - sage: A - B - -1 + 2*z^3 + 2*z^4 - z^5 - z^6 - z^7 + ... - - sage: A = L(1, constant=2, degree=3) - sage: B = L([1,0,0,2,2], constant=2) - sage: X = A - B; X - 0 - sage: type(X._coeff_stream) - - + 1 - 3*z + 3*z^2 - z^3 + sage: M = L(lambda n: n) + sage: M + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: N = M * (1 - M) + sage: N + z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 + ... sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = L(lambda n: 1); N 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M - N; P - -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... + sage: M * N + z + 3*z^2 + 6*z^3 + 10*z^4 + 15*z^5 + 21*z^6 + ... + + sage: L.one() * M is M + True + sage: M * L.one() is M + True """ P = self.parent() left = self._coeff_stream right = other._coeff_stream - if (isinstance(left, CoefficientStream_exact) and isinstance(right, CoefficientStream_exact)): - R = P._laurent_poly_ring - z = R.gen() - c = left._constant - right._constant - pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) - pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) - d = max(left._degree, right._degree) - pl += R([left._constant]*(d-left._degree)).shift(left._degree) - pr += R([right._constant]*(d-right._degree)).shift(right._degree) - p = pl - pr - if not p and not c: - return P.zero() - p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=d)) - if left == right: + if isinstance(left, CoefficientStream_zero) or isinstance(right, CoefficientStream_zero): return P.zero() - return P.element_class(P, CoefficientStream_sub(self._coeff_stream, other._coeff_stream)) + + R = P._laurent_poly_ring + z = R.gen() + if isinstance(left, CoefficientStream_exact): + if not left._constant: + pl = R(sum([left[i] * z**i for i in range(left._approximate_valuation, left._degree)])) + if pl == R.one(): # self == 1 + return other + if isinstance(right, CoefficientStream_exact): + if not right._constant: + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) + p = pl * pr + c = left._constant + p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c)) + elif isinstance(right, CoefficientStream_exact): + if not right._constant: + pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) + if pr == R.one(): # other == 1 + return self + return P.element_class(P, CoefficientStream_cauchy_product(self._coeff_stream, other._coeff_stream)) + + P = self.parent() + left = self._coeff_stream + right = other._coeff_stream + if (isinstance(left, CoefficientStream_exact) + and isinstance(right, CoefficientStream_exact)): + c = left._constant + right._constant + v = min(left.valuation(), right.valuation()) + d = max(left._degree(), right._degree()) + initial_coefficients = [left[i] + right[i] for i in range(v, d)] + if not any(initial_terms) and not c: + return P.zero() + return P.element_class(P, CoefficientStream_exact(initial_terms, P._sparse, + valuation=v, degree=d, constant=c)) + return P.element_class(P, CoefficientStream_add(self._coeff_stream, other._coeff_stream)) def _div_(self, other): """ @@ -655,98 +1134,6 @@ def _div_(self, other): return P.element_class(P, CoefficientStream_cauchy_product(left, CoefficientStream_cauchy_inverse(right))) - def _rmul_(self, scalar): - """ - Return the scalar multiplication of this series by ``scalar``. - - INPUT: - - - ``scalar`` -- an element of the base ring - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: 2*z - 2*z - sage: -1*z - -z - sage: 0*z - 0 - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M * 3 - 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M * 3 - 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: N * 4 - 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... - - sage: 1 * M is M - True - sage: M * 1 is M - True - """ - P = self.parent() - if not scalar: - return P.zero() - if scalar == 1: - return self - - if isinstance(self._coeff_stream, CoefficientStream_exact): - R = P._laurent_poly_ring - z = R.gen() - c = scalar * self._coeff_stream._constant - pl = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) - p = scalar * pl - p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=self._coeff_stream._degree)) - - return P.element_class(P, CoefficientStream_scalar(self._coeff_stream, scalar)) - - def _neg_(self): - """ - Return the negative of this series. - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: z = L.gen() - sage: -(1 - z) - -1 + z - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = -M; P - -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = -M; P - -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... - sage: -(z^2 + 3*z - 4*z^3) - -3*z - z^2 + 4*z^3 - """ - P = self.parent() - if isinstance(self._coeff_stream, CoefficientStream_exact): - R = P._laurent_poly_ring - z = R.gen() - poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) - p = -poly - c = -self._coeff_stream._constant - d = self._coeff_stream._degree - p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c, degree=d)) - # -(-f) = f - if isinstance(self._coeff_stream, CoefficientStream_neg): - return P.element_class(P, self._coeff_stream._series) - return P.element_class(P, CoefficientStream_neg(self._coeff_stream)) - def __invert__(self): """ Return the multiplicative inverse of the element. @@ -776,181 +1163,13 @@ def __invert__(self): poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) if poly == R.gen(): ret = 1 / poly - p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=ret.valuation(), constant=self._coeff_stream._constant)) - # (f^-1)^-1 = f - if isinstance(self._coeff_stream, CoefficientStream_cauchy_inverse): - return P.element_class(P, self._coeff_stream._series) - return P.element_class(P, CoefficientStream_cauchy_inverse(self._coeff_stream)) - - def coefficient(self, n): - """ - Return the coefficient of the term with exponent ``n`` of the series. - - INPUT: - - - ``n`` -- integer - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: F = L(None) - sage: F.define(1 + z*F^2) - sage: F.coefficient(10) - 16796 - sage: F - 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) - sage: e = L(None) - sage: e.define(1 + z*e^2) - sage: e - 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - sage: e._coeff_stream._cache - [1, 1, 2, 5, 14, 42, 132] - sage: e.coefficient(10) - 16796 - sage: e._coeff_stream._cache - [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796] - sage: M = L(lambda n: n^2); M - z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... - sage: M._coeff_stream._cache - [0, 1, 4, 9, 16, 25, 36] - sage: M.coefficient(9) - 81 - sage: M._coeff_stream._cache - [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] - - sage: L = LazyLaurentSeriesRing(ZZ, 'z', sparse=True) - sage: M = L(lambda n: n^2); M - z + 4*z^2 + 9*z^3 + 16*z^4 + 25*z^5 + 36*z^6 + ... - sage: M._coeff_stream._cache - {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36} - sage: M.coefficient(10) - 100 - sage: M._coeff_stream._cache - {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 10: 100} - """ - return self.__getitem__(n) - - def map_coefficients(self, func, ring=None): - """ - Return the series with ``func`` applied to each coefficient of ``self``. - - INPUT: - - - ``func`` -- function that takes in a coefficient and returns - a new coefficient - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: s = z/(1 - 2*z) - sage: t = s.map_coefficients(lambda c: c + 1) - sage: s - z + 2*z^2 + 4*z^3 + 8*z^4 + 16*z^5 + 32*z^6 + 64*z^7 + ... - sage: t - 2*z + 3*z^2 + 5*z^3 + 9*z^4 + 17*z^5 + 33*z^6 + 65*z^7 + ... - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.map_coefficients(lambda c: c + 1); N - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.map_coefficients(lambda c: c + 1); N - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... - """ - P = self.parent() - if isinstance(self._coeff_stream, CoefficientStream_exact): - R = P._laurent_poly_ring - z = R.gen() - p = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) - p = p.map_coefficients(func) - c = func(self._coeff_stream._constant) - if not p and not c: - return P.zero() - p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, self._coeff_stream._is_sparse, valuation=p.valuation(), constant=c)) - return P.element_class(P, CoefficientStream_apply_coeff(self._coeff_stream, func, P.base_ring())) - - def change_ring(self, ring): - """ - Return this series with coefficients converted to elements of ``ring``. - - INPUT: - - - ``ring`` -- a ring - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) - sage: s = 2 + z - sage: t = s.change_ring(QQ) - sage: t^-1 - 1/2 - 1/4*z + 1/8*z^2 - 1/16*z^3 + 1/32*z^4 - 1/64*z^5 + 1/128*z^6 + ... - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = M.change_ring(QQ) - sage: N.parent() - Lazy Laurent Series Ring in z over Rational Field - sage: M.parent() - Lazy Laurent Series Ring in z over Integer Ring - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M.parent() - Lazy Laurent Series Ring in z over Integer Ring - sage: N = M.change_ring(QQ) - sage: N.parent() - Lazy Laurent Series Ring in z over Rational Field - sage: M ^-1 - z^-1 - 2 + z + ... - """ - from .lazy_laurent_series_ring import LazyLaurentSeriesRing - Q = LazyLaurentSeriesRing(ring, names=self.parent().variable_names()) - return Q.element_class(Q, self._coeff_stream) - - def truncate(self, d): - """ - Return this series with its terms of degree >= ``d`` truncated. - - INPUT: - - - ``d`` -- integer - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) - sage: alpha = 1/(1-z) - sage: alpha - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: beta = alpha.truncate(5) - sage: beta - 1 + z + z^2 + z^3 + z^4 - sage: alpha - beta - z^5 + z^6 + ... - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M.truncate(4) - z + 2*z^2 + 3*z^3 - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M.truncate(4) - z + 2*z^2 + 3*z^3 - """ - P = self.parent() - R = P._laurent_poly_ring - z = R.gen() - p = R.sum(self[i] * z**i for i in range(self._coeff_stream._approximate_valuation, d)) - p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=ZZ.zero(), degree=d)) + p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=ret.valuation(), constant=self._coeff_stream._constant)) + # (f^-1)^-1 = f + if isinstance(self._coeff_stream, CoefficientStream_cauchy_inverse): + return P.element_class(P, self._coeff_stream._series) + return P.element_class(P, CoefficientStream_cauchy_inverse(self._coeff_stream)) + def __pow__(self, n): """ @@ -1034,19 +1253,6 @@ 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) - def prec(self): - """ - Return the precision of the series, which is infinity. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) - sage: f.prec() - +Infinity - """ - return infinity - def polynomial(self, degree=None, name=None): r""" Return the polynomial or Laurent polynomial if the series is actually so. @@ -1131,32 +1337,6 @@ def polynomial(self, degree=None, name=None): R = PolynomialRing(S.base_ring(), name=name) return R([self[i] for i in range(m)]) - def valuation(self): - r""" - Return the valuation of ``self``. - - This method determines the valuation of the series by looking for a - nonzero coefficient. Hence if the series happens to be zero, then it - may run forever. - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: s = 1/(1 - z) - 1/(1 - 2*z) - sage: s.valuation() - 1 - sage: t = z - z - sage: t.valuation() - +Infinity - sage: M = L(lambda n: n^2, 0) - sage: M.valuation() - 1 - sage: M = L(lambda n: n^2, 0) - sage: M.valuation() - 1 - """ - return self._coeff_stream.valuation() - def _repr_(self): """ Return the string representation of this Laurent series. @@ -1191,218 +1371,3 @@ def _repr_(self): ret = repr(R([self._coeff_stream[i] for i in range(v, m)]).shift(v)) # TODO: Better handling when ret == 0 but we have not checked up to the constant term return ret + ' + ...' - - def _richcmp_(self, other, op): - r""" - Compare ``self` with ``other`` with respect to the comparison - operator ``op``. - - Equality is verified if the corresponding coefficients of both series - can be checked for equality without computing coefficients - indefinitely. Otherwise an exception is raised to declare that - equality is not decidable. - - Inequality is not defined for lazy Laurent series. - - INPUT: - - - ``other`` -- another Laurent series - - - ``op`` -- comparison operator - - EXAMPLES:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: z + z^2 == z^2 + z - True - sage: z + z^2 != z^2 + z - False - sage: z + z^2 > z^2 + z - False - sage: z + z^2 < z^2 + z - False - """ - if op is op_EQ: - if isinstance(self._coeff_stream, CoefficientStream_zero): # self == 0 - return isinstance(other._coeff_stream, CoefficientStream_zero) - if isinstance(other._coeff_stream, CoefficientStream_zero): # self != 0 but other == 0 - return False - - if (not isinstance(self._coeff_stream, CoefficientStream_exact) - or not isinstance(other._coeff_stream, CoefficientStream_exact)): - # One of the lazy laurent series is not known to eventually be constant - # Implement the checking of the caches here. - n = min(self._coeff_stream._approximate_valuation, other._coeff_stream._approximate_valuation) - m = max(self._coeff_stream._approximate_valuation, other._coeff_stream._approximate_valuation) - for i in range(n, m): - if self[i] != other[i]: - return False - if self._coeff_stream == other._coeff_stream: - return True - raise ValueError("undecidable as lazy Laurent series") - - # Both are CoefficientStream_exact, which implements a full check - return self._coeff_stream == other._coeff_stream - - if op is op_NE: - return not (self == other) - - return False - - def __hash__(self): - """ - Return the hash of ``self`` - - TESTS:: - - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: f = L([1,2,3,4], -5) - sage: g = (1 + f)/(1 - f)^2 - sage: {g: 1} - {z^5 - 2*z^6 + z^7 + 5*z^9 - 11*z^10 + z^11 + ...: 1} - """ - return hash(self._coeff_stream) - - def __bool__(self): - """ - Test whether ``self`` is not zero. - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(GF(2)) - sage: (z-z).is_zero() - True - sage: f = 1/(1 - z) - sage: f.is_zero() - False - sage: M = L(lambda n: n, 0); M - z + z^3 + z^5 + ... - sage: M.is_zero() - False - """ - if isinstance(self._coeff_stream, CoefficientStream_zero): - return False - if isinstance(self._coeff_stream, CoefficientStream_exact): - # This should always end up being True, but let's be careful about it for now... - P = self.paren() - R = P._laurent_poly_ring - z = R.gen() - poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) - return poly or self._coeff_stream._constant - - for a in self._coeff_stream._cache: - if a: - return True - if self[self._coeff_stream._approximate_valuation]: - return True - raise ValueError("undecidable as lazy Laurent series") - - def define(self, s): - r""" - Define an equation by ``self = s``. - - INPUT:: - - - ``s`` -- a Laurent polynomial - - EXAMPLES: - - We begin by constructing the Catalan numbers:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: C = L(None) - sage: C.define(1 + z*C^2) - sage: C - 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... - - The Catalan numbers but with a valuation 1:: - - sage: B = L(None, 1) - sage: B.define(z + B^2) - sage: B - z + z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + ... - - We can define multiple series that are linked:: - - sage: s = L(None) - sage: t = L(None) - sage: s.define(1 + z*t^3) - sage: t.define(1 + z*s^2) - sage: s[:9] - [1, 1, 3, 9, 34, 132, 546, 2327, 10191] - sage: t[:9] - [1, 1, 2, 7, 24, 95, 386, 1641, 7150] - - An bigger example:: - - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: A = L(None, 5) - sage: B = L(None) - sage: C = L(None, 2) - sage: A.define(z^5 + B^2) - sage: B.define(z^5 + C^2) - sage: C.define(z^2 + C^2 + A^2) - sage: A[0:15] - [0, 0, 0, 0, 0, 1, 0, 0, 1, 2, 5, 4, 14, 10, 48] - sage: B[0:15] - [0, 0, 0, 0, 1, 1, 2, 0, 5, 0, 14, 0, 44, 0, 138] - sage: C[0:15] - [0, 0, 1, 0, 1, 0, 2, 0, 5, 0, 15, 0, 44, 2, 142] - - Counting binary trees:: - - sage: L. = LazyLaurentSeriesRing(QQ) - sage: s = L(None, valuation=1) - sage: s.define(z + (s^2+s(z^2))/2) - sage: [s[i] for i in range(9)] - [0, 1, 1, 1, 2, 3, 6, 11, 23] - - The `q`-Catalan numbers:: - - sage: R. = ZZ[] - sage: L. = LazyLaurentSeriesRing(R) - sage: s = L(None) - sage: s.define(1+z*s*s(q*z)) - sage: s - 1 + z + (q + 1)*z^2 + (q^3 + q^2 + 2*q + 1)*z^3 - + (q^6 + q^5 + 2*q^4 + 3*q^3 + 3*q^2 + 3*q + 1)*z^4 - + (q^10 + q^9 + 2*q^8 + 3*q^7 + 5*q^6 + 5*q^5 + 7*q^4 + 7*q^3 + 6*q^2 + 4*q + 1)*z^5 - + (q^15 + q^14 + 2*q^13 + 3*q^12 + 5*q^11 + 7*q^10 + 9*q^9 + 11*q^8 - + 14*q^7 + 16*q^6 + 16*q^5 + 17*q^4 + 14*q^3 + 10*q^2 + 5*q + 1)*z^6 + ... - - We count unlabeled ordered trees by total number of nodes - and number of internal nodes:: - - sage: R. = QQ[] - sage: Q. = LazyLaurentSeriesRing(R) - sage: leaf = z - sage: internal_node = q * z - sage: L = Q(constant=1, degree=1) - sage: T = Q(None, 1) - sage: T.define(leaf + internal_node * L(T)) - sage: [T[i] for i in range(6)] - [0, 1, q, q^2 + q, q^3 + 3*q^2 + q, q^4 + 6*q^3 + 6*q^2 + q] - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: s = L(None) - sage: s.define(1 + z*s^3) - sage: s[:10] - [1, 1, 3, 12, 55, 273, 1428, 7752, 43263, 246675] - - sage: e = L(None) - sage: e.define(1 + z*e) - sage: e.define(1 + z*e) - Traceback (most recent call last): - ... - ValueError: series already defined - sage: z.define(1 + z^2) - Traceback (most recent call last): - ... - ValueError: series already defined - """ - if not isinstance(self._coeff_stream, CoefficientStream_uninitialized) or self._coeff_stream._target is not None: - raise ValueError("series already defined") - self._coeff_stream._target = s._coeff_stream - From 42cdf7a0c77781ebdee866c98e608128021d7733 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Tue, 3 Aug 2021 03:42:41 +0530 Subject: [PATCH 93/98] Added CS_recursive method, fixed the apply_coeff method, and corrected some documentation. --- .../data_structures/coefficient_stream.py | 230 ++++++++++++++---- src/sage/rings/lazy_laurent_series.py | 7 +- src/sage/rings/lazy_laurent_series_ring.py | 34 ++- 3 files changed, 207 insertions(+), 64 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index f0452132b5f..09aab142323 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -4,7 +4,9 @@ This module provides lazy class implementations of basic operators on coefficient streams. The classes implemented in this module can be used to build up more complex streams for different kinds of -series (Laurent, Dirichlet, etc). +series (Laurent, Dirichlet, etc). The classes CS_zero and CS_exact +enable us to bypass the checks in CS._getitem_ in the most important +cases to improve performance. EXAMPLES: @@ -171,7 +173,7 @@ def __getstate__(self): """ Remove the cache from the pickle information so that it can be pickled. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact sage: h = CoefficientStream_exact([1], True) @@ -200,7 +202,7 @@ def __setstate__(self, d): - ``d`` -- a dictionary that needs to be unpickled - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact sage: h = CoefficientStream_exact([-1], True) @@ -312,22 +314,19 @@ def valuation(self): class CoefficientStream_exact(CoefficientStream): r""" - A stream of eventually constant coefficients. + A stream of eventually constant coefficients which helps in + improving performance in important cases. INPUT: - ``initial_values`` -- a list of initial values - - ``is_sparse`` -- a boolean, which specifies whether the series is sparse - - ``valuation`` -- (default: 0), an integer, determining the degree of the first element of ``initial_values`` - - ``degree`` -- (default: None), an integer, determining the degree of the first element which is known to be equal to ``constant`` - - ``constant`` -- (default: 0), an integer, the coefficient of every index larger than or equal to ``degree`` """ @@ -418,6 +417,16 @@ def __getitem__(self, n): return self._initial_coefficients[i] def valuation(self): + """ + Return the valuation of the series. + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: s = CoefficientStream_exact([1], False) + sage: s.valuation() + 0 + """ return self._approximate_valuation def __hash__(self): @@ -439,14 +448,29 @@ def __eq__(self, other): INPUT: - - ``other`` -- a lazy Laurent series which is known to be eventaully geometric + - ``other`` -- a series which is known to be eventaully geometric + sage EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = 1 + z + z^2 + z^3 - sage: m = 1 + z + z^2 + z^3 - sage: f == m + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: s = CoefficientStream_exact([2], False, valuation=-1, degree=2, constant=1) + sage: t = t = CoefficientStream_exact([0, 2, 0], False, valuation=-2, degree=2, constant=1) + sage: [s[i] for i in range(10)] + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] + sage: [t[i] for i in range(10)] + [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] + sage: s == t + True + sage: s = CoefficientStream_exact([2], False, constant=1) + sage: t = CoefficientStream_exact([2], False, valuation=-1, constant=1) + sage: [s[i] for i in range(10)] + [2, 1, 1, 1, 1, 1, 1, 1, 1, 1] + sage: [t[i] for i in range(10)] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + sage: s == t + False + sage: t == t True """ return (isinstance(other, type(self)) @@ -455,6 +479,93 @@ def __eq__(self, other): and self._constant == other._constant) +class CoefficientStream_recursive(CoefficientStream_inexact): + r""" + Class that returns the elements in a recursively defined stream. + + INPUT: + + - ``function`` -- a python function that generates the + coefficients of the series recursively + - ``ring`` -- the base ring of the series + - ``is_sparse`` -- boolean; specifies whether the series is sparse + - ``approximate_valuation`` -- integer; a lower bound for the valuation of the series + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_recursive + sage: def g(s, i): + ....: if i < 0: + ....: return i*2 + ....: else: + ....: return s[i-1] + i + ....: + sage: M = CoefficientStream_recursive(g, ZZ, True, -5) + sage: [M[i] for i in range(-5, 10)] + [-10, -8, -6, -4, -2, -2, -1, 1, 4, 8, 13, 19, 26, 34, 43] + """ + + def __init__(self, function, ring, is_sparse, approximate_valuation): + """ + Initialize. + + TESTS:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_recursive + sage: f = CoefficientStream_recursive(lambda s, n: 1, ZZ, False, 1) + """ + self._coefficient_function = function + self._ring = ring + super().__init__(is_sparse, approximate_valuation) + + def get_coefficient(self, n): + """ + Return the ``n``-th coefficient of ``self``. + + INPUT: + + - ``n`` -- integer; the degree for the coefficient + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_recursive + sage: def g(s, i): + ....: if i < 0: + ....: return 1 + ....: else: + ....: return s[i-1] + 1 + sage: M = CoefficientStream_recursive(g, ZZ, True, -5) + sage: [M[i] for i in range(-5, 10)] + [1, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + sage: M.get_coefficient(11) + 13 + """ + return self._ring(self._coefficient_function(self, n)) + + def iterate_coefficients(self): + """ + A generator for the coefficients of ``self``. + + EXAMPLES:: + + sage: from sage.data_structures.coefficient_stream import CoefficientStream_recursive + sage: def g(s, i): + ....: if i < 0: + ....: return 1 + ....: else: + ....: return s[i-1] + i + sage: M = CoefficientStream_recursive(g, ZZ, False, -5) + sage: n = M.iterate_coefficients() + sage: [next(n) for i in range(-5, 10)] + [1, 1, 1, 1, 1, 1, 2, 4, 7, 11, 16, 22, 29, 37, 46] + """ + n = self._offset + ring = self._ring + while True: + yield ring(self._coefficient_function(self, n)) + n += 1 + + class CoefficientStream_coefficient_function(CoefficientStream_inexact): r""" Class that returns the elements in the coefficient stream. @@ -537,10 +648,14 @@ class CoefficientStream_uninitialized(CoefficientStream_inexact): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: N = L(None) - sage: N - Uninitialized Lazy Laurent Series + sage: from sage.data_structures.coefficient_stream import CoefficientStream_uninitialized + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: one = CoefficientStream_exact([1], True) + sage: C = CoefficientStream_uninitialized(True, 0) + sage: C._target + sage: C._target = one + sage: C.get_coefficient(4) + 0 """ def __init__(self, is_sparse, approximate_valuation): """ @@ -548,10 +663,8 @@ def __init__(self, is_sparse, approximate_valuation): TESTS:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: N = L(None, 2) - sage: N - Uninitialized Lazy Laurent Series + sage: from sage.data_structures.coefficient_stream import CoefficientStream_uninitialized + sage: C = CoefficientStream_uninitialized(False, 0) """ self._target = None super().__init__(is_sparse, approximate_valuation) @@ -566,12 +679,14 @@ def get_coefficient(self, n): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: C = L(None) - sage: C.define(1 + z*C^2) - sage: C._coeff_stream.get_coefficient(4) - 14 - + sage: from sage.data_structures.coefficient_stream import CoefficientStream_uninitialized + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: one = CoefficientStream_exact([1], True) + sage: C = CoefficientStream_uninitialized(True, 0) + sage: C._target + sage: C._target = one + sage: C.get_coefficient(0) + 1 """ return self._target[n] @@ -581,13 +696,15 @@ def iterate_coefficients(self): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: C = L(None) - sage: C.define(1 + z*C^2) - sage: n = C._coeff_stream.iterate_coefficients() - sage: [next(n) for _ in range(6)] - [1, 1, 2, 5, 14, 42] - + sage: from sage.data_structures.coefficient_stream import CoefficientStream_uninitialized + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: z = CoefficientStream_exact([1], True, valuation=1) + sage: C = CoefficientStream_uninitialized(True, 0) + sage: C._target + sage: C._target = z + sage: n = C.iterate_coefficients() + sage: [next(n) for _ in range(10)] + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] """ n = self._approximate_valuation while True: @@ -621,9 +738,12 @@ def __init__(self, series, *args, **kwargs): TESTS:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_neg - sage: CoefficientStream_neg.__base__ - + sage: from sage.data_structures.coefficient_stream import CoefficientStream_unary + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: L = CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 1) + sage: M = CoefficientStream_unary(L, True, 0) + sage: M._series[0] + 0 """ self._series = series super().__init__(*args, **kwargs) @@ -632,7 +752,7 @@ def __hash__(self): """ Return the hash of ``self``. - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: f = ~(1 - z) @@ -649,7 +769,7 @@ def __eq__(self, other): - ``other`` -- a stream of coefficients - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_scalar) sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, False, 1) @@ -694,9 +814,15 @@ def __init__(self, left, right, *args, **kwargs): TESTS:: - sage: from sage.data_structures.coefficient_stream import CoefficientStream_cauchy_product - sage: (CoefficientStream_cauchy_product.__base__).__base__ - + sage: from sage.data_structures.coefficient_stream import CoefficientStream_binary + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: M = CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0) + sage: N = CoefficientStream_coefficient_function(lambda n: -1, ZZ, True, 0) + sage: O = CoefficientStream_binary(M, N, True, 0) + sage: O._left[1] + 1 + sage: O._right[1] + -1 """ self._left = left self._right = right @@ -706,7 +832,7 @@ def __hash__(self): """ Return the hash of ``self``. - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: f = 1/(1 - z) + 1/(1 + z) @@ -723,7 +849,7 @@ def __eq__(self, other): - ``other`` -- a stream of coefficients - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, False, 1) @@ -771,7 +897,7 @@ def __hash__(self): """ Return the hash of ``self``. - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: f = z^2 + z @@ -788,7 +914,8 @@ def __eq__(self, other): - ``other`` -- a stream of coefficients - TESTS:: + EXAMPLES:: + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_cauchy_product) sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, True, 0) sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) @@ -812,7 +939,8 @@ def __eq__(self, other): class CoefficientStream_zero(CoefficientStream): """ - A coefficient Stream that is exactly equal to zero. + A coefficient Stream that is exactly equal to zero that + helps in improving performance in important cases. INPUT: @@ -847,7 +975,7 @@ def __getitem__(self, n): - ``n`` -- integer; the index of the coefficient to be returned - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_zero sage: s = CoefficientStream_zero(True) @@ -875,7 +1003,7 @@ def __hash__(self): """ Return the hash of ``self``. - TESTS:: + EXAMPLES:: sage: from sage.data_structures.coefficient_stream import CoefficientStream_zero sage: s = CoefficientStream_zero(False) @@ -1624,7 +1752,7 @@ def get_coefficient(self, n): sage: [g.get_coefficient(i) for i in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] """ - c = self._ring(self._function(self._series[n])) + c = self._ring(self._function(self._series[n])) if self._series[n] else self._ring(self._series[n]) return c def iterate_coefficients(self): @@ -1642,6 +1770,6 @@ def iterate_coefficients(self): """ n = self._offset while True: - c = self._ring(self._function(self._series[n])) + c = self._ring(self._function(self._series[n])) if self._series[n] else self._ring(self._series[n]) yield c n += 1 diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 5289284adc0..5f666722d77 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -168,11 +168,11 @@ def map_coefficients(self, func, ring=None): P = self.parent() coeff_stream = self._coeff_stream if isinstance(coeff_stream, CoefficientStream_exact): - initial_values = [func(i) if i else 0 for i in coeff_stream._initial_values] - c = func(coeffs._constant) if coeffs._constant else 0 + initial_values = [func(i) if i else 0 for i in coeff_stream._initial_coefficients] + c = func(coeff_stream._constant) if coeff_stream._constant else 0 if not any(initial_values) and not c: return P.zero() - coeff_stream = CoefficientStream_exact(p_list, self._coeff_stream._is_sparse, + coeff_stream = CoefficientStream_exact(initial_values, self._coeff_stream._is_sparse, valuation=coeff_stream._approximate_valuation, degree=coeff_stream._degree, constant=c) @@ -1170,7 +1170,6 @@ def __invert__(self): return P.element_class(P, self._coeff_stream._series) return P.element_class(P, CoefficientStream_cauchy_inverse(self._coeff_stream)) - def __pow__(self, n): """ Return the ``n``-th power of the series. diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 49c5a266e96..22547ffa147 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -66,6 +66,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from inspect import signature + from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -86,7 +88,8 @@ CoefficientStream_zero, CoefficientStream_coefficient_function, CoefficientStream_exact, - CoefficientStream_uninitialized + CoefficientStream_uninitialized, + CoefficientStream_recursive ) @@ -285,6 +288,25 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: g = L([1,3,5,7,9], 5, -1) sage: g z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + ... + + ``x`` can be a function that recursively calculates the elements of + the series. The function must then take an additional parameter (which + must come first) to describe the series. + + sage: def g(s, i): + ....: if i < 0: + ....: return 1 + ....: else: + ....: return s[i-1] + i + sage: L(g, -5) + z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... + sage: s = L(g, -5) + sage: s + z^-5 + z^-4 + z^-3 + z^-2 + z^-1 + 1 + 2*z + ... + sage: s[:10] + [1, 1, 1, 1, 1, 1, 2, 4, 7, 11, 16, 22, 29, 37, 46] + sage: ~s + z^5 - z^6 - z^11 + ... .. TODO:: @@ -316,14 +338,6 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if valuation is None: raise ValueError("you must specify the degree for the polynomial 0") degree = valuation - # print('it breaks here') - # print(x) - # x = R(x) - # print(x) - # x = [R(x)] - # print(x) - # print(x) - # print(type(x)) if x == R.zero(): coeff_stream = CoefficientStream_exact([x], self._sparse, valuation=degree-1, constant=constant) return self.element_class(self, coeff_stream) @@ -344,6 +358,8 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No z = R.gen() p = [x(i) for i in range(valuation, degree)] return self.element_class(self, CoefficientStream_exact(p, self._sparse, valuation=valuation, constant=constant, degree=degree)) + if len(signature(x).parameters) > 1: + return self.element_class(self, CoefficientStream_recursive(x, self.base_ring(), self._sparse, valuation)) return self.element_class(self, CoefficientStream_coefficient_function(x, self.base_ring(), self._sparse, valuation)) raise ValueError(f"unable to convert {x} into a lazy Laurent series") From a8837cab39469bac820d62b8cfc22a63ecd2a18f Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Tue, 3 Aug 2021 23:33:57 +0530 Subject: [PATCH 94/98] inital_values -> initial_coefficients. --- src/sage/rings/lazy_laurent_series.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 5f666722d77..9a5791467bd 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -168,11 +168,11 @@ def map_coefficients(self, func, ring=None): P = self.parent() coeff_stream = self._coeff_stream if isinstance(coeff_stream, CoefficientStream_exact): - initial_values = [func(i) if i else 0 for i in coeff_stream._initial_coefficients] + inital_coefficients = [func(i) if i else 0 for i in coeff_stream._initial_coefficients] c = func(coeff_stream._constant) if coeff_stream._constant else 0 - if not any(initial_values) and not c: + if not any(inital_coefficients) and not c: return P.zero() - coeff_stream = CoefficientStream_exact(initial_values, self._coeff_stream._is_sparse, + coeff_stream = CoefficientStream_exact(inital_coefficients, self._coeff_stream._is_sparse, valuation=coeff_stream._approximate_valuation, degree=coeff_stream._degree, constant=c) @@ -211,8 +211,8 @@ def truncate(self, d): """ P = self.parent() coeff_stream = self._coeff_stream - initial_values = [coeff_stream[i] for i in range(coeff_stream._approximate_valuation, d)] - return P.element_class(P, CoefficientStream_exact(initial_values, P._sparse, + inital_coefficients = [coeff_stream[i] for i in range(coeff_stream._approximate_valuation, d)] + return P.element_class(P, CoefficientStream_exact(inital_coefficients, P._sparse, valuation=coeff_stream._approximate_valuation)) def prec(self): From 3594c698f0b9b72c3fb359d618e416156a4e15fa Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 4 Aug 2021 00:10:37 +0530 Subject: [PATCH 95/98] Documentation done for lazy_laurent_series_ring.py --- src/sage/rings/lazy_laurent_series.py | 46 ++++++++++++++-------- src/sage/rings/lazy_laurent_series_ring.py | 30 ++++++++++---- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 9a5791467bd..67887a59c02 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -24,14 +24,28 @@ sage: f.coefficient(100) 573147844013817084101 -Coefficients are computed and cached only when necessary:: - - sage: f._coeff_stream._cache[100] - 573147844013817084101 - sage: f._coeff_stream._cache[101] - Traceback (most recent call last): - ... - IndexError: list index out of range +Coefficients are computed depending on the type of implementation. +For example, for a dense implementation, all the coefficients upto +the required coefficient are calculated.:: + + sage: s = L(lambda n: n); s + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... + sage: s.coefficient(10) + 10 + sage: s._coeff_stream._cache + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +For a sparse implementation, only the coefficients that are needed are +calculated:: + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: s = L(lambda n: n); s + x + 2*x^2 + 3*x^3 + 4*x^4 + 5*x^5 + 6*x^6 + ... + sage: s.coefficient(10) + 10 + sage: s._coeff_stream._cache + {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 10: 10} + You can do arithmetic with lazy power series:: @@ -966,8 +980,8 @@ def __call__(self, g): g_poly = R(sum([g._coeff_stream[i] * z**i for i in range(g._coeff_stream._approximate_valuation, g._coeff_stream._degree)])) ret = poly(g_poly) if ret.parent() is R: - p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, self._coeff_stream._is_sparse, valuation=ret.valuation())) + inital_coefficients = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(inital_coefficients, self._coeff_stream._is_sparse, valuation=ret.valuation())) except TypeError: # the result is not a Laurent polynomial pass @@ -1056,8 +1070,8 @@ def _mul_(self, other): pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) p = pl * pr c = left._constant - p_list = [p[i] for i in range(p.valuation(), p.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=p.valuation(), constant=c)) + inital_coefficients = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(inital_coefficients, P._sparse, valuation=p.valuation(), constant=c)) elif isinstance(right, CoefficientStream_exact): if not right._constant: pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) @@ -1126,8 +1140,8 @@ def _div_(self, other): ret = pl / pr try: ret = P._laurent_poly_ring(ret) - p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=ret.valuation(), constant=left._constant)) + inital_coefficients = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(inital_coefficients, P._sparse, valuation=ret.valuation(), constant=left._constant)) except (TypeError, ValueError): # We cannot divide the polynomials, so the result must be a series pass @@ -1163,8 +1177,8 @@ def __invert__(self): poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) if poly == R.gen(): ret = 1 / poly - p_list = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(p_list, P._sparse, valuation=ret.valuation(), constant=self._coeff_stream._constant)) + inital_coefficients = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(inital_coefficients, P._sparse, valuation=ret.valuation(), constant=self._coeff_stream._constant)) # (f^-1)^-1 = f if isinstance(self._coeff_stream, CoefficientStream_cauchy_inverse): return P.element_class(P, self._coeff_stream._series) diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 22547ffa147..36b55573aa9 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -50,6 +50,16 @@ sage: s 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + ... +The implementation of the Ring can be either be a sparse or a dense one. +The default is a dense implementation.:: + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: L._sparse + True + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: L._sparse + False + AUTHORS: - Kwankyu Lee (2019-02-24): initial version @@ -101,7 +111,7 @@ class LazyLaurentSeriesRing(UniqueRepresentation, Parent): - ``base_ring`` -- base ring of this Laurent series ring - ``names`` -- name of the generator of this Laurent series ring - - ``sparse`` -- (default: ``False``) whether this series is sparse or not + - ``sparse`` -- (default: ``False``) whether the implementation of the series is sparse or not EXAMPLES:: @@ -216,8 +226,9 @@ def _coerce_map_from_(self, S): R = self._laurent_poly_ring if R.has_coerce_map_from(S): def make_series_from(poly): - p_list = [poly[i] for i in range(poly.valuation(), poly.degree() + 1)] - return self.element_class(self, CoefficientStream_exact(p_list, self._sparse, valuation=poly.valuation())) + inital_coefficients = [poly[i] for i in range(poly.valuation(), poly.degree() + 1)] + coeff_stream = CoefficientStream_exact(inital_coefficients, self._sparse, valuation=poly.valuation()) + return self.element_class(self, coeff_stream) return SetMorphism(Hom(S, self, Sets()), make_series_from) return False @@ -341,8 +352,8 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if x == R.zero(): coeff_stream = CoefficientStream_exact([x], self._sparse, valuation=degree-1, constant=constant) return self.element_class(self, coeff_stream) - p_list = [x[i] for i in range(x.valuation(), x.degree() + 1)] - coeff_stream = CoefficientStream_exact(p_list, self._sparse, valuation=x.valuation(), constant=constant, degree=degree) + inital_coefficients = [x[i] for i in range(x.valuation(), x.degree() + 1)] + coeff_stream = CoefficientStream_exact(inital_coefficients, self._sparse, valuation=x.valuation(), constant=constant, degree=degree) return self.element_class(self, coeff_stream) if isinstance(x, LazyLaurentSeries): if x._coeff_stream._is_sparse is self._sparse: @@ -357,7 +368,8 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No constant = ZZ.zero() z = R.gen() p = [x(i) for i in range(valuation, degree)] - return self.element_class(self, CoefficientStream_exact(p, self._sparse, valuation=valuation, constant=constant, degree=degree)) + coeff_stream = CoefficientStream_exact(p, self._sparse, valuation=valuation, constant=constant, degree=degree) + return self.element_class(self, coeff_stream) if len(signature(x).parameters) > 1: return self.element_class(self, CoefficientStream_recursive(x, self.base_ring(), self._sparse, valuation)) return self.element_class(self, CoefficientStream_coefficient_function(x, self.base_ring(), self._sparse, valuation)) @@ -375,7 +387,8 @@ def _an_element_(self): """ c = self.base_ring()(1) R = self._laurent_poly_ring - return self.element_class(self, CoefficientStream_exact([R.zero()], self._sparse, valuation=-11, constant=c)) + coeff_stream = CoefficientStream_exact([R.zero()], self._sparse, valuation=-11, constant=c) + return self.element_class(self,coeff_stream) @cached_method def one(self): @@ -389,7 +402,8 @@ def one(self): 1 """ R = self._laurent_poly_ring - return self.element_class(self, CoefficientStream_exact([R.one()], self._sparse, constant=ZZ.zero(), degree=1)) + coeff_stream = CoefficientStream_exact([R.one()], self._sparse, constant=ZZ.zero(), degree=1) + return self.element_class(self, coeff_stream) @cached_method def zero(self): From 3b3e0cdd5e9c2e2c1072de30000aca949f1a0faa Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 4 Aug 2021 02:20:42 +0530 Subject: [PATCH 96/98] Completed the second iteration of the documentation. --- .../data_structures/coefficient_stream.py | 47 +- src/sage/rings/lazy_laurent_series.py | 437 +++++++++++++----- src/sage/rings/lazy_laurent_series_ring.py | 1 - 3 files changed, 352 insertions(+), 133 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index 09aab142323..a6de8618d11 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -435,10 +435,10 @@ def __hash__(self): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(QQ) - sage: f = 1 + z + z^2 + z^3 - sage: {f: 1} - {1 + z + z^2 + z^3: 1} + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: s = CoefficientStream_exact([1], False) + sage: hash(s) == hash(s) + True """ return hash((self._initial_coefficients, self._degree, self._constant)) @@ -754,10 +754,11 @@ def __hash__(self): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = ~(1 - z) - sage: {f: 1} - {1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ...: 1} + sage: from sage.data_structures.coefficient_stream import CoefficientStream_unary + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: M = CoefficientStream_unary(CoefficientStream_coefficient_function(lambda n: 1, ZZ, False, 1), True, 0) + sage: hash(M) == hash(M) + True """ return hash((type(self), self._series)) @@ -834,10 +835,13 @@ def __hash__(self): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = 1/(1 - z) + 1/(1 + z) - sage: {f: 1} - {2 + 2*z^2 + 2*z^4 + 2*z^6 + ...: 1} + sage: from sage.data_structures.coefficient_stream import CoefficientStream_binary + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: M = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0) + sage: N = CoefficientStream_coefficient_function(lambda n: -2*n, ZZ, True, 0) + sage: O = CoefficientStream_binary(M, N, True, 0) + sage: hash(O) == hash(O) + True """ return hash((type(self), self._left, self._right)) @@ -875,10 +879,6 @@ class CoefficientStream_binary_commutative(CoefficientStream_binary): Abstract base class for commutative binary operators for the coefficient stream. - INPUT: - - - ``other`` -- a :class:`CoefficientStream` - EXAMPLES:: sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_add) @@ -899,10 +899,13 @@ def __hash__(self): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(ZZ) - sage: f = z^2 + z - sage: {f: 1} - {z + z^2: 1} + sage: from sage.data_structures.coefficient_stream import (CoefficientStream_coefficient_function, CoefficientStream_add) + sage: f = CoefficientStream_coefficient_function(lambda n: 2*n, ZZ, True, 0) + sage: g = CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 1) + sage: h = CoefficientStream_add(f, g) + sage: u = CoefficientStream_add(g, f) + sage: hash(h) == hash(u) + True """ return hash((type(self), frozenset([self._left, self._right]))) @@ -1007,10 +1010,10 @@ def __hash__(self): sage: from sage.data_structures.coefficient_stream import CoefficientStream_zero sage: s = CoefficientStream_zero(False) - sage: a = s.__hash__(); a + sage: a = hash(s); a 0 sage: t = CoefficientStream_zero(False) - sage: b = t.__hash__(); b + sage: b = hash(t); b 0 sage: b == a True diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 67887a59c02..a6f7514cec8 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -112,13 +112,33 @@ ) class LazySequenceElement(ModuleElement): + r""" + An Abstract Base class for the element of a lazy series. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: f = 1/(1 - z) + sage: f.parent() + Lazy Laurent Series Ring in z over Integer Ring + sage: f + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: f = 1/(1 - z) + sage: f.parent() + Lazy Laurent Series Ring in z over Integer Ring + sage: f + 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + """ + def __getitem__(self, n): """ Return the coefficient of the term with exponent ``n`` of the series. INPUT: - - ``n`` -- integer + - ``n`` -- integer; the exponent of the term whose coefficient is desired EXAMPLES:: @@ -136,7 +156,6 @@ def __getitem__(self, n): sage: M = L(lambda n: n) sage: [M[n] for n in range(20)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] - """ R = self.base_ring() if isinstance(n, slice): @@ -160,6 +179,8 @@ def map_coefficients(self, func, ring=None): EXAMPLES:: + Dense Implementation:: + sage: L. = LazyLaurentSeriesRing(ZZ) sage: s = z/(1 - 2*z^2) sage: t = s.map_coefficients(lambda c: c + 1) @@ -172,12 +193,20 @@ def map_coefficients(self, func, ring=None): sage: N = M.map_coefficients(lambda c: c + 1); N 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + Sparse Implementation:: + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = M.map_coefficients(lambda c: c + 1); N 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + An example where the series is known to be exact:: + + sage: f = z + z^2 + z^3 + sage: m = f.map_coefficients(lambda c: c + 1) + sage: m + 2*z + 2*z^2 + 2*z^3 """ P = self.parent() coeff_stream = self._coeff_stream @@ -199,10 +228,12 @@ def truncate(self, d): INPUT: - - ``d`` -- integer + - ``d`` -- integer; the degree from which the series is truncated EXAMPLES:: + Dense Implementation:: + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) sage: alpha = 1/(1-z) sage: alpha @@ -217,11 +248,20 @@ def truncate(self, d): sage: M.truncate(4) z + 2*z^2 + 3*z^3 + Sparse Implementation:: + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M.truncate(4) z + 2*z^2 + 3*z^3 + + Series which are known to be exact can also be truncated:: + + sage: M = z + z^2 + z^3 + z^4 + sage: M.truncate(4) + z + z^2 + z^3 + """ P = self.parent() coeff_stream = self._coeff_stream @@ -264,7 +304,6 @@ def valuation(self): 1 sage: (M - M).valuation() +Infinity - """ return self._coeff_stream.valuation() @@ -283,7 +322,6 @@ def _richcmp_(self, other, op): INPUT: - ``other`` -- another Laurent series - - ``op`` -- comparison operator EXAMPLES:: @@ -403,7 +441,7 @@ def define(self, s): sage: t[:9] [1, 1, 2, 7, 24, 95, 386, 1641, 7150] - An bigger example:: + A bigger example:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: A = L(None, 5) @@ -471,7 +509,6 @@ def define(self, s): Traceback (most recent call last): ... ValueError: series already defined - """ if not isinstance(self._coeff_stream, CoefficientStream_uninitialized) or self._coeff_stream._target is not None: raise ValueError("series already defined") @@ -479,15 +516,61 @@ def define(self, s): class LazySequencesModuleElement(LazySequenceElement): + r""" + An abstract base class for a lazy series. + + INPUT: + + - ``parent`` -- the parent ring of the series + - ``coeff_stream`` -- the stream of coefficients of the series + + EXAMPLES:: + + sage: from sage.rings.lazy_laurent_series import LazySequencesModuleElement + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: M = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: 1, ZZ, True, 0)) + sage: [M._coeff_stream[i] for i in range(10)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + sage: [N._coeff_stream[i] for i in range(10)] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + + Two sequences can be added:: + + sage: O = M + N + sage: [O._coeff_stream[i] for i in range(10)] + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + + Two sequences can be subracted:: + + sage: P = M - N + sage: [P._coeff_stream[i] for i in range(10)] + [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8] + + A sequence can be multiplied by a scalar:: + + sage: Q = 2 * M + sage: [Q._coeff_stream[i] for i in range(10)] + [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] + + The negation of a sequence can also be found:: + + sage: R = -M + sage: [R._coeff_stream[i] for i in range(10)] + [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + + """ def __init__(self, parent, coeff_stream): """ Initialize the series. TESTS:: - sage: L = LazyLaurentSeriesRing(GF(2), 'z') - sage: z = L.gen() - sage: TestSuite(z).run() + sage: from sage.rings.lazy_laurent_series import LazySequencesModuleElement + sage: from sage.data_structures.coefficient_stream import CoefficientStream_exact + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: M = LazySequencesModuleElement(L, CoefficientStream_exact([1], True)) """ ModuleElement.__init__(self, parent) @@ -501,36 +584,35 @@ def _add_(self, other): - ``other`` -- other series - TESTS:: + EXAMPLES:: + + Dense series can be added:: + sage: from sage.rings.lazy_laurent_series import LazySequencesModuleElement + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function, CoefficientStream_exact sage: L. = LazyLaurentSeriesRing(ZZ) - sage: (1 - z)*(1 - z) - 1 - 2*z + z^2 - sage: (1 - z)*(1 - z)*(1 - z) - 1 - 3*z + 3*z^2 - z^3 - sage: z + z - 2*z - sage: z^2 + 3*z^2 - 4*z^2 - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M + N; P - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + sage: M = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: 1 + n, ZZ, False, 0)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: -n, ZZ, False, 0)) + sage: O = M + N + sage: [O._coeff_stream[i] for i in range(10)] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - sage: A = L(1, constant=2, degree=3) - sage: B = L(2, constant=-2, degree=5) - sage: A + B - 3 + 2*z^3 + 2*z^4 + Sparse series can be added:: sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M + N; P - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + sage: M = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: 1 + n, ZZ, True, 0)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: -n, ZZ, True, 0)) + sage: O = M + N + sage: [O._coeff_stream[i] for i in range(10)] + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + + Series which are known to be exact can be added:: + + sage: M = LazySequencesModuleElement(L, CoefficientStream_exact([1], True)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_exact([0, 1], True)) + sage: O = M + N + sage: [O._coeff_stream[i] for i in range(10)] + [1, 1, 0, 0, 0, 0, 0, 0, 0, 0] """ P = self.parent() @@ -559,39 +641,42 @@ def _sub_(self, other): - ``other`` -- other series - TESTS:: + EXAMPLES:: + Dense series can be subtracted:: + + sage: from sage.rings.lazy_laurent_series import LazySequencesModuleElement + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function, CoefficientStream_exact sage: L. = LazyLaurentSeriesRing(ZZ) - sage: z - z - 0 - sage: 3*z - 2*z - z - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M - N; P - -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... + sage: M = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: 1 + n, ZZ, False, 0)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: -n, ZZ, False, 0)) + sage: O = M - N + sage: [O._coeff_stream[i] for i in range(10)] + [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] - sage: A = L(1, constant=2, degree=3) - sage: B = L(2, constant=3, degree=5) - sage: A - B - -1 + 2*z^3 + 2*z^4 - z^5 - z^6 - z^7 + ... + Sparse series can be subtracted:: - sage: A = L(1, constant=2, degree=3) - sage: B = L([1,0,0,2,2], constant=2) - sage: X = A - B; X + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: 1 + n, ZZ, True, 0)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: -n, ZZ, True, 0)) + sage: O = M - N + sage: [O._coeff_stream[i] for i in range(10)] + [1, 3, 5, 7, 9, 11, 13, 15, 17, 19] + + Series which are known to be exact can be subtracted:: + + sage: M = LazySequencesModuleElement(L, CoefficientStream_exact([1], True)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_exact([0, 1], True)) + sage: O = M - N + sage: [O._coeff_stream[i] for i in range(10)] + [1, -1, 0, 0, 0, 0, 0, 0, 0, 0] + + sage: M = LazySequencesModuleElement(L, CoefficientStream_exact([1], True)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_exact([1], True)) + sage: O = M - N + sage: O 0 - sage: type(X._coeff_stream) - - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: P = M - N; P - -1 + z^2 + 2*z^3 + 3*z^4 + 4*z^5 + 5*z^6 + ... """ P = self.parent() left = self._coeff_stream @@ -621,40 +706,65 @@ def _lmul_(self, scalar): - ``scalar`` -- an element of the base ring - TESTS:: + EXAMPLES:: + Dense series can be multiplied with a scalar:: + + sage: from sage.rings.lazy_laurent_series import LazySequencesModuleElement + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function, CoefficientStream_exact sage: L. = LazyLaurentSeriesRing(ZZ) - sage: 2*z - 2*z - sage: -1*z - -z - sage: 0*z + sage: M = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: 1 + n, ZZ, False, 0)) + sage: O = 2 * M + sage: [O._coeff_stream[i] for i in range(10)] + [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] + sage: O = 2 * 3 * M + sage: [O._coeff_stream[i] for i in range(10)] + [6, 12, 18, 24, 30, 36, 42, 48, 54, 60] + sage: O = 1 * M + sage: [O._coeff_stream[i] for i in range(10)] + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + sage: O == M + True + sage: O = 0 * M + sage: O 0 - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M * 3 - 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... - sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: M * 3 - 3*z + 6*z^2 + 9*z^3 + 12*z^4 + 15*z^5 + 18*z^6 + ... - sage: N = L(lambda n: 1); N - 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... - sage: N * 4 - 4 + 4*z + 4*z^2 + 4*z^3 + 4*z^4 + 4*z^5 + 4*z^6 + ... + Sparse series can be multiplied with a scalar:: - sage: 1 * M is M - True - sage: M * 1 is M + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) + sage: M = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: 1 + n, ZZ, True, 0)) + sage: O = 2 * M + sage: [O._coeff_stream[i] for i in range(10)] + [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] + sage: O = 2 * 3 * M + sage: [O._coeff_stream[i] for i in range(10)] + [6, 12, 18, 24, 30, 36, 42, 48, 54, 60] + sage: O = 1 * M + sage: [O._coeff_stream[i] for i in range(10)] + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + sage: O == M True + sage: O = 0 * M + sage: O + 0 - sage: from sage.data_structures.coefficient_stream import CoefficientStream_scalar - sage: isinstance((M*3)._coeff_stream, CoefficientStream_scalar) - True - sage: isinstance((3*M)._coeff_stream, CoefficientStream_scalar) + Series which are known to be exact can be multiplied with a scalar:: + + sage: N = LazySequencesModuleElement(L, CoefficientStream_exact([0, 1], True)) + sage: O = -1 * N + sage: [O._coeff_stream[i] for i in range(10)] + [0, -1, 0, 0, 0, 0, 0, 0, 0, 0] + sage: O = 2 * 4 * N + sage: [O._coeff_stream[i] for i in range(10)] + [0, 8, 0, 0, 0, 0, 0, 0, 0, 0] + sage: O = 1 * N + sage: [O._coeff_stream[i] for i in range(10)] + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0] + sage: O == N True + sage: O = 0 * N + sage: O + 0 """ P = self.parent() @@ -677,24 +787,60 @@ def _neg_(self): """ Return the negative of this series. - TESTS:: + EXAMPLES:: - sage: L = LazyLaurentSeriesRing(ZZ, 'z') - sage: z = L.gen() - sage: -(1 - z) - -1 + z - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = -M; P - -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... + Dense series can be negated:: + + sage: from sage.rings.lazy_laurent_series import LazySequencesModuleElement + sage: from sage.data_structures.coefficient_stream import CoefficientStream_coefficient_function, CoefficientStream_exact + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: M = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: n, ZZ, False, 0)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: -n, ZZ, False, 0)) + sage: O = -N + sage: [O._coeff_stream[i] for i in range(10)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + sage: O = -M + sage: [O._coeff_stream[i] for i in range(10)] + [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + sage: O = -(-M) + sage: [O._coeff_stream[i] for i in range(10)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + sage: O == M + True + + Sparse series can be negated:: sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) - sage: M = L(lambda n: n); M - z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... - sage: P = -M; P - -z - 2*z^2 - 3*z^3 - 4*z^4 - 5*z^5 - 6*z^6 + ... - sage: -(z^2 + 3*z - 4*z^3) - -3*z - z^2 + 4*z^3 + sage: M = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: n, ZZ, True, 0)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_coefficient_function(lambda n: -n, ZZ, True, 0)) + sage: O = -N + sage: [O._coeff_stream[i] for i in range(10)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + sage: O = -M + sage: [O._coeff_stream[i] for i in range(10)] + [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + sage: O = -(-M) + sage: [O._coeff_stream[i] for i in range(10)] + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + sage: O == M + True + + Series which are known to be exact can be negated:: + + sage: M = LazySequencesModuleElement(L, CoefficientStream_exact([1], True)) + sage: N = LazySequencesModuleElement(L, CoefficientStream_exact([0, 1], True)) + sage: O = -N + sage: [O._coeff_stream[i] for i in range(10)] + [0, -1, 0, 0, 0, 0, 0, 0, 0, 0] + sage: O = -M + sage: [O._coeff_stream[i] for i in range(10)] + [-1, 0, 0, 0, 0, 0, 0, 0, 0, 0] + sage: O = -(-M) + sage: [O._coeff_stream[i] for i in range(10)] + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] + sage: O == M + True + """ P = self.parent() coeff_stream = self._coeff_stream @@ -738,6 +884,7 @@ class LazyLaurentSeries(LazySequencesModuleElement): sage: g == f True """ + def change_ring(self, ring): """ Return this series with coefficients converted to elements of ``ring``. @@ -748,6 +895,8 @@ def change_ring(self, ring): EXAMPLES:: + Dense Implementation:: + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) sage: s = 2 + z sage: t = s.change_ring(QQ) @@ -761,6 +910,8 @@ def change_ring(self, ring): sage: M.parent() Lazy Laurent Series Ring in z over Integer Ring + Sparse Implementation:: + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... @@ -945,6 +1096,28 @@ def __call__(self, g): 1 + y^4 - 2*y^5 + 2*y^6 + ... sage: z(y) y + + We look at cases where the composition does not exist. + `g = 0` and `val(f) < 0`:: + + sage: g = L(0) + sage: f = z^-1 + z^-2 + sage: f.valuation() < 0 + True + sage: f(g) + Traceback (most recent call last): + ... + ZeroDivisionError: the valuation of the series must be nonnegative + + `g` is non-zero and `val(g) <= 0 and f has infinitely many non-zero coefficients`:: + + sage: g = z^-1 + z^-2 + sage: g.valuation() <= 0 + True + sage: f = L(lambda n: n, 0) + sage: f(g) + 0 + ... + """ # f = self and compute f(g) P = g.parent() @@ -1012,7 +1185,7 @@ def __call__(self, g): raise NotImplementedError("can only compose with a lazy Laurent series") # Perhaps we just don't yet know if the valuation is positive if g._coeff_stream._approximate_valuation <= 0: - if any(g._coeff_stream[i] for i in range(self._coeff_stream._approximate_valuation)): + if any(g._coeff_stream[i] for i in range(self._coeff_stream._approximate_valuation, 1)): raise ValueError("can only compose with a positive valuation series") g._coeff_stream._approximate_valuation = 1 @@ -1026,19 +1199,26 @@ def _mul_(self, other): - ``other`` -- other series - TESTS:: + EXAMPLES:: + + Lazy Laurent series that are known to be exact can be multiplied:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: (1 - z)*(1 - z) 1 - 2*z + z^2 sage: (1 - z)*(1 - z)*(1 - z) 1 - 3*z + 3*z^2 - z^3 + + Lazy Laurent series that have a dense implementation can be multiplied:: + sage: M = L(lambda n: n) sage: M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: N = M * (1 - M) sage: N z + z^2 - z^3 - 6*z^4 - 15*z^5 - 29*z^6 + ... + + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... @@ -1102,7 +1282,9 @@ def _div_(self, other): - ``other`` -- nonzero series - TESTS:: + EXAMPLES:: + + Lazy Laurent series that have a dense implementation can be divided:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: z/(1 - z) @@ -1114,6 +1296,8 @@ def _div_(self, other): sage: P = M / N; P z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + Lazy Laurent series that have a sparse implementation can be divided:: + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... @@ -1121,6 +1305,13 @@ def _div_(self, other): 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... sage: P = M / N; P z + z^2 + z^3 + z^4 + z^5 + z^6 + ... + + Lazy Laurent series that are known to be exact can be divided:: + + M = z^2 + 2*z + 1 + N = z + 1 + O = M / N; O + z + 1 """ if isinstance(other._coeff_stream, CoefficientStream_zero): raise ZeroDivisionError("cannot divide by 0") @@ -1152,7 +1343,9 @@ def __invert__(self): """ Return the multiplicative inverse of the element. - TESTS:: + EXAMPLES:: + + Lazy Laurent series that have a dense implementation can be inverted:: sage: L. = LazyLaurentSeriesRing(ZZ, sparse=False) sage: ~(1 - z) @@ -1161,6 +1354,9 @@ def __invert__(self): z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: P = ~M; P z^-1 - 2 + z + ... + + Lazy Laurent series that have a sparse implementation can be inverted:: + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... @@ -1169,6 +1365,12 @@ def __invert__(self): sage: ~(~(1 - z)) 1 - z + + Lazy Laurent series that are known to be exact can be inverted:: + + sage: ~z + z^-1 + """ P = self.parent() R = P._laurent_poly_ring @@ -1194,6 +1396,8 @@ def __pow__(self, n): EXAMPLES:: + Lazy Laurent series that have a dense implementation can be raised to the power ``n``:: + sage: L. = LazyLaurentSeriesRing(ZZ) sage: (1 - z)^-1 1 + z + z^2 + z^3 + z^4 + z^5 + z^6 + ... @@ -1208,11 +1412,22 @@ def __pow__(self, n): sage: M ^ 2 z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... + Lazy Laurent series that have a sparse implementation can be raised to the power ``n``:: + sage: L. = LazyLaurentSeriesRing(ZZ, sparse=True) sage: M = L(lambda n: n); M z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + ... sage: M ^ 2 z^2 + 4*z^3 + 10*z^4 + 20*z^5 + 35*z^6 + ... + + Lazy Laurent series that are known to be exact can be raised to the power ``n``:: + + sage: z^2 + z^2 + sage: (1 - z)^2 + 1 - 2*z + z^2 + sage: (1 + z)^2 + 1 + 2*z + z^2 """ if n == 0: return self.parent().one() @@ -1227,7 +1442,6 @@ def approximate_series(self, prec, name=None): INPUT: - ``prec`` -- an integer - - ``name`` -- name of the variable; if it is ``None``, the name of the variable of the series is used @@ -1273,7 +1487,6 @@ def polynomial(self, degree=None, name=None): INPUT: - ``degree`` -- ``None`` or an integer - - ``name`` -- name of the variable; if it is ``None``, the name of the variable of the series is used @@ -1354,11 +1567,15 @@ def _repr_(self): """ Return the string representation of this Laurent series. - TESTS:: + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) sage: -1/(1 + 2*z) -1 + 2*z - 4*z^2 + 8*z^3 - 16*z^4 + 32*z^5 - 64*z^6 + ... + sage: L(None) + Uninitialized Lazy Laurent Series + sage: L(0) + 0 """ if isinstance(self._coeff_stream, CoefficientStream_zero): return '0' diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 36b55573aa9..03c70343f84 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -417,4 +417,3 @@ def zero(self): 0 """ return self.element_class(self, CoefficientStream_zero(self._sparse)) - From ee9d593d8fa46527e1301a82d7da7e488913f614 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 4 Aug 2021 02:30:55 +0530 Subject: [PATCH 97/98] Fixed tiny bug in composition code --- src/sage/rings/lazy_laurent_series.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index a6f7514cec8..f171086d1e9 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -1117,6 +1117,15 @@ def __call__(self, g): sage: f = L(lambda n: n, 0) sage: f(g) 0 + ... + + We cannot compose if `g` has a negative valuation:: + + sage: f = L(lambda n: n, 1) + sage: g = 1 + z + sage: f(g) + Traceback (most recent call last): + ... + ValueError: can only compose with a positive valuation series """ # f = self and compute f(g) @@ -1185,7 +1194,7 @@ def __call__(self, g): raise NotImplementedError("can only compose with a lazy Laurent series") # Perhaps we just don't yet know if the valuation is positive if g._coeff_stream._approximate_valuation <= 0: - if any(g._coeff_stream[i] for i in range(self._coeff_stream._approximate_valuation, 1)): + if any(g._coeff_stream[i] for i in range(min(self._coeff_stream._approximate_valuation, 0), 1)): raise ValueError("can only compose with a positive valuation series") g._coeff_stream._approximate_valuation = 1 From 2be305d38f40742d93f4edb96d267c229a56f929 Mon Sep 17 00:00:00 2001 From: tejasvicsr1 Date: Wed, 4 Aug 2021 02:45:33 +0530 Subject: [PATCH 98/98] Small error fixed, typos fixed. --- .../data_structures/coefficient_stream.py | 10 +++--- src/sage/rings/lazy_laurent_series.py | 32 ++++++++++--------- src/sage/rings/lazy_laurent_series_ring.py | 8 ++--- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/sage/data_structures/coefficient_stream.py b/src/sage/data_structures/coefficient_stream.py index a6de8618d11..1351ad3c814 100644 --- a/src/sage/data_structures/coefficient_stream.py +++ b/src/sage/data_structures/coefficient_stream.py @@ -1047,7 +1047,7 @@ class CoefficientStream_add(CoefficientStream_binary_commutative): """ def __init__(self, left, right): """ - Initalize. + Initialize. TESTS:: @@ -1127,7 +1127,7 @@ class CoefficientStream_sub(CoefficientStream_binary): def __init__(self, left, right): """ - Initalize ``self``. + initialize ``self``. TESTS:: @@ -1208,7 +1208,7 @@ class CoefficientStream_cauchy_product(CoefficientStream_binary_commutative): """ def __init__(self, left, right): """ - Initalize ``self``. + initialize ``self``. TESTS:: @@ -1300,7 +1300,7 @@ class CoefficientStream_div(CoefficientStream_binary): def __init__(self, left, right): """ - Initalize ``self``. + initialize ``self``. TESTS:: @@ -1398,7 +1398,7 @@ class CoefficientStream_composition(CoefficientStream_binary): """ def __init__(self, f, g): """ - Initalize ``self``. + initialize ``self``. TESTS:: diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index f171086d1e9..eb94809e2af 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -211,11 +211,11 @@ def map_coefficients(self, func, ring=None): P = self.parent() coeff_stream = self._coeff_stream if isinstance(coeff_stream, CoefficientStream_exact): - inital_coefficients = [func(i) if i else 0 for i in coeff_stream._initial_coefficients] + initial_coefficients = [func(i) if i else 0 for i in coeff_stream._initial_coefficients] c = func(coeff_stream._constant) if coeff_stream._constant else 0 - if not any(inital_coefficients) and not c: + if not any(initial_coefficients) and not c: return P.zero() - coeff_stream = CoefficientStream_exact(inital_coefficients, self._coeff_stream._is_sparse, + coeff_stream = CoefficientStream_exact(initial_coefficients, self._coeff_stream._is_sparse, valuation=coeff_stream._approximate_valuation, degree=coeff_stream._degree, constant=c) @@ -265,8 +265,8 @@ def truncate(self, d): """ P = self.parent() coeff_stream = self._coeff_stream - inital_coefficients = [coeff_stream[i] for i in range(coeff_stream._approximate_valuation, d)] - return P.element_class(P, CoefficientStream_exact(inital_coefficients, P._sparse, + initial_coefficients = [coeff_stream[i] for i in range(coeff_stream._approximate_valuation, d)] + return P.element_class(P, CoefficientStream_exact(initial_coefficients, P._sparse, valuation=coeff_stream._approximate_valuation)) def prec(self): @@ -1116,7 +1116,9 @@ def __call__(self, g): True sage: f = L(lambda n: n, 0) sage: f(g) - 0 + ... + Traceback (most recent call last): + ... + ValueError: can only compose with a positive valuation series We cannot compose if `g` has a negative valuation:: @@ -1162,8 +1164,8 @@ def __call__(self, g): g_poly = R(sum([g._coeff_stream[i] * z**i for i in range(g._coeff_stream._approximate_valuation, g._coeff_stream._degree)])) ret = poly(g_poly) if ret.parent() is R: - inital_coefficients = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(inital_coefficients, self._coeff_stream._is_sparse, valuation=ret.valuation())) + initial_coefficients = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(initial_coefficients, self._coeff_stream._is_sparse, valuation=ret.valuation())) except TypeError: # the result is not a Laurent polynomial pass @@ -1194,7 +1196,7 @@ def __call__(self, g): raise NotImplementedError("can only compose with a lazy Laurent series") # Perhaps we just don't yet know if the valuation is positive if g._coeff_stream._approximate_valuation <= 0: - if any(g._coeff_stream[i] for i in range(min(self._coeff_stream._approximate_valuation, 0), 1)): + if any(g._coeff_stream[i] for i in range(g._coeff_stream._approximate_valuation, 1)): raise ValueError("can only compose with a positive valuation series") g._coeff_stream._approximate_valuation = 1 @@ -1259,8 +1261,8 @@ def _mul_(self, other): pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) p = pl * pr c = left._constant - inital_coefficients = [p[i] for i in range(p.valuation(), p.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(inital_coefficients, P._sparse, valuation=p.valuation(), constant=c)) + initial_coefficients = [p[i] for i in range(p.valuation(), p.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(initial_coefficients, P._sparse, valuation=p.valuation(), constant=c)) elif isinstance(right, CoefficientStream_exact): if not right._constant: pr = R(sum([right[i] * z**i for i in range(right._approximate_valuation, right._degree)])) @@ -1340,8 +1342,8 @@ def _div_(self, other): ret = pl / pr try: ret = P._laurent_poly_ring(ret) - inital_coefficients = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(inital_coefficients, P._sparse, valuation=ret.valuation(), constant=left._constant)) + initial_coefficients = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(initial_coefficients, P._sparse, valuation=ret.valuation(), constant=left._constant)) except (TypeError, ValueError): # We cannot divide the polynomials, so the result must be a series pass @@ -1388,8 +1390,8 @@ def __invert__(self): poly = R(sum([self._coeff_stream[i] * z**i for i in range(self._coeff_stream._approximate_valuation, self._coeff_stream._degree)])) if poly == R.gen(): ret = 1 / poly - inital_coefficients = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] - return P.element_class(P, CoefficientStream_exact(inital_coefficients, P._sparse, valuation=ret.valuation(), constant=self._coeff_stream._constant)) + initial_coefficients = [ret[i] for i in range(ret.valuation(), ret.degree() + 1)] + return P.element_class(P, CoefficientStream_exact(initial_coefficients, P._sparse, valuation=ret.valuation(), constant=self._coeff_stream._constant)) # (f^-1)^-1 = f if isinstance(self._coeff_stream, CoefficientStream_cauchy_inverse): return P.element_class(P, self._coeff_stream._series) diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index 03c70343f84..0d9a0b42fd0 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -226,8 +226,8 @@ def _coerce_map_from_(self, S): R = self._laurent_poly_ring if R.has_coerce_map_from(S): def make_series_from(poly): - inital_coefficients = [poly[i] for i in range(poly.valuation(), poly.degree() + 1)] - coeff_stream = CoefficientStream_exact(inital_coefficients, self._sparse, valuation=poly.valuation()) + initial_coefficients = [poly[i] for i in range(poly.valuation(), poly.degree() + 1)] + coeff_stream = CoefficientStream_exact(initial_coefficients, self._sparse, valuation=poly.valuation()) return self.element_class(self, coeff_stream) return SetMorphism(Hom(S, self, Sets()), make_series_from) @@ -352,8 +352,8 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if x == R.zero(): coeff_stream = CoefficientStream_exact([x], self._sparse, valuation=degree-1, constant=constant) return self.element_class(self, coeff_stream) - inital_coefficients = [x[i] for i in range(x.valuation(), x.degree() + 1)] - coeff_stream = CoefficientStream_exact(inital_coefficients, self._sparse, valuation=x.valuation(), constant=constant, degree=degree) + initial_coefficients = [x[i] for i in range(x.valuation(), x.degree() + 1)] + coeff_stream = CoefficientStream_exact(initial_coefficients, self._sparse, valuation=x.valuation(), constant=constant, degree=degree) return self.element_class(self, coeff_stream) if isinstance(x, LazyLaurentSeries): if x._coeff_stream._is_sparse is self._sparse: