Ejemplo n.º 1
0
    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
        ])
Ejemplo n.º 2
0
        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)
Ejemplo n.º 3
0
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"
Ejemplo n.º 4
0
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))
Ejemplo n.º 5
0
        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)
Ejemplo n.º 6
0
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
Ejemplo n.º 7
0
 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)
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
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)
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
0
 def phi(lam):
     mu = [p - 1 for p in lam if p > 0]
     nu = sorted(mu + [len(lam)], reverse=True)
     return Partition(nu)