Exemplo n.º 1
0
    def __init__(self, R, n):
        r"""
        EXAMPLES::

            sage: TestSuite(DescentAlgebra(QQ, 4)).run()
        """
        self._n = n
        self._category = FiniteDimensionalAlgebrasWithBasis(R)
        Parent.__init__(self, base=R, category=self._category.WithRealizations())
    def __init__(self, base_ring):
        r"""
        EXAMPLES::

            sage: A = FiniteDimensionalAlgebrasWithBasis(QQ).example(); A
            An example of a finite dimensional algebra with basis:
            the path algebra of the Kronecker quiver
            (containing the arrows a:x->y and b:x->y) over Rational Field
            sage: TestSuite(A).run()
        """
        basis_keys = ['x', 'y', 'a', 'b']
        self._nonzero_products = {
            'xx': 'x',
            'xa': 'a',
            'xb': 'b',
            'yy': 'y',
            'ay': 'a',
            'by': 'b'
        }

        CombinatorialFreeModule.__init__(
            self,
            base_ring,
            basis_keys,
            category=FiniteDimensionalAlgebrasWithBasis(base_ring))
Exemplo n.º 3
0
    def super_categories(self):
        """
        EXAMPLES::

            sage: FiniteDimensionalBialgebrasWithBasis(QQ).super_categories()
            [Category of finite dimensional algebras with basis over Rational Field, Category of finite dimensional coalgebras with basis over Rational Field, Category of bialgebras over Rational Field]
        """
        R = self.base_ring()
        return [
            FiniteDimensionalAlgebrasWithBasis(R),
            FiniteDimensionalCoalgebrasWithBasis(R),
            Bialgebras(R)
        ]
Exemplo n.º 4
0
    def _Hom_(self, B, category):
        """
        Construct a homset of ``self`` and ``B``.

        EXAMPLES::

            sage: A = FiniteDimensionalAlgebra(QQ, [Matrix([1])])
            sage: B = FiniteDimensionalAlgebra(QQ, [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])])
            sage: A._Hom_(B, A.category())
            Set of Homomorphisms from Finite-dimensional algebra of degree 1 over Rational Field to Finite-dimensional algebra of degree 2 over Rational Field
        """
        if category.is_subcategory(FiniteDimensionalAlgebrasWithBasis(self.base_ring())):
            from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra_morphism import FiniteDimensionalAlgebraHomset
            return FiniteDimensionalAlgebraHomset(self, B, category=category)
        return super(FiniteDimensionalAlgebra, self)._Hom_(B, category)
    def __init__(self,
                 k,
                 table,
                 names='e',
                 assume_associative=False,
                 category=None):
        """
        TESTS::

            sage: A = FiniteDimensionalAlgebra(QQ, [])
            sage: A
            Finite-dimensional algebra of degree 0 over Rational Field
            sage: type(A)
            <class 'sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra.FiniteDimensionalAlgebra_with_category'>
            sage: TestSuite(A).run()

            sage: B = FiniteDimensionalAlgebra(GF(7), [Matrix([1])])
            sage: B
            Finite-dimensional algebra of degree 1 over Finite Field of size 7
            sage: TestSuite(B).run()

            sage: C = FiniteDimensionalAlgebra(CC, [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [0, 0]])])
            sage: C
            Finite-dimensional algebra of degree 2 over Complex Field with 53 bits of precision
            sage: TestSuite(C).run()

            sage: FiniteDimensionalAlgebra(GF(3), [Matrix([[1, 0], [0, 1]])])
            Traceback (most recent call last):
            ...
            ValueError: input is not a multiplication table

            sage: D.<a,b> = FiniteDimensionalAlgebra(RR, [Matrix([[1, 0], [0, 1]]), Matrix([[0, 1], [-1, 0]])])
            sage: D.gens()
            (a, b)

            sage: E = FiniteDimensionalAlgebra(QQ, [Matrix([0])])
            sage: E.gens()
            (e,)
        """
        n = len(table)
        self._table = [b.base_extend(k) for b in table]
        if not all([is_Matrix(b) and b.dimensions() == (n, n) for b in table]):
            raise ValueError("input is not a multiplication table")
        self._assume_associative = assume_associative
        # No further validity checks necessary!
        if category is None:
            category = FiniteDimensionalAlgebrasWithBasis(k)
        Algebra.__init__(self, base_ring=k, names=names, category=category)
