def __init__(self,intervals=None,alphabet=None): r""" TESTS:: sage: from sage.dynamics.interval_exchanges.reduced import ReducedPermutationIET sage: p = ReducedPermutationIET() sage: loads(dumps(p)) == p True sage: p = ReducedPermutationIET([['a','b'],['b','a']]) sage: loads(dumps(p)) == p True sage: from sage.dynamics.interval_exchanges.reduced import ReducedPermutationLI sage: p = ReducedPermutationLI() sage: loads(dumps(p)) == p True sage: p = ReducedPermutationLI([['a','a'],['b','b']]) sage: loads(dumps(p)) == p True """ self._hash = None if intervals is None: self._twin = [[],[]] self._alphabet = alphabet else: self._init_twin(intervals) if alphabet is None: self._init_alphabet(intervals) else: alphabet = Alphabet(alphabet) if alphabet.cardinality() < len(self): raise TypeError("The alphabet is too short") self._alphabet = alphabet
def __init__(self, R, names): r""" Initialize ``self``. EXAMPLES:: sage: F = ShuffleAlgebra(QQ, 'xyz'); F Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field sage: TestSuite(F).run() TESTS:: sage: ShuffleAlgebra(24, 'toto') Traceback (most recent call last): ... TypeError: argument R must be a ring """ if R not in Rings(): raise TypeError("argument R must be a ring") self._alphabet = Alphabet(names) self.__ngens = self._alphabet.cardinality() CombinatorialFreeModule.__init__(self, R, Words(names), latex_prefix="", category=(AlgebrasWithBasis(R), CommutativeAlgebras(R)))
def __init__(self, R, names=None): """ Initialize ``self``. TESTS:: sage: A = algebras.FreePreLie(QQ, '@'); A Free PreLie algebra on one generator ['@'] over Rational Field sage: TestSuite(A).run() sage: A = algebras.FreePreLie(QQ, None); A Free PreLie algebra on one generator ['o'] over Rational Field sage: F = algebras.FreePreLie(QQ, 'xy') sage: TestSuite(F).run() # long time """ if names is None: Trees = RootedTrees() key = RootedTree.sort_key self._alphabet = Alphabet(['o']) else: Trees = LabelledRootedTrees() key = LabelledRootedTree.sort_key self._alphabet = names # Here one would need LabelledRootedTrees(names) # so that one can restrict the labels to some fixed set cat = MagmaticAlgebras(R).WithBasis().Graded() & LieAlgebras( R).WithBasis().Graded() CombinatorialFreeModule.__init__(self, R, Trees, latex_prefix="", sorting_key=key, category=cat)
def __init__(self, R, n, names): """ Initialize ``self``. EXAMPLES:: sage: Z.<x,y,z> = algebras.FreeZinbiel(QQ) sage: TestSuite(Z).run() TESTS:: sage: Z.<x,y,z> = algebras.FreeZinbiel(5) Traceback (most recent call last): ... TypeError: argument R must be a ring """ if R not in Rings: raise TypeError("argument R must be a ring") indices = Words(Alphabet(n, names=names)) cat = MagmaticAlgebras(R).WithBasis().Graded() self._n = n CombinatorialFreeModule.__init__(self, R, indices, prefix='Z', category=cat) self._assign_names(names)
def __init__(self, R, names=None): """ Initialize ``self``. TESTS:: sage: A = algebras.FreePreLie(QQ, '@'); A Free PreLie algebra on one generator ['@'] over Rational Field sage: TestSuite(A).run() sage: A = algebras.FreePreLie(QQ, None); A Free PreLie algebra on one generator ['o'] over Rational Field sage: F = algebras.FreePreLie(QQ, 'xy') sage: TestSuite(F).run() # long time """ if names is None: Trees = RootedTrees() key = RootedTree.sort_key self._alphabet = Alphabet(['o']) else: Trees = LabelledRootedTrees() key = LabelledRootedTree.sort_key self._alphabet = names # Here one would need LabelledRootedTrees(names) # so that one can restrict the labels to some fixed set cat = MagmaticAlgebras(R).WithBasis().Graded() & LieAlgebras(R).WithBasis().Graded() CombinatorialFreeModule.__init__(self, R, Trees, latex_prefix="", sorting_key=key, category=cat)
def __classcall_private__(cls, R, names): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: F1 = ShuffleAlgebra(QQ, 'xyz') sage: F2 = ShuffleAlgebra(QQ, ['x','y','z']) sage: F3 = ShuffleAlgebra(QQ, Alphabet('xyz')) sage: F1 is F2 and F1 is F3 True """ return super(ShuffleAlgebra, cls).__classcall__(cls, R, Alphabet(names))
def __classcall_private__(cls, R, names): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: from sage.algebras.shuffle_algebra import DualPBWBasis sage: D1 = DualPBWBasis(QQ, 'ab') sage: D2 = DualPBWBasis(QQ, Alphabet('ab')) sage: D1 is D2 True """ return super(DualPBWBasis, cls).__classcall__(cls, R, Alphabet(names))
def __init__(self, R, n, names, prefix): """ Initialize ``self``. EXAMPLES:: sage: Z.<x,y,z> = algebras.FreeZinbiel(QQ) sage: TestSuite(Z).run() sage: Z = algebras.FreeZinbiel(QQ, ZZ) sage: G = Z.algebra_generators() sage: TestSuite(Z).run(elements=[Z.an_element(), G[1], G[1]*G[2]*G[0]]) TESTS:: sage: Z.<x,y,z> = algebras.FreeZinbiel(5) Traceback (most recent call last): ... TypeError: argument R must be a ring sage: algebras.FreeZinbiel(QQ, ['x', 'y'], prefix='f') Free Zinbiel algebra on generators (f[x], f[y]) over Rational Field """ if R not in Rings(): raise TypeError("argument R must be a ring") if names is None: indices = Words(Alphabet(n), infinite=False) self._n = None else: indices = Words(Alphabet(n, names=names), infinite=False) self._n = n cat = MagmaticAlgebras(R).WithBasis().Graded() CombinatorialFreeModule.__init__(self, R, indices, prefix=prefix, category=cat) if self._n is not None: self._assign_names(names)
def __classcall_private__(cls, R, names): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: F1 = algebras.FreePreLie(QQ, 'xyz') sage: F2 = algebras.FreePreLie(QQ, ['x','y','z']) sage: F3 = algebras.FreePreLie(QQ, Alphabet('xyz')) sage: F1 is F2 and F1 is F3 True """ if R not in Rings(): raise TypeError("argument R must be a ring") return super(FreePreLieAlgebra, cls).__classcall__(cls, R, Alphabet(names))
def alphabet(self): r""" Return alphabet of ``self``. OUTPUT: A set of free monoid generators. EXAMPLES:: sage: from sage.monoids.trace_monoid import TraceMonoid sage: I = (('a','d'), ('d','a'), ('b','c'), ('c','b')) sage: M.<a,b,c,d> = TraceMonoid(I=I) sage: x = b*a*d*a*c*b sage: x.alphabet() {b, a, d, c} """ return Alphabet([g for g, _ in self.value])
def __classcall_private__(cls, R, names=None): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: F1 = algebras.FreeDendriform(QQ, 'xyz') sage: F2 = algebras.FreeDendriform(QQ, ['x','y','z']) sage: F3 = algebras.FreeDendriform(QQ, Alphabet('xyz')) sage: F1 is F2 and F1 is F3 True """ if names is not None: if ',' in names: names = [u for u in names if u != ','] names = Alphabet(names) if R not in Rings(): raise TypeError("argument R must be a ring") return super(FreeDendriformAlgebra, cls).__classcall__(cls, R, names)
def merge(self, other): """ Merge ``self`` with another construction functor, or return None. EXAMPLES:: sage: F = sage.combinat.free_prelie_algebra.PreLieFunctor(['x','y']) sage: G = sage.combinat.free_prelie_algebra.PreLieFunctor(['t']) sage: F.merge(G) PreLie[x,y,t] sage: F.merge(F) PreLie[x,y] Now some actual use cases:: sage: R = algebras.FreePreLie(ZZ, 'xyz') sage: x,y,z = R.gens() sage: 1/2 * x 1/2*B[x[]] sage: parent(1/2 * x) Free PreLie algebra on 3 generators ['x', 'y', 'z'] over Rational Field sage: S = algebras.FreePreLie(QQ, 'zt') sage: z,t = S.gens() sage: x + t B[t[]] + B[x[]] sage: parent(x + t) Free PreLie algebra on 4 generators ['z', 't', 'x', 'y'] over Rational Field """ if isinstance(other, PreLieFunctor): if self.vars == other.vars: return self ret = list(self.vars) cur_vars = set(ret) for v in other.vars: if v not in cur_vars: ret.append(v) return PreLieFunctor(Alphabet(ret)) else: return None
def __init__(self, intervals=None, flips=None, alphabet=None): r""" TESTS:: sage: p = iet.Permutation('a b','b a',reduced=True,flips='a') sage: p == loads(dumps(p)) True sage: p = iet.Permutation('a b','b a',reduced=True,flips='b') sage: p == loads(dumps(p)) True sage: p = iet.Permutation('a b','b a',reduced=True,flips='ab') sage: p == loads(dumps(p)) True sage: p = iet.GeneralizedPermutation('a a','b b',reduced=True,flips='a') sage: p == loads(dumps(p)) True sage: p = iet.GeneralizedPermutation('a a','b b',reduced=True,flips='b') sage: p == loads(dumps(p)) True sage: p = iet.GeneralizedPermutation('a a','b b',reduced=True,flips='ab') sage: p == loads(dumps(p)) True """ self._hash = None if intervals is None: self._twin = [[],[]] self._flips = [[],[]] self._alphabet = None else: if flips is None: flips = [] if alphabet is None : self._init_alphabet(intervals) else : self._alphabet = Alphabet(alphabet) self._init_twin(intervals) self._init_flips(intervals, flips) self._hash = None
def __init__(self, R, names): r""" Initialize ``self``. EXAMPLES:: sage: F = ShuffleAlgebra(QQ, 'xyz'); F Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field sage: TestSuite(F).run() TESTS:: sage: ShuffleAlgebra(24, 'toto') Traceback (most recent call last): ... TypeError: argument R must be a ring """ if R not in Rings(): raise TypeError("argument R must be a ring") self._alphabet = Alphabet(names) self.__ngens = self._alphabet.cardinality() CombinatorialFreeModule.__init__(self, R, Words(names), latex_prefix = "", category = (AlgebrasWithBasis(R), CommutativeAlgebras(R)))
class FreePreLieAlgebra(CombinatorialFreeModule): r""" The free pre-Lie algebra. Pre-Lie algebras are non-associative algebras, where the product `*` satisfies .. MATH:: (x * y) * z - x * (y * z) = (x * z) * y - x * (z * y). We use here the convention where the associator .. MATH:: (x, y, z) := (x * y) * z - x * (y * z) is symmetric in its two rightmost arguments. This is sometimes called a right pre-Lie algebra. They have appeared in numerical analysis and deformation theory. The free Pre-Lie algebra on a given set `E` has an explicit description using rooted trees, just as the free associative algebra can be described using words. The underlying vector space has a basis indexed by finite rooted trees endowed with a map from their vertices to `E`. In this basis, the product of two (decorated) rooted trees `S * T` is the sum over vertices of `S` of the rooted tree obtained by adding one edge from the root of `T` to the given vertex of `S`. The root of these trees is taken to be the root of `S`. The free pre-Lie algebra can also be considered as the free algebra over the PreLie operad. .. WARNING:: The usual binary operator ``*`` can be used for the pre-Lie product. Beware that it but must be parenthesized properly, as the pre-Lie product is not associative. By default, a multiple product will be taken with left parentheses. EXAMPLES:: sage: F = algebras.FreePreLie(ZZ, 'xyz') sage: x,y,z = F.gens() sage: (x * y) * z B[x[y[z[]]]] + B[x[y[], z[]]] sage: (x * y) * z - x * (y * z) == (x * z) * y - x * (z * y) True The free pre-Lie algebra is non-associative:: sage: x * (y * z) == (x * y) * z False The default product is with left parentheses:: sage: x * y * z == (x * y) * z True sage: x * y * z * x == ((x * y) * z) * x True The NAP product as defined in [Liv2006]_ is also implemented on the same vector space:: sage: N = F.nap_product sage: N(x*y,z*z) B[x[y[], z[z[]]]] When ``None`` is given as input, unlabelled trees are used instead:: sage: F1 = algebras.FreePreLie(QQ, None) sage: w = F1.gen(0); w B[[]] sage: w * w * w * w B[[[[[]]]]] + B[[[[], []]]] + 3*B[[[], [[]]]] + B[[[], [], []]] However, it is equally possible to use labelled trees instead:: sage: F1 = algebras.FreePreLie(QQ, 'q') sage: w = F1.gen(0); w B[q[]] sage: w * w * w * w B[q[q[q[q[]]]]] + B[q[q[q[], q[]]]] + 3*B[q[q[], q[q[]]]] + B[q[q[], q[], q[]]] The set `E` can be infinite:: sage: F = algebras.FreePreLie(QQ, ZZ) sage: w = F.gen(1); w B[1[]] sage: x = F.gen(2); x B[-1[]] sage: y = F.gen(3); y B[2[]] sage: w*x B[1[-1[]]] sage: (w*x)*y B[1[-1[2[]]]] + B[1[-1[], 2[]]] sage: w*(x*y) B[1[-1[2[]]]] .. NOTE:: Variables names can be ``None``, a list of strings, a string or an integer. When ``None`` is given, unlabelled rooted trees are used. When a single string is given, each letter is taken as a variable. See :func:`sage.combinat.words.alphabet.build_alphabet`. .. WARNING:: Beware that the underlying combinatorial free module is based either on ``RootedTrees`` or on ``LabelledRootedTrees``, with no restriction on the labellings. This means that all code calling the :meth:`basis` method would not give meaningful results, since :meth:`basis` returns many "chaff" elements that do not belong to the algebra. REFERENCES: - [ChLi]_ - [Liv2006]_ """ @staticmethod def __classcall_private__(cls, R, names=None): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: F1 = algebras.FreePreLie(QQ, 'xyz') sage: F2 = algebras.FreePreLie(QQ, 'x,y,z') sage: F3 = algebras.FreePreLie(QQ, ['x','y','z']) sage: F4 = algebras.FreePreLie(QQ, Alphabet('xyz')) sage: F1 is F2 and F1 is F3 and F1 is F4 True """ if names is not None: if ',' in names: names = [u for u in names if u != ','] names = Alphabet(names) if R not in Rings(): raise TypeError("argument R must be a ring") return super(FreePreLieAlgebra, cls).__classcall__(cls, R, names) def __init__(self, R, names=None): """ Initialize ``self``. TESTS:: sage: A = algebras.FreePreLie(QQ, '@'); A Free PreLie algebra on one generator ['@'] over Rational Field sage: TestSuite(A).run() sage: A = algebras.FreePreLie(QQ, None); A Free PreLie algebra on one generator ['o'] over Rational Field sage: F = algebras.FreePreLie(QQ, 'xy') sage: TestSuite(F).run() # long time """ if names is None: Trees = RootedTrees() key = RootedTree.sort_key self._alphabet = Alphabet(['o']) else: Trees = LabelledRootedTrees() key = LabelledRootedTree.sort_key self._alphabet = names # Here one would need LabelledRootedTrees(names) # so that one can restrict the labels to some fixed set cat = MagmaticAlgebras(R).WithBasis().Graded() & LieAlgebras( R).WithBasis().Graded() CombinatorialFreeModule.__init__(self, R, Trees, latex_prefix="", sorting_key=key, category=cat) def variable_names(self): r""" Return the names of the variables. EXAMPLES:: sage: R = algebras.FreePreLie(QQ, 'xy') sage: R.variable_names() {'x', 'y'} sage: R = algebras.FreePreLie(QQ, None) sage: R.variable_names() {'o'} """ return self._alphabet def _repr_(self): """ Return the string representation of ``self``. EXAMPLES:: sage: algebras.FreePreLie(QQ, '@') # indirect doctest Free PreLie algebra on one generator ['@'] over Rational Field """ n = self.algebra_generators().cardinality() if n == 1: gen = "one generator" else: gen = "{} generators".format(n) s = "Free PreLie algebra on {} {} over {}" try: return s.format(gen, self._alphabet.list(), self.base_ring()) except NotImplementedError: return s.format(gen, self._alphabet, self.base_ring()) def gen(self, i): r""" Return the ``i``-th generator of the algebra. INPUT: - ``i`` -- an integer EXAMPLES:: sage: F = algebras.FreePreLie(ZZ, 'xyz') sage: F.gen(0) B[x[]] sage: F.gen(4) Traceback (most recent call last): ... IndexError: argument i (= 4) must be between 0 and 2 """ G = self.algebra_generators() n = G.cardinality() if i < 0 or not i < n: m = "argument i (= {}) must be between 0 and {}".format(i, n - 1) raise IndexError(m) return G[G.keys().unrank(i)] @cached_method def algebra_generators(self): r""" Return the generators of this algebra. These are the rooted trees with just one vertex. EXAMPLES:: sage: A = algebras.FreePreLie(ZZ, 'fgh'); A Free PreLie algebra on 3 generators ['f', 'g', 'h'] over Integer Ring sage: list(A.algebra_generators()) [B[f[]], B[g[]], B[h[]]] sage: A = algebras.FreePreLie(QQ, ['x1','x2']) sage: list(A.algebra_generators()) [B[x1[]], B[x2[]]] """ Trees = self.basis().keys() return Family(self._alphabet, lambda a: self.monomial(Trees([], a))) def change_ring(self, R): """ Return the free pre-Lie algebra in the same variables over `R`. INPUT: - `R` -- a ring EXAMPLES:: sage: A = algebras.FreePreLie(ZZ, 'fgh') sage: A.change_ring(QQ) Free PreLie algebra on 3 generators ['f', 'g', 'h'] over Rational Field """ return FreePreLieAlgebra(R, names=self.variable_names()) def gens(self): """ Return the generators of ``self`` (as an algebra). EXAMPLES:: sage: A = algebras.FreePreLie(ZZ, 'fgh') sage: A.gens() (B[f[]], B[g[]], B[h[]]) """ return tuple(self.algebra_generators()) def degree_on_basis(self, t): """ Return the degree of a rooted tree in the free Pre-Lie algebra. This is the number of vertices. EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: A.degree_on_basis(RT([RT([])])) 2 """ return t.node_number() @cached_method def an_element(self): """ Return an element of ``self``. EXAMPLES:: sage: A = algebras.FreePreLie(QQ, 'xy') sage: A.an_element() B[x[x[x[x[]]]]] + B[x[x[], x[x[]]]] """ o = self.gen(0) return (o * o) * (o * o) def some_elements(self): """ Return some elements of the free pre-Lie algebra. EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: A.some_elements() [B[[]], B[[[]]], B[[[[[]]]]] + B[[[], [[]]]], B[[[[]]]] + B[[[], []]], B[[[]]]] With several generators:: sage: A = algebras.FreePreLie(QQ, 'xy') sage: A.some_elements() [B[x[]], B[x[x[]]], B[x[x[x[x[]]]]] + B[x[x[], x[x[]]]], B[x[x[x[]]]] + B[x[x[], x[]]], B[x[x[y[]]]] + B[x[x[], y[]]]] """ o = self.gen(0) x = o * o y = o G = self.algebra_generators() # Take only the first 3 generators, otherwise the final element is too big if G.cardinality() < 3: for w in G: y = y * w else: K = G.keys() for i in range(3): y = y * G[K.unrank(i)] return [o, x, x * x, x * o, y] def product_on_basis(self, x, y): """ Return the pre-Lie product of two trees. This is the sum over all graftings of the root of `y` over a vertex of `x`. The root of the resulting trees is the root of `x`. .. SEEALSO:: :meth:`pre_Lie_product` EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: x = RT([RT([])]) sage: A.product_on_basis(x, x) B[[[[[]]]]] + B[[[], [[]]]] """ return self.sum(self.basis()[u] for u in x.graft_list(y)) pre_Lie_product_on_basis = product_on_basis @lazy_attribute def pre_Lie_product(self): """ Return the pre-Lie product. .. SEEALSO:: :meth:`pre_Lie_product_on_basis` EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: x = A(RT([RT([])])) sage: A.pre_Lie_product(x, x) B[[[[[]]]]] + B[[[], [[]]]] """ plb = self.pre_Lie_product_on_basis return self._module_morphism(self._module_morphism(plb, position=0, codomain=self), position=1) def bracket_on_basis(self, x, y): r""" Return the Lie bracket of two trees. This is the commutator `[x, y] = x * y - y * x` of the pre-Lie product. .. SEEALSO:: :meth:`pre_Lie_product_on_basis` EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: x = RT([RT([])]) sage: y = RT([x]) sage: A.bracket_on_basis(x, y) -B[[[[], [[]]]]] + B[[[], [[[]]]]] - B[[[[]], [[]]]] """ return self.product_on_basis(x, y) - self.product_on_basis(y, x) def nap_product_on_basis(self, x, y): """ Return the NAP product of two trees. This is the grafting of the root of `y` over the root of `x`. The root of the resulting tree is the root of `x`. .. SEEALSO:: :meth:`nap_product` EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: x = RT([RT([])]) sage: A.nap_product_on_basis(x, x) B[[[], [[]]]] """ return self.basis()[x.graft_on_root(y)] @lazy_attribute def nap_product(self): """ Return the NAP product. .. SEEALSO:: :meth:`nap_product_on_basis` EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: x = A(RT([RT([])])) sage: A.nap_product(x, x) B[[[], [[]]]] """ npb = self.nap_product_on_basis return self._module_morphism(self._module_morphism(npb, position=0, codomain=self), position=1) def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. EXAMPLES:: sage: R = algebras.FreePreLie(QQ, 'xy') sage: x, y = R.gens() sage: R(x) B[x[]] sage: R(x+4*y) B[x[]] + 4*B[y[]] sage: Trees = R.basis().keys() sage: R(Trees([],'x')) B[x[]] sage: D = algebras.FreePreLie(ZZ, 'xy') sage: X, Y = D.gens() sage: R(X-Y).parent() Free PreLie algebra on 2 generators ['x', 'y'] over Rational Field TESTS:: sage: R.<x,y> = algebras.FreePreLie(QQ) sage: S.<z> = algebras.FreePreLie(GF(3)) sage: R(z) Traceback (most recent call last): ... TypeError: not able to convert this to this algebra """ if (isinstance(x, (RootedTree, LabelledRootedTree)) and x in self.basis().keys()): return self.monomial(x) try: P = x.parent() if isinstance(P, FreePreLieAlgebra): if P is self: return x if self._coerce_map_from_(P): return self.element_class(self, x.monomial_coefficients()) except AttributeError: raise TypeError('not able to convert this to this algebra') else: raise TypeError('not able to convert this to this algebra') # Ok, not a pre-Lie algebra element (or should not be viewed as one). def _coerce_map_from_(self, R): r""" Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are - free pre-Lie algebras whose set `E` of labels is a subset of the corresponding self of ``set`, and whose base ring has a coercion map into ``self.base_ring()`` EXAMPLES:: sage: F = algebras.FreePreLie(GF(7), 'xyz'); F Free PreLie algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 Elements of the free pre-Lie algebra canonically coerce in:: sage: x, y, z = F.gens() sage: F.coerce(x+y) == x+y True The free pre-Lie algebra over `\ZZ` on `x, y, z` coerces in, since `\ZZ` coerces to `\GF{7}`:: sage: G = algebras.FreePreLie(ZZ, 'xyz') sage: Gx,Gy,Gz = G.gens() sage: z = F.coerce(Gx+Gy); z B[x[]] + B[y[]] sage: z.parent() is F True However, `\GF{7}` does not coerce to `\ZZ`, so the free pre-Lie algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: sage: G.coerce(y) Traceback (most recent call last): ... TypeError: no canonical coercion from Free PreLie algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 to Free PreLie algebra on 3 generators ['x', 'y', 'z'] over Integer Ring TESTS:: sage: F = algebras.FreePreLie(ZZ, 'xyz') sage: G = algebras.FreePreLie(QQ, 'xyz') sage: H = algebras.FreePreLie(ZZ, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) True sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) False sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False """ # free prelie algebras in a subset of variables # over any base that coerces in: if isinstance(R, FreePreLieAlgebra): if all(x in self.variable_names() for x in R.variable_names()): if self.base_ring().has_coerce_map_from(R.base_ring()): return True return False def construction(self): """ Return a pair ``(F, R)``, where ``F`` is a :class:`PreLieFunctor` and `R` is a ring, such that ``F(R)`` returns ``self``. EXAMPLES:: sage: P = algebras.FreePreLie(ZZ, 'x,y') sage: x,y = P.gens() sage: F, R = P.construction() sage: F PreLie[x,y] sage: R Integer Ring sage: F(ZZ) is P True sage: F(QQ) Free PreLie algebra on 2 generators ['x', 'y'] over Rational Field """ return PreLieFunctor(self.variable_names()), self.base_ring()
def Permutations_iterator(nintervals=None, irreducible=True, reduced=False, alphabet=None): r""" Returns an iterator over permutations. This iterator allows you to iterate over permutations with given constraints. If you want to iterate over permutations coming from a given stratum you have to use the module :mod:`~sage.dynamics.flat_surfaces.strata` and generate Rauzy diagrams from connected components. INPUT: - ``nintervals`` - non negative integer - ``irreducible`` - boolean (default: True) - ``reduced`` - boolean (default: False) - ``alphabet`` - alphabet (default: None) OUTPUT: iterator -- an iterator over permutations EXAMPLES: Generates all reduced permutations with given number of intervals:: sage: P = iet.Permutations_iterator(nintervals=2,alphabet="ab",reduced=True) doctest:warning ... DeprecationWarning: iet_Permutations_iterator is deprecated and will be removed from Sage. You are advised to install the surface_dynamics package via: sage -pip install surface_dynamics If you do not have write access to the Sage installation you can alternatively do sage -pip install surface_dynamics --user The package surface_dynamics subsumes all flat surface related computation that are currently available in Sage. See more information at http://www.labri.fr/perso/vdelecro/surface-dynamics/latest/ See http://trac.sagemath.org/20695 for details. sage: for p in P: ....: print(p) ....: print("* *") a b b a * * sage: P = iet.Permutations_iterator(nintervals=3,alphabet="abc",reduced=True) sage: for p in P: ....: print(p) ....: print("* * *") a b c b c a * * * a b c c a b * * * a b c c b a * * * TESTS:: sage: P = iet.Permutations_iterator(nintervals=None, alphabet=None) Traceback (most recent call last): ... ValueError: You must specify an alphabet or a length sage: P = iet.Permutations_iterator(nintervals=None, alphabet=ZZ) Traceback (most recent call last): ... ValueError: You must specify a length with infinite alphabet """ from sage.dynamics.surface_dynamics_deprecation import surface_dynamics_deprecation surface_dynamics_deprecation("iet_Permutations_iterator") from .labelled import LabelledPermutationsIET_iterator from .reduced import ReducedPermutationsIET_iterator from sage.combinat.words.alphabet import Alphabet from sage.rings.infinity import Infinity if nintervals is None: if alphabet is None: raise ValueError("You must specify an alphabet or a length") else: alphabet = Alphabet(alphabet) if alphabet.cardinality() is Infinity: raise ValueError("You must specify a length with infinite alphabet") nintervals = alphabet.cardinality() elif alphabet is None: alphabet = list(range(1, nintervals + 1)) if reduced: return ReducedPermutationsIET_iterator(nintervals, irreducible=irreducible, alphabet=alphabet) else: return LabelledPermutationsIET_iterator(nintervals, irreducible=irreducible, alphabet=alphabet)
def Permutations_iterator(nintervals=None, irreducible=True, reduced=False, alphabet=None): r""" Returns an iterator over permutations. This iterator allows you to iterate over permutations with given constraints. If you want to iterate over permutations coming from a given stratum you have to use the module :mod:`~sage.dynamics.flat_surfaces.strata` and generate Rauzy diagrams from connected components. INPUT: - ``nintervals`` - non negative integer - ``irreducible`` - boolean (default: True) - ``reduced`` - boolean (default: False) - ``alphabet`` - alphabet (default: None) OUTPUT: iterator -- an iterator over permutations EXAMPLES: Generates all reduced permutations with given number of intervals:: sage: P = iet.Permutations_iterator(nintervals=2,alphabet="ab",reduced=True) doctest:warning ... DeprecationWarning: iet_Permutations_iterator is deprecated and will be removed from Sage. You are advised to install the surface_dynamics package via: sage -pip install surface_dynamics If you do not have write access to the Sage installation you can alternatively do sage -pip install surface_dynamics --user The package surface_dynamics subsumes all flat surface related computation that are currently available in Sage. See more information at http://www.labri.fr/perso/vdelecro/surface-dynamics/latest/ See http://trac.sagemath.org/20695 for details. sage: for p in P: ....: print(p) ....: print("* *") a b b a * * sage: P = iet.Permutations_iterator(nintervals=3,alphabet="abc",reduced=True) sage: for p in P: ....: print(p) ....: print("* * *") a b c b c a * * * a b c c a b * * * a b c c b a * * * TESTS:: sage: P = iet.Permutations_iterator(nintervals=None, alphabet=None) Traceback (most recent call last): ... ValueError: You must specify an alphabet or a length sage: P = iet.Permutations_iterator(nintervals=None, alphabet=ZZ) Traceback (most recent call last): ... ValueError: You must specify a length with infinite alphabet """ from sage.dynamics.surface_dynamics_deprecation import surface_dynamics_deprecation surface_dynamics_deprecation("iet_Permutations_iterator") from .labelled import LabelledPermutationsIET_iterator from .reduced import ReducedPermutationsIET_iterator from sage.combinat.words.alphabet import Alphabet from sage.rings.infinity import Infinity if nintervals is None: if alphabet is None: raise ValueError("You must specify an alphabet or a length") else: alphabet = Alphabet(alphabet) if alphabet.cardinality() is Infinity: raise ValueError( "You must specify a length with infinite alphabet") nintervals = alphabet.cardinality() elif alphabet is None: alphabet = list(range(1, nintervals + 1)) if reduced: return ReducedPermutationsIET_iterator(nintervals, irreducible=irreducible, alphabet=alphabet) else: return LabelledPermutationsIET_iterator(nintervals, irreducible=irreducible, alphabet=alphabet)
def Permutations_iterator(nintervals=None, irreducible=True, reduced=False, alphabet=None): r""" Returns an iterator over permutations. This iterator allows you to iterate over permutations with given constraints. If you want to iterate over permutations coming from a given stratum you have to use the module :mod:`~sage.combinat.iet.strata` and generate Rauzy diagrams from connected components. INPUT: - ``nintervals`` - non negative integer - ``irreducible`` - boolean (default: True) - ``reduced`` - boolean (default: False) - ``alphabet`` - alphabet (default: None) OUTPUT: iterator -- an iterator over permutations EXAMPLES:: sage: from surface_dynamics import * Generates all reduced permutations with given number of intervals:: sage: P = iet.Permutations_iterator(nintervals=2,alphabet="ab",reduced=True) sage: for p in P: print("%s\n* *" % p) a b b a * * sage: P = iet.Permutations_iterator(nintervals=3,alphabet="abc",reduced=True) sage: for p in P: print("%s\n* * *" % p) a b c b c a * * * a b c c a b * * * a b c c b a * * * """ from .labelled import LabelledPermutationsIET_iterator from .reduced import ReducedPermutationsIET_iterator if nintervals is None: if alphabet is None: raise ValueError("You must specify an alphabet or a length") else: alphabet = Alphabet(alphabet) if alphabet.cardinality() is Infinity: raise ValueError( "You must specify a length with infinite alphabet") nintervals = alphabet.cardinality() elif alphabet is None: alphabet = range(1, nintervals + 1) if reduced: return ReducedPermutationsIET_iterator(nintervals, irreducible=irreducible, alphabet=alphabet) else: return LabelledPermutationsIET_iterator(nintervals, irreducible=irreducible, alphabet=alphabet)
class ShuffleAlgebra(CombinatorialFreeModule): r""" The shuffle algebra on some generators over a base ring. Shuffle algebras are commutative and associative algebras, with a basis indexed by words. The product of two words `w_1 \cdot w_2` is given by the sum over the shuffle product of `w_1` and `w_2`. .. SEEALSO:: For more on shuffle products, see :mod:`~sage.combinat.words.shuffle_product` and :meth:`~sage.combinat.words.finite_word.FiniteWord_class.shuffle()`. REFERENCES: - :wikipedia:`Shuffle algebra` INPUT: - ``R`` -- ring - ``names`` -- generator names (string) EXAMPLES:: sage: F = ShuffleAlgebra(QQ, 'xyz'); F Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field sage: mul(F.gens()) B[word: xyz] + B[word: xzy] + B[word: yxz] + B[word: yzx] + B[word: zxy] + B[word: zyx] sage: mul([ F.gen(i) for i in range(2) ]) + mul([ F.gen(i+1) for i in range(2) ]) B[word: xy] + B[word: yx] + B[word: yz] + B[word: zy] sage: S = ShuffleAlgebra(ZZ, 'abcabc'); S Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring sage: S.base_ring() Integer Ring sage: G = ShuffleAlgebra(S, 'mn'); G Shuffle Algebra on 2 generators ['m', 'n'] over Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring sage: G.base_ring() Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring Shuffle algebras commute with their base ring:: sage: K = ShuffleAlgebra(QQ,'ab') sage: a,b = K.gens() sage: K.is_commutative() True sage: L = ShuffleAlgebra(K,'cd') sage: c,d = L.gens() sage: L.is_commutative() True sage: s = a*b^2 * c^3; s (12*B[word:abb]+12*B[word:bab]+12*B[word:bba])*B[word: ccc] sage: parent(s) Shuffle Algebra on 2 generators ['c', 'd'] over Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field sage: c^3 * a * b^2 (12*B[word:abb]+12*B[word:bab]+12*B[word:bba])*B[word: ccc] Shuffle algebras are commutative:: sage: c^3 * b * a * b == c * a * c * b^2 * c True We can also manipulate elements in the basis and coerce elements from our base field:: sage: F = ShuffleAlgebra(QQ, 'abc') sage: B = F.basis() sage: B[Word('bb')] * B[Word('ca')] B[word: bbca] + B[word: bcab] + B[word: bcba] + B[word: cabb] + B[word: cbab] + B[word: cbba] sage: 1 - B[Word('bb')] * B[Word('ca')] / 2 B[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] """ def __init__(self, R, names): r""" Initialize ``self``. EXAMPLES:: sage: F = ShuffleAlgebra(QQ, 'xyz'); F Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field sage: TestSuite(F).run() TESTS:: sage: ShuffleAlgebra(24, 'toto') Traceback (most recent call last): ... TypeError: argument R must be a ring """ if R not in Rings(): raise TypeError("argument R must be a ring") self._alphabet = Alphabet(names) self.__ngens = self._alphabet.cardinality() CombinatorialFreeModule.__init__(self, R, Words(names), latex_prefix="", category=(AlgebrasWithBasis(R), CommutativeAlgebras(R))) def variable_names(self): r""" Return the names of the variables. EXAMPLES:: sage: R = ShuffleAlgebra(QQ,'xy') sage: R.variable_names() {'x', 'y'} """ return self._alphabet def is_commutative(self): r""" Return ``True`` as the shuffle algebra is commutative. EXAMPLES:: sage: R = ShuffleAlgebra(QQ,'x') sage: R.is_commutative() True sage: R = ShuffleAlgebra(QQ,'xy') sage: R.is_commutative() True """ return True def _repr_(self): r""" Text representation of this shuffle algebra. EXAMPLES:: sage: F = ShuffleAlgebra(QQ,'xyz') sage: F # indirect doctest Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field sage: ShuffleAlgebra(ZZ,'a') Shuffle Algebra on one generator ['a'] over Integer Ring """ if self.__ngens == 1: gen = "one generator" else: gen = "%s generators" % self.__ngens return "Shuffle Algebra on " + gen + " %s over %s" % ( self._alphabet.list(), self.base_ring()) @cached_method def one_basis(self): r""" Return the empty word, which index of `1` of this algebra, as per :meth:`AlgebrasWithBasis.ParentMethods.one_basis`. EXAMPLES:: sage: A = ShuffleAlgebra(QQ,'a') sage: A.one_basis() word: sage: A.one() B[word: ] """ return self.basis().keys()([]) def product_on_basis(self, w1, w2): r""" Return the product of basis elements ``w1`` and ``w2``, as per :meth:`AlgebrasWithBasis.ParentMethods.product_on_basis()`. INPUT: - ``w1``, ``w2`` -- Basis elements EXAMPLES:: sage: A = ShuffleAlgebra(QQ,'abc') sage: W = A.basis().keys() sage: A.product_on_basis(W("acb"), W("cba")) B[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] sage: (a,b,c) = A.algebra_generators() sage: a * (1-b)^2 * c 2*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] """ return sum(self.basis()[u] for u in w1.shuffle(w2)) def gen(self, i): r""" The ``i``-th generator of the algebra. INPUT: - ``i`` -- an integer EXAMPLES:: sage: F = ShuffleAlgebra(ZZ,'xyz') sage: F.gen(0) B[word: x] sage: F.gen(4) Traceback (most recent call last): ... IndexError: argument i (= 4) must be between 0 and 2 """ n = self.__ngens if i < 0 or not i < n: raise IndexError("argument i (= %s) must be between 0 and %s" % (i, n - 1)) return self.algebra_generators()[i] @cached_method def algebra_generators(self): r""" Return the generators of this algebra. EXAMPLES:: sage: A = ShuffleAlgebra(ZZ,'fgh'); A Shuffle Algebra on 3 generators ['f', 'g', 'h'] over Integer Ring sage: A.algebra_generators() Family (B[word: f], B[word: g], B[word: h]) """ Words = self.basis().keys() return Family([self.monomial(Words(a)) for a in self._alphabet]) # FIXME: use this once the keys argument of FiniteFamily will be honoured # for the specifying the order of the elements in the family #return Family(self._alphabet, lambda a: self.term(self.basis().keys()(a))) gens = algebra_generators def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. EXAMPLES:: sage: R = ShuffleAlgebra(QQ,'xy') sage: x, y = R.gens() sage: R(3) # indirect doctest 3*B[word: ] sage: R(x) B[word: x] """ P = x.parent() if isinstance(P, ShuffleAlgebra): if P is self: return x if not (P is self.base_ring()): return self.element_class(self, x.monomial_coefficients()) # ok, not a shuffle algebra element (or should not be viewed as one). if isinstance(x, basestring): from sage.misc.sage_eval import sage_eval return sage_eval(x, locals=self.gens_dict()) R = self.base_ring() # coercion via base ring x = R(x) if x == 0: return self.element_class(self, {}) else: return self.from_base_ring_from_one_basis(x) def _coerce_impl(self, x): r""" Canonical coercion of ``x`` into ``self``. Here is what canonically coerces to ``self``: - this shuffle algebra, - anything that coerces to the base ring of this shuffle algebra, - any shuffle algebra on the same variables, whose base ring coerces to the base ring of this shuffle algebra. EXAMPLES:: sage: F = ShuffleAlgebra(GF(7), 'xyz'); F Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 Elements of the shuffle algebra canonically coerce in:: sage: x, y, z = F.gens() sage: F.coerce(x*y) # indirect doctest B[word: xy] + B[word: yx] Elements of the integers coerce in, since there is a coerce map from `\ZZ` to GF(7):: sage: F.coerce(1) # indirect doctest B[word: ] There is no coerce map from `\QQ` to `\GF{7}`:: sage: F.coerce(2/3) # indirect doctest Traceback (most recent call last): ... TypeError: no canonical coercion from Rational Field to Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 Elements of the base ring coerce in:: sage: F.coerce(GF(7)(5)) 5*B[word: ] The shuffle algebra over `\ZZ` on `x, y, z` coerces in, since `\ZZ` coerces to `\GF{7}`:: sage: G = ShuffleAlgebra(ZZ,'xyz') sage: Gx,Gy,Gz = G.gens() sage: z = F.coerce(Gx**2 * Gy);z 2*B[word: xxy] + 2*B[word: xyx] + 2*B[word: yxx] sage: z.parent() is F True However, `\GF{7}` does not coerce to `\ZZ`, so the shuffle algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: sage: G.coerce(x^3*y) Traceback (most recent call last): ... TypeError: no canonical coercion from Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 to Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Integer Ring """ try: R = x.parent() # shuffle algebras in the same variables over any base # that coerces in: if isinstance(R, ShuffleAlgebra): if R.variable_names() == self.variable_names(): if self.has_coerce_map_from(R.base_ring()): return self(x) else: raise TypeError( "no natural map between bases of shuffle algebras") except AttributeError: pass # any ring that coerces to the base ring of this shuffle algebra. return self._coerce_try(x, [self.base_ring()]) def _coerce_map_from_(self, R): r""" Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are - Shuffle Algebras in the same variables over a base with a coercion map into ``self.base_ring()``. - Anything with a coercion into ``self.base_ring()``. TESTS:: sage: F = ShuffleAlgebra(ZZ, 'xyz') sage: G = ShuffleAlgebra(QQ, 'xyz') sage: H = ShuffleAlgebra(ZZ, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) False sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) True sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False """ # shuffle algebras in the same variable over any base that coerces in: if isinstance(R, ShuffleAlgebra): if R.variable_names() == self.variable_names(): if self.base_ring().has_coerce_map_from(R.base_ring()): return True else: return False return self.base_ring().has_coerce_map_from(R)
class FreeDendriformAlgebra(CombinatorialFreeModule): r""" The free dendriform algebra. Dendriform algebras are associative algebras, where the associative product `*` is decomposed as a sum of two binary operations .. MATH:: x * y = x \succ y + x \prec y that satisfy the axioms: .. MATH:: (x \succ y) \prec z = x \succ (y \prec z), .. MATH:: (x \prec y) \prec z = x \prec (y * z). .. MATH:: (x * y) \succ z = x \succ (y \succ z). The free Dendriform algebra on a given set `E` has an explicit description using (planar) binary trees, just as the free associative algebra can be described using words. The underlying vector space has a basis indexed by finite binary trees endowed with a map from their vertices to `E`. In this basis, the associative product of two (decorated) binary trees `S * T` is the sum over all possible ways of identifying (glueing) the rightmost path in `S` and the leftmost path in `T`. The decomposition of the associative product as the sum of two binary operations `\succ` and `\prec` is made by separating the terms according to the origin of the root vertex. For `x \succ y`, one keeps the terms where the root vertex comes from `y`, whereas for `x \prec y` one keeps the terms where the root vertex comes from `x`. The free dendriform algebra can also be considered as the free algebra over the Dendriform operad. .. NOTE:: The usual binary operator `*` is used for the associative product. EXAMPLES:: sage: F = algebras.FreeDendriform(ZZ, 'xyz') sage: x,y,z = F.gens() sage: (x * y) * z B[x[., y[., z[., .]]]] + B[x[., z[y[., .], .]]] + B[y[x[., .], z[., .]]] + B[z[x[., y[., .]], .]] + B[z[y[x[., .], .], .]] The free dendriform algebra is associative:: sage: x * (y * z) == (x * y) * z True The associative product decomposes in two parts:: sage: x * y == F.prec(x, y) + F.succ(x, y) True The axioms hold:: sage: F.prec(F.succ(x, y), z) == F.succ(x, F.prec(y, z)) True sage: F.prec(F.prec(x, y), z) == F.prec(x, y * z) True sage: F.succ(x * y, z) == F.succ(x, F.succ(y, z)) True When there is only one generator, unlabelled trees are used instead:: sage: F1 = algebras.FreeDendriform(QQ) sage: w = F1.gen(0); w B[[., .]] sage: w * w * w B[[., [., [., .]]]] + B[[., [[., .], .]]] + B[[[., .], [., .]]] + B[[[., [., .]], .]] + B[[[[., .], .], .]] REFERENCES: - [LR1998]_ """ @staticmethod def __classcall_private__(cls, R, names=None): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: F1 = algebras.FreeDendriform(QQ, 'xyz') sage: F2 = algebras.FreeDendriform(QQ, ['x','y','z']) sage: F3 = algebras.FreeDendriform(QQ, Alphabet('xyz')) sage: F1 is F2 and F1 is F3 True """ if names is not None: if ',' in names: names = [u for u in names if u != ','] names = Alphabet(names) if R not in Rings(): raise TypeError("argument R must be a ring") return super(FreeDendriformAlgebra, cls).__classcall__(cls, R, names) def __init__(self, R, names=None): """ Initialize ``self``. TESTS:: sage: A = algebras.FreeDendriform(QQ, '@'); A Free Dendriform algebra on one generator ['@'] over Rational Field sage: TestSuite(A).run() # long time (3s) sage: F = algebras.FreeDendriform(QQ, 'xy') sage: TestSuite(F).run() # long time (3s) """ if names is None: Trees = BinaryTrees() key = BinaryTree._sort_key self._alphabet = Alphabet(['o']) else: Trees = LabelledBinaryTrees() key = LabelledBinaryTree._sort_key self._alphabet = names # Here one would need LabelledBinaryTrees(names) # so that one can restrict the labels to some fixed set cat = HopfAlgebras(R).WithBasis().Graded().Connected() CombinatorialFreeModule.__init__(self, R, Trees, latex_prefix="", sorting_key=key, category=cat) def variable_names(self): r""" Return the names of the variables. EXAMPLES:: sage: R = algebras.FreeDendriform(QQ, 'xy') sage: R.variable_names() {'x', 'y'} """ return self._alphabet def _repr_(self): """ Return the string representation of ``self``. EXAMPLES:: sage: algebras.FreeDendriform(QQ, '@') # indirect doctest Free Dendriform algebra on one generator ['@'] over Rational Field """ n = self.algebra_generators().cardinality() if n == 1: gen = "one generator" else: gen = "{} generators".format(n) s = "Free Dendriform algebra on {} {} over {}" try: return s.format(gen, self._alphabet.list(), self.base_ring()) except NotImplementedError: return s.format(gen, self._alphabet, self.base_ring()) def gen(self, i): r""" Return the ``i``-th generator of the algebra. INPUT: - ``i`` -- an integer EXAMPLES:: sage: F = algebras.FreeDendriform(ZZ, 'xyz') sage: F.gen(0) B[x[., .]] sage: F.gen(4) Traceback (most recent call last): ... IndexError: argument i (= 4) must be between 0 and 2 """ G = self.algebra_generators() n = G.cardinality() if i < 0 or not i < n: m = "argument i (= {}) must be between 0 and {}".format(i, n - 1) raise IndexError(m) return G[G.keys().unrank(i)] @cached_method def algebra_generators(self): r""" Return the generators of this algebra. These are the binary trees with just one vertex. EXAMPLES:: sage: A = algebras.FreeDendriform(ZZ, 'fgh'); A Free Dendriform algebra on 3 generators ['f', 'g', 'h'] over Integer Ring sage: list(A.algebra_generators()) [B[f[., .]], B[g[., .]], B[h[., .]]] sage: A = algebras.FreeDendriform(QQ, ['x1','x2']) sage: list(A.algebra_generators()) [B[x1[., .]], B[x2[., .]]] """ Trees = self.basis().keys() return Family(self._alphabet, lambda a: self.monomial(Trees([], a))) def change_ring(self, R): """ Return the free dendriform algebra in the same variables over `R`. INPUT: - ``R`` -- a ring EXAMPLES:: sage: A = algebras.FreeDendriform(ZZ, 'fgh') sage: A.change_ring(QQ) Free Dendriform algebra on 3 generators ['f', 'g', 'h'] over Rational Field """ return FreeDendriformAlgebra(R, names=self.variable_names()) def gens(self): """ Return the generators of ``self`` (as an algebra). EXAMPLES:: sage: A = algebras.FreeDendriform(ZZ, 'fgh') sage: A.gens() (B[f[., .]], B[g[., .]], B[h[., .]]) """ return tuple(self.algebra_generators()) def degree_on_basis(self, t): """ Return the degree of a binary tree in the free Dendriform algebra. This is the number of vertices. EXAMPLES:: sage: A = algebras.FreeDendriform(QQ,'@') sage: RT = A.basis().keys() sage: u = RT([], '@') sage: A.degree_on_basis(u.over(u)) 2 """ return t.node_number() @cached_method def an_element(self): """ Return an element of ``self``. EXAMPLES:: sage: A = algebras.FreeDendriform(QQ, 'xy') sage: A.an_element() B[x[., .]] + 2*B[x[., x[., .]]] + 2*B[x[x[., .], .]] """ o = self.gen(0) return o + 2 * o * o def some_elements(self): """ Return some elements of the free dendriform algebra. EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: A.some_elements() [B[.], B[[., .]], B[[., [., .]]] + B[[[., .], .]], B[.] + B[[., [., .]]] + B[[[., .], .]]] With several generators:: sage: A = algebras.FreeDendriform(QQ, 'xy') sage: A.some_elements() [B[.], B[x[., .]], B[x[., x[., .]]] + B[x[x[., .], .]], B[.] + B[x[., x[., .]]] + B[x[x[., .], .]]] """ u = self.one() o = self.gen(0) x = o * o y = u + x return [u, o, x, y] def one_basis(self): """ Return the index of the unit. EXAMPLES:: sage: A = algebras.FreeDendriform(QQ, '@') sage: A.one_basis() . sage: A = algebras.FreeDendriform(QQ, 'xy') sage: A.one_basis() . """ Trees = self.basis().keys() return Trees(None) def product_on_basis(self, x, y): r""" Return the `*` associative dendriform product of two trees. This is the sum over all possible ways of identifying the rightmost path in `x` and the leftmost path in `y`. Every term corresponds to a shuffle of the vertices on the rightmost path in `x` and the vertices on the leftmost path in `y`. .. SEEALSO:: - :meth:`succ_product_on_basis`, :meth:`prec_product_on_basis` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = RT([]) sage: A.product_on_basis(x, x) B[[., [., .]]] + B[[[., .], .]] """ return self.sum(self.basis()[u] for u in x.dendriform_shuffle(y)) def succ_product_on_basis(self, x, y): r""" Return the `\succ` dendriform product of two trees. This is the sum over all possible ways to identify the rightmost path in `x` and the leftmost path in `y`, with the additional condition that the root vertex of the result comes from `y`. The usual symbol for this operation is `\succ`. .. SEEALSO:: - :meth:`product_on_basis`, :meth:`prec_product_on_basis` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = RT([]) sage: A.succ_product_on_basis(x, x) B[[[., .], .]] TESTS:: sage: u = A.one().support()[0] sage: A.succ_product_on_basis(u, u) Traceback (most recent call last): ... ValueError: dendriform products | < | and | > | are not defined """ if y.is_empty(): if x.is_empty(): raise ValueError("dendriform products | < | and | > | are " "not defined") else: return [] if x.is_empty(): return [y] K = self.basis().keys() if hasattr(y, 'label'): return self.sum(self.basis()[K([u, y[1]], y.label())] for u in x.dendriform_shuffle(y[0])) return self.sum(self.basis()[K([u, y[1]])] for u in x.dendriform_shuffle(y[0])) @lazy_attribute def succ(self): r""" Return the `\succ` dendriform product. This is the sum over all possible ways of identifying the rightmost path in `x` and the leftmost path in `y`, with the additional condition that the root vertex of the result comes from `y`. The usual symbol for this operation is `\succ`. .. SEEALSO:: :meth:`product`, :meth:`prec`, :meth:`over`, :meth:`under` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = A.gen(0) sage: A.succ(x, x) B[[[., .], .]] """ suc = self.succ_product_on_basis return self._module_morphism(self._module_morphism(suc, position=0, codomain=self), position=1) def prec_product_on_basis(self, x, y): r""" Return the `\prec` dendriform product of two trees. This is the sum over all possible ways of identifying the rightmost path in `x` and the leftmost path in `y`, with the additional condition that the root vertex of the result comes from `x`. The usual symbol for this operation is `\prec`. .. SEEALSO:: - :meth:`product_on_basis`, :meth:`succ_product_on_basis` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = RT([]) sage: A.prec_product_on_basis(x, x) B[[., [., .]]] TESTS:: sage: u = A.one().support()[0] sage: A.prec_product_on_basis(u, u) Traceback (most recent call last): ... ValueError: dendriform products | < | and | > | are not defined """ if x.is_empty() and y.is_empty(): raise ValueError("dendriform products | < | and | > | are " "not defined") if x.is_empty(): return [] if y.is_empty(): return [x] K = self.basis().keys() if hasattr(y, 'label'): return self.sum(self.basis()[K([x[0], u], x.label())] for u in x[1].dendriform_shuffle(y)) return self.sum(self.basis()[K([x[0], u])] for u in x[1].dendriform_shuffle(y)) @lazy_attribute def prec(self): r""" Return the `\prec` dendriform product. This is the sum over all possible ways to identify the rightmost path in `x` and the leftmost path in `y`, with the additional condition that the root vertex of the result comes from `x`. The usual symbol for this operation is `\prec`. .. SEEALSO:: :meth:`product`, :meth:`succ`, :meth:`over`, :meth:`under` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = A.gen(0) sage: A.prec(x, x) B[[., [., .]]] """ pre = self.prec_product_on_basis return self._module_morphism(self._module_morphism(pre, position=0, codomain=self), position=1) @lazy_attribute def over(self): r""" Return the over product. The over product `x/y` is the binary tree obtained by grafting the root of `y` at the rightmost leaf of `x`. The usual symbol for this operation is `/`. .. SEEALSO:: :meth:`product`, :meth:`succ`, :meth:`prec`, :meth:`under` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = A.gen(0) sage: A.over(x, x) B[[., [., .]]] """ def ov(x, y): return self._monomial(x.over(y)) return self._module_morphism(self._module_morphism(ov, position=0, codomain=self), position=1) @lazy_attribute def under(self): r""" Return the under product. The over product `x \backslash y` is the binary tree obtained by grafting the root of `x` at the leftmost leaf of `y`. The usual symbol for this operation is `\backslash`. .. SEEALSO:: :meth:`product`, :meth:`succ`, :meth:`prec`, :meth:`over` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = A.gen(0) sage: A.under(x, x) B[[[., .], .]] """ def und(x, y): return self._monomial(x.under(y)) return self._module_morphism(self._module_morphism(und, position=0, codomain=self), position=1) def coproduct_on_basis(self, x): """ Return the coproduct of a binary tree. EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: x = A.gen(0) sage: ascii_art(A.coproduct(A.one())) # indirect doctest 1 # 1 sage: ascii_art(A.coproduct(x)) # indirect doctest 1 # B + B # 1 o o sage: A = algebras.FreeDendriform(QQ, 'xyz') sage: x, y, z = A.gens() sage: w = A.under(z,A.over(x,y)) sage: A.coproduct(z) B[.] # B[z[., .]] + B[z[., .]] # B[.] sage: A.coproduct(w) B[.] # B[x[z[., .], y[., .]]] + B[x[., .]] # B[z[., y[., .]]] + B[x[., .]] # B[y[z[., .], .]] + B[x[., y[., .]]] # B[z[., .]] + B[x[z[., .], .]] # B[y[., .]] + B[x[z[., .], y[., .]]] # B[.] """ B = self.basis() Trees = B.keys() if not x.node_number(): return self.one().tensor(self.one()) L, R = list(x) try: root = x.label() except AttributeError: root = '@' resu = self.one().tensor(self.monomial(x)) resu += sum(cL * cR * self.monomial(Trees([LL[0], RR[0]], root)).tensor( self.monomial(LL[1]) * self.monomial(RR[1])) for LL, cL in self.coproduct_on_basis(L) for RR, cR in self.coproduct_on_basis(R)) return resu # after this line : coercion def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. EXAMPLES:: sage: R = algebras.FreeDendriform(QQ, 'xy') sage: x, y = R.gens() sage: R(x) B[x[., .]] sage: R(x+4*y) B[x[., .]] + 4*B[y[., .]] sage: Trees = R.basis().keys() sage: R(Trees([],'x')) B[x[., .]] sage: D = algebras.FreeDendriform(ZZ, 'xy') sage: X, Y = D.gens() sage: R(X-Y).parent() Free Dendriform algebra on 2 generators ['x', 'y'] over Rational Field """ if x in self.basis().keys(): return self.monomial(x) try: P = x.parent() if isinstance(P, FreeDendriformAlgebra): if P is self: return x return self.element_class(self, x.monomial_coefficients()) except AttributeError: raise TypeError('not able to coerce this in this algebra') # Ok, not a dendriform algebra element (or should not be viewed as one). def _coerce_map_from_(self, R): r""" Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are - free dendriform algebras in a subset of variables of ``self`` over a base with a coercion map into ``self.base_ring()`` EXAMPLES:: sage: F = algebras.FreeDendriform(GF(7), 'xyz'); F Free Dendriform algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 Elements of the free dendriform algebra canonically coerce in:: sage: x, y, z = F.gens() sage: F.coerce(x+y) == x+y True The free dendriform algebra over `\ZZ` on `x, y, z` coerces in, since `\ZZ` coerces to `\GF{7}`:: sage: G = algebras.FreeDendriform(ZZ, 'xyz') sage: Gx,Gy,Gz = G.gens() sage: z = F.coerce(Gx+Gy); z B[x[., .]] + B[y[., .]] sage: z.parent() is F True However, `\GF{7}` does not coerce to `\ZZ`, so the free dendriform algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: sage: G.coerce(y) Traceback (most recent call last): ... TypeError: no canonical coercion from Free Dendriform algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 to Free Dendriform algebra on 3 generators ['x', 'y', 'z'] over Integer Ring TESTS:: sage: F = algebras.FreeDendriform(ZZ, 'xyz') sage: G = algebras.FreeDendriform(QQ, 'xyz') sage: H = algebras.FreeDendriform(ZZ, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) True sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) False sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False """ # free dendriform algebras in a subset of variables # over any base that coerces in: if isinstance(R, FreeDendriformAlgebra): if all(x in self.variable_names() for x in R.variable_names()): if self.base_ring().has_coerce_map_from(R.base_ring()): return True return False def construction(self): """ Return a pair ``(F, R)``, where ``F`` is a :class:`DendriformFunctor` and `R` is a ring, such that ``F(R)`` returns ``self``. EXAMPLES:: sage: P = algebras.FreeDendriform(ZZ, 'x,y') sage: x,y = P.gens() sage: F, R = P.construction() sage: F Dendriform[x,y] sage: R Integer Ring sage: F(ZZ) is P True sage: F(QQ) Free Dendriform algebra on 2 generators ['x', 'y'] over Rational Field """ return DendriformFunctor(self.variable_names()), self.base_ring()
class GrossmanLarsonAlgebra(CombinatorialFreeModule): r""" The Grossman-Larson Hopf Algebra. The Grossman-Larson Hopf Algebras are Hopf algebras with a basis indexed by forests of decorated rooted trees. They are the universal enveloping algebras of free pre-Lie algebras, seen as Lie algebras. The Grossman-Larson Hopf algebra on a given set `E` has an explicit description using rooted forests. The underlying vector space has a basis indexed by finite rooted forests endowed with a map from their vertices to `E` (called the "labeling"). In this basis, the product of two (decorated) rooted forests `S * T` is a sum over all maps from the set of roots of `T` to the union of a singleton `\{\#\}` and the set of vertices of `S`. Given such a map, one defines a new forest as follows. Starting from the disjoint union of all rooted trees of `S` and `T`, one adds an edge from every root of `T` to its image when this image is not the fake vertex labelled ``#``. The coproduct sends a rooted forest `T` to the sum of all tensors `T_1 \otimes T_2` obtained by splitting the connected components of `T` into two subsets and letting `T_1` be the forest formed by the first subset and `T_2` the forest formed by the second. This yields a connected graded Hopf algebra (the degree of a forest is its number of vertices). See [Pana2002]_ (Section 2) and [GroLar1]_. (Note that both references use rooted trees rather than rooted forests, so think of each rooted forest grafted onto a new root. Also, the product is reversed, so they are defining the opposite algebra structure.) .. WARNING:: For technical reasons, instead of using forests as labels for the basis, we use rooted trees. Their root vertex should be considered as a fake vertex. This fake root vertex is labelled ``'#'`` when labels are present. EXAMPLES:: sage: G = algebras.GrossmanLarson(QQ, 'xy') sage: x, y = G.single_vertex_all() sage: ascii_art(x*y) B + B # #_ | / / x x y | y sage: ascii_art(x*x*x) B + B + 3*B + B # # #_ _#__ | | / / / / / x x_ x x x x x | / / | x x x x | x The Grossman-Larson algebra is associative:: sage: z = x * y sage: x * (y * z) == (x * y) * z True It is not commutative:: sage: x * y == y * x False When ``None`` is given as input, unlabelled forests are used instead; this corresponds to a `1`-element set `E`:: sage: G = algebras.GrossmanLarson(QQ, None) sage: x = G.single_vertex_all()[0] sage: ascii_art(x*x) B + B o o_ | / / o o o | o .. NOTE:: Variables names can be ``None``, a list of strings, a string or an integer. When ``None`` is given, unlabelled rooted forests are used. When a single string is given, each letter is taken as a variable. See :func:`sage.combinat.words.alphabet.build_alphabet`. .. WARNING:: Beware that the underlying combinatorial free module is based either on ``RootedTrees`` or on ``LabelledRootedTrees``, with no restriction on the labellings. This means that all code calling the :meth:`basis` method would not give meaningful results, since :meth:`basis` returns many "chaff" elements that do not belong to the algebra. REFERENCES: - [Pana2002]_ - [GroLar1]_ """ @staticmethod def __classcall_private__(cls, R, names=None): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: F1 = algebras.GrossmanLarson(QQ, 'xyz') sage: F2 = algebras.GrossmanLarson(QQ, ['x','y','z']) sage: F3 = algebras.GrossmanLarson(QQ, Alphabet('xyz')) sage: F1 is F2 and F1 is F3 True """ if names is not None: if names not in ZZ and ',' in names: names = [u for u in names if u != ','] names = Alphabet(names) if R not in Rings(): raise TypeError("argument R must be a ring") return super(GrossmanLarsonAlgebra, cls).__classcall__(cls, R, names) def __init__(self, R, names=None): """ Initialize ``self``. TESTS:: sage: A = algebras.GrossmanLarson(QQ, '@'); A Grossman-Larson Hopf algebra on one generator ['@'] over Rational Field sage: TestSuite(A).run() # long time sage: F = algebras.GrossmanLarson(QQ, 'xy') sage: TestSuite(F).run() # long time sage: A = algebras.GrossmanLarson(QQ, None); A Grossman-Larson Hopf algebra on one generator ['o'] over Rational Field sage: F = algebras.GrossmanLarson(QQ, ['x','y']); F Grossman-Larson Hopf algebra on 2 generators ['x', 'y'] over Rational Field sage: A = algebras.GrossmanLarson(QQ, []); A Grossman-Larson Hopf algebra on 0 generators [] over Rational Field """ if names is None: Trees = RootedTrees() key = RootedTree.sort_key self._alphabet = Alphabet(['o']) else: Trees = LabelledRootedTrees() key = LabelledRootedTree.sort_key self._alphabet = names # Here one would need LabelledRootedTrees(names) # so that one can restrict the labels to some fixed set cat = HopfAlgebras(R).WithBasis().Graded() CombinatorialFreeModule.__init__(self, R, Trees, latex_prefix="", sorting_key=key, category=cat) def variable_names(self): r""" Return the names of the variables. This returns the set `E` (as a family). EXAMPLES:: sage: R = algebras.GrossmanLarson(QQ, 'xy') sage: R.variable_names() {'x', 'y'} sage: R = algebras.GrossmanLarson(QQ, ['a','b']) sage: R.variable_names() {'a', 'b'} sage: R = algebras.GrossmanLarson(QQ, 2) sage: R.variable_names() {0, 1} sage: R = algebras.GrossmanLarson(QQ, None) sage: R.variable_names() {'o'} """ return self._alphabet def _repr_(self): """ Return the string representation of ``self``. EXAMPLES:: sage: algebras.GrossmanLarson(QQ, '@') # indirect doctest Grossman-Larson Hopf algebra on one generator ['@'] over Rational Field sage: algebras.GrossmanLarson(QQ, None) # indirect doctest Grossman-Larson Hopf algebra on one generator ['o'] over Rational Field sage: algebras.GrossmanLarson(QQ, ['a','b']) Grossman-Larson Hopf algebra on 2 generators ['a', 'b'] over Rational Field """ n = len(self.single_vertex_all()) if n == 1: gen = "one generator" else: gen = "{} generators".format(n) s = "Grossman-Larson Hopf algebra on {} {} over {}" try: return s.format(gen, self._alphabet.list(), self.base_ring()) except NotImplementedError: return s.format(gen, self._alphabet, self.base_ring()) def single_vertex(self, i): r""" Return the ``i``-th rooted forest with one vertex. This is the rooted forest with just one vertex, labelled by the ``i``-th element of the label list. .. SEEALSO:: :meth:`single_vertex_all`. INPUT: - ``i`` -- a nonnegative integer EXAMPLES:: sage: F = algebras.GrossmanLarson(ZZ, 'xyz') sage: F.single_vertex(0) B[#[x[]]] sage: F.single_vertex(4) Traceback (most recent call last): ... IndexError: argument i (= 4) must be between 0 and 2 """ G = self.single_vertex_all() n = len(G) if i < 0 or not i < n: m = "argument i (= {}) must be between 0 and {}".format(i, n - 1) raise IndexError(m) return G[i] def single_vertex_all(self): """ Return the rooted forests with one vertex in ``self``. They freely generate the Lie algebra of primitive elements as a pre-Lie algebra. .. SEEALSO:: :meth:`single_vertex`. EXAMPLES:: sage: A = algebras.GrossmanLarson(ZZ, 'fgh') sage: A.single_vertex_all() (B[#[f[]]], B[#[g[]]], B[#[h[]]]) sage: A = algebras.GrossmanLarson(QQ, ['x1','x2']) sage: A.single_vertex_all() (B[#[x1[]]], B[#[x2[]]]) sage: A = algebras.GrossmanLarson(ZZ, None) sage: A.single_vertex_all() (B[[[]]],) """ Trees = self.basis().keys() return tuple(Family(self._alphabet, lambda a: self.monomial(Trees([Trees([], a)], ROOT)))) def _first_ngens(self, n): """ Return the first generators. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, ['x1','x2']) sage: A._first_ngens(2) (B[#[x1[]]], B[#[x2[]]]) sage: A = algebras.GrossmanLarson(ZZ, None) sage: A._first_ngens(1) (B[[[]]],) """ return self.single_vertex_all()[:n] def change_ring(self, R): """ Return the Grossman-Larson algebra in the same variables over `R`. INPUT: - `R` -- a ring EXAMPLES:: sage: A = algebras.GrossmanLarson(ZZ, 'fgh') sage: A.change_ring(QQ) Grossman-Larson Hopf algebra on 3 generators ['f', 'g', 'h'] over Rational Field """ return GrossmanLarsonAlgebra(R, names=self.variable_names()) def degree_on_basis(self, t): """ Return the degree of a rooted forest in the Grossman-Larson algebra. This is the total number of vertices of the forest. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, '@') sage: RT = A.basis().keys() sage: A.degree_on_basis(RT([RT([])])) 1 """ return t.node_number() - 1 @cached_method def an_element(self): """ Return an element of ``self``. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, 'xy') sage: A.an_element() B[#[x[]]] + 2*B[#[x[x[]]]] + 2*B[#[x[], x[]]] """ o = self.single_vertex(0) return o + 2 * o * o def some_elements(self): """ Return some elements of the Grossman-Larson Hopf algebra. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, None) sage: A.some_elements() [B[[[]]], B[[]] + B[[[[]]]] + B[[[], []]], 4*B[[[[]]]] + 4*B[[[], []]]] With several generators:: sage: A = algebras.GrossmanLarson(QQ, 'xy') sage: A.some_elements() [B[#[x[]]], B[#[]] + B[#[x[x[]]]] + B[#[x[], x[]]], B[#[x[x[]]]] + 3*B[#[x[y[]]]] + B[#[x[], x[]]] + 3*B[#[x[], y[]]]] """ o = self.single_vertex(0) o1 = self.single_vertex_all()[-1] x = o * o y = o * o1 return [o, 1 + x, x + 3 * y] def product_on_basis(self, x, y): """ Return the product of two forests `x` and `y`. This is the sum over all possible ways for the components of the forest `y` to either fall side-by-side with components of `x` or be grafted on a vertex of `x`. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, None) sage: RT = A.basis().keys() sage: x = RT([RT([])]) sage: A.product_on_basis(x, x) B[[[[]]]] + B[[[], []]] Check that the product is the correct one:: sage: A = algebras.GrossmanLarson(QQ, 'uv') sage: RT = A.basis().keys() sage: Tu = RT([RT([],'u')],'#') sage: Tv = RT([RT([],'v')],'#') sage: A.product_on_basis(Tu, Tv) B[#[u[v[]]]] + B[#[u[], v[]]] """ return self.sum(self.basis()[x.single_graft(y, graftingFunction)] for graftingFunction in product(list(x.paths()), repeat=len(y))) def one_basis(self): """ Return the empty rooted forest. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, 'ab') sage: A.one_basis() #[] sage: A = algebras.GrossmanLarson(QQ, None) sage: A.one_basis() [] """ Trees = self.basis().keys() return Trees([], ROOT) def coproduct_on_basis(self, x): """ Return the coproduct of a forest. EXAMPLES:: sage: G = algebras.GrossmanLarson(QQ,2) sage: x, y = G.single_vertex_all() sage: ascii_art(G.coproduct(x)) # indirect doctest 1 # B + B # 1 # # | | 0 0 sage: ascii_art(G.coproduct(y*x)) # indirect doctest 1 # B + 1 # B + B # B + B # 1 + B # B + B # 1 #_ # # # #_ # # # / / | | | / / | | | 0 1 1 0 1 0 1 1 0 1 | | 0 0 """ B = self.basis() Trees = B.keys() subtrees = list(x) num_subtrees = len(subtrees) indx = list(range(num_subtrees)) return sum(B[Trees([subtrees[i] for i in S], ROOT)].tensor( B[Trees([subtrees[i] for i in indx if i not in S], ROOT)]) for k in range(num_subtrees + 1) for S in combinations(indx, k)) def counit_on_basis(self, x): """ Return the counit on a basis element. This is zero unless the forest `x` is empty. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, 'xy') sage: RT = A.basis().keys() sage: x = RT([RT([],'x')],'#') sage: A.counit_on_basis(x) 0 sage: A.counit_on_basis(RT([],'#')) 1 """ if x.node_number() == 1: return self.base_ring().one() return self.base_ring().zero() def antipode_on_basis(self, x): """ Return the antipode of a forest. EXAMPLES:: sage: G = algebras.GrossmanLarson(QQ,2) sage: x, y = G.single_vertex_all() sage: G.antipode(x) # indirect doctest -B[#[0[]]] sage: G.antipode(y*x) # indirect doctest B[#[0[1[]]]] + B[#[0[], 1[]]] """ B = self.basis() Trees = B.keys() subtrees = list(x) if not subtrees: return self.one() num_subtrees = len(subtrees) indx = list(range(num_subtrees)) return sum(- self.antipode_on_basis(Trees([subtrees[i] for i in S], ROOT)) * B[Trees([subtrees[i] for i in indx if i not in S], ROOT)] for k in range(num_subtrees) for S in combinations(indx, k)) def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. EXAMPLES:: sage: R = algebras.GrossmanLarson(QQ, 'xy') sage: x, y = R.single_vertex_all() sage: R(x) B[#[x[]]] sage: R(x+4*y) B[#[x[]]] + 4*B[#[y[]]] sage: Trees = R.basis().keys() sage: R(Trees([],'#')) B[#[]] sage: D = algebras.GrossmanLarson(ZZ, 'xy') sage: X, Y = D.single_vertex_all() sage: R(X-Y).parent() Grossman-Larson Hopf algebra on 2 generators ['x', 'y'] over Rational Field TESTS:: sage: Trees = R.basis().keys() sage: R(Trees([],'x')) Traceback (most recent call last): ... ValueError: incorrect root label sage: R.<x,y> = algebras.GrossmanLarson(QQ) sage: S.<z> = algebras.GrossmanLarson(GF(3)) sage: R(z) Traceback (most recent call last): ... TypeError: not able to convert this to this algebra """ if (isinstance(x, (RootedTree, LabelledRootedTree)) and x in self.basis().keys()): if hasattr(x, 'label') and x.label() != ROOT: raise ValueError('incorrect root label') return self.monomial(x) try: P = x.parent() if isinstance(P, GrossmanLarsonAlgebra): if P is self: return x if self._coerce_map_from_(P): return self.element_class(self, x.monomial_coefficients()) except AttributeError: raise TypeError('not able to convert this to this algebra') else: raise TypeError('not able to convert this to this algebra') # Ok, not an element (or should not be viewed as one). def _coerce_map_from_(self, R): r""" Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are - Grossman-Larson Hopf algebras whose set `E` of labels is a subset of the corresponding self of ``set`, and whose base ring has a coercion map into ``self.base_ring()`` EXAMPLES:: sage: F = algebras.GrossmanLarson(GF(7), 'xyz'); F Grossman-Larson Hopf algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 Elements of the Grossman-Larson Hopf algebra canonically coerce in:: sage: x, y, z = F.single_vertex_all() sage: F.coerce(x+y) == x+y True The Grossman-Larson Hopf algebra over `\ZZ` on `x, y, z` coerces in, since `\ZZ` coerces to `\GF{7}`:: sage: G = algebras.GrossmanLarson(ZZ, 'xyz') sage: Gx,Gy,Gz = G.single_vertex_all() sage: z = F.coerce(Gx+Gy); z B[#[x[]]] + B[#[y[]]] sage: z.parent() is F True However, `\GF{7}` does not coerce to `\ZZ`, so the Grossman-Larson algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: sage: G.coerce(y) Traceback (most recent call last): ... TypeError: no canonical coercion from Grossman-Larson Hopf algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 to Grossman-Larson Hopf algebra on 3 generators ['x', 'y', 'z'] over Integer Ring TESTS:: sage: F = algebras.GrossmanLarson(ZZ, 'xyz') sage: G = algebras.GrossmanLarson(QQ, 'xyz') sage: H = algebras.GrossmanLarson(ZZ, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) True sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) False sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False """ # Grossman-Larson algebras containing the same variables # over any base that coerces in: if isinstance(R, GrossmanLarsonAlgebra): if all(x in self.variable_names() for x in R.variable_names()): if self.base_ring().has_coerce_map_from(R.base_ring()): return True return False
class FreeDendriformAlgebra(CombinatorialFreeModule): r""" The free dendriform algebra. Dendriform algebras are associative algebras, where the associative product `*` is decomposed as a sum of two binary operations .. MATH:: x * y = x \succ y + x \prec y that satisfy the axioms: .. MATH:: (x \succ y) \prec z = x \succ (y \prec z), .. MATH:: (x \prec y) \prec z = x \prec (y * z). .. MATH:: (x * y) \succ z = x \succ (y \succ z). The free Dendriform algebra on a given set `E` has an explicit description using (planar) binary trees, just as the free associative algebra can be described using words. The underlying vector space has a basis indexed by finite binary trees endowed with a map from their vertices to `E`. In this basis, the associative product of two (decorated) binary trees `S * T` is the sum over all possible ways of identifying (glueing) the rightmost path in `S` and the leftmost path in `T`. The decomposition of the associative product as the sum of two binary operations `\succ` and `\prec` is made by separating the terms according to the origin of the root vertex. For `x \succ y`, one keeps the terms where the root vertex comes from `y`, whereas for `x \prec y` one keeps the terms where the root vertex comes from `x`. The free dendriform algebra can also be considered as the free algebra over the Dendriform operad. .. NOTE:: The usual binary operator `*` is used for the associative product. EXAMPLES:: sage: F = algebras.FreeDendriform(ZZ, 'xyz') sage: x,y,z = F.gens() sage: (x * y) * z B[x[., y[., z[., .]]]] + B[x[., z[y[., .], .]]] + B[y[x[., .], z[., .]]] + B[z[x[., y[., .]], .]] + B[z[y[x[., .], .], .]] The free dendriform algebra is associative:: sage: x * (y * z) == (x * y) * z True The associative product decomposes in two parts:: sage: x * y == F.prec(x, y) + F.succ(x, y) True The axioms hold:: sage: F.prec(F.succ(x, y), z) == F.succ(x, F.prec(y, z)) True sage: F.prec(F.prec(x, y), z) == F.prec(x, y * z) True sage: F.succ(x * y, z) == F.succ(x, F.succ(y, z)) True When there is only one generator, unlabelled trees are used instead:: sage: F1 = algebras.FreeDendriform(QQ) sage: w = F1.gen(0); w B[[., .]] sage: w * w * w B[[., [., [., .]]]] + B[[., [[., .], .]]] + B[[[., .], [., .]]] + B[[[., [., .]], .]] + B[[[[., .], .], .]] REFERENCES: - [LodayRonco]_ """ @staticmethod def __classcall_private__(cls, R, names=None): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: F1 = algebras.FreeDendriform(QQ, 'xyz') sage: F2 = algebras.FreeDendriform(QQ, ['x','y','z']) sage: F3 = algebras.FreeDendriform(QQ, Alphabet('xyz')) sage: F1 is F2 and F1 is F3 True """ if names is not None: if ',' in names: names = [u for u in names if u != ','] names = Alphabet(names) if R not in Rings(): raise TypeError("argument R must be a ring") return super(FreeDendriformAlgebra, cls).__classcall__(cls, R, names) def __init__(self, R, names=None): """ Initialize ``self``. TESTS:: sage: A = algebras.FreeDendriform(QQ, '@'); A Free Dendriform algebra on one generator ['@'] over Rational Field sage: TestSuite(A).run() # long time (3s) sage: F = algebras.FreeDendriform(QQ, 'xy') sage: TestSuite(F).run() # long time (3s) """ if names is None: Trees = BinaryTrees() key = BinaryTree._sort_key self._alphabet = Alphabet(['o']) else: Trees = LabelledBinaryTrees() key = LabelledBinaryTree._sort_key self._alphabet = names # Here one would need LabelledBinaryTrees(names) # so that one can restrict the labels to some fixed set cat = HopfAlgebras(R).WithBasis().Graded().Connected() CombinatorialFreeModule.__init__(self, R, Trees, latex_prefix="", sorting_key=key, category=cat) def variable_names(self): r""" Return the names of the variables. EXAMPLES:: sage: R = algebras.FreeDendriform(QQ, 'xy') sage: R.variable_names() {'x', 'y'} """ return self._alphabet def _repr_(self): """ Return the string representation of ``self``. EXAMPLES:: sage: algebras.FreeDendriform(QQ, '@') # indirect doctest Free Dendriform algebra on one generator ['@'] over Rational Field """ n = self.algebra_generators().cardinality() if n == 1: gen = "one generator" else: gen = "{} generators".format(n) s = "Free Dendriform algebra on {} {} over {}" try: return s.format(gen, self._alphabet.list(), self.base_ring()) except NotImplementedError: return s.format(gen, self._alphabet, self.base_ring()) def gen(self, i): r""" Return the ``i``-th generator of the algebra. INPUT: - ``i`` -- an integer EXAMPLES:: sage: F = algebras.FreeDendriform(ZZ, 'xyz') sage: F.gen(0) B[x[., .]] sage: F.gen(4) Traceback (most recent call last): ... IndexError: argument i (= 4) must be between 0 and 2 """ G = self.algebra_generators() n = G.cardinality() if i < 0 or not i < n: m = "argument i (= {}) must be between 0 and {}".format(i, n - 1) raise IndexError(m) return G[G.keys().unrank(i)] @cached_method def algebra_generators(self): r""" Return the generators of this algebra. These are the binary trees with just one vertex. EXAMPLES:: sage: A = algebras.FreeDendriform(ZZ, 'fgh'); A Free Dendriform algebra on 3 generators ['f', 'g', 'h'] over Integer Ring sage: list(A.algebra_generators()) [B[f[., .]], B[g[., .]], B[h[., .]]] sage: A = algebras.FreeDendriform(QQ, ['x1','x2']) sage: list(A.algebra_generators()) [B[x1[., .]], B[x2[., .]]] """ Trees = self.basis().keys() return Family(self._alphabet, lambda a: self.monomial(Trees([], a))) def change_ring(self, R): """ Return the free dendriform algebra in the same variables over `R`. INPUT: - ``R`` -- a ring EXAMPLES:: sage: A = algebras.FreeDendriform(ZZ, 'fgh') sage: A.change_ring(QQ) Free Dendriform algebra on 3 generators ['f', 'g', 'h'] over Rational Field """ return FreeDendriformAlgebra(R, names=self.variable_names()) def gens(self): """ Return the generators of ``self`` (as an algebra). EXAMPLES:: sage: A = algebras.FreeDendriform(ZZ, 'fgh') sage: A.gens() (B[f[., .]], B[g[., .]], B[h[., .]]) """ return tuple(self.algebra_generators()) def degree_on_basis(self, t): """ Return the degree of a binary tree in the free Dendriform algebra. This is the number of vertices. EXAMPLES:: sage: A = algebras.FreeDendriform(QQ,'@') sage: RT = A.basis().keys() sage: u = RT([], '@') sage: A.degree_on_basis(u.over(u)) 2 """ return t.node_number() @cached_method def an_element(self): """ Return an element of ``self``. EXAMPLES:: sage: A = algebras.FreeDendriform(QQ, 'xy') sage: A.an_element() B[x[., .]] + 2*B[x[., x[., .]]] + 2*B[x[x[., .], .]] """ o = self.gen(0) return o + 2 * o * o def some_elements(self): """ Return some elements of the free dendriform algebra. EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: A.some_elements() [B[.], B[[., .]], B[[., [., .]]] + B[[[., .], .]], B[.] + B[[., [., .]]] + B[[[., .], .]]] With several generators:: sage: A = algebras.FreeDendriform(QQ, 'xy') sage: A.some_elements() [B[.], B[x[., .]], B[x[., x[., .]]] + B[x[x[., .], .]], B[.] + B[x[., x[., .]]] + B[x[x[., .], .]]] """ u = self.one() o = self.gen(0) x = o * o y = u + x return [u, o, x, y] def one_basis(self): """ Return the index of the unit. EXAMPLES:: sage: A = algebras.FreeDendriform(QQ, '@') sage: A.one_basis() . sage: A = algebras.FreeDendriform(QQ, 'xy') sage: A.one_basis() . """ Trees = self.basis().keys() return Trees(None) def product_on_basis(self, x, y): r""" Return the `*` associative dendriform product of two trees. This is the sum over all possible ways of identifying the rightmost path in `x` and the leftmost path in `y`. Every term corresponds to a shuffle of the vertices on the rightmost path in `x` and the vertices on the leftmost path in `y`. .. SEEALSO:: - :meth:`succ_product_on_basis`, :meth:`prec_product_on_basis` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = RT([]) sage: A.product_on_basis(x, x) B[[., [., .]]] + B[[[., .], .]] """ return self.sum(self.basis()[u] for u in x.dendriform_shuffle(y)) def succ_product_on_basis(self, x, y): r""" Return the `\succ` dendriform product of two trees. This is the sum over all possible ways to identify the rightmost path in `x` and the leftmost path in `y`, with the additional condition that the root vertex of the result comes from `y`. The usual symbol for this operation is `\succ`. .. SEEALSO:: - :meth:`product_on_basis`, :meth:`prec_product_on_basis` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = RT([]) sage: A.succ_product_on_basis(x, x) B[[[., .], .]] TESTS:: sage: u = A.one().support()[0] sage: A.succ_product_on_basis(u, u) Traceback (most recent call last): ... ValueError: dendriform products | < | and | > | are not defined """ if y.is_empty(): if x.is_empty(): raise ValueError("dendriform products | < | and | > | are " "not defined") else: return [] if x.is_empty(): return [y] K = self.basis().keys() if hasattr(y, 'label'): return self.sum(self.basis()[K([u, y[1]], y.label())] for u in x.dendriform_shuffle(y[0])) return self.sum(self.basis()[K([u, y[1]])] for u in x.dendriform_shuffle(y[0])) @lazy_attribute def succ(self): r""" Return the `\succ` dendriform product. This is the sum over all possible ways of identifying the rightmost path in `x` and the leftmost path in `y`, with the additional condition that the root vertex of the result comes from `y`. The usual symbol for this operation is `\succ`. .. SEEALSO:: :meth:`product`, :meth:`prec`, :meth:`over`, :meth:`under` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = A.gen(0) sage: A.succ(x, x) B[[[., .], .]] """ suc = self.succ_product_on_basis return self._module_morphism(self._module_morphism(suc, position=0, codomain=self), position=1) def prec_product_on_basis(self, x, y): r""" Return the `\prec` dendriform product of two trees. This is the sum over all possible ways of identifying the rightmost path in `x` and the leftmost path in `y`, with the additional condition that the root vertex of the result comes from `x`. The usual symbol for this operation is `\prec`. .. SEEALSO:: - :meth:`product_on_basis`, :meth:`succ_product_on_basis` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = RT([]) sage: A.prec_product_on_basis(x, x) B[[., [., .]]] TESTS:: sage: u = A.one().support()[0] sage: A.prec_product_on_basis(u, u) Traceback (most recent call last): ... ValueError: dendriform products | < | and | > | are not defined """ if x.is_empty() and y.is_empty(): raise ValueError("dendriform products | < | and | > | are " "not defined") if x.is_empty(): return [] if y.is_empty(): return [x] K = self.basis().keys() if hasattr(y, 'label'): return self.sum(self.basis()[K([x[0], u], x.label())] for u in x[1].dendriform_shuffle(y)) return self.sum(self.basis()[K([x[0], u])] for u in x[1].dendriform_shuffle(y)) @lazy_attribute def prec(self): r""" Return the `\prec` dendriform product. This is the sum over all possible ways to identify the rightmost path in `x` and the leftmost path in `y`, with the additional condition that the root vertex of the result comes from `x`. The usual symbol for this operation is `\prec`. .. SEEALSO:: :meth:`product`, :meth:`succ`, :meth:`over`, :meth:`under` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = A.gen(0) sage: A.prec(x, x) B[[., [., .]]] """ pre = self.prec_product_on_basis return self._module_morphism(self._module_morphism(pre, position=0, codomain=self), position=1) @lazy_attribute def over(self): r""" Return the over product. The over product `x/y` is the binary tree obtained by grafting the root of `y` at the rightmost leaf of `x`. The usual symbol for this operation is `/`. .. SEEALSO:: :meth:`product`, :meth:`succ`, :meth:`prec`, :meth:`under` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = A.gen(0) sage: A.over(x, x) B[[., [., .]]] """ def ov(x, y): return self._monomial(x.over(y)) return self._module_morphism(self._module_morphism(ov, position=0, codomain=self), position=1) @lazy_attribute def under(self): r""" Return the under product. The over product `x \backslash y` is the binary tree obtained by grafting the root of `x` at the leftmost leaf of `y`. The usual symbol for this operation is `\backslash`. .. SEEALSO:: :meth:`product`, :meth:`succ`, :meth:`prec`, :meth:`over` EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: RT = A.basis().keys() sage: x = A.gen(0) sage: A.under(x, x) B[[[., .], .]] """ def und(x, y): return self._monomial(x.under(y)) return self._module_morphism(self._module_morphism(und, position=0, codomain=self), position=1) def coproduct_on_basis(self, x): """ Return the coproduct of a binary tree. EXAMPLES:: sage: A = algebras.FreeDendriform(QQ) sage: x = A.gen(0) sage: ascii_art(A.coproduct(A.one())) # indirect doctest 1 # 1 sage: ascii_art(A.coproduct(x)) # indirect doctest 1 # B + B # 1 o o sage: A = algebras.FreeDendriform(QQ, 'xyz') sage: x, y, z = A.gens() sage: w = A.under(z,A.over(x,y)) sage: A.coproduct(z) B[.] # B[z[., .]] + B[z[., .]] # B[.] sage: A.coproduct(w) B[.] # B[x[z[., .], y[., .]]] + B[x[., .]] # B[z[., y[., .]]] + B[x[., .]] # B[y[z[., .], .]] + B[x[., y[., .]]] # B[z[., .]] + B[x[z[., .], .]] # B[y[., .]] + B[x[z[., .], y[., .]]] # B[.] """ B = self.basis() Trees = B.keys() if not x.node_number(): return self.one().tensor(self.one()) L, R = list(x) try: root = x.label() except AttributeError: root = '@' resu = self.one().tensor(self.monomial(x)) resu += sum(cL * cR * self.monomial(Trees([LL[0], RR[0]], root)).tensor( self.monomial(LL[1]) * self.monomial(RR[1])) for LL, cL in self.coproduct_on_basis(L) for RR, cR in self.coproduct_on_basis(R)) return resu # after this line : coercion def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. EXAMPLES:: sage: R = algebras.FreeDendriform(QQ, 'xy') sage: x, y = R.gens() sage: R(x) B[x[., .]] sage: R(x+4*y) B[x[., .]] + 4*B[y[., .]] sage: Trees = R.basis().keys() sage: R(Trees([],'x')) B[x[., .]] sage: D = algebras.FreeDendriform(ZZ, 'xy') sage: X, Y = D.gens() sage: R(X-Y).parent() Free Dendriform algebra on 2 generators ['x', 'y'] over Rational Field """ if x in self.basis().keys(): return self.monomial(x) try: P = x.parent() if isinstance(P, FreeDendriformAlgebra): if P is self: return x return self.element_class(self, x.monomial_coefficients()) except AttributeError: raise TypeError('not able to coerce this in this algebra') # Ok, not a dendriform algebra element (or should not be viewed as one). def _coerce_map_from_(self, R): r""" Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are - free dendriform algebras in a subset of variables of ``self`` over a base with a coercion map into ``self.base_ring()`` EXAMPLES:: sage: F = algebras.FreeDendriform(GF(7), 'xyz'); F Free Dendriform algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 Elements of the free dendriform algebra canonically coerce in:: sage: x, y, z = F.gens() sage: F.coerce(x+y) == x+y True The free dendriform algebra over `\ZZ` on `x, y, z` coerces in, since `\ZZ` coerces to `\GF{7}`:: sage: G = algebras.FreeDendriform(ZZ, 'xyz') sage: Gx,Gy,Gz = G.gens() sage: z = F.coerce(Gx+Gy); z B[x[., .]] + B[y[., .]] sage: z.parent() is F True However, `\GF{7}` does not coerce to `\ZZ`, so the free dendriform algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: sage: G.coerce(y) Traceback (most recent call last): ... TypeError: no canonical coercion from Free Dendriform algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 to Free Dendriform algebra on 3 generators ['x', 'y', 'z'] over Integer Ring TESTS:: sage: F = algebras.FreeDendriform(ZZ, 'xyz') sage: G = algebras.FreeDendriform(QQ, 'xyz') sage: H = algebras.FreeDendriform(ZZ, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) True sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) False sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False """ # free dendriform algebras in a subset of variables # over any base that coerces in: if isinstance(R, FreeDendriformAlgebra): if all(x in self.variable_names() for x in R.variable_names()): if self.base_ring().has_coerce_map_from(R.base_ring()): return True return False def construction(self): """ Return a pair ``(F, R)``, where ``F`` is a :class:`DendriformFunctor` and `R` is a ring, such that ``F(R)`` returns ``self``. EXAMPLES:: sage: P = algebras.FreeDendriform(ZZ, 'x,y') sage: x,y = P.gens() sage: F, R = P.construction() sage: F Dendriform[x,y] sage: R Integer Ring sage: F(ZZ) is P True sage: F(QQ) Free Dendriform algebra on 2 generators ['x', 'y'] over Rational Field """ return DendriformFunctor(self.variable_names()), self.base_ring()
class FreePreLieAlgebra(CombinatorialFreeModule): r""" The free pre-Lie algebra. Pre-Lie algebras are non-associative algebras, where the product `*` satisfies .. MATH:: (x * y) * z - x * (y * z) = (x * z) * y - x * (z * y). We use here the convention where the associator .. MATH:: (x, y, z) := (x * y) * z - x * (y * z) is symmetric in its two rightmost arguments. This is sometimes called a right pre-Lie algebra. They have appeared in numerical analysis and deformation theory. The free Pre-Lie algebra on a given set `E` has an explicit description using rooted trees, just as the free associative algebra can be described using words. The underlying vector space has a basis indexed by finite rooted trees endowed with a map from their vertices to `E`. In this basis, the product of two (decorated) rooted trees `S * T` is the sum over vertices of `S` of the rooted tree obtained by adding one edge from the root of `T` to the given vertex of `S`. The root of these trees is taken to be the root of `S`. The free pre-Lie algebra can also be considered as the free algebra over the PreLie operad. .. WARNING:: The usual binary operator ``*`` can be used for the pre-Lie product. Beware that it but must be parenthesized properly, as the pre-Lie product is not associative. By default, a multiple product will be taken with left parentheses. EXAMPLES:: sage: F = algebras.FreePreLie(ZZ, 'xyz') sage: x,y,z = F.gens() sage: (x * y) * z B[x[y[z[]]]] + B[x[y[], z[]]] sage: (x * y) * z - x * (y * z) == (x * z) * y - x * (z * y) True The free pre-Lie algebra is non-associative:: sage: x * (y * z) == (x * y) * z False The default product is with left parentheses:: sage: x * y * z == (x * y) * z True sage: x * y * z * x == ((x * y) * z) * x True The NAP product as defined in [Liv2006]_ is also implemented on the same vector space:: sage: N = F.nap_product sage: N(x*y,z*z) B[x[y[], z[z[]]]] When ``None`` is given as input, unlabelled trees are used instead:: sage: F1 = algebras.FreePreLie(QQ, None) sage: w = F1.gen(0); w B[[]] sage: w * w * w * w B[[[[[]]]]] + B[[[[], []]]] + 3*B[[[], [[]]]] + B[[[], [], []]] However, it is equally possible to use labelled trees instead:: sage: F1 = algebras.FreePreLie(QQ, 'q') sage: w = F1.gen(0); w B[q[]] sage: w * w * w * w B[q[q[q[q[]]]]] + B[q[q[q[], q[]]]] + 3*B[q[q[], q[q[]]]] + B[q[q[], q[], q[]]] The set `E` can be infinite:: sage: F = algebras.FreePreLie(QQ, ZZ) sage: w = F.gen(1); w B[1[]] sage: x = F.gen(2); x B[-1[]] sage: y = F.gen(3); y B[2[]] sage: w*x B[1[-1[]]] sage: (w*x)*y B[1[-1[2[]]]] + B[1[-1[], 2[]]] sage: w*(x*y) B[1[-1[2[]]]] .. NOTE:: Variables names can be ``None``, a list of strings, a string or an integer. When ``None`` is given, unlabelled rooted trees are used. When a single string is given, each letter is taken as a variable. See :func:`sage.combinat.words.alphabet.build_alphabet`. .. WARNING:: Beware that the underlying combinatorial free module is based either on ``RootedTrees`` or on ``LabelledRootedTrees``, with no restriction on the labellings. This means that all code calling the :meth:`basis` method would not give meaningful results, since :meth:`basis` returns many "chaff" elements that do not belong to the algebra. REFERENCES: - [ChLi]_ - [Liv2006]_ """ @staticmethod def __classcall_private__(cls, R, names=None): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: F1 = algebras.FreePreLie(QQ, 'xyz') sage: F2 = algebras.FreePreLie(QQ, 'x,y,z') sage: F3 = algebras.FreePreLie(QQ, ['x','y','z']) sage: F4 = algebras.FreePreLie(QQ, Alphabet('xyz')) sage: F1 is F2 and F1 is F3 and F1 is F4 True """ if names is not None: if ',' in names: names = [u for u in names if u != ','] names = Alphabet(names) if R not in Rings(): raise TypeError("argument R must be a ring") return super(FreePreLieAlgebra, cls).__classcall__(cls, R, names) def __init__(self, R, names=None): """ Initialize ``self``. TESTS:: sage: A = algebras.FreePreLie(QQ, '@'); A Free PreLie algebra on one generator ['@'] over Rational Field sage: TestSuite(A).run() sage: A = algebras.FreePreLie(QQ, None); A Free PreLie algebra on one generator ['o'] over Rational Field sage: F = algebras.FreePreLie(QQ, 'xy') sage: TestSuite(F).run() # long time """ if names is None: Trees = RootedTrees() key = RootedTree.sort_key self._alphabet = Alphabet(['o']) else: Trees = LabelledRootedTrees() key = LabelledRootedTree.sort_key self._alphabet = names # Here one would need LabelledRootedTrees(names) # so that one can restrict the labels to some fixed set cat = MagmaticAlgebras(R).WithBasis().Graded() & LieAlgebras(R).WithBasis().Graded() CombinatorialFreeModule.__init__(self, R, Trees, latex_prefix="", sorting_key=key, category=cat) def variable_names(self): r""" Return the names of the variables. EXAMPLES:: sage: R = algebras.FreePreLie(QQ, 'xy') sage: R.variable_names() {'x', 'y'} sage: R = algebras.FreePreLie(QQ, None) sage: R.variable_names() {'o'} """ return self._alphabet def _repr_(self): """ Return the string representation of ``self``. EXAMPLES:: sage: algebras.FreePreLie(QQ, '@') # indirect doctest Free PreLie algebra on one generator ['@'] over Rational Field """ n = self.algebra_generators().cardinality() if n == 1: gen = "one generator" else: gen = "{} generators".format(n) s = "Free PreLie algebra on {} {} over {}" try: return s.format(gen, self._alphabet.list(), self.base_ring()) except NotImplementedError: return s.format(gen, self._alphabet, self.base_ring()) def gen(self, i): r""" Return the ``i``-th generator of the algebra. INPUT: - ``i`` -- an integer EXAMPLES:: sage: F = algebras.FreePreLie(ZZ, 'xyz') sage: F.gen(0) B[x[]] sage: F.gen(4) Traceback (most recent call last): ... IndexError: argument i (= 4) must be between 0 and 2 """ G = self.algebra_generators() n = G.cardinality() if i < 0 or not i < n: m = "argument i (= {}) must be between 0 and {}".format(i, n - 1) raise IndexError(m) return G[G.keys().unrank(i)] @cached_method def algebra_generators(self): r""" Return the generators of this algebra. These are the rooted trees with just one vertex. EXAMPLES:: sage: A = algebras.FreePreLie(ZZ, 'fgh'); A Free PreLie algebra on 3 generators ['f', 'g', 'h'] over Integer Ring sage: list(A.algebra_generators()) [B[f[]], B[g[]], B[h[]]] sage: A = algebras.FreePreLie(QQ, ['x1','x2']) sage: list(A.algebra_generators()) [B[x1[]], B[x2[]]] """ Trees = self.basis().keys() return Family(self._alphabet, lambda a: self.monomial(Trees([], a))) def change_ring(self, R): """ Return the free pre-Lie algebra in the same variables over `R`. INPUT: - `R` -- a ring EXAMPLES:: sage: A = algebras.FreePreLie(ZZ, 'fgh') sage: A.change_ring(QQ) Free PreLie algebra on 3 generators ['f', 'g', 'h'] over Rational Field """ return FreePreLieAlgebra(R, names=self.variable_names()) def gens(self): """ Return the generators of ``self`` (as an algebra). EXAMPLES:: sage: A = algebras.FreePreLie(ZZ, 'fgh') sage: A.gens() (B[f[]], B[g[]], B[h[]]) """ return tuple(self.algebra_generators()) def degree_on_basis(self, t): """ Return the degree of a rooted tree in the free Pre-Lie algebra. This is the number of vertices. EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: A.degree_on_basis(RT([RT([])])) 2 """ return t.node_number() @cached_method def an_element(self): """ Return an element of ``self``. EXAMPLES:: sage: A = algebras.FreePreLie(QQ, 'xy') sage: A.an_element() B[x[x[x[x[]]]]] + B[x[x[], x[x[]]]] """ o = self.gen(0) return (o * o) * (o * o) def some_elements(self): """ Return some elements of the free pre-Lie algebra. EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: A.some_elements() [B[[]], B[[[]]], B[[[[[]]]]] + B[[[], [[]]]], B[[[[]]]] + B[[[], []]], B[[[]]]] With several generators:: sage: A = algebras.FreePreLie(QQ, 'xy') sage: A.some_elements() [B[x[]], B[x[x[]]], B[x[x[x[x[]]]]] + B[x[x[], x[x[]]]], B[x[x[x[]]]] + B[x[x[], x[]]], B[x[x[y[]]]] + B[x[x[], y[]]]] """ o = self.gen(0) x = o * o y = o G = self.algebra_generators() # Take only the first 3 generators, otherwise the final element is too big if G.cardinality() < 3: for w in G: y = y * w else: K = G.keys() for i in range(3): y = y * G[K.unrank(i)] return [o, x, x * x, x * o, y] def product_on_basis(self, x, y): """ Return the pre-Lie product of two trees. This is the sum over all graftings of the root of `y` over a vertex of `x`. The root of the resulting trees is the root of `x`. .. SEEALSO:: :meth:`pre_Lie_product` EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: x = RT([RT([])]) sage: A.product_on_basis(x, x) B[[[[[]]]]] + B[[[], [[]]]] """ return self.sum(self.basis()[u] for u in x.graft_list(y)) pre_Lie_product_on_basis = product_on_basis @lazy_attribute def pre_Lie_product(self): """ Return the pre-Lie product. .. SEEALSO:: :meth:`pre_Lie_product_on_basis` EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: x = A(RT([RT([])])) sage: A.pre_Lie_product(x, x) B[[[[[]]]]] + B[[[], [[]]]] """ plb = self.pre_Lie_product_on_basis return self._module_morphism(self._module_morphism(plb, position=0, codomain=self), position=1) def bracket_on_basis(self, x, y): r""" Return the Lie bracket of two trees. This is the commutator `[x, y] = x * y - y * x` of the pre-Lie product. .. SEEALSO:: :meth:`pre_Lie_product_on_basis` EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: x = RT([RT([])]) sage: y = RT([x]) sage: A.bracket_on_basis(x, y) -B[[[[], [[]]]]] + B[[[], [[[]]]]] - B[[[[]], [[]]]] """ return self.product_on_basis(x, y) - self.product_on_basis(y, x) def nap_product_on_basis(self, x, y): """ Return the NAP product of two trees. This is the grafting of the root of `y` over the root of `x`. The root of the resulting tree is the root of `x`. .. SEEALSO:: :meth:`nap_product` EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: x = RT([RT([])]) sage: A.nap_product_on_basis(x, x) B[[[], [[]]]] """ return self.basis()[x.graft_on_root(y)] @lazy_attribute def nap_product(self): """ Return the NAP product. .. SEEALSO:: :meth:`nap_product_on_basis` EXAMPLES:: sage: A = algebras.FreePreLie(QQ, None) sage: RT = A.basis().keys() sage: x = A(RT([RT([])])) sage: A.nap_product(x, x) B[[[], [[]]]] """ npb = self.nap_product_on_basis return self._module_morphism(self._module_morphism(npb, position=0, codomain=self), position=1) def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. EXAMPLES:: sage: R = algebras.FreePreLie(QQ, 'xy') sage: x, y = R.gens() sage: R(x) B[x[]] sage: R(x+4*y) B[x[]] + 4*B[y[]] sage: Trees = R.basis().keys() sage: R(Trees([],'x')) B[x[]] sage: D = algebras.FreePreLie(ZZ, 'xy') sage: X, Y = D.gens() sage: R(X-Y).parent() Free PreLie algebra on 2 generators ['x', 'y'] over Rational Field TESTS:: sage: R.<x,y> = algebras.FreePreLie(QQ) sage: S.<z> = algebras.FreePreLie(GF(3)) sage: R(z) Traceback (most recent call last): ... TypeError: not able to convert this to this algebra """ if (isinstance(x, (RootedTree, LabelledRootedTree)) and x in self.basis().keys()): return self.monomial(x) try: P = x.parent() if isinstance(P, FreePreLieAlgebra): if P is self: return x if self._coerce_map_from_(P): return self.element_class(self, x.monomial_coefficients()) except AttributeError: raise TypeError('not able to convert this to this algebra') else: raise TypeError('not able to convert this to this algebra') # Ok, not a pre-Lie algebra element (or should not be viewed as one). def _coerce_map_from_(self, R): r""" Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are - free pre-Lie algebras whose set `E` of labels is a subset of the corresponding self of ``set`, and whose base ring has a coercion map into ``self.base_ring()`` EXAMPLES:: sage: F = algebras.FreePreLie(GF(7), 'xyz'); F Free PreLie algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 Elements of the free pre-Lie algebra canonically coerce in:: sage: x, y, z = F.gens() sage: F.coerce(x+y) == x+y True The free pre-Lie algebra over `\ZZ` on `x, y, z` coerces in, since `\ZZ` coerces to `\GF{7}`:: sage: G = algebras.FreePreLie(ZZ, 'xyz') sage: Gx,Gy,Gz = G.gens() sage: z = F.coerce(Gx+Gy); z B[x[]] + B[y[]] sage: z.parent() is F True However, `\GF{7}` does not coerce to `\ZZ`, so the free pre-Lie algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: sage: G.coerce(y) Traceback (most recent call last): ... TypeError: no canonical coercion from Free PreLie algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 to Free PreLie algebra on 3 generators ['x', 'y', 'z'] over Integer Ring TESTS:: sage: F = algebras.FreePreLie(ZZ, 'xyz') sage: G = algebras.FreePreLie(QQ, 'xyz') sage: H = algebras.FreePreLie(ZZ, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) True sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) False sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False """ # free prelie algebras in a subset of variables # over any base that coerces in: if isinstance(R, FreePreLieAlgebra): if all(x in self.variable_names() for x in R.variable_names()): if self.base_ring().has_coerce_map_from(R.base_ring()): return True return False def construction(self): """ Return a pair ``(F, R)``, where ``F`` is a :class:`PreLieFunctor` and `R` is a ring, such that ``F(R)`` returns ``self``. EXAMPLES:: sage: P = algebras.FreePreLie(ZZ, 'x,y') sage: x,y = P.gens() sage: F, R = P.construction() sage: F PreLie[x,y] sage: R Integer Ring sage: F(ZZ) is P True sage: F(QQ) Free PreLie algebra on 2 generators ['x', 'y'] over Rational Field """ return PreLieFunctor(self.variable_names()), self.base_ring()
class GrossmanLarsonAlgebra(CombinatorialFreeModule): r""" The Grossman-Larson Hopf Algebra. The Grossman-Larson Hopf Algebras are Hopf algebras with a basis indexed by forests of decorated rooted trees. They are the universal enveloping algebras of free pre-Lie algebras, seen as Lie algebras. The Grossman-Larson Hopf algebra on a given set `E` has an explicit description using rooted forests. The underlying vector space has a basis indexed by finite rooted forests endowed with a map from their vertices to `E` (called the "labeling"). In this basis, the product of two (decorated) rooted forests `S * T` is a sum over all maps from the set of roots of `T` to the union of a singleton `\{\#\}` and the set of vertices of `S`. Given such a map, one defines a new forest as follows. Starting from the disjoint union of all rooted trees of `S` and `T`, one adds an edge from every root of `T` to its image when this image is not the fake vertex labelled ``#``. The coproduct sends a rooted forest `T` to the sum of all tensors `T_1 \otimes T_2` obtained by splitting the connected components of `T` into two subsets and letting `T_1` be the forest formed by the first subset and `T_2` the forest formed by the second. This yields a connected graded Hopf algebra (the degree of a forest is its number of vertices). See [Pana2002]_ (Section 2) and [GroLar1]_. (Note that both references use rooted trees rather than rooted forests, so think of each rooted forest grafted onto a new root. Also, the product is reversed, so they are defining the opposite algebra structure.) .. WARNING:: For technical reasons, instead of using forests as labels for the basis, we use rooted trees. Their root vertex should be considered as a fake vertex. This fake root vertex is labelled ``'#'`` when labels are present. EXAMPLES:: sage: G = algebras.GrossmanLarson(QQ, 'xy') sage: x, y = G.single_vertex_all() sage: ascii_art(x*y) B + B # #_ | / / x x y | y sage: ascii_art(x*x*x) B + B + 3*B + B # # #_ _#__ | | / / / / / x x_ x x x x x | / / | x x x x | x The Grossman-Larson algebra is associative:: sage: z = x * y sage: x * (y * z) == (x * y) * z True It is not commutative:: sage: x * y == y * x False When ``None`` is given as input, unlabelled forests are used instead; this corresponds to a `1`-element set `E`:: sage: G = algebras.GrossmanLarson(QQ, None) sage: x = G.single_vertex_all()[0] sage: ascii_art(x*x) B + B o o_ | / / o o o | o .. NOTE:: Variables names can be ``None``, a list of strings, a string or an integer. When ``None`` is given, unlabelled rooted forests are used. When a single string is given, each letter is taken as a variable. See :func:`sage.combinat.words.alphabet.build_alphabet`. .. WARNING:: Beware that the underlying combinatorial free module is based either on ``RootedTrees`` or on ``LabelledRootedTrees``, with no restriction on the labellings. This means that all code calling the :meth:`basis` method would not give meaningful results, since :meth:`basis` returns many "chaff" elements that do not belong to the algebra. REFERENCES: - [Pana2002]_ - [GroLar1]_ """ @staticmethod def __classcall_private__(cls, R, names=None): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: F1 = algebras.GrossmanLarson(QQ, 'xyz') sage: F2 = algebras.GrossmanLarson(QQ, ['x','y','z']) sage: F3 = algebras.GrossmanLarson(QQ, Alphabet('xyz')) sage: F1 is F2 and F1 is F3 True """ if names is not None: if names not in ZZ and ',' in names: names = [u for u in names if u != ','] names = Alphabet(names) if R not in Rings(): raise TypeError("argument R must be a ring") return super(GrossmanLarsonAlgebra, cls).__classcall__(cls, R, names) def __init__(self, R, names=None): """ Initialize ``self``. TESTS:: sage: A = algebras.GrossmanLarson(QQ, '@'); A Grossman-Larson Hopf algebra on one generator ['@'] over Rational Field sage: TestSuite(A).run() # long time sage: F = algebras.GrossmanLarson(QQ, 'xy') sage: TestSuite(F).run() # long time sage: A = algebras.GrossmanLarson(QQ, None); A Grossman-Larson Hopf algebra on one generator ['o'] over Rational Field sage: F = algebras.GrossmanLarson(QQ, ['x','y']); F Grossman-Larson Hopf algebra on 2 generators ['x', 'y'] over Rational Field sage: A = algebras.GrossmanLarson(QQ, []); A Grossman-Larson Hopf algebra on 0 generators [] over Rational Field """ if names is None: Trees = RootedTrees() key = RootedTree.sort_key self._alphabet = Alphabet(['o']) else: Trees = LabelledRootedTrees() key = LabelledRootedTree.sort_key self._alphabet = names # Here one would need LabelledRootedTrees(names) # so that one can restrict the labels to some fixed set cat = HopfAlgebras(R).WithBasis().Graded() CombinatorialFreeModule.__init__(self, R, Trees, latex_prefix="", sorting_key=key, category=cat) def variable_names(self): r""" Return the names of the variables. This returns the set `E` (as a family). EXAMPLES:: sage: R = algebras.GrossmanLarson(QQ, 'xy') sage: R.variable_names() {'x', 'y'} sage: R = algebras.GrossmanLarson(QQ, ['a','b']) sage: R.variable_names() {'a', 'b'} sage: R = algebras.GrossmanLarson(QQ, 2) sage: R.variable_names() {0, 1} sage: R = algebras.GrossmanLarson(QQ, None) sage: R.variable_names() {'o'} """ return self._alphabet def _repr_(self): """ Return the string representation of ``self``. EXAMPLES:: sage: algebras.GrossmanLarson(QQ, '@') # indirect doctest Grossman-Larson Hopf algebra on one generator ['@'] over Rational Field sage: algebras.GrossmanLarson(QQ, None) # indirect doctest Grossman-Larson Hopf algebra on one generator ['o'] over Rational Field sage: algebras.GrossmanLarson(QQ, ['a','b']) Grossman-Larson Hopf algebra on 2 generators ['a', 'b'] over Rational Field """ n = len(self.single_vertex_all()) if n == 1: gen = "one generator" else: gen = "{} generators".format(n) s = "Grossman-Larson Hopf algebra on {} {} over {}" try: return s.format(gen, self._alphabet.list(), self.base_ring()) except NotImplementedError: return s.format(gen, self._alphabet, self.base_ring()) def single_vertex(self, i): r""" Return the ``i``-th rooted forest with one vertex. This is the rooted forest with just one vertex, labelled by the ``i``-th element of the label list. .. SEEALSO:: :meth:`single_vertex_all`. INPUT: - ``i`` -- a nonnegative integer EXAMPLES:: sage: F = algebras.GrossmanLarson(ZZ, 'xyz') sage: F.single_vertex(0) B[#[x[]]] sage: F.single_vertex(4) Traceback (most recent call last): ... IndexError: argument i (= 4) must be between 0 and 2 """ G = self.single_vertex_all() n = len(G) if i < 0 or not i < n: m = "argument i (= {}) must be between 0 and {}".format(i, n - 1) raise IndexError(m) return G[i] def single_vertex_all(self): """ Return the rooted forests with one vertex in ``self``. They freely generate the Lie algebra of primitive elements as a pre-Lie algebra. .. SEEALSO:: :meth:`single_vertex`. EXAMPLES:: sage: A = algebras.GrossmanLarson(ZZ, 'fgh') sage: A.single_vertex_all() (B[#[f[]]], B[#[g[]]], B[#[h[]]]) sage: A = algebras.GrossmanLarson(QQ, ['x1','x2']) sage: A.single_vertex_all() (B[#[x1[]]], B[#[x2[]]]) sage: A = algebras.GrossmanLarson(ZZ, None) sage: A.single_vertex_all() (B[[[]]],) """ Trees = self.basis().keys() return tuple( Family(self._alphabet, lambda a: self.monomial(Trees([Trees([], a)], ROOT)))) def _first_ngens(self, n): """ Return the first generators. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, ['x1','x2']) sage: A._first_ngens(2) (B[#[x1[]]], B[#[x2[]]]) sage: A = algebras.GrossmanLarson(ZZ, None) sage: A._first_ngens(1) (B[[[]]],) """ return self.single_vertex_all()[:n] def change_ring(self, R): """ Return the Grossman-Larson algebra in the same variables over `R`. INPUT: - `R` -- a ring EXAMPLES:: sage: A = algebras.GrossmanLarson(ZZ, 'fgh') sage: A.change_ring(QQ) Grossman-Larson Hopf algebra on 3 generators ['f', 'g', 'h'] over Rational Field """ return GrossmanLarsonAlgebra(R, names=self.variable_names()) def degree_on_basis(self, t): """ Return the degree of a rooted forest in the Grossman-Larson algebra. This is the total number of vertices of the forest. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, '@') sage: RT = A.basis().keys() sage: A.degree_on_basis(RT([RT([])])) 1 """ return t.node_number() - 1 @cached_method def an_element(self): """ Return an element of ``self``. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, 'xy') sage: A.an_element() B[#[x[]]] + 2*B[#[x[x[]]]] + 2*B[#[x[], x[]]] """ o = self.single_vertex(0) return o + 2 * o * o def some_elements(self): """ Return some elements of the Grossman-Larson Hopf algebra. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, None) sage: A.some_elements() [B[[[]]], B[[]] + B[[[[]]]] + B[[[], []]], 4*B[[[[]]]] + 4*B[[[], []]]] With several generators:: sage: A = algebras.GrossmanLarson(QQ, 'xy') sage: A.some_elements() [B[#[x[]]], B[#[]] + B[#[x[x[]]]] + B[#[x[], x[]]], B[#[x[x[]]]] + 3*B[#[x[y[]]]] + B[#[x[], x[]]] + 3*B[#[x[], y[]]]] """ o = self.single_vertex(0) o1 = self.single_vertex_all()[-1] x = o * o y = o * o1 return [o, 1 + x, x + 3 * y] def product_on_basis(self, x, y): """ Return the product of two forests `x` and `y`. This is the sum over all possible ways for the components of the forest `y` to either fall side-by-side with components of `x` or be grafted on a vertex of `x`. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, None) sage: RT = A.basis().keys() sage: x = RT([RT([])]) sage: A.product_on_basis(x, x) B[[[[]]]] + B[[[], []]] Check that the product is the correct one:: sage: A = algebras.GrossmanLarson(QQ, 'uv') sage: RT = A.basis().keys() sage: Tu = RT([RT([],'u')],'#') sage: Tv = RT([RT([],'v')],'#') sage: A.product_on_basis(Tu, Tv) B[#[u[v[]]]] + B[#[u[], v[]]] """ return self.sum( self.basis()[x.single_graft(y, graftingFunction)] for graftingFunction in product(list(x.paths()), repeat=len(y))) def one_basis(self): """ Return the empty rooted forest. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, 'ab') sage: A.one_basis() #[] sage: A = algebras.GrossmanLarson(QQ, None) sage: A.one_basis() [] """ Trees = self.basis().keys() return Trees([], ROOT) def coproduct_on_basis(self, x): """ Return the coproduct of a forest. EXAMPLES:: sage: G = algebras.GrossmanLarson(QQ,2) sage: x, y = G.single_vertex_all() sage: ascii_art(G.coproduct(x)) # indirect doctest 1 # B + B # 1 # # | | 0 0 sage: Delta_xy = G.coproduct(y*x) sage: ascii_art(Delta_xy) # random indirect doctest 1 # B + 1 # B + B # B + B # 1 + B # B + B # 1 #_ # # # #_ # # # / / | | | / / | | | 0 1 1 0 1 0 1 1 0 1 | | 0 0 TESTS:: sage: Delta_xy.coefficients() [1, 1, 1, 1, 1, 1] sage: sortkey = G.print_options()['sorting_key'] sage: doublekey = lambda tt: (sortkey(tt[0]), sortkey(tt[1])) sage: sorted(Delta_xy.monomial_coefficients(), key=doublekey) [(#[], #[1[0[]]]), (#[], #[0[], 1[]]), (#[0[]], #[1[]]), (#[1[]], #[0[]]), (#[1[0[]]], #[]), (#[0[], 1[]], #[])] """ B = self.basis() Trees = B.keys() subtrees = list(x) num_subtrees = len(subtrees) indx = list(range(num_subtrees)) return sum(B[Trees([subtrees[i] for i in S], ROOT)].tensor(B[Trees( [subtrees[i] for i in indx if i not in S], ROOT)]) for k in range(num_subtrees + 1) for S in combinations(indx, k)) def counit_on_basis(self, x): """ Return the counit on a basis element. This is zero unless the forest `x` is empty. EXAMPLES:: sage: A = algebras.GrossmanLarson(QQ, 'xy') sage: RT = A.basis().keys() sage: x = RT([RT([],'x')],'#') sage: A.counit_on_basis(x) 0 sage: A.counit_on_basis(RT([],'#')) 1 """ if x.node_number() == 1: return self.base_ring().one() return self.base_ring().zero() def antipode_on_basis(self, x): """ Return the antipode of a forest. EXAMPLES:: sage: G = algebras.GrossmanLarson(QQ,2) sage: x, y = G.single_vertex_all() sage: G.antipode(x) # indirect doctest -B[#[0[]]] sage: G.antipode(y*x) # indirect doctest B[#[0[1[]]]] + B[#[0[], 1[]]] """ B = self.basis() Trees = B.keys() subtrees = list(x) if not subtrees: return self.one() num_subtrees = len(subtrees) indx = list(range(num_subtrees)) return sum( -self.antipode_on_basis(Trees([subtrees[i] for i in S], ROOT)) * B[Trees([subtrees[i] for i in indx if i not in S], ROOT)] for k in range(num_subtrees) for S in combinations(indx, k)) def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. EXAMPLES:: sage: R = algebras.GrossmanLarson(QQ, 'xy') sage: x, y = R.single_vertex_all() sage: R(x) B[#[x[]]] sage: R(x+4*y) B[#[x[]]] + 4*B[#[y[]]] sage: Trees = R.basis().keys() sage: R(Trees([],'#')) B[#[]] sage: D = algebras.GrossmanLarson(ZZ, 'xy') sage: X, Y = D.single_vertex_all() sage: R(X-Y).parent() Grossman-Larson Hopf algebra on 2 generators ['x', 'y'] over Rational Field TESTS:: sage: Trees = R.basis().keys() sage: R(Trees([],'x')) Traceback (most recent call last): ... ValueError: incorrect root label sage: R.<x,y> = algebras.GrossmanLarson(QQ) sage: R(x) is x True sage: S.<z> = algebras.GrossmanLarson(GF(3)) sage: R(z) Traceback (most recent call last): ... TypeError: not able to convert this to this algebra """ if (isinstance(x, (RootedTree, LabelledRootedTree)) and x in self.basis().keys()): if hasattr(x, 'label') and x.label() != ROOT: raise ValueError('incorrect root label') return self.monomial(x) try: P = x.parent() if isinstance(P, GrossmanLarsonAlgebra): if P is self: return x if self._coerce_map_from_(P): return self.element_class(self, x.monomial_coefficients()) except AttributeError: raise TypeError('not able to convert this to this algebra') else: raise TypeError('not able to convert this to this algebra') # Ok, not an element (or should not be viewed as one). def _coerce_map_from_(self, R): r""" Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are - Grossman-Larson Hopf algebras whose set `E` of labels is a subset of the corresponding self of ``set`, and whose base ring has a coercion map into ``self.base_ring()`` EXAMPLES:: sage: F = algebras.GrossmanLarson(GF(7), 'xyz'); F Grossman-Larson Hopf algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 Elements of the Grossman-Larson Hopf algebra canonically coerce in:: sage: x, y, z = F.single_vertex_all() sage: F.coerce(x+y) == x+y True The Grossman-Larson Hopf algebra over `\ZZ` on `x, y, z` coerces in, since `\ZZ` coerces to `\GF{7}`:: sage: G = algebras.GrossmanLarson(ZZ, 'xyz') sage: Gx,Gy,Gz = G.single_vertex_all() sage: z = F.coerce(Gx+Gy); z B[#[x[]]] + B[#[y[]]] sage: z.parent() is F True However, `\GF{7}` does not coerce to `\ZZ`, so the Grossman-Larson algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: sage: G.coerce(y) Traceback (most recent call last): ... TypeError: no canonical coercion from Grossman-Larson Hopf algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 to Grossman-Larson Hopf algebra on 3 generators ['x', 'y', 'z'] over Integer Ring TESTS:: sage: F = algebras.GrossmanLarson(ZZ, 'xyz') sage: G = algebras.GrossmanLarson(QQ, 'xyz') sage: H = algebras.GrossmanLarson(ZZ, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) True sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) False sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False """ # Grossman-Larson algebras containing the same variables # over any base that coerces in: if isinstance(R, GrossmanLarsonAlgebra): if all(x in self.variable_names() for x in R.variable_names()): if self.base_ring().has_coerce_map_from(R.base_ring()): return True return False
class ShuffleAlgebra(CombinatorialFreeModule): r""" The shuffle algebra on some generators over a base ring. Shuffle algebras are commutative and associative algebras, with a basis indexed by words. The product of two words `w_1 \cdot w_2` is given by the sum over the shuffle product of `w_1` and `w_2`. .. SEEALSO:: For more on shuffle products, see :mod:`~sage.combinat.words.shuffle_product` and :meth:`~sage.combinat.words.finite_word.FiniteWord_class.shuffle()`. REFERENCES: - :wikipedia:`Shuffle algebra` INPUT: - ``R`` -- ring - ``names`` -- generator names (string) EXAMPLES:: sage: F = ShuffleAlgebra(QQ, 'xyz'); F Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field sage: mul(F.gens()) B[word: xyz] + B[word: xzy] + B[word: yxz] + B[word: yzx] + B[word: zxy] + B[word: zyx] sage: mul([ F.gen(i) for i in range(2) ]) + mul([ F.gen(i+1) for i in range(2) ]) B[word: xy] + B[word: yx] + B[word: yz] + B[word: zy] sage: S = ShuffleAlgebra(ZZ, 'abcabc'); S Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring sage: S.base_ring() Integer Ring sage: G = ShuffleAlgebra(S, 'mn'); G Shuffle Algebra on 2 generators ['m', 'n'] over Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring sage: G.base_ring() Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring Shuffle algebras commute with their base ring:: sage: K = ShuffleAlgebra(QQ,'ab') sage: a,b = K.gens() sage: K.is_commutative() True sage: L = ShuffleAlgebra(K,'cd') sage: c,d = L.gens() sage: L.is_commutative() True sage: s = a*b^2 * c^3; s (12*B[word:abb]+12*B[word:bab]+12*B[word:bba])*B[word: ccc] sage: parent(s) Shuffle Algebra on 2 generators ['c', 'd'] over Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field sage: c^3 * a * b^2 (12*B[word:abb]+12*B[word:bab]+12*B[word:bba])*B[word: ccc] Shuffle algebras are commutative:: sage: c^3 * b * a * b == c * a * c * b^2 * c True We can also manipulate elements in the basis and coerce elements from our base field:: sage: F = ShuffleAlgebra(QQ, 'abc') sage: B = F.basis() sage: B[Word('bb')] * B[Word('ca')] B[word: bbca] + B[word: bcab] + B[word: bcba] + B[word: cabb] + B[word: cbab] + B[word: cbba] sage: 1 - B[Word('bb')] * B[Word('ca')] / 2 B[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] """ def __init__(self, R, names): r""" Initialize ``self``. EXAMPLES:: sage: F = ShuffleAlgebra(QQ, 'xyz'); F Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field sage: TestSuite(F).run() TESTS:: sage: ShuffleAlgebra(24, 'toto') Traceback (most recent call last): ... TypeError: argument R must be a ring """ if R not in Rings(): raise TypeError("argument R must be a ring") self._alphabet = Alphabet(names) self.__ngens = self._alphabet.cardinality() CombinatorialFreeModule.__init__(self, R, Words(names), latex_prefix = "", category = (AlgebrasWithBasis(R), CommutativeAlgebras(R))) def variable_names(self): r""" Return the names of the variables. EXAMPLES:: sage: R = ShuffleAlgebra(QQ,'xy') sage: R.variable_names() {'x', 'y'} """ return self._alphabet def is_commutative(self): r""" Return ``True`` as the shuffle algebra is commutative. EXAMPLES:: sage: R = ShuffleAlgebra(QQ,'x') sage: R.is_commutative() True sage: R = ShuffleAlgebra(QQ,'xy') sage: R.is_commutative() True """ return True def _repr_(self): r""" Text representation of this shuffle algebra. EXAMPLES:: sage: F = ShuffleAlgebra(QQ,'xyz') sage: F # indirect doctest Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field sage: ShuffleAlgebra(ZZ,'a') Shuffle Algebra on one generator ['a'] over Integer Ring """ if self.__ngens == 1: gen = "one generator" else: gen = "%s generators" %self.__ngens return "Shuffle Algebra on "+ gen +" %s over %s"%( self._alphabet.list(), self.base_ring()) @cached_method def one_basis(self): r""" Return the empty word, which index of `1` of this algebra, as per :meth:`AlgebrasWithBasis.ParentMethods.one_basis`. EXAMPLES:: sage: A = ShuffleAlgebra(QQ,'a') sage: A.one_basis() word: sage: A.one() B[word: ] """ return self.basis().keys()([]) def product_on_basis(self, w1, w2): r""" Return the product of basis elements ``w1`` and ``w2``, as per :meth:`AlgebrasWithBasis.ParentMethods.product_on_basis()`. INPUT: - ``w1``, ``w2`` -- Basis elements EXAMPLES:: sage: A = ShuffleAlgebra(QQ,'abc') sage: W = A.basis().keys() sage: A.product_on_basis(W("acb"), W("cba")) B[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] sage: (a,b,c) = A.algebra_generators() sage: a * (1-b)^2 * c 2*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] """ return sum(self.basis()[u] for u in w1.shuffle(w2)) def gen(self,i): r""" The ``i``-th generator of the algebra. INPUT: - ``i`` -- an integer EXAMPLES:: sage: F = ShuffleAlgebra(ZZ,'xyz') sage: F.gen(0) B[word: x] sage: F.gen(4) Traceback (most recent call last): ... IndexError: argument i (= 4) must be between 0 and 2 """ n = self.__ngens if i < 0 or not i < n: raise IndexError("argument i (= %s) must be between 0 and %s"%(i, n-1)) return self.algebra_generators()[i] @cached_method def algebra_generators(self): r""" Return the generators of this algebra. EXAMPLES:: sage: A = ShuffleAlgebra(ZZ,'fgh'); A Shuffle Algebra on 3 generators ['f', 'g', 'h'] over Integer Ring sage: A.algebra_generators() Family (B[word: f], B[word: g], B[word: h]) """ Words = self.basis().keys() return Family( [self.monomial(Words(a)) for a in self._alphabet] ) # FIXME: use this once the keys argument of FiniteFamily will be honoured # for the specifying the order of the elements in the family #return Family(self._alphabet, lambda a: self.term(self.basis().keys()(a))) gens = algebra_generators def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. EXAMPLES:: sage: R = ShuffleAlgebra(QQ,'xy') sage: x, y = R.gens() sage: R(3) # indirect doctest 3*B[word: ] sage: R(x) B[word: x] """ P = x.parent() if isinstance(P, ShuffleAlgebra): if P is self: return x if not (P is self.base_ring()): return self.element_class(self, x.monomial_coefficients()) # ok, not a shuffle algebra element (or should not be viewed as one). if isinstance(x, basestring): from sage.misc.sage_eval import sage_eval return sage_eval(x,locals=self.gens_dict()) R = self.base_ring() # coercion via base ring x = R(x) if x == 0: return self.element_class(self,{}) else: return self.from_base_ring_from_one_basis(x) def _coerce_impl(self, x): r""" Canonical coercion of ``x`` into ``self``. Here is what canonically coerces to ``self``: - this shuffle algebra, - anything that coerces to the base ring of this shuffle algebra, - any shuffle algebra on the same variables, whose base ring coerces to the base ring of this shuffle algebra. EXAMPLES:: sage: F = ShuffleAlgebra(GF(7), 'xyz'); F Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 Elements of the shuffle algebra canonically coerce in:: sage: x, y, z = F.gens() sage: F.coerce(x*y) # indirect doctest B[word: xy] + B[word: yx] Elements of the integers coerce in, since there is a coerce map from `\ZZ` to GF(7):: sage: F.coerce(1) # indirect doctest B[word: ] There is no coerce map from `\QQ` to `\GF{7}`:: sage: F.coerce(2/3) # indirect doctest Traceback (most recent call last): ... TypeError: no canonical coercion from Rational Field to Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 Elements of the base ring coerce in:: sage: F.coerce(GF(7)(5)) 5*B[word: ] The shuffle algebra over `\ZZ` on `x, y, z` coerces in, since `\ZZ` coerces to `\GF{7}`:: sage: G = ShuffleAlgebra(ZZ,'xyz') sage: Gx,Gy,Gz = G.gens() sage: z = F.coerce(Gx**2 * Gy);z 2*B[word: xxy] + 2*B[word: xyx] + 2*B[word: yxx] sage: z.parent() is F True However, `\GF{7}` does not coerce to `\ZZ`, so the shuffle algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: sage: G.coerce(x^3*y) Traceback (most recent call last): ... TypeError: no canonical coercion from Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7 to Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Integer Ring """ try: R = x.parent() # shuffle algebras in the same variables over any base # that coerces in: if isinstance(R,ShuffleAlgebra): if R.variable_names() == self.variable_names(): if self.has_coerce_map_from(R.base_ring()): return self(x) else: raise TypeError("no natural map between bases of shuffle algebras") except AttributeError: pass # any ring that coerces to the base ring of this shuffle algebra. return self._coerce_try(x, [self.base_ring()]) def _coerce_map_from_(self, R): r""" Return ``True`` if there is a coercion from ``R`` into ``self`` and ``False`` otherwise. The things that coerce into ``self`` are - Shuffle Algebras in the same variables over a base with a coercion map into ``self.base_ring()``. - Anything with a coercion into ``self.base_ring()``. TESTS:: sage: F = ShuffleAlgebra(ZZ, 'xyz') sage: G = ShuffleAlgebra(QQ, 'xyz') sage: H = ShuffleAlgebra(ZZ, 'y') sage: F._coerce_map_from_(G) False sage: G._coerce_map_from_(F) True sage: F._coerce_map_from_(H) False sage: F._coerce_map_from_(QQ) False sage: G._coerce_map_from_(QQ) True sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z')) False """ # shuffle algebras in the same variable over any base that coerces in: if isinstance(R, ShuffleAlgebra): if R.variable_names() == self.variable_names(): if self.base_ring().has_coerce_map_from(R.base_ring()): return True else: return False return self.base_ring().has_coerce_map_from(R)
def Permutations_iterator(nintervals=None, irreducible=True, reduced=False, alphabet=None): r""" Returns an iterator over permutations. This iterator allows you to iterate over permutations with given constraints. If you want to iterate over permutations coming from a given stratum you have to use the module :mod:`~sage.dynamics.flat_surfaces.strata` and generate Rauzy diagrams from connected components. INPUT: - ``nintervals`` - non negative integer - ``irreducible`` - boolean (default: True) - ``reduced`` - boolean (default: False) - ``alphabet`` - alphabet (default: None) OUTPUT: iterator -- an iterator over permutations EXAMPLES: Generates all reduced permutations with given number of intervals:: sage: P = iet.Permutations_iterator(nintervals=2,alphabet="ab",reduced=True) sage: for p in P: print p, "\n* *" a b b a * * sage: P = iet.Permutations_iterator(nintervals=3,alphabet="abc",reduced=True) sage: for p in P: print p, "\n* * *" a b c b c a * * * a b c c a b * * * a b c c b a * * * TESTS:: sage: P = iet.Permutations_iterator(nintervals=None, alphabet=None) Traceback (most recent call last): ... ValueError: You must specify an alphabet or a length sage: P = iet.Permutations_iterator(nintervals=None, alphabet=ZZ) Traceback (most recent call last): ... ValueError: You must specify a length with infinite alphabet """ from labelled import LabelledPermutationsIET_iterator from reduced import ReducedPermutationsIET_iterator from sage.combinat.words.alphabet import Alphabet from sage.rings.infinity import Infinity if nintervals is None: if alphabet is None: raise ValueError("You must specify an alphabet or a length") else: alphabet = Alphabet(alphabet) if alphabet.cardinality() is Infinity: raise ValueError("You must specify a length with infinite alphabet") nintervals = alphabet.cardinality() elif alphabet is None: alphabet = range(1, nintervals+1) if reduced: return ReducedPermutationsIET_iterator(nintervals, irreducible=irreducible, alphabet=alphabet) else: return LabelledPermutationsIET_iterator(nintervals, irreducible=irreducible, alphabet=alphabet)