def _llt_generic(self, skp, stat): r""" Takes in partition, list of partitions, or a list of skew partitions as well as a function which takes in two partitions and a level and returns a coefficient. INPUT: - ``self`` -- a family of LLT symmetric functions bases - ``skp`` -- a partition or a list of partitions or a list of skew partitions - ``stat`` -- a function which accepts two partitions and a value for the level and returns a coefficient which is a polynomial in a parameter `t`. The first partition is the index of the LLT function, the second partition is the index of the monomial basis element. OUTPUT: - returns the monomial expansion of the LLT symmetric function indexed by ``skp`` EXAMPLES:: sage: L3 = SymmetricFunctions(FractionField(QQ['t'])).llt(3) sage: f = lambda skp,mu,level: QQ(1) sage: L3._llt_generic([3,2,1],f) m[1, 1] + m[2] sage: L3._llt_generic([[2,1],[1],[2]],f) m[1, 1, 1, 1, 1, 1] + m[2, 1, 1, 1, 1] + m[2, 2, 1, 1] + m[2, 2, 2] + m[3, 1, 1, 1] + m[3, 2, 1] + m[3, 3] + m[4, 1, 1] + m[4, 2] + m[5, 1] + m[6] sage: L3._llt_generic([[[2,2],[1]],[[2,1],[]]],f) m[1, 1, 1, 1] + m[2, 1, 1] + m[2, 2] + m[3, 1] + m[4] """ if skp in _Partitions: m = (sum(skp) / self.level()).floor() if m == 0: raise ValueError("level (%s=) must divide %s " % (sum(skp), self.level())) mu = Partitions(ZZ(sum(skp) / self.level())) elif isinstance( skp, list ) and skp[0] in sage.combinat.skew_partition.SkewPartitions(): #skp is a list of skew partitions skp2 = [ Partition(core=[], quotient=[skp[i][0] for i in range(len(skp))]) ] skp2 += [ Partition(core=[], quotient=[skp[i][1] for i in range(len(skp))]) ] mu = Partitions( ZZ((skp2[0].size() - skp2[1].size()) / self.level())) skp = skp2 elif isinstance(skp, list) and skp[0] in _Partitions: #skp is a list of partitions skp = Partition(core=[], quotient=skp) mu = Partitions(ZZ(sum(skp) / self.level())) else: raise ValueError("LLT polynomials not defined for %s" % skp) BR = self.base_ring() return sum([ BR(stat(skp, nu, self.level()).subs(t=self.t)) * self._m(nu) for nu in mu ])
def verschiebung(self, n): r""" Return the image of the symmetric function ``self`` under the `n`-th Verschiebung operator. The `n`-th Verschiebung operator `\mathbf{V}_n` is defined to be the unique algebra endomorphism `V` of the ring of symmetric functions that satisfies `V(h_r) = h_{r/n}` for every positive integer `r` divisible by `n`, and satisfies `V(h_r) = 0` for every positive integer `r` not divisible by `n`. This operator `\mathbf{V}_n` is a Hopf algebra endomorphism. For every nonnegative integer `r` with `n \mid r`, it satisfies .. MATH:: \mathbf{V}_n(h_r) = h_{r/n}, \quad \mathbf{V}_n(p_r) = n p_{r/n}, \quad \mathbf{V}_n(e_r) = (-1)^{r - r/n} e_{r/n} (where `h` is the complete homogeneous basis, `p` is the powersum basis, and `e` is the elementary basis). For every nonnegative integer `r` with `n \nmid r`, it satisfes .. MATH:: \mathbf{V}_n(h_r) = \mathbf{V}_n(p_r) = \mathbf{V}_n(e_r) = 0. The `n`-th Verschiebung operator is also called the `n`-th Verschiebung endomorphism. Its name derives from the Verschiebung (German for "shift") endomorphism of the Witt vectors. The `n`-th Verschiebung operator is adjoint to the `n`-th Frobenius operator (see :meth:`frobenius` for its definition) with respect to the Hall scalar product (:meth:`scalar`). The action of the `n`-th Verschiebung operator on the Schur basis can also be computed explicitly. The following (probably clumsier than necessary) description can be obtained by solving exercise 7.61 in Stanley's [STA]_. Let `\lambda` be a partition. Let `n` be a positive integer. If the `n`-core of `\lambda` is nonempty, then `\mathbf{V}_n(s_\lambda) = 0`. Otherwise, the following method computes `\mathbf{V}_n(s_\lambda)`: Write the partition `\lambda` in the form `(\lambda_1, \lambda_2, \ldots, \lambda_{ns})` for some nonnegative integer `s`. (If `n` does not divide the length of `\lambda`, then this is achieved by adding trailing zeroes to `\lambda`.) Set `\beta_i = \lambda_i + ns - i` for every `s \in \{ 1, 2, \ldots, ns \}`. Then, `(\beta_1, \beta_2, \ldots, \beta_{ns})` is a strictly decreasing sequence of nonnegative integers. Stably sort the list `(1, 2, \ldots, ns)` in order of (weakly) increasing remainder of `-1 - \beta_i` modulo `n`. Let `\xi` be the sign of the permutation that is used for this sorting. Let `\psi` be the sign of the permutation that is used to stably sort the list `(1, 2, \ldots, ns)` in order of (weakly) increasing remainder of `i - 1` modulo `n`. (Notice that `\psi = (-1)^{n(n-1)s(s-1)/4}`.) Then, `\mathbf{V}_n(s_\lambda) = \xi \psi \prod_{i = 0}^{n - 1} s_{\lambda^{(i)}}`, where `(\lambda^{(0)}, \lambda^{(1)}, \ldots, \lambda^{(n - 1)})` is the `n`-quotient of `\lambda`. INPUT: - ``n`` -- a positive integer OUTPUT: The result of applying the `n`-th Verschiebung operator (on the ring of symmetric functions) to ``self``. EXAMPLES:: sage: Sym = SymmetricFunctions(ZZ) sage: p = Sym.p() sage: p[3].verschiebung(2) 0 sage: p[4].verschiebung(4) 4*p[1] The Verschiebung endomorphisms are multiplicative:: sage: all( all( p(lam).verschiebung(2) * p(mu).verschiebung(2) ....: == (p(lam) * p(mu)).verschiebung(2) ....: for mu in Partitions(4) ) ....: for lam in Partitions(4) ) True Testing the adjointness between the Frobenius operators `\mathbf{f}_n` and the Verschiebung operators `\mathbf{V}_n`:: sage: Sym = SymmetricFunctions(QQ) sage: p = Sym.p() sage: all( all( p(lam).verschiebung(2).scalar(p(mu)) ....: == p(lam).scalar(p(mu).frobenius(2)) ....: for mu in Partitions(2) ) ....: for lam in Partitions(4) ) True TESTS: Let us check that this method on the powersum basis gives the same result as the implementation in :mod:`sage.combinat.sf.sfa` on the monomial basis:: sage: Sym = SymmetricFunctions(QQ) sage: p = Sym.p(); m = Sym.m() sage: all( m(p(lam)).verschiebung(3) == m(p(lam).verschiebung(3)) ....: for lam in Partitions(6) ) True sage: all( p(m(lam)).verschiebung(2) == p(m(lam).verschiebung(2)) ....: for lam in Partitions(4) ) True """ parent = self.parent() p_coords_of_self = self.monomial_coefficients().items() dct = { Partition([i // n for i in lam]): coeff * (n**len(lam)) for (lam, coeff) in p_coords_of_self if all(i % n == 0 for i in lam) } result_in_p_basis = parent._from_dict(dct) return parent(result_in_p_basis)
def SymmetricGroupRepresentation(partition, implementation="specht", ring=None, cache_matrices=True): r""" The irreducible representation of the symmetric group corresponding to ``partition``. INPUT: - ``partition`` - a partition of a positive integer - ``implementation`` - string (default: "specht"), one of: - "seminormal": for Young's seminormal representation - "orthogonal": for Young's orthogonal representation - "specht": for Specht's representation - ``ring`` - the ring over which the representation is defined. - ``cache_matrices`` - boolean (default: True) if True, then any representation matrices that are computed are cached. EXAMPLES: Young's orthogonal representation: the matrices are orthogonal. :: sage: orth = SymmetricGroupRepresentation([2,1], "orthogonal"); orth Orthogonal representation of the symmetric group corresponding to [2, 1] sage: all(a*a.transpose() == a.parent().identity_matrix() for a in orth) True :: sage: orth = SymmetricGroupRepresentation([3,2], "orthogonal"); orth Orthogonal representation of the symmetric group corresponding to [3, 2] sage: orth([2,1,3,4,5]) [ 1 0 0 0 0] [ 0 1 0 0 0] [ 0 0 -1 0 0] [ 0 0 0 1 0] [ 0 0 0 0 -1] sage: orth([1,3,2,4,5]) [ 1 0 0 0 0] [ 0 -1/2 1/2*sqrt(3) 0 0] [ 0 1/2*sqrt(3) 1/2 0 0] [ 0 0 0 -1/2 1/2*sqrt(3)] [ 0 0 0 1/2*sqrt(3) 1/2] sage: orth([1,2,4,3,5]) [ -1/3 2/3*sqrt(2) 0 0 0] [2/3*sqrt(2) 1/3 0 0 0] [ 0 0 1 0 0] [ 0 0 0 1 0] [ 0 0 0 0 -1] The Specht Representation:: sage: spc = SymmetricGroupRepresentation([3,2], "specht") sage: spc.scalar_product_matrix(Permutation([1,2,3,4,5])) [ 1 0 0 0 0] [ 0 -1 0 0 0] [ 0 0 1 0 0] [ 0 0 0 1 0] [-1 0 0 0 -1] sage: spc.scalar_product_matrix(Permutation([5,4,3,2,1])) [ 1 -1 0 1 0] [ 0 0 1 0 -1] [ 0 0 0 -1 1] [ 0 1 -1 -1 1] [-1 0 0 0 -1] sage: spc([5,4,3,2,1]) [ 1 -1 0 1 0] [ 0 0 -1 0 1] [ 0 0 0 -1 1] [ 0 1 -1 -1 1] [ 0 1 0 -1 1] sage: spc.verify_representation() True By default, any representation matrices that are computed are cached:: sage: spc = SymmetricGroupRepresentation([3,2], "specht") sage: spc([5,4,3,2,1]) [ 1 -1 0 1 0] [ 0 0 -1 0 1] [ 0 0 0 -1 1] [ 0 1 -1 -1 1] [ 0 1 0 -1 1] sage: spc._cache__representation_matrix {(([5, 4, 3, 2, 1],), ()): [ 1 -1 0 1 0] [ 0 0 -1 0 1] [ 0 0 0 -1 1] [ 0 1 -1 -1 1] [ 0 1 0 -1 1]} This can be turned off with the keyword cache_matrices:: sage: spc = SymmetricGroupRepresentation([3,2], "specht", cache_matrices=False) sage: spc([5,4,3,2,1]) [ 1 -1 0 1 0] [ 0 0 -1 0 1] [ 0 0 0 -1 1] [ 0 1 -1 -1 1] [ 0 1 0 -1 1] sage: hasattr(spc, '_cache__representation_matrix') False NOTES: The implementation is based on the paper [Las]. REFERENCES: - [Las] Alain Lascoux, `Young representations of the symmetric group <http://phalanstere.univ-mlv.fr/~al/ARTICLES/ProcCrac.ps.gz/>`_. AUTHORS: - Franco Saliola (2009-04-23) """ partition = Partition(partition) if implementation == "seminormal": return YoungRepresentation_Seminormal(partition, ring=ring, cache_matrices=cache_matrices) elif implementation == "orthogonal": return YoungRepresentation_Orthogonal(partition, ring=ring, cache_matrices=cache_matrices) elif implementation == "specht": return SpechtRepresentation(partition, ring=ring, cache_matrices=cache_matrices) else: raise NotImplementedError, "only seminormal, orthogonal and specht are implemented"
def YangBaxterGraph(partition=None, root=None, operators=None): r""" Construct the Yang-Baxter graph from ``root`` by repeated application of ``operators``, or the Yang-Baxter graph associated to ``partition``. INPUT: The user needs to provide either ``partition`` or both ``root`` and ``operators``, where - ``partition`` -- a partition of a positive integer - ``root`` -- the root vertex - ``operator`` - a function that maps vertices `u` to a list of tuples of the form `(v, l)` where `v` is a successor of `u` and `l` is the label of the edge from `u` to `v`. OUTPUT: - Either: - :class:`YangBaxterGraph_partition` - if partition is defined - :class:`YangBaxterGraph_generic` - if partition is ``None`` EXAMPLES: The Yang-Baxter graph defined by a partition `[p_1,\dots,p_k]` is the labelled directed graph with vertex set obtained by bubble-sorting `(p_k-1,p_k-2,\dots,0,\dots,p_1-1,p_1-2,\dots,0)`; there is an arrow from `u` to `v` labelled by `i` if `v` is obtained by swapping the `i`-th and `(i+1)`-th elements of `u`. For example, if the partition is `[3,1]`, then we begin with `(0,2,1,0)` and generate all tuples obtained from it by swapping two adjacent entries if they are increasing:: sage: from sage.combinat.yang_baxter_graph import SwapIncreasingOperator sage: bubbleswaps = [SwapIncreasingOperator(i) for i in range(3)] sage: Y = YangBaxterGraph(root=(0,2,1,0), operators=bubbleswaps); Y Yang-Baxter graph with root vertex (0, 2, 1, 0) sage: Y.vertices() [(2, 0, 1, 0), (2, 1, 0, 0), (0, 2, 1, 0)] The ``partition`` keyword is a shorthand for the above construction. :: sage: Y = YangBaxterGraph(partition=[3,1]); Y Yang-Baxter graph of [3, 1], with top vertex (0, 2, 1, 0) sage: Y.vertices() [(0, 2, 1, 0), (2, 0, 1, 0), (2, 1, 0, 0)] The permutahedron can be realized as a Yang-Baxter graph. :: sage: from sage.combinat.yang_baxter_graph import SwapIncreasingOperator sage: swappers = [SwapIncreasingOperator(i) for i in range(3)] sage: Y = YangBaxterGraph(root=(1,2,3,4), operators=swappers); Y Yang-Baxter graph with root vertex (1, 2, 3, 4) sage: Y.plot() Graphics object consisting of 97 graphics primitives The Cayley graph of a finite group can be realized as a Yang-Baxter graph. :: sage: def left_multiplication_by(g): ....: return lambda h : h*g sage: G = CyclicPermutationGroup(4) sage: operators = [ left_multiplication_by(gen) for gen in G.gens() ] sage: Y = YangBaxterGraph(root=G.identity(), operators=operators); Y Yang-Baxter graph with root vertex () sage: Y.plot(edge_labels=False) Graphics object consisting of 9 graphics primitives sage: G = SymmetricGroup(4) sage: operators = [left_multiplication_by(gen) for gen in G.gens()] sage: Y = YangBaxterGraph(root=G.identity(), operators=operators); Y Yang-Baxter graph with root vertex () sage: Y.plot(edge_labels=False) Graphics object consisting of 96 graphics primitives AUTHORS: - Franco Saliola (2009-04-23) """ if partition is None: return YangBaxterGraph_generic(root=root, operators=operators) else: return YangBaxterGraph_partition(partition=Partition(partition))
def frobenius(self, n): r""" Return the image of the symmetric function ``self`` under the `n`-th Frobenius operator. The `n`-th Frobenius operator `\mathbf{f}_n` is defined to be the map from the ring of symmetric functions to itself that sends every symmetric function `P(x_1, x_2, x_3, \ldots)` to `P(x_1^n, x_2^n, x_3^n, \ldots)`. This operator `\mathbf{f}_n` is a Hopf algebra endomorphism, and satisfies .. MATH:: \mathbf{f}_n m_{(\lambda_1, \lambda_2, \lambda_3, \ldots)} = m_{(n\lambda_1, n\lambda_2, n\lambda_3, \ldots)} for every partition `(\lambda_1, \lambda_2, \lambda_3, \ldots)` (where `m` means the monomial basis). Moreover, `\mathbf{f}_n (p_r) = p_{nr}` for every positive integer `r` (where `p_k` denotes the `k`-th powersum symmetric function). The `n`-th Frobenius operator is also called the `n`-th Frobenius endomorphism. It is not related to the Frobenius map which connects the ring of symmetric functions with the representation theory of the symmetric group. The `n`-th Frobenius operator is also the `n`-th Adams operator of the `\Lambda`-ring of symmetric functions over the integers. The `n`-th Frobenius operator can also be described via plethysm: Every symmetric function `P` satisfies `\mathbf{f}_n(P) = p_n \circ P = P \circ p_n`, where `p_n` is the `n`-th powersum symmetric function, and `\circ` denotes (outer) plethysm. INPUT: - ``n`` -- a positive integer OUTPUT: The result of applying the `n`-th Frobenius operator (on the ring of symmetric functions) to ``self``. EXAMPLES:: sage: Sym = SymmetricFunctions(ZZ) sage: p = Sym.p() sage: p[3].frobenius(2) p[6] sage: p[4,2,1].frobenius(3) p[12, 6, 3] sage: p([]).frobenius(4) p[] sage: p[3].frobenius(1) p[3] sage: (p([3]) - p([2]) + p([])).frobenius(3) p[] - p[6] + p[9] TESTS: Let us check that this method on the powersum basis gives the same result as the implementation in :mod:`sage.combinat.sf.sfa` on the complete homogeneous basis:: sage: Sym = SymmetricFunctions(QQ) sage: p = Sym.p(); h = Sym.h() sage: all( h(p(lam)).frobenius(3) == h(p(lam).frobenius(3)) ....: for lam in Partitions(3) ) True sage: all( p(h(lam)).frobenius(2) == p(h(lam).frobenius(2)) ....: for lam in Partitions(4) ) True .. SEEALSO:: :meth:`~sage.combinat.sf.sfa.SymmetricFunctionAlgebra_generic_Element.plethysm` """ dct = { Partition([n * i for i in lam]): coeff for (lam, coeff) in self.monomial_coefficients().items() } return self.parent()._from_dict(dct)
def GL_irreducible_character(n, mu, KK): r""" Return the character of the irreducible module indexed by ``mu`` of `GL(n)` over the field ``KK``. INPUT: - ``n`` -- a positive integer - ``mu`` -- a partition of at most ``n`` parts - ``KK`` -- a field OUTPUT: a symmetric function which should be interpreted in ``n`` variables to be meaningful as a character EXAMPLES: Over `\QQ`, the irreducible character for `\mu` is the Schur function associated to `\mu`, plus garbage terms (Schur functions associated to partitions with more than `n` parts):: sage: from sage.algebras.schur_algebra import GL_irreducible_character sage: sbasis = SymmetricFunctions(QQ).s() sage: z = GL_irreducible_character(2, [2], QQ) sage: sbasis(z) s[2] sage: z = GL_irreducible_character(4, [3, 2], QQ) sage: sbasis(z) -5*s[1, 1, 1, 1, 1] + s[3, 2] Over a Galois field, the irreducible character for `\mu` will in general be smaller. In characteristic `p`, for a one-part partition `(r)`, where `r = a_0 + p a_1 + p^2 a_2 + \dots`, the result is (see [GreenPoly]_, after 5.5d) the product of `h[a_0], h[a_1]( pbasis[p]), h[a_2] ( pbasis[p^2]), \dots,` which is consistent with the following :: sage: from sage.algebras.schur_algebra import GL_irreducible_character sage: GL_irreducible_character(2, [7], GF(3)) m[4, 3] + m[6, 1] + m[7] """ mbasis = SymmetricFunctions(QQ).m() r = sum(mu) M = SchurTensorModule(KK, n, r) A = M._schur SGA = M._sga #make ST the superstandard tableau of shape mu from sage.combinat.tableau import from_shape_and_word ST = from_shape_and_word(mu, range(1, r + 1), convention='English') #make ell the reading word of the highest weight tableau of shape mu ell = [i + 1 for i, l in enumerate(mu) for dummy in range(l)] e = M.basis()[tuple(ell)] # the element e_l # This is the notation `\{X\}` from just before (5.3a) of [GreenPoly]_. S = SGA._indices BracC = SGA._from_dict({S(x.tuple()): x.sign() for x in ST.column_stabilizer()}, remove_zeros=False) f = e * BracC # M.action_by_symmetric_group_algebra(e, BracC) # [Green, Theorem 5.3b] says that a basis of the Carter-Lusztig # module V_\mu is given by taking this f, and multiplying by all # xi_{i,ell} with ell as above and i semistandard. carter_lusztig = [] for T in SemistandardTableaux(mu, max_entry=n): i = tuple(flatten(T)) schur_rep = schur_representative_from_index(i, tuple(ell)) y = A.basis()[schur_rep] * e # M.action_by_Schur_alg(A.basis()[schur_rep], e) carter_lusztig.append(y.to_vector()) #Therefore, we now have carter_lusztig as a list giving the basis #of `V_\mu` #We want to think of expressing this character as a sum of monomial #symmetric functions. #We will determine a basis element for each m_\lambda in the #character, and we want to keep track of them by \lambda. #That means that we only want to pick out the basis elements above for #those semistandard words whose content is a partition. contents = Partitions(r, max_length=n).list() # all partitions of r, length at most n # JJ will consist of a list for each element of `contents`, # recording the list # of semistandard tableaux words with that content # graded_basis will consist of the a corresponding basis element graded_basis = [] JJ = [] for i in range(len(contents)): graded_basis.append([]) JJ.append([]) for T in SemistandardTableaux(mu, max_entry=n): i = tuple(flatten(T)) # Get the content of T con = [0] * n for a in i: con[a - 1] += 1 try: P = Partition(con) P_index = contents.index(P) JJ[P_index].append(i) schur_rep = schur_representative_from_index(i, tuple(ell)) x = A.basis()[schur_rep] * f # M.action_by_Schur_alg(A.basis()[schur_rep], f) graded_basis[P_index].append(x.to_vector()) except ValueError: pass #There is an inner product on the Carter-Lusztig module V_\mu; its #maximal submodule is exactly the kernel of the inner product. #Now, for each possible partition content, we look at the graded piece of #that degree, and we record how these elements pair with each of the #elements of carter_lusztig. #The kernel of this pairing is the part of this graded piece which is #not in the irreducible module for \mu. length = len(carter_lusztig) phi = mbasis.zero() for aa in range(len(contents)): mat = [] for kk in range(len(JJ[aa])): temp = [] for j in range(length): temp.append(graded_basis[aa][kk].inner_product(carter_lusztig[j])) mat.append(temp) angle = Matrix(mat) phi += (len(JJ[aa]) - angle.nullity()) * mbasis(contents[aa]) return phi
def P(i): return Partition([i]) if i else Partition([])
def SymmetricGroupRepresentation(partition, implementation="specht", ring=None, cache_matrices=True): r""" The irreducible representation of the symmetric group corresponding to ``partition``. INPUT: - ``partition`` -- a partition of a positive integer - ``implementation`` -- string (default: ``"specht"``), one of: * ``"seminormal"`` - for Young's seminormal representation * ``"orthogonal"`` - for Young's orthogonal representation * ``"specht"`` - for Specht's representation - ``ring`` -- the ring over which the representation is defined - ``cache_matrices`` -- boolean (default: ``True``) if ``True``, then any representation matrices that are computed are cached EXAMPLES: Young's orthogonal representation: the matrices are orthogonal. :: sage: orth = SymmetricGroupRepresentation([2,1], "orthogonal"); orth Orthogonal representation of the symmetric group corresponding to [2, 1] sage: all(a*a.transpose() == a.parent().identity_matrix() for a in orth) True :: sage: orth = SymmetricGroupRepresentation([3,2], "orthogonal"); orth Orthogonal representation of the symmetric group corresponding to [3, 2] sage: orth([2,1,3,4,5]) [ 1 0 0 0 0] [ 0 1 0 0 0] [ 0 0 -1 0 0] [ 0 0 0 1 0] [ 0 0 0 0 -1] sage: orth([1,3,2,4,5]) [ 1 0 0 0 0] [ 0 -1/2 1/2*sqrt(3) 0 0] [ 0 1/2*sqrt(3) 1/2 0 0] [ 0 0 0 -1/2 1/2*sqrt(3)] [ 0 0 0 1/2*sqrt(3) 1/2] sage: orth([1,2,4,3,5]) [ -1/3 2/3*sqrt(2) 0 0 0] [2/3*sqrt(2) 1/3 0 0 0] [ 0 0 1 0 0] [ 0 0 0 1 0] [ 0 0 0 0 -1] The Specht representation:: sage: spc = SymmetricGroupRepresentation([3,2], "specht") sage: spc.scalar_product_matrix(Permutation([1,2,3,4,5])) [ 1 0 0 0 0] [ 0 -1 0 0 0] [ 0 0 1 0 0] [ 0 0 0 1 0] [-1 0 0 0 -1] sage: spc.scalar_product_matrix(Permutation([5,4,3,2,1])) [ 1 -1 0 1 0] [ 0 0 1 0 -1] [ 0 0 0 -1 1] [ 0 1 -1 -1 1] [-1 0 0 0 -1] sage: spc([5,4,3,2,1]) [ 1 -1 0 1 0] [ 0 0 -1 0 1] [ 0 0 0 -1 1] [ 0 1 -1 -1 1] [ 0 1 0 -1 1] sage: spc.verify_representation() True By default, any representation matrices that are computed are cached:: sage: spc = SymmetricGroupRepresentation([3,2], "specht") sage: spc([5,4,3,2,1]) [ 1 -1 0 1 0] [ 0 0 -1 0 1] [ 0 0 0 -1 1] [ 0 1 -1 -1 1] [ 0 1 0 -1 1] sage: spc._cache__representation_matrix {(([5, 4, 3, 2, 1],), ()): [ 1 -1 0 1 0] [ 0 0 -1 0 1] [ 0 0 0 -1 1] [ 0 1 -1 -1 1] [ 0 1 0 -1 1]} This can be turned off with the keyword ``cache_matrices``:: sage: spc = SymmetricGroupRepresentation([3,2], "specht", cache_matrices=False) sage: spc([5,4,3,2,1]) [ 1 -1 0 1 0] [ 0 0 -1 0 1] [ 0 0 0 -1 1] [ 0 1 -1 -1 1] [ 0 1 0 -1 1] sage: hasattr(spc, '_cache__representation_matrix') False .. NOTE:: The implementation is based on the paper [Las]_. REFERENCES: .. [Las] Alain Lascoux, 'Young representations of the symmetric group.' http://phalanstere.univ-mlv.fr/~al/ARTICLES/ProcCrac.ps.gz AUTHORS: - Franco Saliola (2009-04-23) """ partition = Partition(partition) Rep = SymmetricGroupRepresentations(sum(partition), implementation=implementation, ring=ring, cache_matrices=cache_matrices) return Rep(partition)
def __classcall_private__(cls, cartan_type, shapes=None, shape=None): """ Normalizes the input arguments to ensure unique representation, and to delegate the construction of spin tableaux. EXAMPLES:: sage: T1 = crystals.Tableaux(CartanType(['A',3]), shape = [2,2]) sage: T2 = crystals.Tableaux(['A',3], shape = (2,2)) sage: T3 = crystals.Tableaux(['A',3], shapes = ([2,2],)) sage: T2 is T1, T3 is T1 (True, True) sage: T1 = crystals.Tableaux(['A', [1,1]], shape=[3,1,1,1]) sage: T1 Crystal of BKK tableaux of shape [3, 1, 1, 1] of gl(2|2) sage: T2 = crystals.Tableaux(['A', [1,1]], [3,1,1,1]) sage: T1 is T2 True """ cartan_type = CartanType(cartan_type) if cartan_type.letter == 'A' and isinstance(cartan_type, SuperCartanType_standard): if shape is None: shape = shapes from sage.combinat.crystals.bkk_crystals import CrystalOfBKKTableaux return CrystalOfBKKTableaux(cartan_type, shape=shape) if cartan_type.letter == 'Q': if any(shape[i] == shape[i + 1] for i in range(len(shape) - 1)): raise ValueError("not a strict partition") shape = Partition(shape) return CrystalOfQueerTableaux(cartan_type, shape=shape) n = cartan_type.rank() # standardize shape/shapes input into a tuple of tuples assert operator.xor(shape is not None, shapes is not None) if shape is not None: shapes = (shape, ) spin_shapes = tuple(tuple(shape) for shape in shapes) try: shapes = tuple( tuple(trunc(i) for i in shape) for shape in spin_shapes) except Exception: raise ValueError( "shapes should all be partitions or half-integer partitions") if spin_shapes == shapes: return super(CrystalOfTableaux, cls).__classcall__(cls, cartan_type, shapes) # Handle the construction of a crystals of spin tableaux # Caveat: this currently only supports all shapes being half # integer partitions of length the rank for type B and D. In # particular, for type D, the spins all have to be plus or all # minus spins if any(len(sh) != n for sh in shapes): raise ValueError( "the length of all half-integer partition shapes should be the rank" ) if any(2 * i % 2 != 1 for shape in spin_shapes for i in shape): raise ValueError( "shapes should be either all partitions or all half-integer partitions" ) if cartan_type.type() == 'D': if all(i >= 0 for shape in spin_shapes for i in shape): S = CrystalOfSpinsPlus(cartan_type) elif all(shape[-1] < 0 for shape in spin_shapes): S = CrystalOfSpinsMinus(cartan_type) else: raise ValueError( "in type D spins should all be positive or negative") else: if any(i < 0 for shape in spin_shapes for i in shape): raise ValueError("shapes should all be partitions") S = CrystalOfSpins(cartan_type) B = CrystalOfTableaux(cartan_type, shapes=shapes) T = TensorProductOfCrystals(S, B, generators=[[S.module_generators[0], x] for x in B.module_generators]) T.rename("The crystal of tableaux of type %s and shape(s) %s" % (cartan_type, list(list(shape) for shape in spin_shapes))) T.shapes = spin_shapes return T
def hall_polynomial(nu, mu, la, q=None): r""" Return the (classical) Hall polynomial `P^{\nu}_{\mu,\lambda}` (where `\nu`, `\mu` and `\lambda` are the inputs ``nu``, ``mu`` and ``la``). Let `\nu,\mu,\lambda` be partitions. The Hall polynomial `P^{\nu}_{\mu,\lambda}(q)` (in the indeterminate `q`) is defined as follows: Specialize `q` to a prime power, and consider the category of `\GF{q}`-vector spaces with a distinguished nilpotent endomorphism. The morphisms in this category shall be the linear maps commuting with the distinguished endomorphisms. The *type* of an object in the category will be the Jordan type of the distinguished endomorphism; this is a partition. Now, if `N` is any fixed object of type `\nu` in this category, then the polynomial `P^{\nu}_{\mu,\lambda}(q)` specialized at the prime power `q` counts the number of subobjects `L` of `N` having type `\lambda` such that the quotient object `N / L` has type `\mu`. This determines the values of the polynomial `P^{\nu}_{\mu,\lambda}` at infinitely many points (namely, at all prime powers), and hence `P^{\nu}_{\mu,\lambda}` is uniquely determined. The degree of this polynomial is at most `n(\nu) - n(\lambda) - n(\mu)`, where `n(\kappa) = \sum_i (i-1) \kappa_i` for every partition `\kappa`. (If this is negative, then the polynomial is zero.) These are the structure coefficients of the :class:`(classical) Hall algebra <HallAlgebra>`. If `\lvert \nu \rvert \neq \lvert \mu \rvert + \lvert \lambda \rvert`, then we have `P^{\nu}_{\mu,\lambda} = 0`. More generally, if the Littlewood-Richardson coefficient `c^{\nu}_{\mu,\lambda}` vanishes, then `P^{\nu}_{\mu,\lambda} = 0`. The Hall polynomials satisfy the symmetry property `P^{\nu}_{\mu,\lambda} = P^{\nu}_{\lambda,\mu}`. ALGORITHM: If `\lambda = (1^r)` and `\lvert \nu \rvert = \lvert \mu \rvert + \lvert \lambda \rvert`, then we compute `P^{\nu}_{\mu,\lambda}` as follows (cf. Example 2.4 in [Sch2006]_): First, write `\nu = (1^{l_1}, 2^{l_2}, \ldots, n^{l_n})`, and define a sequence `r = r_0 \geq r_1 \geq \cdots \geq r_n` such that .. MATH:: \mu = \left( 1^{l_1 - r_0 + 2r_1 - r_2}, 2^{l_2 - r_1 + 2r_2 - r_3}, \ldots , (n-1)^{l_{n-1} - r_{n-2} + 2r_{n-1} - r_n}, n^{l_n - r_{n-1} + r_n} \right). Thus if `\mu = (1^{m_1}, \ldots, n^{m_n})`, we have the following system of equations: .. MATH:: \begin{aligned} m_1 & = l_1 - r + 2r_1 - r_2, \\ m_2 & = l_2 - r_1 + 2r_2 - r_3, \\ & \vdots , \\ m_{n-1} & = l_{n-1} - r_{n-2} + 2r_{n-1} - r_n, \\ m_n & = l_n - r_{n-1} + r_n \end{aligned} and solving for `r_i` and back substituting we obtain the equations: .. MATH:: \begin{aligned} r_n & = r_{n-1} + m_n - l_n, \\ r_{n-1} & = r_{n-2} + m_{n-1} - l_{n-1} + m_n - l_n, \\ & \vdots , \\ r_1 & = r + \sum_{k=1}^n (m_k - l_k), \end{aligned} or in general we have the recursive equation: .. MATH:: r_i = r_{i-1} + \sum_{k=i}^n (m_k - l_k). This, combined with the condition that `r_0 = r`, determines the `r_i` uniquely (recursively). Next we define .. MATH:: t = (r_{n-2} - r_{n-1})(l_n - r_{n-1}) + (r_{n-3} - r_{n-2})(l_{n-1} + l_n - r_{n-2}) + \cdots + (r_0 - r_1)(l_2 + \cdots + l_n - r_1), and with these notations we have .. MATH:: P^{\nu}_{\mu,(1^r)} = q^t \binom{l_n}{r_{n-1}}_q \binom{l_{n-1}}{r_{n-2} - r_{n-1}}_q \cdots \binom{l_1}{r_0 - r_1}_q. To compute `P^{\nu}_{\mu,\lambda}` in general, we compute the product `I_{\mu} I_{\lambda}` in the Hall algebra and return the coefficient of `I_{\nu}`. EXAMPLES:: sage: from sage.combinat.hall_polynomial import hall_polynomial sage: hall_polynomial([1,1],[1],[1]) q + 1 sage: hall_polynomial([2],[1],[1]) 1 sage: hall_polynomial([2,1],[2],[1]) q sage: hall_polynomial([2,2,1],[2,1],[1,1]) q^2 + q sage: hall_polynomial([2,2,2,1],[2,2,1],[1,1]) q^4 + q^3 + q^2 sage: hall_polynomial([3,2,2,1], [3,2], [2,1]) q^6 + q^5 sage: hall_polynomial([4,2,1,1], [3,1,1], [2,1]) 2*q^3 + q^2 - q - 1 sage: hall_polynomial([4,2], [2,1], [2,1], 0) 1 """ if q is None: q = ZZ['q'].gen() R = q.parent() # Make sure they are partitions nu = Partition(nu) mu = Partition(mu) la = Partition(la) if sum(nu) != sum(mu) + sum(la): return R.zero() if all(x == 1 for x in la): r = [len(la)] # r will be [r_0, r_1, ..., r_n]. exp_nu = nu.to_exp() # exp_nu == [l_1, l_2, ..., l_n]. exp_mu = mu.to_exp() # exp_mu == [m_1, m_2, ..., m_n]. n = max(len(exp_nu), len(exp_mu)) for k in range(n): r.append(r[-1] + sum(exp_mu[k:]) - sum(exp_nu[k:])) # Now, r is [r_0, r_1, ..., r_n]. exp_nu += [0] * (n - len(exp_nu)) # Pad with 0's until it has length n # Note that all -1 for exp_nu is due to indexing t = sum((r[k - 2] - r[k - 1]) * (sum(exp_nu[k - 1:]) - r[k - 1]) for k in range(2, n + 1)) if t < 0: # This case needs short-circuiting, since otherwise q**-t # might throw an exception if q is non-invertible. return R.zero() return q**t * q_binomial(exp_nu[n-1], r[n-1], q) \ * prod([q_binomial(exp_nu[k-1], r[k-1] - r[k], q) for k in range(1, n)], R.one()) from sage.algebras.hall_algebra import HallAlgebra H = HallAlgebra(R, q) return (H[mu] * H[la]).coefficient(nu)
def is_gale_ryser(r, s): r""" Tests whether the given sequences satisfy the condition of the Gale-Ryser theorem. Given a binary matrix `B` of dimension `n\times m`, the vector of row sums is defined as the vector whose `i^{\mbox{th}}` component is equal to the sum of the `i^{\mbox{th}}` row in `A`. The vector of column sums is defined similarly. If, given a binary matrix, these two vectors are easy to compute, the Gale-Ryser theorem lets us decide whether, given two non-negative vectors `r,s`, there exists a binary matrix whose row/colum sums vectors are `r` and `s`. This functions answers accordingly. INPUT: - ``r``, ``s`` -- lists of non-negative integers. ALGORITHM: Without loss of generality, we can assume that: - The two given sequences do not contain any `0` ( which would correspond to an empty column/row ) - The two given sequences are ordered in decreasing order (reordering the sequence of row (resp. column) sums amounts to reordering the rows (resp. columns) themselves in the matrix, which does not alter the columns (resp. rows) sums. We can then assume that `r` and `s` are partitions (see the corresponding class ``Partition``) If `r^*` denote the conjugate of `r`, the Gale-Ryser theorem asserts that a binary Matrix satisfying the constraints exists if and only if `s\preceq r^*`, where `\preceq` denotes the domination order on partitions. EXAMPLES:: sage: from sage.combinat.integer_vector import is_gale_ryser sage: is_gale_ryser([4,2,2],[3,3,1,1]) True sage: is_gale_ryser([4,2,1,1],[3,3,1,1]) True sage: is_gale_ryser([3,2,1,1],[3,3,1,1]) False REMARK: In the literature, what we are calling a Gale-Ryser sequence sometimes goes by the (rather generic-sounding) term ''realizable sequence''. """ # The sequences only contan non-negative integers if [x for x in r if x < 0] or [x for x in s if x < 0]: return False # builds the corresponding partitions, i.e. # removes the 0 and sorts the sequences from sage.combinat.partition import Partition r2 = Partition(sorted([x for x in r if x > 0], reverse=True)) s2 = Partition(sorted([x for x in s if x > 0], reverse=True)) # If the two sequences only contained zeroes if len(r2) == 0 and len(s2) == 0: return True rstar = Partition(r2).conjugate() # same number of 1s domination return len(rstar) <= len(s2) and sum(r2) == sum(s2) and rstar.dominates(s)
def phi(lam): mu = [p - 1 for p in lam if p > 0] nu = sorted(mu + [len(lam)], reverse=True) return Partition(nu)