diff --git a/src/sage/categories/graded_algebras_with_basis.py b/src/sage/categories/graded_algebras_with_basis.py index e80c1b00bf4..8527c214277 100644 --- a/src/sage/categories/graded_algebras_with_basis.py +++ b/src/sage/categories/graded_algebras_with_basis.py @@ -126,6 +126,23 @@ def free_graded_module(self, generator_degrees, names=None): from sage.modules.fp_graded.free_module import FreeGradedModule return FreeGradedModule(self, generator_degrees, names=names) + def formal_series_ring(self): + r""" + Return the completion of all formal linear combinations of + ``self`` with finite linear combinations in each homogeneous + degree (computed lazily). + + EXAMPLES:: + + sage: NCSF = NonCommutativeSymmetricFunctions(QQ) + sage: S = NCSF.Complete() + sage: L = S.formal_series_ring() + sage: L + Lazy completion of Non-Commutative Symmetric Functions over + the Rational Field in the Complete basis + """ + from sage.rings.lazy_series_ring import LazyCompletionGradedAlgebra + return LazyCompletionGradedAlgebra(self) class ElementMethods: pass diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index d1fd77e8c69..9cf982ce6ca 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -1449,6 +1449,27 @@ def coeff_of_m_mu_in_result(mu): distinct=True) return self(r) + def formal_series_ring(self): + r""" + Return the completion of all formal linear combinations of + ``self`` with finite linear combinations in each homogeneous + degree (computed lazily). + + EXAMPLES:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: L = s.formal_series_ring() + sage: L + Lazy completion of Symmetric Functions over Integer Ring in the Schur basis + + TESTS:: + + sage: type(L) + + """ + from sage.rings.lazy_series_ring import LazySymmetricFunctions + return LazySymmetricFunctions(self) + class FilteredSymmetricFunctionsBases(Category_realization_of_parent): r""" The category of filtered bases of the ring of symmetric functions. diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 50a8577504e..9f4cd1b0f39 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -3615,7 +3615,86 @@ def polynomial(self, degree=None, names=None): return R.sum(self[:m]) -class LazySymmetricFunction(LazyCauchyProductSeries): +class LazyCompletionGradedAlgebraElement(LazyCauchyProductSeries): + """ + An element of a completion of a graded algebra that is computed lazily. + """ + def _format_series(self, formatter, format_strings=False): + r""" + Return nonzero ``self`` formatted by ``formatter``. + + TESTS:: + + sage: h = SymmetricFunctions(ZZ).h() + sage: e = SymmetricFunctions(ZZ).e() + sage: L = LazySymmetricFunctions(tensor([h, e])) + sage: f = L(lambda n: sum(tensor([h[k], e[n-k]]) for k in range(n+1))) + sage: f._format_series(repr) + '(h[]#e[]) + + (h[]#e[1]+h[1]#e[]) + + (h[]#e[2]+h[1]#e[1]+h[2]#e[]) + + (h[]#e[3]+h[1]#e[2]+h[2]#e[1]+h[3]#e[]) + + (h[]#e[4]+h[1]#e[3]+h[2]#e[2]+h[3]#e[1]+h[4]#e[]) + + (h[]#e[5]+h[1]#e[4]+h[2]#e[3]+h[3]#e[2]+h[4]#e[1]+h[5]#e[]) + + (h[]#e[6]+h[1]#e[5]+h[2]#e[4]+h[3]#e[3]+h[4]#e[2]+h[5]#e[1]+h[6]#e[]) + + O^7' + """ + P = self.parent() + cs = self._coeff_stream + v = cs._approximate_order + if isinstance(cs, Stream_exact): + if not cs._constant: + m = cs._degree + else: + m = cs._degree + P.options.constant_length + else: + m = v + P.options.display_length + + atomic_repr = P._internal_poly_ring.base_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, Stream_exact) or cs._constant: + if P._internal_poly_ring.base_ring() is P.base_ring(): + bigO = ["O(%s)" % P._monomial(1, m)] + else: + bigO = ["O^%s" % 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 = " + ") + + return poly + + +class LazySymmetricFunction(LazyCompletionGradedAlgebraElement): r""" A symmetric function where each degree is computed lazily. @@ -3752,80 +3831,6 @@ def __call__(self, *args, check=True): plethysm = __call__ - def _format_series(self, formatter, format_strings=False): - r""" - Return nonzero ``self`` formatted by ``formatter``. - - TESTS:: - - sage: h = SymmetricFunctions(ZZ).h() - sage: e = SymmetricFunctions(ZZ).e() - sage: L = LazySymmetricFunctions(tensor([h, e])) - sage: f = L(lambda n: sum(tensor([h[k], e[n-k]]) for k in range(n+1))) - sage: f._format_series(repr) - '(h[]#e[]) - + (h[]#e[1]+h[1]#e[]) - + (h[]#e[2]+h[1]#e[1]+h[2]#e[]) - + (h[]#e[3]+h[1]#e[2]+h[2]#e[1]+h[3]#e[]) - + (h[]#e[4]+h[1]#e[3]+h[2]#e[2]+h[3]#e[1]+h[4]#e[]) - + (h[]#e[5]+h[1]#e[4]+h[2]#e[3]+h[3]#e[2]+h[4]#e[1]+h[5]#e[]) - + (h[]#e[6]+h[1]#e[5]+h[2]#e[4]+h[3]#e[3]+h[4]#e[2]+h[5]#e[1]+h[6]#e[]) - + O^7' - """ - P = self.parent() - cs = self._coeff_stream - v = cs._approximate_order - if isinstance(cs, Stream_exact): - if not cs._constant: - m = cs._degree - else: - m = cs._degree + P.options.constant_length - else: - m = v + P.options.display_length - - atomic_repr = P._internal_poly_ring.base_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, Stream_exact) or cs._constant: - if P._internal_poly_ring.base_ring() is P.base_ring(): - bigO = ["O(%s)" % P._monomial(1, m)] - else: - bigO = ["O^%s" % 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 = " + ") - - return poly - def symmetric_function(self, degree=None): r""" Return ``self`` as a symmetric function if ``self`` is actually so. diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 2626384097c..94a3c4ac465 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -10,6 +10,7 @@ :class:`LazyLaurentSeriesRing` | The ring of lazy Laurent series. :class:`LazyTaylorSeriesRing` | The ring of (possibly multivariate) lazy Taylor series. + :class:`LazyCompletionGradedAlgebra` | The completion of a graded alebra consisting of formal series. :class:`LazySymmetricFunctions` | The ring of (possibly multivariate) lazy symmetric functions. :class:`LazyDirichletSeriesRing` | The ring of lazy Dirichlet series. @@ -51,6 +52,7 @@ from sage.rings.lazy_series import (LazyModuleElement, LazyLaurentSeries, LazyTaylorSeries, + LazyCompletionGradedAlgebraElement, LazySymmetricFunction, LazyDirichletSeries) from sage.structure.global_options import GlobalOptions @@ -1479,27 +1481,49 @@ def _an_element_(self): ###################################################################### -class LazySymmetricFunctions(LazySeriesRing): - """ - The ring of lazy symmetric functions. +class LazyCompletionGradedAlgebra(LazySeriesRing): + r""" + The completion of a graded alebra consisting of formal series. + + For a graded algebra `A`, we can form a completion of `A` consisting of + all formal series of `A` such that each homogeneous component is + a finite linear combination of basis elements of `A`. INPUT: - - ``basis`` -- the ring of symmetric functions + - ``basis`` -- a graded algebra - ``names`` -- name(s) of the alphabets - - ``sparse`` -- (default: ``True``) whether we use a sparse or a dense representation + - ``sparse`` -- (default: ``True``) whether we use a sparse or + a dense representation EXAMPLES:: - sage: s = SymmetricFunctions(ZZ).s() - sage: LazySymmetricFunctions(s) - Lazy Symmetric Functions over Integer Ring in the Schur basis + sage: NCSF = NonCommutativeSymmetricFunctions(QQ) + sage: S = NCSF.Complete() + sage: L = S.formal_series_ring() + sage: L + Lazy completion of Non-Commutative Symmetric Functions over the Rational Field in the Complete basis - sage: m = SymmetricFunctions(ZZ).m() - sage: LazySymmetricFunctions(tensor([s, m])) - Lazy Symmetric Functions over Integer Ring in the Schur basis # Symmetric Functions over Integer Ring in the monomial basis + sage: f = 1 / (1 - L(S[1])) + sage: f + S[] + S[1] + (S[1,1]) + (S[1,1,1]) + (S[1,1,1,1]) + (S[1,1,1,1,1]) + (S[1,1,1,1,1,1]) + O^7 + sage: g = 1 / (1 - L(S[2])) + sage: g + S[] + S[2] + (S[2,2]) + (S[2,2,2]) + O^7 + sage: f * g + S[] + S[1] + (S[1,1]+S[2]) + (S[1,1,1]+S[1,2]) + + (S[1,1,1,1]+S[1,1,2]+S[2,2]) + (S[1,1,1,1,1]+S[1,1,1,2]+S[1,2,2]) + + (S[1,1,1,1,1,1]+S[1,1,1,1,2]+S[1,1,2,2]+S[2,2,2]) + O^7 + sage: g * f + S[] + S[1] + (S[1,1]+S[2]) + (S[1,1,1]+S[2,1]) + + (S[1,1,1,1]+S[2,1,1]+S[2,2]) + (S[1,1,1,1,1]+S[2,1,1,1]+S[2,2,1]) + + (S[1,1,1,1,1,1]+S[2,1,1,1,1]+S[2,2,1,1]+S[2,2,2]) + O^7 + sage: f * g - g * f + (S[1,2]-S[2,1]) + (S[1,1,2]-S[2,1,1]) + + (S[1,1,1,2]+S[1,2,2]-S[2,1,1,1]-S[2,2,1]) + + (S[1,1,1,1,2]+S[1,1,2,2]-S[2,1,1,1,1]-S[2,2,1,1]) + O^7 """ - Element = LazySymmetricFunction + Element = LazyCompletionGradedAlgebraElement def __init__(self, basis, sparse=True, category=None): """ @@ -1533,7 +1557,11 @@ def __init__(self, basis, sparse=True, category=None): Parent.__init__(self, base=base_ring, category=category) self._sparse = sparse self._laurent_poly_ring = basis - self._internal_poly_ring = PolynomialRing(self._laurent_poly_ring, "DUMMY_VARIABLE") + if self._laurent_poly_ring not in Rings().Commutative(): + from sage.algebras.free_algebra import FreeAlgebra + self._internal_poly_ring = FreeAlgebra(self._laurent_poly_ring, 1, "DUMMY_VARIABLE") + else: + self._internal_poly_ring = PolynomialRing(self._laurent_poly_ring, "DUMMY_VARIABLE") def _repr_(self): """ @@ -1543,9 +1571,9 @@ def _repr_(self): sage: s = SymmetricFunctions(GF(2)).s() sage: LazySymmetricFunctions(s) - Lazy Symmetric Functions over Finite Field of size 2 in the Schur basis + Lazy completion of Symmetric Functions over Finite Field of size 2 in the Schur basis """ - return "Lazy {}".format(self._laurent_poly_ring) + return "Lazy completion of {}".format(self._laurent_poly_ring) def _latex_(self): r""" @@ -1572,21 +1600,23 @@ def _monomial(self, c, n): sage: L = LazySymmetricFunctions(m) sage: L._monomial(s[2,1], 3) 2*m[1, 1, 1] + m[2, 1] - """ L = self._laurent_poly_ring return L(c) def _element_constructor_(self, x=None, valuation=None, degree=None, check=True): - """ - Construct a lazy symmetric function from ``x``. + r""" + Construct a lazy element in ``self`` from ``x``. INPUT: - - ``x`` -- data used to the define a symmetric function - - ``valuation`` -- integer (optional); integer; a lower bound for the valuation of the series - - ``degree`` -- (optional) the degree when the symmetric function has finite support - - ``check`` -- (optional) check that coefficients are homogeneous of the correct degree when they are retrieved + - ``x`` -- data used to the define a lazy element + - ``valuation`` -- integer (optional); integer; a lower bound for + the valuation of the series + - ``degree`` -- (optional) the degree when the lazy element + has finite support + - ``check`` -- (optional) check that coefficients are homogeneous of + the correct degree when they are retrieved EXAMPLES:: @@ -1640,24 +1670,22 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, check=True) sage: L(lambda n: n)[3]; Traceback (most recent call last): ... - ValueError: coefficient 3*h[] # e[] should be a symmetric function of homogeneous degree 3 but has degree 0 + ValueError: coefficient 3*h[] # e[] should be an element of homogeneous degree 3 but has degree 0 sage: L([1, 2, 3]); Traceback (most recent call last): ... - ValueError: coefficient 2*h[] # e[] should be a symmetric function of homogeneous degree 1 but has degree 0 + ValueError: coefficient 2*h[] # e[] should be an element of homogeneous degree 1 but has degree 0 sage: L(lambda n: n, degree=3); Traceback (most recent call last): ... - ValueError: coefficient h[] # e[] should be a symmetric function of homogeneous degree 1 but has degree 0 - + ValueError: coefficient h[] # e[] should be an element of homogeneous degree 1 but has degree 0 """ if valuation is None: valuation = 0 if valuation < 0: - raise ValueError("the valuation of a lazy symmetric function must be nonnegative") - + raise ValueError("the valuation of a lazy completion element must be nonnegative") R = self._laurent_poly_ring if x is None: @@ -1680,10 +1708,14 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, check=True) p_dict[d] = p_dict.get(d, 0) + f else: for f in x.terms(): - d = sum(p.size() for p in f.support()) + try: + d = f.degree() + except (TypeError, ValueError, AttributeError): + # FIXME: Fallback for symmetric functions in multiple variables + d = sum(p.size() for p in f.support()) p_dict[d] = p_dict.get(d, 0) + f - v = min(p_dict.keys()) - d = max(p_dict.keys()) + v = min(p_dict) + d = max(p_dict) p_list = [p_dict.get(i, 0) for i in range(v, d + 1)] coeff_stream = Stream_exact(p_list, self._sparse, @@ -1692,7 +1724,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, check=True) degree=degree) return self.element_class(self, coeff_stream) - if isinstance(x, LazySymmetricFunction): + if isinstance(x, self.Element): 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 @@ -1707,17 +1739,25 @@ def check_homogeneous_of_degree(f, d): if d1 == d: return except ValueError: - raise ValueError("coefficient %s should be a symmetric function of homogeneous degree %s" % (f, d)) - raise ValueError("coefficient %s should be a symmetric function of homogeneous degree %s but has degree %s" % (f, d, d1)) + raise ValueError("coefficient %s should be an element of homogeneous degree %s" % (f, d)) + raise ValueError("coefficient %s should be an element of homogeneous degree %s but has degree %s" % (f, d, d1)) else: def check_homogeneous_of_degree(f, d): if not f: return for m in f.monomials(): - for t in m.support(): - d1 = sum(p.size() for p in t) - if d1 != d: - raise ValueError("coefficient %s should be a symmetric function of homogeneous degree %s but has degree %s" % (f, d, d1)) + try: + d1 = m.degree() + except AttributeError: + # FIXME: Fallback for symmetric functions in multiple variables + for t in m.support(): + d1 = sum(p.size() for p in t) + if d1 != d: + raise ValueError("coefficient %s should be an element of homogeneous degree %s but has degree %s" % (f, d, d1)) + except (TypeError, ValueError): + raise ValueError("coefficient %s is not homogeneous") + if d1 != d: + raise ValueError("coefficient %s should be an element of homogeneous degree %s but has degree %s" % (f, d, d1)) if isinstance(x, (tuple, list)): if degree is None: @@ -1751,7 +1791,7 @@ def y(n): else: coeff_stream = Stream_function(x, coeff_ring, self._sparse, valuation) return self.element_class(self, coeff_stream) - raise ValueError(f"unable to convert {x} into a lazy symmetric function") + raise ValueError(f"unable to convert {x} into a lazy completion element") def _an_element_(self): """ @@ -1768,6 +1808,32 @@ def _an_element_(self): coeff_stream = Stream_exact([R.one()], self._sparse, order=1, constant=0) return self.element_class(self, coeff_stream) + +###################################################################### + +class LazySymmetricFunctions(LazyCompletionGradedAlgebra): + """ + The ring of lazy symmetric functions. + + INPUT: + + - ``basis`` -- the ring of symmetric functions + - ``names`` -- name(s) of the alphabets + - ``sparse`` -- (default: ``True``) whether we use a sparse or a dense representation + + EXAMPLES:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: LazySymmetricFunctions(s) + Lazy completion of Symmetric Functions over Integer Ring in the Schur basis + + sage: m = SymmetricFunctions(ZZ).m() + sage: LazySymmetricFunctions(tensor([s, m])) + Lazy completion of Symmetric Functions over Integer Ring in the Schur basis # Symmetric Functions over Integer Ring in the monomial basis + """ + Element = LazySymmetricFunction + + ###################################################################### class LazyDirichletSeriesRing(LazySeriesRing):