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))
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) ]
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)
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))
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)
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