Exemplo n.º 6
0
    def __init__(self, R, n):
        """
        TESTS::

            sage: QS3 = SymmetricGroupAlgebra(QQ, 3)
            sage: TestSuite(QS3).run()
        """
        self.n = n
        self._name = "Symmetric group algebra of order %s" % self.n
        CombinatorialFreeModule.__init__(
            self,
            R,
            permutation.Permutations(n),
            prefix='',
            latex_prefix='',
            category=(GroupAlgebras(R), FiniteDimensionalAlgebrasWithBasis(R)))
        # This is questionable, and won't be inherited properly
        if n > 0:
            S = SymmetricGroupAlgebra(R, n - 1)
            self.register_coercion(S.canonical_embedding(self))
Exemplo n.º 7
0
    def __init__(self, W, q1, q2, base_ring, prefix):
        """
        EXAMPLES::

            sage: R.<q1,q2>=QQ[]
            sage: H = IwahoriHeckeAlgebraT("A2",q1,q2,base_ring=Frac(R),prefix="t"); H
            The Iwahori Hecke Algebra of Type A2 in q1,q2 over Fraction Field of Multivariate Polynomial Ring in q1, q2 over Rational Field and prefix t
            sage: TestSuite(H).run()

         """
        self._cartan_type = W.cartan_type()
        self._prefix = prefix
        self._index_set = W.index_set()
        self._q1 = base_ring(q1)
        self._q2 = base_ring(q2)

        if W.is_finite():
            category = FiniteDimensionalAlgebrasWithBasis(base_ring)
        else:
            category = AlgebrasWithBasis(base_ring)
        CombinatorialFreeModule.__init__(self, base_ring, W, category=category)
