Path: blob/master/src/sage/algebras/shuffle_algebra.py
8818 views
# -*- coding: utf-8 -*-1r"""2Shuffle algebras34AUTHORS:56- Frédéric Chapoton (2013-03): Initial version7- Matthieu Deneufchatel (2013-07): Implemented dual PBW basis8"""910#*****************************************************************************11# Copyright (C) 2013 Frédéric Chapoton <chapoton-math-univ-lyon1-fr>12#13# Distributed under the terms of the GNU General Public License (GPL)14# http://www.gnu.org/licenses/15#*****************************************************************************1617from sage.categories.rings import Rings18from sage.categories.algebras_with_basis import AlgebrasWithBasis19from sage.categories.commutative_algebras import CommutativeAlgebras20from sage.categories.coalgebras_with_basis import CoalgebrasWithBasis21from sage.categories.tensor import TensorProductsCategory, tensor22from sage.combinat.free_module import CombinatorialFreeModule23from sage.combinat.words.alphabet import Alphabet24from sage.combinat.words.words import Words25from sage.combinat.words.word import Word26from sage.combinat.words.finite_word import FiniteWord_class27from sage.misc.cachefunc import cached_method28from sage.misc.lazy_attribute import lazy_attribute29from sage.misc.misc_c import prod30from sage.sets.family import Family3132class ShuffleAlgebra(CombinatorialFreeModule):33r"""34The shuffle algebra on some generators over a base ring.3536Shuffle algebras are commutative and associative algebras, with a37basis indexed by words. The product of two words `w_1 \cdot w_2` is given38by the sum over the shuffle product of `w_1` and `w_2`.3940.. SEEALSO::4142For more on shuffle products, see43:mod:`~sage.combinat.words.shuffle_product` and44:meth:`~sage.combinat.words.finite_word.FiniteWord_class.shuffle()`.4546REFERENCES:4748- :wikipedia:`Shuffle algebra`4950INPUT:5152- ``R`` -- ring5354- ``names`` -- generator names (string or an alphabet)5556EXAMPLES::5758sage: F = ShuffleAlgebra(QQ, 'xyz'); F59Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field6061sage: mul(F.gens())62B[word: xyz] + B[word: xzy] + B[word: yxz] + B[word: yzx] + B[word: zxy] + B[word: zyx]6364sage: mul([ F.gen(i) for i in range(2) ]) + mul([ F.gen(i+1) for i in range(2) ])65B[word: xy] + B[word: yx] + B[word: yz] + B[word: zy]6667sage: S = ShuffleAlgebra(ZZ, 'abcabc'); S68Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring69sage: S.base_ring()70Integer Ring7172sage: G = ShuffleAlgebra(S, 'mn'); G73Shuffle Algebra on 2 generators ['m', 'n'] over Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring74sage: G.base_ring()75Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring7677Shuffle algebras commute with their base ring::7879sage: K = ShuffleAlgebra(QQ,'ab')80sage: a,b = K.gens()81sage: K.is_commutative()82True83sage: L = ShuffleAlgebra(K,'cd')84sage: c,d = L.gens()85sage: L.is_commutative()86True87sage: s = a*b^2 * c^3; s88(12*B[word:abb]+12*B[word:bab]+12*B[word:bba])*B[word: ccc]89sage: parent(s)90Shuffle Algebra on 2 generators ['c', 'd'] over Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field91sage: c^3 * a * b^292(12*B[word:abb]+12*B[word:bab]+12*B[word:bba])*B[word: ccc]9394Shuffle algebras are commutative::9596sage: c^3 * b * a * b == c * a * c * b^2 * c97True9899We can also manipulate elements in the basis and coerce elements from our100base field::101102sage: F = ShuffleAlgebra(QQ, 'abc')103sage: B = F.basis()104sage: B[Word('bb')] * B[Word('ca')]105B[word: bbca] + B[word: bcab] + B[word: bcba] + B[word: cabb] + B[word: cbab] + B[word: cbba]106sage: 1 - B[Word('bb')] * B[Word('ca')] / 2107B[word: ] - 1/2*B[word: bbca] - 1/2*B[word: bcab] - 1/2*B[word: bcba] - 1/2*B[word: cabb] - 1/2*B[word: cbab] - 1/2*B[word: cbba]108"""109@staticmethod110def __classcall_private__(cls, R, names):111"""112Normalize input to ensure a unique representation.113114EXAMPLES::115116sage: F1 = ShuffleAlgebra(QQ, 'xyz')117sage: F2 = ShuffleAlgebra(QQ, ['x','y','z'])118sage: F3 = ShuffleAlgebra(QQ, Alphabet('xyz'))119sage: F1 is F2 and F1 is F3120True121"""122return super(ShuffleAlgebra, cls).__classcall__(cls, R, Alphabet(names))123124def __init__(self, R, names):125r"""126Initialize ``self``.127128EXAMPLES::129130sage: F = ShuffleAlgebra(QQ, 'xyz'); F131Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field132sage: TestSuite(F).run()133134TESTS::135136sage: ShuffleAlgebra(24, 'toto')137Traceback (most recent call last):138...139TypeError: argument R must be a ring140"""141if R not in Rings():142raise TypeError("argument R must be a ring")143self._alphabet = names144self.__ngens = self._alphabet.cardinality()145CombinatorialFreeModule.__init__(self, R, Words(names),146latex_prefix="",147category=(AlgebrasWithBasis(R), CommutativeAlgebras(R), CoalgebrasWithBasis(R)))148149def variable_names(self):150r"""151Return the names of the variables.152153EXAMPLES::154155sage: R = ShuffleAlgebra(QQ,'xy')156sage: R.variable_names()157{'x', 'y'}158"""159return self._alphabet160161def is_commutative(self):162r"""163Return ``True`` as the shuffle algebra is commutative.164165EXAMPLES::166167sage: R = ShuffleAlgebra(QQ,'x')168sage: R.is_commutative()169True170sage: R = ShuffleAlgebra(QQ,'xy')171sage: R.is_commutative()172True173"""174return True175176def _repr_(self):177r"""178Text representation of this shuffle algebra.179180EXAMPLES::181182sage: F = ShuffleAlgebra(QQ,'xyz')183sage: F # indirect doctest184Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field185186sage: ShuffleAlgebra(ZZ,'a')187Shuffle Algebra on one generator ['a'] over Integer Ring188"""189if self.__ngens == 1:190gen = "one generator"191else:192gen = "%s generators" %self.__ngens193return "Shuffle Algebra on "+ gen +" %s over %s"%(194self._alphabet.list(), self.base_ring())195196@cached_method197def one_basis(self):198r"""199Return the empty word, which index of `1` of this algebra,200as per :meth:`AlgebrasWithBasis.ParentMethods.one_basis`.201202EXAMPLES::203204sage: A = ShuffleAlgebra(QQ,'a')205sage: A.one_basis()206word:207sage: A.one()208B[word: ]209"""210return self.basis().keys()([])211212def product_on_basis(self, w1, w2):213r"""214Return the product of basis elements ``w1`` and ``w2``, as per215:meth:`AlgebrasWithBasis.ParentMethods.product_on_basis()`.216217INPUT:218219- ``w1``, ``w2`` -- Basis elements220221EXAMPLES::222223sage: A = ShuffleAlgebra(QQ,'abc')224sage: W = A.basis().keys()225sage: A.product_on_basis(W("acb"), W("cba"))226B[word: acbacb] + B[word: acbcab] + 2*B[word: acbcba] + 2*B[word: accbab] + 4*B[word: accbba] + B[word: cabacb] + B[word: cabcab] + B[word: cabcba] + B[word: cacbab] + 2*B[word: cacbba] + 2*B[word: cbaacb] + B[word: cbacab] + B[word: cbacba]227228sage: (a,b,c) = A.algebra_generators()229sage: a * (1-b)^2 * c2302*B[word: abbc] - 2*B[word: abc] + 2*B[word: abcb] + B[word: ac] - 2*B[word: acb] + 2*B[word: acbb] + 2*B[word: babc] - 2*B[word: bac] + 2*B[word: bacb] + 2*B[word: bbac] + 2*B[word: bbca] - 2*B[word: bca] + 2*B[word: bcab] + 2*B[word: bcba] + B[word: ca] - 2*B[word: cab] + 2*B[word: cabb] - 2*B[word: cba] + 2*B[word: cbab] + 2*B[word: cbba]231"""232return sum(self.basis()[u] for u in w1.shuffle(w2))233234def gen(self,i):235r"""236The ``i``-th generator of the algebra.237238INPUT:239240- ``i`` -- an integer241242EXAMPLES::243244sage: F = ShuffleAlgebra(ZZ,'xyz')245sage: F.gen(0)246B[word: x]247248sage: F.gen(4)249Traceback (most recent call last):250...251IndexError: argument i (= 4) must be between 0 and 2252"""253n = self.__ngens254if i < 0 or not i < n:255raise IndexError("argument i (= %s) must be between 0 and %s"%(i, n-1))256return self.algebra_generators()[i]257258def coproduct_on_basis(self, w):259"""260Return the coproduct of the element of the basis indexed by261the word ``w``.262263INPUT:264265- ``w`` -- a word266267EXAMPLES::268269sage: F = ShuffleAlgebra(QQ,'ab')270sage: F.coproduct_on_basis(Word('a'))271B[word: ] # B[word: a] + B[word: a] # B[word: ]272sage: F.coproduct_on_basis(Word('aba'))273B[word: ] # B[word: aba] + B[word: a] # B[word: ab] + B[word: a] # B[word: ba]274+ B[word: aa] # B[word: b] + B[word: ab] # B[word: a] + B[word: aba] # B[word: ]275+ B[word: b] # B[word: aa] + B[word: ba] # B[word: a]276sage: F.coproduct_on_basis(Word())277B[word: ] # B[word: ]278"""279if len(w) == 0:280return self.tensor_square().monomial((self.one_basis(), self.one_basis()))281if len(w) == 1:282return self.tensor_square().sum_of_terms([283((w, self.one_basis()), 1),284((self.one_basis(), w), 1) ], distinct=True)285286B = self.basis()287result = self.coproduct_on_basis(Word([w[0]]))288for i in w[1:]:289result = self.tensor_square().sum_of_terms([290((Word(v1)*Word(u1), Word(v2)*Word(u2)), coeff1 * coeff2)291for ((u1,u2),coeff1) in self.coproduct_on_basis(Word([i]))292for ((v1,v2),coeff2) in result ])293return result294295def coproduct(self, S):296"""297Return the coproduct of the series ``S``.298299EXAMPLES::300301sage: F = ShuffleAlgebra(QQ,'ab')302sage: S = F.an_element(); S303B[word: ] + 2*B[word: a] + 3*B[word: b]304sage: F.coproduct(S)305B[word: ] # B[word: ] + 2*B[word: ] # B[word: a] + 3*B[word: ] # B[word: b]306+ 2*B[word: a] # B[word: ] + 3*B[word: b] # B[word: ]307sage: F.coproduct(F.one())308B[word: ] # B[word: ]309"""310return sum([c * self.coproduct_on_basis(i)311for i,c in S.monomial_coefficients().items()])312313def counit(self,S):314"""315Return the counit of ``S``.316317EXAMPLES::318319sage: F = ShuffleAlgebra(QQ,'ab')320sage: S = F.an_element(); S321B[word: ] + 2*B[word: a] + 3*B[word: b]322sage: F.counit(S)3231324"""325return S.monomial_coefficients().get(Word(), 0)326327@cached_method328def algebra_generators(self):329r"""330Return the generators of this algebra.331332EXAMPLES::333334sage: A = ShuffleAlgebra(ZZ,'fgh'); A335Shuffle Algebra on 3 generators ['f', 'g', 'h'] over Integer Ring336sage: A.algebra_generators()337Family (B[word: f], B[word: g], B[word: h])338"""339Words = self.basis().keys()340return Family( [self.monomial(Words(a)) for a in self._alphabet] )341# FIXME: use this once the keys argument of FiniteFamily will be honoured342# for the specifying the order of the elements in the family343#return Family(self._alphabet, lambda a: self.term(self.basis().keys()(a)))344345gens = algebra_generators346347def _element_constructor_(self, x):348r"""349Convert ``x`` into ``self``.350351EXAMPLES::352353sage: R = ShuffleAlgebra(QQ,'xy')354sage: x, y = R.gens()355sage: R(3)3563*B[word: ]357sage: R(x)358B[word: x]359sage: R('xyy')360B[word: xyy]361sage: R(Word('xyx'))362B[word: xyx]363"""364if isinstance(x, (str, FiniteWord_class)):365W = self.basis().keys()366return self.monomial(W(x))367368P = x.parent()369if isinstance(P, ShuffleAlgebra):370if P is self:371return x372if not (P is self.base_ring()):373return self.element_class(self, x.monomial_coefficients())374if isinstance(P, DualPBWBasis):375return self(P.expansion(x))376# ok, not a shuffle algebra element (or should not be viewed as one).377if isinstance(x, basestring):378from sage.misc.sage_eval import sage_eval379return sage_eval(x,locals=self.gens_dict())380R = self.base_ring()381# coercion via base ring382x = R(x)383if x == 0:384return self.element_class(self,{})385else:386return self.from_base_ring_from_one_basis(x)387388def _coerce_impl(self, x):389r"""390Canonical coercion of ``x`` into ``self``.391392Here is what canonically coerces to ``self``:393394- this shuffle algebra,395396- anything that coerces to the base ring of this shuffle algebra,397398- any shuffle algebra on the same variables, whose base ring399coerces to the base ring of this shuffle algebra.400401EXAMPLES::402403sage: F = ShuffleAlgebra(GF(7), 'xyz'); F404Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7405406Elements of the shuffle algebra canonically coerce in::407408sage: x, y, z = F.gens()409sage: F.coerce(x*y) # indirect doctest410B[word: xy] + B[word: yx]411412Elements of the integers coerce in, since there is a coerce map413from `\ZZ` to GF(7)::414415sage: F.coerce(1) # indirect doctest416B[word: ]417418There is no coerce map from `\QQ` to `\GF{7}`::419420sage: F.coerce(2/3) # indirect doctest421Traceback (most recent call last):422...423TypeError: no canonical coercion from Rational Field to Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7424425Elements of the base ring coerce in::426427sage: F.coerce(GF(7)(5))4285*B[word: ]429430The shuffle algebra over `\ZZ` on `x, y, z` coerces in, since431`\ZZ` coerces to `\GF{7}`::432433sage: G = ShuffleAlgebra(ZZ,'xyz')434sage: Gx,Gy,Gz = G.gens()435sage: z = F.coerce(Gx**2 * Gy);z4362*B[word: xxy] + 2*B[word: xyx] + 2*B[word: yxx]437sage: z.parent() is F438True439440However, `\GF{7}` does not coerce to `\ZZ`, so the shuffle441algebra over `\GF{7}` does not coerce to the one over `\ZZ`::442443sage: G.coerce(x^3*y)444Traceback (most recent call last):445...446TypeError: no canonical coercion from Shuffle Algebra on 3 generators447['x', 'y', 'z'] over Finite Field of size 7 to Shuffle Algebra on 3448generators ['x', 'y', 'z'] over Integer Ring449"""450try:451R = x.parent()452453# shuffle algebras in the same variables over any base454# that coerces in:455if isinstance(R,ShuffleAlgebra):456if R.variable_names() == self.variable_names():457if self.has_coerce_map_from(R.base_ring()):458return self(x)459else:460raise TypeError("no natural map between bases of shuffle algebras")461462if isinstance(R, DualPBWBasis):463return self(R.expansion(x))464465except AttributeError:466pass467468# any ring that coerces to the base ring of this shuffle algebra.469return self._coerce_try(x, [self.base_ring()])470471def _coerce_map_from_(self, R):472r"""473Return ``True`` if there is a coercion from ``R`` into ``self``474and ``False`` otherwise.475476The things that coerce into ``self`` are477478- Shuffle Algebras in the same variables over a base with a coercion479map into ``self.base_ring()``.480481- Anything with a coercion into ``self.base_ring()``.482483TESTS::484485sage: F = ShuffleAlgebra(ZZ, 'xyz')486sage: G = ShuffleAlgebra(QQ, 'xyz')487sage: H = ShuffleAlgebra(ZZ, 'y')488sage: F._coerce_map_from_(G)489False490sage: G._coerce_map_from_(F)491True492sage: F._coerce_map_from_(H)493False494sage: F._coerce_map_from_(QQ)495False496sage: G._coerce_map_from_(QQ)497True498sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z'))499False500sage: F._coerce_map_from_(F.dual_pbw_basis())501True502"""503# shuffle algebras in the same variable over any base that coerces in:504if isinstance(R, ShuffleAlgebra):505if R.variable_names() == self.variable_names():506if self.base_ring().has_coerce_map_from(R.base_ring()):507return True508else:509return False510511if isinstance(R, DualPBWBasis):512return self.has_coerce_map_from(R._alg)513514return self.base_ring().has_coerce_map_from(R)515516def dual_pbw_basis(self):517"""518Return the dual PBW of ``self``.519520EXAMPLES::521522sage: A = ShuffleAlgebra(QQ, 'ab')523sage: A.dual_pbw_basis()524The dual Poincare-Birkhoff-Witt basis of Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field525"""526return DualPBWBasis(self.base_ring(), self._alphabet)527528def to_dual_pbw_element(self, w):529"""530Return the element `w` of ``self`` expressed in the dual PBW basis.531532INPUT:533534- ``w`` -- an element of the shuffle algebra535536EXAMPLES::537538sage: A = ShuffleAlgebra(QQ, 'ab')539sage: f = 2 * A(Word()) + A(Word('ab')); f5402*B[word: ] + B[word: ab]541sage: A.to_dual_pbw_element(f)5422*S[word: ] + S[word: ab]543sage: A.to_dual_pbw_element(A.one())544S[word: ]545sage: S = A.dual_pbw_basis()546sage: elt = S.expansion_on_basis(Word('abba')); elt5472*B[word: aabb] + B[word: abab] + B[word: abba]548sage: A.to_dual_pbw_element(elt)549S[word: abba]550sage: A.to_dual_pbw_element(2*A(Word('aabb')) + A(Word('abab')))551S[word: abab]552sage: S.expansion(S('abab'))5532*B[word: aabb] + B[word: abab]554"""555D = self.dual_pbw_basis()556l = {}557W = self.basis().keys()558while w != self.zero():559support = [W(i[0]) for i in list(w)]560min_elt = W(support[0])561if len(support) > 1:562for word in support[1:len(support)-1]:563if min_elt.lex_less(word):564min_elt = W(word)565coeff = list(w)[support.index(min_elt)][1]566l[min_elt] = l.get(min_elt, 0) + coeff567w = w - coeff * D.expansion_on_basis(W(min_elt))568569return D.sum_of_terms([(m, c) for m,c in l.items() if c != 0])570571class DualPBWBasis(CombinatorialFreeModule):572r"""573The basis dual to the Poincare-Birkhoff-Witt basis of the free algebra.574575We recursively define the dual PBW basis as the basis of the576shuffle algebra given by577578.. MATH::579580S_w = \begin{cases}581w & |w| = 1, \\582x S_u & w = xu \text{ and } w \in \mathrm{Lyn}(X), \\583\displaystyle \frac{S_{\ell_{i_1}}^{\ast \alpha_1} \ast \cdots584\ast S_{\ell_{i_k}}^{\ast \alpha_k}}{\alpha_1! \cdots \alpha_k!} &585w = \ell_{i_1}^{\alpha_1} \cdots \ell_{i_k}^{\alpha_k} \text{ with }586\ell_1 > \cdots > \ell_k \in \mathrm{Lyn}(X).587\end{cases}588589where `S \ast T` denotes the shuffle product of `S` and `T` and590`\mathrm{Lyn}(X)` is the set of Lyndon words in the alphabet `X`.591592The definition may be found in Theorem 5.3 of [Reuten1993]_.593594INPUT:595596- ``R`` -- ring597598- ``names`` -- names of the generators (string or an alphabet)599600REFERENCES:601602.. [Reuten1993] C. Reutenauer. *Free Lie Algebras*. Number 7 in603London Math. Soc. Monogr. (N.S.). Oxford University Press. (1993).604605EXAMPLES::606607sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()608sage: S609The dual Poincare-Birkhoff-Witt basis of Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field610sage: S.one()611S[word: ]612sage: S.one_basis()613word:614sage: T = ShuffleAlgebra(QQ, 'abcd').dual_pbw_basis(); T615The dual Poincare-Birkhoff-Witt basis of Shuffle Algebra on 4 generators ['a', 'b', 'c', 'd'] over Rational Field616sage: T.algebra_generators()617(S[word: a], S[word: b], S[word: c], S[word: d])618619TESTS:620621We check conversion between the bases::622623sage: A = ShuffleAlgebra(QQ, 'ab')624sage: S = A.dual_pbw_basis()625sage: W = Words('ab', 5)626sage: all(S(A(S(w))) == S(w) for w in W)627True628sage: all(A(S(A(w))) == A(w) for w in W)629True630"""631@staticmethod632def __classcall_private__(cls, R, names):633"""634Normalize input to ensure a unique representation.635636EXAMPLES::637638sage: from sage.algebras.shuffle_algebra import DualPBWBasis639sage: D1 = DualPBWBasis(QQ, 'ab')640sage: D2 = DualPBWBasis(QQ, Alphabet('ab'))641sage: D1 is D2642True643"""644return super(DualPBWBasis, cls).__classcall__(cls, R, Alphabet(names))645646def __init__(self, R, names):647"""648Initialize ``self``.649650EXAMPLES::651652sage: D = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()653sage: TestSuite(D).run()654"""655self._alphabet = names656self._alg = ShuffleAlgebra(R, names)657CombinatorialFreeModule.__init__(self, R, Words(names), prefix='S',658category=(AlgebrasWithBasis(R), CommutativeAlgebras(R), CoalgebrasWithBasis(R)))659660def _repr_(self):661"""662Return a string representation of ``self``.663664EXAMPLES::665666sage: ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()667The dual Poincare-Birkhoff-Witt basis of Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field668"""669return "The dual Poincare-Birkhoff-Witt basis of {}".format(self._alg)670671def _element_constructor_(self, x):672"""673Construct an element of ``self`` from ``x``.674675EXAMPLES::676677sage: A = ShuffleAlgebra(QQ, 'ab')678sage: S = A.dual_pbw_basis()679sage: S('abaab')680S[word: abaab]681sage: S(Word('aba'))682S[word: aba]683sage: S(A('ab'))684S[word: ab]685"""686if isinstance(x, (str, FiniteWord_class)):687W = self.basis().keys()688x = W(x)689elif isinstance(x.parent(), ShuffleAlgebra):690return self._alg.to_dual_pbw_element(self._alg(x))691return super(DualPBWBasis, self)._element_constructor_(x)692693def _coerce_map_from_(self, R):694"""695Return ``True`` if there is a coercion from ``R`` into ``self`` and696``False`` otherwise. The things that coerce into ``self`` are:697698- Anything that coerces into the associated shuffle algebra of ``self``699700TESTS::701702sage: F = ShuffleAlgebra(ZZ, 'xyz').dual_pbw_basis()703sage: G = ShuffleAlgebra(QQ, 'xyz').dual_pbw_basis()704sage: H = ShuffleAlgebra(ZZ, 'y').dual_pbw_basis()705sage: F._coerce_map_from_(G)706False707sage: G._coerce_map_from_(F)708True709sage: F._coerce_map_from_(H)710False711sage: F._coerce_map_from_(QQ)712False713sage: G._coerce_map_from_(QQ)714True715sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z'))716False717sage: F._coerce_map_from_(F._alg)718True719"""720return self._alg.has_coerce_map_from(R)721722def one_basis(self):723"""724Return the indexing element of the basis element `1`.725726EXAMPLES::727728sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()729sage: S.one_basis()730word:731"""732W = self.basis().keys()733return W([])734735def algebra_generators(self):736"""737Return the algebra generators of ``self``.738739EXAMPLES::740741sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()742sage: S.algebra_generators()743(S[word: a], S[word: b])744"""745W = self.basis().keys()746return tuple(self.monomial(W(a)) for a in self._alphabet)747748gens = algebra_generators749750def gen(self, i):751"""752Return the ``i``-th generator of ``self``.753754EXAMPLES::755756sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()757sage: S.gen(0)758S[word: a]759sage: S.gen(1)760S[word: b]761"""762return self.algebra_generators()[i]763764def shuffle_algebra(self):765"""766Return the associated shuffle algebra of ``self``.767768EXAMPLES::769770sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()771sage: S.shuffle_algebra()772Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field773"""774return self._alg775776def product(self, u, v):777"""778Return the product of two elements ``u`` and ``v``.779780EXAMPLES::781782sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()783sage: a,b = S.gens()784sage: S.product(a, b)785S[word: ba]786sage: S.product(b, a)787S[word: ba]788sage: S.product(b^2*a, a*b*a)78936*S[word: bbbaaa]790791TESTS:792793Check that multiplication agrees with the multiplication in the794shuffle algebra::795796sage: A = ShuffleAlgebra(QQ, 'ab')797sage: S = A.dual_pbw_basis()798sage: a,b = S.gens()799sage: A(a*b)800B[word: ab] + B[word: ba]801sage: A(a*b*a)8022*B[word: aab] + 2*B[word: aba] + 2*B[word: baa]803sage: S(A(a)*A(b)*A(a)) == a*b*a804True805"""806return self(self.expansion(u) * self.expansion(v))807808@lazy_attribute809def expansion(self):810"""811Return the morphism corresponding to the expansion into words of812the shuffle algebra.813814EXAMPLES::815816sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()817sage: f = S('ab') + S('aba')818sage: S.expansion(f)8192*B[word: aab] + B[word: ab] + B[word: aba]820"""821return self.module_morphism(self.expansion_on_basis, codomain=self._alg)822823@cached_method824def expansion_on_basis(self, w):825r"""826Return the expansion of `S_w` in words of the shuffle algebra.827828INPUT:829830- ``w`` -- a word831832EXAMPLES::833834sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()835sage: S.expansion_on_basis(Word())836B[word: ]837sage: S.expansion_on_basis(Word()).parent()838Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field839sage: S.expansion_on_basis(Word('abba'))8402*B[word: aabb] + B[word: abab] + B[word: abba]841sage: S.expansion_on_basis(Word())842B[word: ]843sage: S.expansion_on_basis(Word('abab'))8442*B[word: aabb] + B[word: abab]845"""846from sage.functions.other import factorial847if len(w) == 0:848return self._alg.one()849if len(w) == 1:850return self._alg.monomial(w)851852if w.is_lyndon():853W = self.basis().keys()854letter = W(w[0])855expansion = self.expansion_on_basis(W(w[1:]))856return self._alg.sum_of_terms([(letter * i, c) for i,c in expansion])857858lf = w.lyndon_factorization()859powers = {}860for i in lf:861powers[i] = powers.get(i, 0) + 1862denom = prod(factorial(p) for p in powers.values())863result = self._alg.prod(self.expansion_on_basis(i) for i in lf)864return self._alg(result / denom)865866class Element(CombinatorialFreeModule.Element):867"""868An element in the dual PBW basis.869"""870def expand(self):871"""872Expand ``self`` in words of the shuffle algebra.873874EXAMPLES::875876sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()877sage: f = S('ab') + S('bab')878sage: f.expand()879B[word: ab] + 2*B[word: abb] + B[word: bab]880"""881return self.parent().expansion(self)882883884885