From 5c59f3367cea3d3d061334742c6a5f82ce66e04d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 20 Aug 2023 17:50:22 +0200 Subject: [PATCH] unify docstrings and uncontroversial doctests from #35480 and #35485 --- src/sage/data_structures/stream.py | 65 +++++++++++++++----------- src/sage/rings/lazy_series.py | 73 ++++++++++++++++++++++++------ src/sage/rings/lazy_series_ring.py | 6 +-- 3 files changed, 101 insertions(+), 43 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 4993d38043a..d1a4f99ba3a 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -185,7 +185,7 @@ def __ne__(self, other): def is_nonzero(self): r""" Return ``True`` if and only if this stream is known - to be nonzero. + to be non-zero. The default implementation is ``False``. @@ -242,7 +242,7 @@ def __init__(self, is_sparse, true_order): def is_nonzero(self): r""" - Return ``True`` if and only if the cache contains a nonzero element. + Return ``True`` if and only if the cache contains a non-zero element. EXAMPLES:: @@ -331,7 +331,7 @@ def __setstate__(self, d): def __getitem__(self, n): """ - Return the `n`-th coefficient of ``self``. + Return the ``n``-th coefficient of ``self``. INPUT: @@ -432,7 +432,7 @@ def iterate_coefficients(self): def order(self): r""" Return the order of ``self``, which is the minimum index ``n`` such - that ``self[n]`` is nonzero. + that ``self[n]`` is non-zero. EXAMPLES:: @@ -658,7 +658,7 @@ def __init__(self, initial_coefficients, constant=None, degree=None, order=None) # complicated otherwise for i, v in enumerate(initial_coefficients): if v: - # We have found the first nonzero coefficient + # We have found the first non-zero coefficient order += i initial_coefficients = initial_coefficients[i:] if order + len(initial_coefficients) == self._degree: @@ -737,7 +737,7 @@ def __getitem__(self, n): def order(self): r""" Return the order of ``self``, which is the minimum index - ``n`` such that ``self[n]`` is nonzero. + ``n`` such that ``self[n]`` is non-zero. EXAMPLES:: @@ -868,9 +868,9 @@ def __ne__(self, other): def is_nonzero(self): r""" Return ``True`` if and only if this stream is known - to be nonzero. + to be non-zero. - An assumption of this class is that it is nonzero. + An assumption of this class is that it is non-zero. EXAMPLES:: @@ -1580,7 +1580,7 @@ def get_coefficient(self, n): def is_nonzero(self): r""" Return ``True`` if and only if this stream is known - to be nonzero. + to be non-zero. EXAMPLES:: @@ -1598,6 +1598,14 @@ def is_nonzero(self): return self._left.is_nonzero() and self._right.is_nonzero() +class Stream_cauchy_mul_commutative(Stream_cauchy_mul, Stream_binaryCommutative): + """ + Operator for multiplication of two coefficient streams using the + Cauchy product for commutative multiplication of coefficients. + """ + pass + + class Stream_dirichlet_convolve(Stream_binary): r""" Operator for the Dirichlet convolution of two streams. @@ -1646,7 +1654,7 @@ def _approximate_order(self): or self._right._approximate_order <= 0): raise ValueError("Dirichlet convolution is only defined for " "coefficient streams with minimal index of " - "nonzero coefficient at least 1") + "non-zero coefficient at least 1") return self._left._approximate_order * self._right._approximate_order def get_coefficient(self, n): @@ -2224,6 +2232,11 @@ class Stream_scalar(Stream_inexact): Base class for operators multiplying a coefficient stream by a scalar. + INPUT: + + - ``series`` -- a :class:`Stream` + - ``scalar`` -- a non-zero, non-one scalar + .. TODO:: This does not inherit from :class:`Stream_unary`, because of @@ -2310,7 +2323,7 @@ def __eq__(self, other): def is_nonzero(self): r""" Return ``True`` if and only if this stream is known - to be nonzero. + to be non-zero. EXAMPLES:: @@ -2343,7 +2356,7 @@ class Stream_rmul(Stream_scalar): INPUT: - ``series`` -- a :class:`Stream` - - ``scalar`` -- a scalar + - ``scalar`` -- a non-zero, non-one scalar EXAMPLES:: @@ -2385,7 +2398,7 @@ class Stream_lmul(Stream_scalar): INPUT: - ``series`` -- a :class:`Stream` - - ``scalar`` -- a scalar + - ``scalar`` -- a non-zero, non-one scalar EXAMPLES:: @@ -2492,7 +2505,7 @@ def get_coefficient(self, n): def is_nonzero(self): r""" Return ``True`` if and only if this stream is known - to be nonzero. + to be non-zero. EXAMPLES:: @@ -2630,9 +2643,9 @@ def iterate_coefficients(self): def is_nonzero(self): r""" Return ``True`` if and only if this stream is known - to be nonzero. + to be non-zero. - An assumption of this class is that it is nonzero. + An assumption of this class is that it is non-zero. EXAMPLES:: @@ -2647,7 +2660,7 @@ def is_nonzero(self): class Stream_map_coefficients(Stream_inexact): r""" - The stream with ``function`` applied to each nonzero coefficient + The stream with ``function`` applied to each non-zero coefficient of ``series``. INPUT: @@ -2774,7 +2787,7 @@ def is_undefined(self): class Stream_shift(Stream): """ - Operator for shifting a nonzero, nonexact stream. + Operator for shifting a non-zero, non-exact stream. Instances of this class share the cache with its input stream. @@ -2820,7 +2833,7 @@ def _approximate_order(self): def order(self): r""" Return the order of ``self``, which is the minimum index - ``n`` such that ``self[n]`` is nonzero. + ``n`` such that ``self[n]`` is non-zero. EXAMPLES:: @@ -2889,9 +2902,9 @@ def __eq__(self, other): def is_nonzero(self): r""" Return ``True`` if and only if this stream is known - to be nonzero. + to be non-zero. - An assumption of this class is that it is nonzero. + An assumption of this class is that it is non-zero. EXAMPLES:: @@ -2911,7 +2924,7 @@ def is_undefined(self): class Stream_truncated(Stream_inexact): """ - Operator for shifting a nonzero, nonexact stream that has + Operator for shifting a non-zero, non-exact stream that has been shifted below its minimal valuation. Instances of this class share the cache with its input stream. @@ -3072,7 +3085,7 @@ def __eq__(self, other): def order(self): """ Return the order of ``self``, which is the minimum index ``n`` such - that ``self[n]`` is nonzero. + that ``self[n]`` is non-zero. EXAMPLES:: @@ -3118,7 +3131,7 @@ def order(self): def is_nonzero(self): r""" Return ``True`` if and only if this stream is known - to be nonzero. + to be non-zero. EXAMPLES:: @@ -3166,7 +3179,7 @@ def is_undefined(self): class Stream_derivative(Stream_inexact): """ - Operator for taking derivatives of a stream. + Operator for taking derivatives of a non-exact stream. INPUT: @@ -3278,7 +3291,7 @@ def __eq__(self, other): def is_nonzero(self): r""" Return ``True`` if and only if this stream is known - to be nonzero. + to be non-zero. EXAMPLES:: diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 7d2b0950922..b52a121b620 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -196,6 +196,19 @@ sage: L = LazySymmetricFunctions(s) # optional - sage.combinat sage.rings.finite_rings sage: check(L, lambda n: sum(k*s(la) for k, la in enumerate(Partitions(n))), # optional - sage.combinat sage.rings.finite_rings ....: valuation=0) + +Check that we can invert matrices:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: a11 = 1 + L(lambda n: 1 if not n else 0, valuation=0) + sage: a12 = 1 + L(lambda n: 1 if n == 1 else 0, valuation=0) + sage: a21 = 1 + L(lambda n: 1 if n == 2 else 0, valuation=0) + sage: a22 = 1 + L(lambda n: 1 if n == 3 else 0, valuation=0) + sage: m = matrix([[a11, a12], [a21, a22]]) + sage: m.inverse() + [ 1 + z + 2*z^2 + 3*z^3 + 4*z^4 + 5*z^5 + 6*z^6 + O(z^7) -1 - 2*z - 3*z^2 - 4*z^3 - 5*z^4 - 6*z^5 - 7*z^6 + O(z^7)] + [ -1 - z - 3*z^2 - 3*z^3 - 5*z^4 - 5*z^5 - 7*z^6 + O(z^7) 2 + 2*z + 4*z^2 + 4*z^3 + 6*z^4 + 6*z^5 + 8*z^6 + O(z^7)] + """ # **************************************************************************** @@ -219,6 +232,7 @@ from sage.combinat.partition import Partition, Partitions from sage.misc.derivative import derivative_parse from sage.categories.integral_domains import IntegralDomains +from sage.categories.rings import Rings from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ @@ -228,6 +242,7 @@ from sage.data_structures.stream import ( Stream_add, Stream_cauchy_mul, + Stream_cauchy_mul_commutative, Stream_sub, Stream_cauchy_compose, Stream_lmul, @@ -1051,9 +1066,9 @@ def __bool__(self): False sage: M = L(lambda n: 2*n if n < 10 else 1, valuation=0); M # optional - sage.rings.finite_rings O(z^7) - sage: bool(M) + sage: bool(M) # optional - sage.rings.finite_rings True - sage: M[15] + sage: M[15] # optional - sage.rings.finite_rings 1 sage: bool(M) # optional - sage.rings.finite_rings True @@ -1061,9 +1076,9 @@ def __bool__(self): sage: L. = LazyLaurentSeriesRing(GF(2), sparse=True) # optional - sage.rings.finite_rings sage: M = L(lambda n: 2*n if n < 10 else 1, valuation=0); M # optional - sage.rings.finite_rings O(z^7) - sage: bool(M) + sage: bool(M) # optional - sage.rings.finite_rings True - sage: M[15] + sage: M[15] # optional - sage.rings.finite_rings 1 sage: bool(M) # optional - sage.rings.finite_rings True @@ -1216,7 +1231,7 @@ def define(self, s): sage: binomial(2000, 1000) / C[1000] 1001 - The Catalan numbers but with a valuation 1:: + The Catalan numbers but with a valuation `1`:: sage: B = L.undefined(valuation=1) sage: B.define(z + B^2) @@ -1970,6 +1985,21 @@ def _acted_upon_(self, scalar, self_on_left): sage: 1 * M is M True + + TESTS: + + Check that non-commutativity is taken into account:: + + sage: M = MatrixSpace(ZZ, 2) + sage: L. = LazyPowerSeriesRing(M) + sage: f = L(lambda n: matrix([[1,n],[0,1]])) + sage: m = matrix([[1,0],[1,1]]) + sage: (m * f - f * m)[1] + [-1 0] + [ 0 1] + sage: m * f[1] - f[1] * m + [-1 0] + [ 0 1] """ # With the current design, the coercion model does not have # enough information to detect a priori that this method only @@ -2008,7 +2038,7 @@ def _acted_upon_(self, scalar, self_on_left): order=v, constant=c, degree=coeff_stream._degree)) - if self_on_left or R.is_commutative(): + if self_on_left or R in Rings().Commutative(): return P.element_class(P, Stream_lmul(coeff_stream, scalar, P.is_sparse())) return P.element_class(P, Stream_rmul(coeff_stream, scalar, @@ -2966,6 +2996,9 @@ def _mul_(self, other): and right.order() == 0 and not right._constant): return self # right == 1 + if ((isinstance(left, Stream_cauchy_invert) and left._series == right) + or (isinstance(right, Stream_cauchy_invert) and right._series == left)): + return P.one() # The product is exact if and only if both factors are exact # and one of the factors has eventually 0 coefficients: # (p + a x^d/(1-x))(q + b x^e/(1-x)) @@ -3011,7 +3044,11 @@ def _mul_(self, other): constant=c) return P.element_class(P, coeff_stream) - return P.element_class(P, Stream_cauchy_mul(left, right, P.is_sparse())) + if P in Rings().Commutative(): + coeff_stream = Stream_cauchy_mul_commutative(left, right, P.is_sparse()) + else: + coeff_stream = Stream_cauchy_mul(left, right, P.is_sparse()) + return P.element_class(P, coeff_stream) def __pow__(self, n): r""" @@ -3144,7 +3181,7 @@ def __invert__(self): sage: g = L([2], valuation=-1, constant=1); g 2*x^-1 + 1 + x + x^2 + O(x^3) sage: g * g^-1 - 1 + O(x^7) + 1 sage: L. = LazyPowerSeriesRing(QQ) sage: ~(x + x^2) @@ -3327,7 +3364,7 @@ def _div_(self, other): return self # self is right - if left is right: + if left == right: return P.one() if (P._minimal_valuation is not None @@ -3402,7 +3439,11 @@ def _div_(self, other): # P._minimal_valuation is zero, because we allow division by # series of positive valuation right_inverse = Stream_cauchy_invert(right) - return P.element_class(P, Stream_cauchy_mul(left, right_inverse, P.is_sparse())) + if P in Rings().Commutative(): + coeff_stream = Stream_cauchy_mul_commutative(left, right_inverse, P.is_sparse()) + else: + coeff_stream = Stream_cauchy_mul(left, right_inverse, P.is_sparse()) + return P.element_class(P, coeff_stream) def _floordiv_(self, other): r""" @@ -3493,7 +3534,9 @@ def exp(self): d_self = Stream_function(lambda n: (n + 1) * coeff_stream[n + 1], False, 0) f = P.undefined(valuation=0) - d_self_f = Stream_cauchy_mul(d_self, f._coeff_stream, False) + # d_self and f._coeff_stream always commute, the coefficients + # of the product are of the form sum_{k=1}^n a_k a_{n+1-k}. + d_self_f = Stream_cauchy_mul_commutative(d_self, f._coeff_stream, False) int_d_self_f = Stream_function(lambda n: d_self_f[n-1] / R(n) if n else R.one(), False, 0) f._coeff_stream._target = int_d_self_f @@ -3542,9 +3585,11 @@ def log(self): # multivariate power series d_self = Stream_function(lambda n: (n + 1) * coeff_stream[n + 1], P.is_sparse(), 0) - d_self_quo_self = Stream_cauchy_mul(d_self, - Stream_cauchy_invert(coeff_stream), - P.is_sparse()) + coeff_stream_inverse = Stream_cauchy_invert(coeff_stream) + # d_self and coeff_stream_inverse always commute + d_self_quo_self = Stream_cauchy_mul_commutative(d_self, + coeff_stream_inverse, + P.is_sparse()) int_d_self_quo_self = Stream_function(lambda n: d_self_quo_self[n-1] / R(n), P.is_sparse(), 1) return P.element_class(P, int_d_self_quo_self) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index c5350cde77e..aebe887ae58 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -2398,10 +2398,10 @@ def __init__(self, basis, sparse=True, category=None): if basis not in GradedAlgebrasWithBasis: raise ValueError("basis should be in GradedAlgebrasWithBasis") self._arity = 1 - category = Algebras(base_ring.category()) - if base_ring in IntegralDomains(): + category = Algebras(basis.category()) + if basis in IntegralDomains(): category &= IntegralDomains() - elif base_ring in Rings().Commutative(): + elif basis in Rings().Commutative(): category = category.Commutative() if base_ring.is_zero():