Exemplo n.º 8
0
class DescentAlgebra(UniqueRepresentation, Parent):
    r"""
    Solomon's descent algebra.

    The descent algebra `\Sigma_n` over a ring `R` is a subalgebra of the
    symmetric group algebra `R S_n`. (The product in the latter algebra
    is defined by `(pq)(i) = q(p(i))` for any two permutations `p` and
    `q` in `S_n` and every `i \in \{ 1, 2, \ldots, n \}`. The algebra
    `\Sigma_n` inherits this product.)

    There are three bases currently implemented for `\Sigma_n`:

    - the standard basis `D_S` of (sums of) descent classes, indexed by
      subsets `S` of `\{1, 2, \ldots, n-1\}`,
    - the subset basis `B_p`, indexed by compositions `p` of `n`,
    - the idempotent basis `I_p`, indexed by compositions `p` of `n`,
      which is used to construct the mutually orthogonal idempotents
      of the symmetric group algebra.

    The idempotent basis is only defined when `R` is a `\QQ`-algebra.

    We follow the notations and conventions in [GR1989]_, apart from the
    order of multiplication being different from the one used in that
    article. Schocker's exposition [Schocker2004]_, in turn, uses the
    same order of multiplication as we are, but has different notations
    for the bases.

    INPUT:

    - ``R`` -- the base ring

    - ``n`` -- a nonnegative integer

    REFERENCES:

    .. [GR1989] \C. Reutenauer, A. M. Garsia. *A decomposition of Solomon's
       descent algebra.* Adv. Math. **77** (1989).
       http://www.lacim.uqam.ca/~christo/Publi%C3%A9s/1989/Decomposition%20Solomon.pdf

    .. [Atkinson] \M. D. Atkinson. *Solomon's descent algebra revisited.*
       Bull. London Math. Soc. 24 (1992) 545-551.
       http://www.cs.otago.ac.nz/staffpriv/mike/Papers/Descent/DescAlgRevisited.pdf

    .. [MR-Desc] \C. Malvenuto, C. Reutenauer, *Duality between
       quasi-symmetric functions and the Solomon descent algebra*,
       Journal of Algebra 177 (1995), no. 3, 967-982.
       http://www.lacim.uqam.ca/~christo/Publi%C3%A9s/1995/Duality.pdf

    .. [Schocker2004] Manfred Schocker, *The descent algebra of the
       symmetric group*. Fields Inst. Comm. 40 (2004), pp. 145-161.
       http://www.mathematik.uni-bielefeld.de/~ringel/schocker-neu.ps

    EXAMPLES::

        sage: DA = DescentAlgebra(QQ, 4)
        sage: D = DA.D(); D
        Descent algebra of 4 over Rational Field in the standard basis
        sage: B = DA.B(); B
        Descent algebra of 4 over Rational Field in the subset basis
        sage: I = DA.I(); I
        Descent algebra of 4 over Rational Field in the idempotent basis
        sage: basis_B = B.basis()
        sage: elt = basis_B[Composition([1,2,1])] + 4*basis_B[Composition([1,3])]; elt
        B[1, 2, 1] + 4*B[1, 3]
        sage: D(elt)
        5*D{} + 5*D{1} + D{1, 3} + D{3}
        sage: I(elt)
        7/6*I[1, 1, 1, 1] + 2*I[1, 1, 2] + 3*I[1, 2, 1] + 4*I[1, 3]


    As syntactic sugar, one can use the notation ``D[i,...,l]`` to
    construct elements of the basis; note that for the empty set one
    must use ``D[[]]`` due to Python's syntax::

        sage: D[[]] + D[2] + 2*D[1,2]
        D{} + 2*D{1, 2} + D{2}

    The same syntax works for the other bases::

        sage: I[1,2,1] + 3*I[4] + 2*I[3,1]
        I[1, 2, 1] + 2*I[3, 1] + 3*I[4]

    TESTS:

    We check that we can go back and forth between our bases::

        sage: DA = DescentAlgebra(QQ, 4)
        sage: D = DA.D()
        sage: B = DA.B()
        sage: I = DA.I()
        sage: all(D(B(b)) == b for b in D.basis())
        True
        sage: all(D(I(b)) == b for b in D.basis())
        True
        sage: all(B(D(b)) == b for b in B.basis())
        True
        sage: all(B(I(b)) == b for b in B.basis())
        True
        sage: all(I(D(b)) == b for b in I.basis())
        True
        sage: all(I(B(b)) == b for b in I.basis())
        True
    """
    def __init__(self, R, n):
        r"""
        EXAMPLES::

            sage: TestSuite(DescentAlgebra(QQ, 4)).run()
        """
        self._n = n
        self._category = FiniteDimensionalAlgebrasWithBasis(R)
        Parent.__init__(self,
                        base=R,
                        category=self._category.WithRealizations())

    def _repr_(self):
        r"""
        Return a string representation of ``self``.

        EXAMPLES::

            sage: DescentAlgebra(QQ, 4)
            Descent algebra of 4 over Rational Field
        """
        return "Descent algebra of {0} over {1}".format(
            self._n, self.base_ring())

    def a_realization(self):
        r"""
        Return a particular realization of ``self`` (the `B`-basis).

        EXAMPLES::

            sage: DA = DescentAlgebra(QQ, 4)
            sage: DA.a_realization()
            Descent algebra of 4 over Rational Field in the subset basis
        """
        return self.B()

    class D(CombinatorialFreeModule, BindableClass):
        r"""
        The standard basis of a descent algebra.

        This basis is indexed by `S \subseteq \{1, 2, \ldots, n-1\}`,
        and the basis vector indexed by `S` is the sum of all permutations,
        taken in the symmetric group algebra `R S_n`, whose descent set is `S`.
        We denote this basis vector by `D_S`.

        Occasionally this basis appears in literature but indexed by
        compositions of `n` rather than subsets of
        `\{1, 2, \ldots, n-1\}`. The equivalence between these two
        indexings is owed to the bijection from the power set of
        `\{1, 2, \ldots, n-1\}` to the set of all compositions of `n`
        which sends every subset `\{i_1, i_2, \ldots, i_k\}` of
        `\{1, 2, \ldots, n-1\}` (with `i_1 < i_2 < \cdots < i_k`) to
        the composition `(i_1, i_2-i_1, \ldots, i_k-i_{k-1}, n-i_k)`.

        The basis element corresponding to a composition `p` (or to
        the subset of `\{1, 2, \ldots, n-1\}`) is denoted `\Delta^p`
        in [Schocker2004]_.

        EXAMPLES::

            sage: DA = DescentAlgebra(QQ, 4)
            sage: D = DA.D()
            sage: list(D.basis())
            [D{}, D{1}, D{2}, D{3}, D{1, 2}, D{1, 3}, D{2, 3}, D{1, 2, 3}]

            sage: DA = DescentAlgebra(QQ, 0)
            sage: D = DA.D()
            sage: list(D.basis())
            [D{}]
        """
        def __init__(self, alg, prefix="D"):
            r"""
            Initialize ``self``.

            EXAMPLES::

                sage: TestSuite(DescentAlgebra(QQ, 4).D()).run()
            """
            self._prefix = prefix
            self._basis_name = "standard"
            CombinatorialFreeModule.__init__(self,
                                             alg.base_ring(),
                                             SubsetsSorted(range(1, alg._n)),
                                             category=DescentAlgebraBases(alg),
                                             bracket="",
                                             prefix=prefix)

            # Change of basis:
            B = alg.B()
            self.module_morphism(
                self.to_B_basis, codomain=B,
                category=self.category()).register_as_coercion()

            B.module_morphism(B.to_D_basis,
                              codomain=self,
                              category=self.category()).register_as_coercion()

        def _element_constructor_(self, x):
            """
            Construct an element of ``self``.

            EXAMPLES::

                sage: D = DescentAlgebra(QQ, 4).D()
                sage: D([1, 3])
                D{1, 3}
            """
            if isinstance(x, (list, set)):
                x = tuple(x)
            if isinstance(x, tuple):
                return self.monomial(x)
            return CombinatorialFreeModule._element_constructor_(self, x)

        # We need to overwrite this since our basis elements must be indexed by tuples
        def _repr_term(self, S):
            r"""
            EXAMPLES::

                sage: DA = DescentAlgebra(QQ, 4)
                sage: DA.D()._repr_term((1, 3))
                'D{1, 3}'
            """
            return self._prefix + '{' + repr(list(S))[1:-1] + '}'

        def product_on_basis(self, S, T):
            r"""
            Return `D_S D_T`, where `S` and `T` are subsets of `[n-1]`.

            EXAMPLES::

                sage: DA = DescentAlgebra(QQ, 4)
                sage: D = DA.D()
                sage: D.product_on_basis((1, 3), (2,))
                D{} + D{1} + D{1, 2} + 2*D{1, 2, 3} + D{1, 3} + D{2} + D{2, 3} + D{3}
            """
            return self(self.to_B_basis(S) * self.to_B_basis(T))

        @cached_method
        def one_basis(self):
            r"""
            Return the identity element, as per
            ``AlgebrasWithBasis.ParentMethods.one_basis``.

            EXAMPLES::

                sage: DescentAlgebra(QQ, 4).D().one_basis()
                ()
                sage: DescentAlgebra(QQ, 0).D().one_basis()
                ()

                sage: all( U * DescentAlgebra(QQ, 3).D().one() == U
                ....:      for U in DescentAlgebra(QQ, 3).D().basis() )
                True
            """
            return tuple([])

        @cached_method
        def to_B_basis(self, S):
            r"""
            Return `D_S` as a linear combination of `B_p`-basis elements.

            EXAMPLES::

                sage: DA = DescentAlgebra(QQ, 4)
                sage: D = DA.D()
                sage: B = DA.B()
                sage: map(B, D.basis()) # indirect doctest
                [B[4],
                 B[1, 3] - B[4],
                 B[2, 2] - B[4],
                 B[3, 1] - B[4],
                 B[1, 1, 2] - B[1, 3] - B[2, 2] + B[4],
                 B[1, 2, 1] - B[1, 3] - B[3, 1] + B[4],
                 B[2, 1, 1] - B[2, 2] - B[3, 1] + B[4],
                 B[1, 1, 1, 1] - B[1, 1, 2] - B[1, 2, 1] + B[1, 3]
                  - B[2, 1, 1] + B[2, 2] + B[3, 1] - B[4]]
            """
            B = self.realization_of().B()

            if not S:
                return B.one()

            n = self.realization_of()._n
            C = Compositions(n)
            return B.sum_of_terms([(C.from_subset(T,
                                                  n), (-1)**(len(S) - len(T)))
                                   for T in SubsetsSorted(S)])

        def to_symmetric_group_algebra_on_basis(self, S):
            """
            Return `D_S` as a linear combination of basis elements in the
            symmetric group algebra.

            EXAMPLES::

                sage: D = DescentAlgebra(QQ, 4).D()
                sage: [D.to_symmetric_group_algebra_on_basis(tuple(b))
                ....:  for b in Subsets(3)]
                [[1, 2, 3, 4],
                 [2, 1, 3, 4] + [3, 1, 2, 4] + [4, 1, 2, 3],
                 [1, 3, 2, 4] + [1, 4, 2, 3] + [2, 3, 1, 4]
                  + [2, 4, 1, 3] + [3, 4, 1, 2],
                 [1, 2, 4, 3] + [1, 3, 4, 2] + [2, 3, 4, 1],
                 [3, 2, 1, 4] + [4, 2, 1, 3] + [4, 3, 1, 2],
                 [2, 1, 4, 3] + [3, 1, 4, 2] + [3, 2, 4, 1]
                  + [4, 1, 3, 2] + [4, 2, 3, 1],
                 [1, 4, 3, 2] + [2, 4, 3, 1] + [3, 4, 2, 1],
                 [4, 3, 2, 1]]
            """
            n = self.realization_of()._n
            SGA = SymmetricGroupAlgebra(self.base_ring(), n)
            # Need to convert S to a list of positions by -1 for indexing
            P = Permutations(descents=([x - 1 for x in S], n))
            return SGA.sum_of_terms([(p, 1) for p in P])

        def __getitem__(self, S):
            """
            Return the basis element indexed by ``S``.

            INPUT:

            - ``S`` -- a subset of `[n-1]`

            EXAMPLES::

                sage: D = DescentAlgebra(QQ, 4).D()
                sage: D[3]
                D{3}
                sage: D[1, 3]
                D{1, 3}
                sage: D[[]]
                D{}

            TESTS::

                sage: D = DescentAlgebra(QQ, 0).D()
                sage: D[[]]
                D{}
            """
            n = self.realization_of()._n
            if S in ZZ:
                if S >= n or S <= 0:
                    raise ValueError(
                        "({0},) is not a subset of {{1, ..., {1}}}".format(
                            S, n - 1))
                return self.monomial((S, ))
            if not S:
                return self.one()
            S = sorted(S)
            if S[-1] >= n or S[0] <= 0:
                raise ValueError(
                    "{0} is not a subset of {{1, ..., {1}}}".format(S, n - 1))
            return self.monomial(tuple(S))

    standard = D

    class B(CombinatorialFreeModule, BindableClass):
        r"""
        The subset basis of a descent algebra (indexed by compositions).

        The subset basis `(B_S)_{S \subseteq \{1, 2, \ldots, n-1\}}` of
        `\Sigma_n` is formed by

        .. MATH::

            B_S = \sum_{T \subseteq S} D_T,

        where `(D_S)_{S \subseteq \{1, 2, \ldots, n-1\}}` is the
        :class:`standard basis <DescentAlgebra.D>`. However it is more
        natural to index the subset basis by compositions
        of `n` under the bijection `\{i_1, i_2, \ldots, i_k\} \mapsto
        (i_1, i_2 - i_1, i_3 - i_2, \ldots, i_k - i_{k-1}, n - i_k)`
        (where `i_1 < i_2 < \cdots < i_k`), which is what Sage uses to
        index the basis.

        The basis element `B_p` is denoted `\Xi^p` in [Schocker2004]_.

        By using compositions of `n`, the product `B_p B_q` becomes a
        sum over the non-negative-integer matrices `M` with row sum `p`
        and column sum `q`. The summand corresponding to `M` is `B_c`,
        where `c` is the composition obtained by reading `M` row-by-row
        from left-to-right and top-to-bottom and removing all zeroes.
        This multiplication rule is commonly called "Solomon's Mackey
        formula".

        EXAMPLES::

            sage: DA = DescentAlgebra(QQ, 4)
            sage: B = DA.B()
            sage: list(B.basis())
            [B[1, 1, 1, 1], B[1, 1, 2], B[1, 2, 1], B[1, 3],
             B[2, 1, 1], B[2, 2], B[3, 1], B[4]]
        """
        def __init__(self, alg, prefix="B"):
            r"""
            Initialize ``self``.

            EXAMPLES::

                sage: TestSuite(DescentAlgebra(QQ, 4).B()).run()
            """
            self._prefix = prefix
            self._basis_name = "subset"
            CombinatorialFreeModule.__init__(self,
                                             alg.base_ring(),
                                             Compositions(alg._n),
                                             category=DescentAlgebraBases(alg),
                                             bracket="",
                                             prefix=prefix)

            S = NonCommutativeSymmetricFunctions(alg.base_ring()).Complete()
            self.module_morphism(self.to_nsym,
                                 codomain=S,
                                 category=Algebras(
                                     alg.base_ring())).register_as_coercion()

        def product_on_basis(self, p, q):
            r"""
            Return `B_p B_q`, where `p` and `q` are compositions of `n`.

            EXAMPLES::

                sage: DA = DescentAlgebra(QQ, 4)
                sage: B = DA.B()
                sage: p = Composition([1,2,1])
                sage: q = Composition([3,1])
                sage: B.product_on_basis(p, q)
                B[1, 1, 1, 1] + 2*B[1, 2, 1]
            """
            IM = IntegerMatrices(list(p), list(q))
            P = Compositions(self.realization_of()._n)
            to_composition = lambda m: P([x for x in m.list() if x != 0])
            return self.sum_of_monomials([to_composition(_) for _ in IM])

        @cached_method
        def one_basis(self):
            r"""
            Return the identity element which is the composition `[n]`, as per
            ``AlgebrasWithBasis.ParentMethods.one_basis``.

            EXAMPLES::

                sage: DescentAlgebra(QQ, 4).B().one_basis()
                [4]
                sage: DescentAlgebra(QQ, 0).B().one_basis()
                []

                sage: all( U * DescentAlgebra(QQ, 3).B().one() == U
                ....:      for U in DescentAlgebra(QQ, 3).B().basis() )
                True
            """
            n = self.realization_of()._n
            P = Compositions(n)
            if not n:  # n == 0
                return P([])
            return P([n])

        @cached_method
        def to_I_basis(self, p):
            r"""
            Return `B_p` as a linear combination of `I`-basis elements.

            This is done using the formula

            .. MATH::

                B_p = \sum_{q \leq p} \frac{1}{\mathbf{k}!(q,p)} I_q,

            where `\leq` is the refinement order and `\mathbf{k}!(q,p)` is
            defined as follows: When `q \leq p`, we can write `q` as a
            concatenation `q_{(1)} q_{(2)} \cdots q_{(k)}` with each `q_{(i)}`
            being a composition of the `i`-th entry of `p`, and then
            we set `\mathbf{k}!(q,p)` to be
            `l(q_{(1)})! l(q_{(2)})! \cdots l(q_{(k)})!`, where `l(r)`
            denotes the number of parts of any composition `r`.

            EXAMPLES::

                sage: DA = DescentAlgebra(QQ, 4)
                sage: B = DA.B()
                sage: I = DA.I()
                sage: map(I, B.basis()) # indirect doctest
                [I[1, 1, 1, 1],
                 1/2*I[1, 1, 1, 1] + I[1, 1, 2],
                 1/2*I[1, 1, 1, 1] + I[1, 2, 1],
                 1/6*I[1, 1, 1, 1] + 1/2*I[1, 1, 2] + 1/2*I[1, 2, 1] + I[1, 3],
                 1/2*I[1, 1, 1, 1] + I[2, 1, 1],
                 1/4*I[1, 1, 1, 1] + 1/2*I[1, 1, 2] + 1/2*I[2, 1, 1] + I[2, 2],
                 1/6*I[1, 1, 1, 1] + 1/2*I[1, 2, 1] + 1/2*I[2, 1, 1] + I[3, 1],
                 1/24*I[1, 1, 1, 1] + 1/6*I[1, 1, 2] + 1/6*I[1, 2, 1]
                  + 1/2*I[1, 3] + 1/6*I[2, 1, 1] + 1/2*I[2, 2] + 1/2*I[3, 1] + I[4]]
            """
            I = self.realization_of().I()

            def coeff(p, q):
                ret = QQ.one()
                last = 0
                for val in p:
                    count = 0
                    s = 0
                    while s != val:
                        s += q[last + count]
                        count += 1
                    ret /= factorial(count)
                    last += count
                return ret

            return I.sum_of_terms([(q, coeff(p, q)) for q in p.finer()])

        @cached_method
        def to_D_basis(self, p):
            r"""
            Return `B_p` as a linear combination of `D`-basis elements.

            EXAMPLES::

                sage: DA = DescentAlgebra(QQ, 4)
                sage: B = DA.B()
                sage: D = DA.D()
                sage: map(D, B.basis()) # indirect doctest
                [D{} + D{1} + D{1, 2} + D{1, 2, 3}
                  + D{1, 3} + D{2} + D{2, 3} + D{3},
                 D{} + D{1} + D{1, 2} + D{2},
                 D{} + D{1} + D{1, 3} + D{3},
                 D{} + D{1},
                 D{} + D{2} + D{2, 3} + D{3},
                 D{} + D{2},
                 D{} + D{3},
                 D{}]

            TESTS:

            Check to make sure the empty case is handled correctly::

                sage: DA = DescentAlgebra(QQ, 0)
                sage: B = DA.B()
                sage: D = DA.D()
                sage: map(D, B.basis())
                [D{}]
            """
            D = self.realization_of().D()

            if not p:
                return D.one()

            return D.sum_of_terms([(tuple(sorted(s)), 1)
                                   for s in p.to_subset().subsets()])

        def to_nsym(self, p):
            """
            Return `B_p` as an element in `NSym`, the non-commutative
            symmetric functions.

            This maps `B_p` to `S_p` where `S` denotes the Complete basis of
            `NSym`.

            EXAMPLES::

                sage: B = DescentAlgebra(QQ, 4).B()
                sage: S = NonCommutativeSymmetricFunctions(QQ).Complete()
                sage: map(S, B.basis()) # indirect doctest
                [S[1, 1, 1, 1],
                 S[1, 1, 2],
                 S[1, 2, 1],
                 S[1, 3],
                 S[2, 1, 1],
                 S[2, 2],
                 S[3, 1],
                 S[4]]
            """
            S = NonCommutativeSymmetricFunctions(self.base_ring()).Complete()
            return S.monomial(p)

    subset = B

    class I(CombinatorialFreeModule, BindableClass):
        r"""
        The idempotent basis of a descent algebra.

        The idempotent basis `(I_p)_{p \models n}` is a basis for `\Sigma_n`
        whenever the ground ring is a `\QQ`-algebra. One way to compute it
        is using the formula (Theorem 3.3 in [GR1989]_)

        .. MATH::

            I_p = \sum_{q \leq p}
            \frac{(-1)^{l(q)-l(p)}}{\mathbf{k}(q,p)} B_q,

        where `\leq` is the refinement order and `l(r)` denotes the number
        of parts of any composition `r`, and where `\mathbf{k}(q,p)` is
        defined as follows: When `q \leq p`, we can write `q` as a
        concatenation `q_{(1)} q_{(2)} \cdots q_{(k)}` with each `q_{(i)}`
        being a composition of the `i`-th entry of `p`, and then
        we set `\mathbf{k}(q,p)` to be the product
        `l(q_{(1)}) l(q_{(2)}) \cdots l(q_{(k)})`.

        Let `\lambda(p)` denote the partition obtained from a composition
        `p` by sorting. This basis is called the idempotent basis since for
        any `q` such that `\lambda(p) = \lambda(q)`, we have:

        .. MATH::

            I_p I_q = s(\lambda) I_p

        where `\lambda` denotes `\lambda(p) = \lambda(q)`, and where
        `s(\lambda)` is the stabilizer of `\lambda` in `S_n`. (This is
        part of Theorem 4.2 in [GR1989]_.)

        It is also straightforward to compute the idempotents `E_{\lambda}`
        for the symmetric group algebra by the formula
        (Theorem 3.2 in [GR1989]_):

        .. MATH::

            E_{\lambda} = \frac{1}{k!} \sum_{\lambda(p) = \lambda} I_p.

        .. NOTE::

            The basis elements are not orthogonal idempotents.

        EXAMPLES::

            sage: DA = DescentAlgebra(QQ, 4)
            sage: I = DA.I()
            sage: list(I.basis())
            [I[1, 1, 1, 1], I[1, 1, 2], I[1, 2, 1], I[1, 3], I[2, 1, 1], I[2, 2], I[3, 1], I[4]]
        """
        def __init__(self, alg, prefix="I"):
            r"""
            Initialize ``self``.

            EXAMPLES::

                sage: TestSuite(DescentAlgebra(QQ, 4).B()).run()
            """
            self._prefix = prefix
            self._basis_name = "idempotent"
            CombinatorialFreeModule.__init__(self,
                                             alg.base_ring(),
                                             Compositions(alg._n),
                                             category=DescentAlgebraBases(alg),
                                             bracket="",
                                             prefix=prefix)

            ## Change of basis:
            B = alg.B()
            self.module_morphism(
                self.to_B_basis, codomain=B,
                category=self.category()).register_as_coercion()

            B.module_morphism(B.to_I_basis,
                              codomain=self,
                              category=self.category()).register_as_coercion()

        def product_on_basis(self, p, q):
            r"""
            Return `I_p I_q`, where `p` and `q` are compositions of `n`.

            EXAMPLES::

                sage: DA = DescentAlgebra(QQ, 4)
                sage: I = DA.I()
                sage: p = Composition([1,2,1])
                sage: q = Composition([3,1])
                sage: I.product_on_basis(p, q)
                0
                sage: I.product_on_basis(p, p)
                2*I[1, 2, 1]
            """
            # These do not act as orthogonal idempotents, so we have to lift
            #   to the B basis to do the multiplication
            # TODO: if the partitions of p and q match, return s*I_p where
            #   s is the size of the stabilizer of the partition of p
            return self(self.to_B_basis(p) * self.to_B_basis(q))

        @cached_method
        def one(self):
            r"""
            Return the identity element, which is `B_{[n]}`, in the `I` basis.

            EXAMPLES::

                sage: DescentAlgebra(QQ, 4).I().one()
                1/24*I[1, 1, 1, 1] + 1/6*I[1, 1, 2] + 1/6*I[1, 2, 1]
                 + 1/2*I[1, 3] + 1/6*I[2, 1, 1] + 1/2*I[2, 2]
                 + 1/2*I[3, 1] + I[4]
                sage: DescentAlgebra(QQ, 0).I().one()
                I[]

            TESTS::

                sage: all( U * DescentAlgebra(QQ, 3).I().one() == U
                ....:      for U in DescentAlgebra(QQ, 3).I().basis() )
                True
            """
            B = self.realization_of().B()
            return B.to_I_basis(B.one_basis())

        def one_basis(self):
            """
            The element `1` is not (generally) a basis vector in the `I`
            basis, thus this returns a ``TypeError``.

            EXAMPLES::

                sage: DescentAlgebra(QQ, 4).I().one_basis()
                Traceback (most recent call last):
                ...
                TypeError: 1 is not a basis element in the I basis.
            """
            raise TypeError("1 is not a basis element in the I basis.")

        @cached_method
        def to_B_basis(self, p):
            r"""
            Return `I_p` as a linear combination of `B`-basis elements.

            This is computed using the formula (Theorem 3.3 in [GR1989]_)

            .. MATH::

                I_p = \sum_{q \leq p}
                \frac{(-1)^{l(q)-l(p)}}{\mathbf{k}(q,p)} B_q,

            where `\leq` is the refinement order and `l(r)` denotes the number
            of parts of any composition `r`, and where `\mathbf{k}(q,p)` is
            defined as follows: When `q \leq p`, we can write `q` as a
            concatenation `q_{(1)} q_{(2)} \cdots q_{(k)}` with each `q_{(i)}`
            being a composition of the `i`-th entry of `p`, and then
            we set `\mathbf{k}(q,p)` to be
            `l(q_{(1)}) l(q_{(2)}) \cdots l(q_{(k)})`.

            EXAMPLES::

                sage: DA = DescentAlgebra(QQ, 4)
                sage: B = DA.B()
                sage: I = DA.I()
                sage: map(B, I.basis()) # indirect doctest
                [B[1, 1, 1, 1],
                 -1/2*B[1, 1, 1, 1] + B[1, 1, 2],
                 -1/2*B[1, 1, 1, 1] + B[1, 2, 1],
                 1/3*B[1, 1, 1, 1] - 1/2*B[1, 1, 2] - 1/2*B[1, 2, 1] + B[1, 3],
                 -1/2*B[1, 1, 1, 1] + B[2, 1, 1],
                 1/4*B[1, 1, 1, 1] - 1/2*B[1, 1, 2] - 1/2*B[2, 1, 1] + B[2, 2],
                 1/3*B[1, 1, 1, 1] - 1/2*B[1, 2, 1] - 1/2*B[2, 1, 1] + B[3, 1],
                 -1/4*B[1, 1, 1, 1] + 1/3*B[1, 1, 2] + 1/3*B[1, 2, 1]
                  - 1/2*B[1, 3] + 1/3*B[2, 1, 1] - 1/2*B[2, 2]
                  - 1/2*B[3, 1] + B[4]]
            """
            B = self.realization_of().B()

            def coeff(p, q):
                ret = QQ.one()
                last = 0
                for val in p:
                    count = 0
                    s = 0
                    while s != val:
                        s += q[last + count]
                        count += 1
                    ret /= count
                    last += count
                if (len(q) - len(p)) % 2 == 1:
                    ret = -ret
                return ret

            return B.sum_of_terms([(q, coeff(p, q)) for q in p.finer()])

        def idempotent(self, la):
            """
            Return the idempotent corresponding to the partition ``la``
            of `n`.

            EXAMPLES::

                sage: I = DescentAlgebra(QQ, 4).I()
                sage: E = I.idempotent([3,1]); E
                1/2*I[1, 3] + 1/2*I[3, 1]
                sage: E*E == E
                True
                sage: E2 = I.idempotent([2,1,1]); E2
                1/6*I[1, 1, 2] + 1/6*I[1, 2, 1] + 1/6*I[2, 1, 1]
                sage: E2*E2 == E2
                True
                sage: E*E2 == I.zero()
                True
            """
            from sage.combinat.permutation import Permutations
            k = len(la)
            C = Compositions(self.realization_of()._n)
            return self.sum_of_terms([(C(x), ~QQ(factorial(k)))
                                      for x in Permutations(la)])

    idempotent = I