From 2d5285b32491f7b5a6de5a2e154407a1def0e9a6 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 5 Aug 2021 12:20:13 +0200 Subject: [PATCH] improve repr, fix doctests --- src/sage/rings/lazy_laurent_series.py | 218 ++++++++++++++------- src/sage/rings/lazy_laurent_series_ring.py | 28 +-- 2 files changed, 160 insertions(+), 86 deletions(-) diff --git a/src/sage/rings/lazy_laurent_series.py b/src/sage/rings/lazy_laurent_series.py index 1847f924fd8..3f7a603c28a 100644 --- a/src/sage/rings/lazy_laurent_series.py +++ b/src/sage/rings/lazy_laurent_series.py @@ -1080,19 +1080,19 @@ def _lmul_(self, scalar): sage: 2*g 2/2^z sage: -1*g - -1/2^z + -1/(2^z) sage: 0*g 0 sage: M = L(lambda n: n); M - 1 + 2/2^z + 3/3^z + 4/4^z + 5/5^z + 6/6^z + 7/7^z + ... + 1 + 2/2^z + 3/3^z + 4/4^z + 5/5^z + 6/6^z + 7/7^z + O(1/(8^z)) sage: M * 3 - 3 + 6/2^z + 9/3^z + 12/4^z + 15/5^z + 18/6^z + 21/7^z + ... + 3 + 6/2^z + 9/3^z + 12/4^z + 15/5^z + 18/6^z + 21/7^z + O(1/(8^z)) sage: L = LazyDirichletSeriesRing(ZZ, "z", sparse=True) sage: M = L(lambda n: n); M - 1 + 2/2^z + 3/3^z + 4/4^z + 5/5^z + 6/6^z + 7/7^z + ... + 1 + 2/2^z + 3/3^z + 4/4^z + 5/5^z + 6/6^z + 7/7^z + O(1/(8^z)) sage: M * 3 - 3 + 6/2^z + 9/3^z + 12/4^z + 15/5^z + 18/6^z + 21/7^z + ... + 3 + 6/2^z + 9/3^z + 12/4^z + 15/5^z + 18/6^z + 21/7^z + O(1/(8^z)) sage: 1 * M is M True @@ -1233,9 +1233,9 @@ def _mul_(self, other): sage: L. = LazyTaylorSeriesRing(ZZ) sage: (1 - x)*(1 - y) - 1 + -x - y + x*y + 1 + (-x-y) + x*y sage: (1 - x)*(1 - y)*(1 - z) - 1 + -x - y - z + x*y + x*z + y*z + -x*y*z + 1 + (-x-y-z) + (x*y+x*z+y*z) + (-x*y*z) """ P = self.parent() @@ -1243,7 +1243,10 @@ def _mul_(self, other): right = other._coeff_stream if isinstance(left, CoefficientStream_zero) or isinstance(right, CoefficientStream_zero): return P.zero() - + if isinstance(left, CoefficientStream_exact) and left._initial_coefficients == (P._coeff_ring.one(),) and left.valuation() == 0: + return other # self == 1 + if isinstance(right, CoefficientStream_exact) and right._initial_coefficients == (P._coeff_ring.one(),) and right.valuation() == 0: + return self # the product is exact if and only if both of the factors are # exact, and one has eventually 0 coefficients: # (p + a x^d/(1-x))(q + b x^e/(1-x)) @@ -1260,10 +1263,7 @@ def _mul_(self, other): v = left.valuation() + right.valuation() coeff_stream = CoefficientStream_exact(initial_coefficients, P._sparse, valuation=v) return P.element_class(P, coeff_stream) - if isinstance(left, CoefficientStream_exact) and left._initial_coefficients == (P._coeff_ring.one(),) and left.valuation() == 0: - return other # self == 1 - if isinstance(right, CoefficientStream_exact) and right._initial_coefficients == (P._coeff_ring.one(),) and right.valuation() == 0: - return self + return P.element_class(P, CoefficientStream_cauchy_product(left, right)) def __invert__(self): @@ -2031,24 +2031,18 @@ class LazyTaylorSeries(LazySequencesModuleElement, LazyCauchyProductSeries): EXAMPLES:: - sage: L. = LazyTaylorSeriesRing(ZZ) - sage: L(lambda i: i, valuation=3, constant=(-1, 6)) - 3*z^3 + 4*z^4 + 5*z^5 + -z^6 + -z^7 + -z^8 + ... - sage: L(lambda i: i, valuation=3, constant=-1, degree=6) - 3*z^3 + 4*z^4 + 5*z^5 + -z^6 + -z^7 + -z^8 + ... - - :: - - 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 + sage: L. = LazyTaylorSeriesRing(ZZ) + sage: f = 1 / (1 - x^2 + y^3); f + 1 + x^2 + (-y^3) + x^4 + (-2*x^2*y^3) + (x^6+y^6) + O(x,y)^7 + sage: P. = PowerSeriesRing(ZZ, default_prec=101) + sage: g = 1 / (1 - x^2 + y^3); f[100] - g[100] + 0 Lazy Taylor 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 + ... + 1 + x^2 + (-y^3) + x^4 + (-2*x^2*y^3) + (x^6+y^6) + O(x,y)^7 sage: g == f True """ @@ -2066,7 +2060,7 @@ def change_ring(self, ring): 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 + ... + 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 + O(z^7) sage: t.parent() Lazy Taylor Series Ring in z over Rational Field @@ -2082,15 +2076,14 @@ def _format_series(self, formatter, format_strings=False): TESTS:: sage: L. = LazyTaylorSeriesRing(QQ) - sage: f = 1 / (2 - x^2 - y) - sage: f._format_series(ascii_art, True) + sage: f = 1 / (2 - x^2 + y) + sage: f._format_series(repr) + '1/2 + (-1/4*y) + (1/4*x^2+1/8*y^2) + (-1/4*x^2*y-1/16*y^3) + (1/8*x^4+3/16*x^2*y^2+1/32*y^4) + (-3/16*x^4*y-1/8*x^2*y^3-1/64*y^5) + (1/16*x^6+3/16*x^4*y^2+5/64*x^2*y^4+1/128*y^6) + O(x,y)^7' + sage: f = (2 - x^2 + y) + sage: f._format_series(repr) + '2 + y + (-x^2)' """ - if format_strings: - strformat = formatter - else: - strformat = lambda x: x - P = self.parent() cs = self._coeff_stream v = cs._approximate_valuation @@ -2102,13 +2095,48 @@ def _format_series(self, formatter, format_strings=False): else: m = v + P.options.display_length - # atomic_repr = P._coeff_ring._repr_option('element_is_atomic') + atomic_repr = P._coeff_ring._repr_option('element_is_atomic') + mons = [P.monomial(self[i], i) for i in range(v, m) if self[i]] + if not isinstance(cs, CoefficientStream_exact) or cs._constant: + if P._coeff_ring is P.base_ring(): + bigO = ["O(%s)" % P.monomial(1, m)] + else: + bigO = ["O(%s)^%s" % (', '.join(str(g) for g in P._names), m)] + else: + bigO = [] + + from sage.misc.latex import latex + from sage.typeset.unicode_art import unicode_art + from sage.typeset.ascii_art import ascii_art + from sage.misc.repr import repr_lincomb + from sage.typeset.symbols import ascii_left_parenthesis, ascii_right_parenthesis + from sage.typeset.symbols import unicode_left_parenthesis, unicode_right_parenthesis + if formatter == repr: + poly = repr_lincomb([(1, m) for m in mons + bigO], strip_one=True) + elif formatter == latex: + poly = repr_lincomb([(1, m) for m in mons + bigO], is_latex=True, strip_one=True) + elif formatter == ascii_art: + if atomic_repr: + poly = ascii_art(*(mons + bigO), sep = " + ") + else: + def parenthesize(m): + a = ascii_art(m) + h = a.height() + return ascii_art(ascii_left_parenthesis.character_art(h), + a, ascii_right_parenthesis.character_art(h)) + poly = ascii_art(*([parenthesize(m) for m in mons] + bigO), sep = " + ") + elif formatter == unicode_art: + if atomic_repr: + poly = unicode_art(*(mons + bigO), sep = " + ") + else: + def parenthesize(m): + a = unicode_art(m) + h = a.height() + return unicode_art(unicode_left_parenthesis.character_art(h), + a, unicode_right_parenthesis.character_art(h)) + poly = unicode_art(*([parenthesize(m) for m in mons] + bigO), sep = " + ") - poly = [formatter(P.monomial(self[i], i)) for i in range(v, m) if self[i]] - poly = " + ".join(poly) - if isinstance(cs, CoefficientStream_exact) and not cs._constant: - return poly - return poly + strformat(" + O({})".format(formatter(P.monomial(1, m)))) + return poly class LazyDirichletSeries(LazySequencesModuleElement): @@ -2149,16 +2177,16 @@ def _mul_(self, other): sage: L = LazyDirichletSeriesRing(ZZ, "z") sage: g = L(constant=1); g - 1 + 1/(2^z) + 1/(3^z) + ... + 1 + 1/(2^z) + 1/(3^z) + O(1/(4^z)) sage: g*g - 1 + 2/2^z + 2/3^z + 3/4^z + 2/5^z + 4/6^z + 2/7^z + ... + 1 + 2/2^z + 2/3^z + 3/4^z + 2/5^z + 4/6^z + 2/7^z + O(1/(8^z)) sage: [number_of_divisors(n) for n in range(1, 8)] [1, 2, 2, 3, 2, 4, 2] sage: mu = L(moebius); mu - 1 + -1/2^z + -1/3^z + -1/5^z + 1/(6^z) + -1/7^z + ... + 1 - 1/(2^z) - 1/(3^z) - 1/(5^z) + 1/(6^z) - 1/(7^z) + O(1/(8^z)) sage: g*mu - 1 + ... + 1 + O(1/(8^z)) sage: L.one() * mu is mu True sage: mu * L.one() is mu @@ -2167,6 +2195,15 @@ def _mul_(self, other): P = self.parent() left = self._coeff_stream right = other._coeff_stream + if (isinstance(left, CoefficientStream_exact) + and left._initial_coefficients == (P._coeff_ring.one(),) + and left.valuation() == 1): + return other # self == 1 + if (isinstance(right, CoefficientStream_exact) + and right._initial_coefficients == (P._coeff_ring.one(),) + and right.valuation() == 1): + return self + coeff = CoefficientStream_dirichlet_convolution(left, right) return P.element_class(P, coeff) @@ -2178,10 +2215,10 @@ def __invert__(self): sage: L = LazyDirichletSeriesRing(ZZ, "z", sparse=False) sage: ~L(constant=1) - L(moebius) - 0 + O(1/(8^z)) sage: L = LazyDirichletSeriesRing(ZZ, "z", sparse=True) sage: ~L(constant=1) - L(moebius) - 0 + O(1/(8^z)) """ P = self.parent() @@ -2220,38 +2257,75 @@ def __pow__(self, n): return generic_power(self, n) - def _repr_(self): + def _format_series(self, formatter, format_strings=False): """ - Return the string representation of this Dirichlet series. + Return nonzero ``self`` formatted by ``formatter``. TESTS:: - sage: L = LazyDirichletSeriesRing(ZZ, "z") - """ - if isinstance(self._coeff_stream, CoefficientStream_zero): - return '0' - if isinstance(self._coeff_stream, CoefficientStream_uninitialized) and self._coeff_stream._target is None: - return 'Uninitialized Lazy Dirichlet Series' + sage: L = LazyDirichletSeriesRing(QQ, "s") + sage: f = L(constant=1) + sage: f._format_series(repr) + '1 + 1/(2^s) + 1/(3^s) + O(1/(4^s))' + + sage: L([1,-1,1])._format_series(repr) + '1 - 1/(2^s) + 1/(3^s)' - atomic_repr = self.base_ring()._repr_option('element_is_atomic') - X = self.parent().variable_name() - v = self._coeff_stream._approximate_valuation + sage: L([1,-1,1])._format_series(ascii_art) + -s -s + 1 + -2 + 3 + + """ + P = self.parent() + cs = self._coeff_stream + v = cs._approximate_valuation + if isinstance(cs, CoefficientStream_exact): + if not cs._constant: + m = cs._degree + else: + m = cs._degree + P.options.constant_length + else: + m = v + P.options.display_length - if not isinstance(self._coeff_stream, CoefficientStream_exact): - m = v + 7 # long enough + atomic_repr = P._coeff_ring._repr_option('element_is_atomic') + mons = [P.monomial(self[i], i) for i in range(v, m) if self[i]] + if not isinstance(cs, CoefficientStream_exact) or cs._constant: + if P._coeff_ring is P.base_ring(): + bigO = ["O(%s)" % P.monomial(1, m)] + else: + bigO = ["O(%s)^%s" % (', '.join(str(g) for g in P._names), m)] else: - m = self._coeff_stream._degree + 3 - - # Use the symbolic ring printing - from sage.calculus.var import var - from sage.symbolic.ring import SR - variable = var(self.parent().variable_name()) - ret = " + ".join([repr(SR(self._coeff_stream[i])*i**(-variable)) - for i in range(v, m) if self._coeff_stream[i]]) - if not ret: - return "0" - # TODO: Better handling when ret == 0 but we have not checked up to the constant term + bigO = [] - if isinstance(self._coeff_stream, CoefficientStream_exact) and not self._coeff_stream._constant: - return ret - return ret + ' + ...' + from sage.misc.latex import latex + from sage.typeset.unicode_art import unicode_art + from sage.typeset.ascii_art import ascii_art + from sage.misc.repr import repr_lincomb + from sage.typeset.symbols import ascii_left_parenthesis, ascii_right_parenthesis + from sage.typeset.symbols import unicode_left_parenthesis, unicode_right_parenthesis + if formatter == repr: + poly = repr_lincomb([(1, m) for m in mons + bigO], strip_one=True) + elif formatter == latex: + poly = repr_lincomb([(1, m) for m in mons + bigO], is_latex=True, strip_one=True) + elif formatter == ascii_art: + if atomic_repr: + poly = ascii_art(*(mons + bigO), sep = " + ") + else: + def parenthesize(m): + a = ascii_art(m) + h = a.height() + return ascii_art(ascii_left_parenthesis.character_art(h), + a, ascii_right_parenthesis.character_art(h)) + poly = ascii_art(*([parenthesize(m) for m in mons] + bigO), sep = " + ") + elif formatter == unicode_art: + if atomic_repr: + poly = unicode_art(*(mons + bigO), sep = " + ") + else: + def parenthesize(m): + a = unicode_art(m) + h = a.height() + return unicode_art(unicode_left_parenthesis.character_art(h), + a, unicode_right_parenthesis.character_art(h)) + poly = unicode_art(*([parenthesize(m) for m in mons] + bigO), sep = " + ") + + return poly diff --git a/src/sage/rings/lazy_laurent_series_ring.py b/src/sage/rings/lazy_laurent_series_ring.py index e7957cc70a5..baf7cf0667a 100644 --- a/src/sage/rings/lazy_laurent_series_ring.py +++ b/src/sage/rings/lazy_laurent_series_ring.py @@ -735,28 +735,28 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: L = LazyTaylorSeriesRing(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 + ... + 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + O(z^13) 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 + ... + 5*z^5 + 6*z^6 + 7*z^7 + 8*z^8 + 9*z^9 + z^10 + z^11 + z^12 + O(z^13) sage: X = L(constant=5, degree=2); X - 5*z^2 + 5*z^3 + 5*z^4 + ... + 5*z^2 + 5*z^3 + 5*z^4 + O(z^5) sage: X.valuation() 2 sage: e = L(lambda n: n+1); e - 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + ... + 1 + 2*z + 3*z^2 + 4*z^3 + 5*z^4 + 6*z^5 + 7*z^6 + O(z^7) sage: f = e^-1; f - 1 + -2*z + z^2 + ... + 1 - 2*z + z^2 + O(z^7) sage: f.coefficient(10) 0 sage: f[20] 0 sage: L(valuation=2, constant=1) - z^2 + z^3 + z^4 + ... + z^2 + z^3 + z^4 + O(z^5) sage: L(constant=1) - 1 + z + z^2 + ... + 1 + z + z^2 + O(z^3) Alternatively, ``x`` can be a list of elements of the base ring. Then these elements are read as coefficients of the terms of @@ -768,7 +768,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No z + 2*z^2 + 3*z^3 + 4*z^4 sage: g = L([1,3,5,7,9], 5, -1); g - z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 + -z^10 + -z^11 + -z^12 + ... + z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + O(z^13) .. TODO:: @@ -1042,20 +1042,20 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: L(3) 3 sage: L(lambda i: i, constant=1, degree=6) - 1 + 2/2^z + 3/3^z + 4/4^z + 5/5^z + 1/(6^z) + 1/(7^z) + 1/(8^z) + ... + 1 + 2/2^z + 3/3^z + 4/4^z + 5/5^z + 1/(6^z) + 1/(7^z) + 1/(8^z) + O(1/(9^z)) sage: X = L(constant=5, degree=3); X - 5/3^z + 5/4^z + 5/5^z + ... + 5/3^z + 5/4^z + 5/5^z + O(1/(6^z)) sage: X.valuation() 3 sage: e = L(moebius); e - 1 + -1/2^z + -1/3^z + -1/5^z + 1/(6^z) + -1/7^z + ... + 1 - 1/(2^z) - 1/(3^z) - 1/(5^z) + 1/(6^z) - 1/(7^z) + O(1/(8^z)) sage: L([0], constant=1) - 1/(2^z) + 1/(3^z) + 1/(4^z) + ... + 1/(2^z) + 1/(3^z) + 1/(4^z) + O(1/(5^z)) sage: L(constant=1) - 1 + 1/(2^z) + 1/(3^z) + ... + 1 + 1/(2^z) + 1/(3^z) + O(1/(4^z)) Alternatively, ``x`` can be a list of elements of the base ring. Then these elements are read as coefficients of the terms of @@ -1066,7 +1066,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: f = L([1,2,3,4], 4); f 1/(4^z) + 2/5^z + 3/6^z + 4/7^z sage: g = L([1,3,5,7,9], 6, -1); g - 1/(6^z) + 3/7^z + 5/8^z + 7/9^z + 9/10^z + -1/11^z + -1/12^z + -1/13^z + ... + 1/(6^z) + 3/7^z + 5/8^z + 7/9^z + 9/10^z - 1/(11^z) - 1/(12^z) - 1/(13^z) + O(1/(14^z)) TESTS::