Exemple #1
0
    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
Exemple #2
0
    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)))
Exemple #3
0
    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)
Exemple #4
0
    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)
Exemple #5
0
    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)
Exemple #6
0
    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))
Exemple #8
0
    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))
Exemple #10
0
    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])
Exemple #11
0
    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)
Exemple #12
0
    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
Exemple #13
0
    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
Exemple #14
0
    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)))
Exemple #15
0
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()
Exemple #16
0
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)
Exemple #17
0
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)
Exemple #18
0
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)
Exemple #19
0
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)
Exemple #20
0
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()
Exemple #23
0
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
Exemple #25
0
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)
Exemple #26
0
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)