예제 #1
0
def lr_pairs(mu, nu, la=None, n=None):
    tmu = TimedTableau(SemistandardTableaux(mu).first(), rows=True)
    for tnu in SemistandardTableaux(nu, max_entry=len(nu)):
        tnu = TimedTableau(tnu, rows=True)
        w = tnu.concatenate(tmu)
        if la is None or w.weight() == la:
            yield tmu, tnu
예제 #2
0
    def random_element(self):
        """
        Return a uniformly random Gelfand-Tsetlin pattern with specified top row.

        EXAMPLES::

            sage: g = GelfandTsetlinPatterns(top_row = [4, 3, 1, 1])
            sage: x = g.random_element()
            sage: x in g
            True
            sage: x[0] == [4, 3, 1, 1]
            True
            sage: x.check()

            sage: g = GelfandTsetlinPatterns(top_row=[4, 3, 2, 1], strict=True)
            sage: x = g.random_element()
            sage: x in g
            True
            sage: x[0] == [4, 3, 2, 1]
            True
            sage: x.is_strict()
            True
            sage: x.check()
        """
        if self._strict:
            return self._cftp(1)
        else:
            l = [i for i in self._row if i > 0]
            return SemistandardTableaux(l, max_entry=self._n).random_element(
            ).to_Gelfand_Tsetlin_pattern()
예제 #3
0
    def SSTPoset(s, f=None):
        """
        The poset on semistandard tableaux of shape ``s`` and largest
        entry ``f`` that is ordered by componentwise comparison of the
        entries.

        INPUT:

        - ``s`` - shape of the tableaux

        - ``f`` - maximum fill number.  This is an optional
          argument.  If no maximal number is given, it will use
          the number of cells in the shape.

        NOTE: This is a basic implementation and most certainly
        not the most efficient.

        EXAMPLES::

            sage: Posets.SSTPoset([2,1])
            Finite poset containing 8 elements

            sage: Posets.SSTPoset([2,1],4)
            Finite poset containing 20 elements

            sage: Posets.SSTPoset([2,1],2).cover_relations()
            [[[[1, 1], [2]], [[1, 2], [2]]]]

            sage: Posets.SSTPoset([3,2]).bottom()  # long time (6s on sage.math, 2012)
            [[1, 1, 1], [2, 2]]

            sage: Posets.SSTPoset([3,2],4).maximal_elements()
            [[[3, 3, 4], [4, 4]]]
        """
        from sage.combinat.tableau import SemistandardTableaux

        def tableaux_is_less_than(a, b):
            atstring = []
            btstring = []
            for i in a:
                atstring += i
            for i in b:
                btstring += i
            for i in range(len(atstring)):
                if atstring[i] > btstring[i]:
                    return False
            return True

        if f is None:
            f = 0
            for i in s:
                f += i
        E = SemistandardTableaux(s, max_entry=f)
        return Poset((E, tableaux_is_less_than))
예제 #4
0
def littlewood_richardson_pairs(mu, nu, la=None, n=None):
    """
    Return all pairs `x` and `y` of tableau words of
    shape ``mu`` and ``nu`` such that such that `xy`
    is Yamanouchi of shape ``la``.
    """
    if n is None:
        if la:
            n = len(la)
        else:
            n = len(nu) + len(mu)
    X = SemistandardTableaux(mu).first()
    for Y in SemistandardTableaux(nu, max_entry=n):
        x = TimedWord(X.to_word())
        y = TimedWord(Y.to_word())
        z = y.concatenate(x)
        if z.is_yamanouchi():
            if la is None:
                yield x, y
            elif z.weight() == la:
                yield x, y
예제 #5
0
    def to_tableau(self):
        """
        Return ``self`` as a semistandard Young tableau.

        The conversion from a Gelfand-Tsetlin pattern to a semistandard Young
        tableaux is as follows. Let `G` be a Gelfand-Tsetlin pattern with
        `\lambda^{(k)}` being the `(n-k+1)`-st row (note that this is a
        partition).  The definition of `G` implies

        .. MATH::

            \lambda^{(0)} \subseteq \lambda^{(1)} \subseteq \cdots \subseteq
            \lambda^{(n)},

        where `\lambda^{(0)}` is the empty partition, and each skew shape
        `\lambda^{(k)} / \lambda^{(k-1)}` is a horizontal strip. Thus define
        `T(G)` by inserting `k` into the squares of the skew shape
        `\lambda^{(k)} / \lambda^{(k-1)}`, for `k=1,\dots,n`.

        EXAMPLES::

            sage: G = GelfandTsetlinPatterns()
            sage: elt = G([[3,2,1],[2,1],[1]])
            sage: T = elt.to_tableau(); T
            [[1, 2, 3], [2, 3], [3]]
            sage: T.pp()
              1  2  3
              2  3
              3
            sage: G(T) == elt
            True
        """
        ret = []
        for i, row in enumerate(reversed(self)):
            for j, val in enumerate(row):
                if j >= len(ret):
                    if val == 0:
                        break
                    ret.append([i + 1] * val)
                else:
                    ret[j].extend([i + 1] * (val - len(ret[j])))
        S = SemistandardTableaux()
        return S(ret)
예제 #6
0
    def __contains__(self, t):
        """
        Check if ``t`` is contained in ``self``.

        TESTS::

            sage: LR = LittlewoodRichardsonTableaux([3,2,1], [[2,1],[2,1]])
            sage: SST = SemistandardTableaux([3,2,1], [2,1,2,1])
            sage: [t for t in SST if t in LR]
            [[[1, 1, 3], [2, 3], [4]], [[1, 1, 3], [2, 4], [3]]]
            sage: [t for t in SST if t in LR] == LR.list()
            True

            sage: LR = LittlewoodRichardsonTableaux([3,2,1], [[2,1],[2,1]])
            sage: T = [[1,1,3], [2,3], [4]]
            sage: T in LR
            True
        """
        return (SemistandardTableaux.__contains__(self, t)
                and is_littlewood_richardson(t, self._heights))
예제 #7
0
파일: lr_tableau.py 프로젝트: sagemath/sage
    def __contains__(self, t):
        """
        Check if ``t`` is contained in ``self``.

        TESTS::

            sage: LR = LittlewoodRichardsonTableaux([3,2,1], [[2,1],[2,1]])
            sage: SST = SemistandardTableaux([3,2,1], [2,1,2,1])
            sage: [t for t in SST if t in LR]
            [[[1, 1, 3], [2, 3], [4]], [[1, 1, 3], [2, 4], [3]]]
            sage: [t for t in SST if t in LR] == LR.list()
            True

            sage: LR = LittlewoodRichardsonTableaux([3,2,1], [[2,1],[2,1]])
            sage: T = [[1,1,3], [2,3], [4]]
            sage: T in LR
            True
        """
        return (SemistandardTableaux.__contains__(self, t)
                and is_littlewood_richardson(t, self._heights))
예제 #8
0
    def __iter__(self):
        """
        Iterate over ``self``.

        EXAMPLES::

            sage: list(PlanePartitions((1,2,1)))
            [Plane partition [[0, 0]],
             Plane partition [[1, 0]],
             Plane partition [[1, 1]]]
        """
        A = self._box[0]
        B = self._box[1]
        C = self._box[2]
        from sage.combinat.tableau import SemistandardTableaux
        for T in SemistandardTableaux([B for i in range(A)], max_entry=C+A):
            PP = [[0 for i in range(B)] for j in range(A)]
            for r in range(A):
                for c in range(B):
                    PP[A-1-r][B-1-c] = T[r][c] - r - 1
            yield self.element_class(self, PP, check=False)
예제 #9
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 [Gr2007]_,
    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, list(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 [Gr2007]_.
    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
예제 #10
0
def hecke_insertion_reverse(p, q, output='array'):
    r"""
    Return the reverse Hecke insertion of ``(p, q)``.

    .. SEEALSO::

        :func:`RSK_inverse`

    EXAMPLES::

        sage: w = [5, 4, 1, 3, 4, 2, 5, 1, 2, 1, 4, 2, 4]
        sage: P,Q = RSK(w, insertion='hecke')
        sage: wp = RSK_inverse(P, Q, insertion='hecke', output='list'); wp
        [5, 4, 1, 3, 4, 2, 5, 1, 2, 1, 4, 2, 4]
        sage: wp == w
        True
    """
    if p.shape() != q.shape():
        raise ValueError("p(=%s) and q(=%s) must have the same shape"%(p, q))
    from sage.combinat.tableau import SemistandardTableaux
    if p not in SemistandardTableaux():
        raise ValueError("p(=%s) must be a semistandard tableau"%p)

    from bisect import bisect_left
    # Make a copy of p and q since this is destructive to it
    p_copy = [list(row) for row in p]
    q_copy = [[list(v) for v in row] for row in q]
    # We shall work on these copies of p and q. Notice that p might get
    # some empty rows in the process; we do not bother pruning them, as
    # they do not matter.

    #upper_row and lower_row will be the upper and lower rows of the
    #generalized permutation we get as a result, but both reversed.
    upper_row = []
    lower_row = []
    d = {}
    for ri, row in enumerate(q):
        for ci, entry in enumerate(row):
            for val in entry:
                if val in d:
                    d[val][ci] = ri
                else:
                    d[val] = {ci: ri}
    #d is now a double family such that for every integers k and j,
    #the value d[k][j] is the row i such that the (i, j)-th cell of
    #q is filled with k.
    for value, row_dict in sorted(d.items(), key=lambda x: -x[0]):
        for i in sorted(row_dict.values(), reverse=True):
            # These are always the right-most entry
            should_be_value = q_copy[i][-1].pop()
            assert value == should_be_value
            if not q_copy[i][-1]:
                # That is, if value was alone in cell q_copy[i][-1].
                q_copy[i].pop()
                x = p_copy[i].pop()
            else:
                x = p_copy[i][-1]
            while i > 0:
                i -= 1
                row = p_copy[i]
                y_pos = bisect_left(row,x) - 1
                y = row[y_pos]
                # Check to see if we can swap x for y
                if ((y_pos == len(row) - 1 or x < row[y_pos+1])
                    and (i == len(p_copy) - 1 or len(p_copy[i+1]) <= y_pos
                         or x < p_copy[i+1][y_pos])):
                    row[y_pos] = x
                x = y
            upper_row.append(value)
            lower_row.append(x)

    if output == 'array':
        return [list(reversed(upper_row)), list(reversed(lower_row))]
    is_standard = (upper_row == range(len(upper_row), 0, -1))
    if output == 'word':
        if not is_standard:
            raise TypeError("q must be standard to have a %s as valid output"%output)
        from sage.combinat.words.word import Word
        return Word(reversed(lower_row))
    if output == 'list':
        if not is_standard:
            raise TypeError("q must be standard to have a %s as valid output"%output)
        return list(reversed(lower_row))
    raise ValueError("invalid output option")
예제 #11
0
def RSK_inverse(p, q, output='array', insertion='RSK'):
    r"""
    Return the generalized permutation corresponding to the pair of
    tableaux `(p,q)` under the inverse of the Robinson-Schensted-Knuth
    algorithm.

    For more information on the bijection, see :func:`RSK`.

    INPUT:

    - ``p``, ``q`` -- Two semi-standard tableaux of the same shape, or
      (in the case when Hecke insertion is used) an increasing tableau and
      a set-valued tableau of the same shape (see the note below for the
      format of the set-valued tableau)

    - ``output`` -- (Default: ``'array'``) if ``q`` is semi-standard:

      - ``'array'`` -- as a two-line array (i.e. generalized permutation or
        biword)
      -  ``'matrix'`` -- as an integer matrix

      and if ``q`` is standard, we can have the output:

      - ``'word'`` -- as a word

      and additionally if ``p`` is standard, we can also have the output:

      - ``'permutation'`` -- as a permutation

    - ``insertion`` -- (Default: ``RSK``) The insertion algorithm used in the
      bijection. Currently the following are supported:

      - ``'RSK'`` -- Robinson-Schensted-Knuth insertion
      - ``'EG'`` -- Edelman-Greene insertion
      - ``'hecke'`` -- Hecke insertion

    .. NOTE::

        In the case of Hecke insertion, the input variable ``q`` should
        be a set-valued tableau, encoded as a tableau whose entries are
        strictly increasing tuples of positive integers. Each such tuple
        encodes the set of its entries.

    EXAMPLES:

    If both ``p`` and ``q`` are standard::

        sage: t1 = Tableau([[1, 2, 5], [3], [4]])
        sage: t2 = Tableau([[1, 2, 3], [4], [5]])
        sage: RSK_inverse(t1, t2)
        [[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
        sage: RSK_inverse(t1, t2, 'word')
        word: 14532
        sage: RSK_inverse(t1, t2, 'matrix')
        [1 0 0 0 0]
        [0 0 0 1 0]
        [0 0 0 0 1]
        [0 0 1 0 0]
        [0 1 0 0 0]
        sage: RSK_inverse(t1, t2, 'permutation')
        [1, 4, 5, 3, 2]
        sage: RSK_inverse(t1, t1, 'permutation')
        [1, 4, 3, 2, 5]
        sage: RSK_inverse(t2, t2, 'permutation')
        [1, 2, 5, 4, 3]
        sage: RSK_inverse(t2, t1, 'permutation')
        [1, 5, 4, 2, 3]

    If the first tableau is semistandard::

        sage: p = Tableau([[1,2,2],[3]]); q = Tableau([[1,2,4],[3]])
        sage: ret = RSK_inverse(p, q); ret
        [[1, 2, 3, 4], [1, 3, 2, 2]]
        sage: RSK_inverse(p, q, 'word')
        word: 1322

    In general::

        sage: p = Tableau([[1,2,2],[2]]); q = Tableau([[1,3,3],[2]])
        sage: RSK_inverse(p, q)
        [[1, 2, 3, 3], [2, 1, 2, 2]]
        sage: RSK_inverse(p, q, 'matrix')
        [0 1]
        [1 0]
        [0 2]

    Using Edelman-Greene insertion::

        sage: pq = RSK([2,1,2,3,2], insertion='EG'); pq
        [[[1, 2, 3], [2, 3]], [[1, 3, 4], [2, 5]]]
        sage: RSK_inverse(*pq, insertion='EG')
        [2, 1, 2, 3, 2]

    Using Hecke insertion::

        sage: w = [5, 4, 1, 3, 4, 2, 5, 1, 2, 1, 4, 2, 4]
        sage: pq = RSK(w, insertion='hecke')
        sage: RSK_inverse(*pq, insertion='hecke', output='list')
        [5, 4, 1, 3, 4, 2, 5, 1, 2, 1, 4, 2, 4]

    .. NOTE::

        The constructor of ``Tableau`` accepts not only semistandard
        tableaux, but also arbitrary lists that are fillings of a
        partition diagram. (And such lists are used, e.g., for the
        set-valued tableau ``q`` that is passed to
        ``RSK_inverse(p, q, insertion='hecke')``.)
        The user is responsible for ensuring that the tableaux passed to
        ``RSK_inverse`` are of the right types (semistandard, standard,
        increasing, set-valued as needed).

    TESTS:

    From empty tableaux::

        sage: RSK_inverse(Tableau([]), Tableau([]))
        [[], []]

    Check that :func:`RSK_inverse` is the inverse of :func:`RSK` on the
    different types of inputs/outputs::

        sage: f = lambda p: RSK_inverse(*RSK(p), output='permutation')
        sage: all(p == f(p) for n in range(7) for p in Permutations(n))
        True
        sage: all(RSK_inverse(*RSK(w), output='word') == w for n in range(4) for w in Words(5, n))
        True
        sage: from sage.combinat.integer_matrices import IntegerMatrices
        sage: M = IntegerMatrices([1,2,2,1], [3,1,1,1])
        sage: all(RSK_inverse(*RSK(m), output='matrix') == m for m in M)
        True

        sage: n = ZZ.random_element(200)
        sage: p = Permutations(n).random_element()
        sage: is_fine = True if p == f(p) else p ; is_fine
        True

    Same for Edelman-Greene (but we are checking only the reduced words that
    can be obtained using the ``reduced_word()`` method from permutations)::

        sage: g = lambda w: RSK_inverse(*RSK(w, insertion='EG'), insertion='EG', output='permutation')
        sage: all(p.reduced_word() == g(p.reduced_word()) for n in range(7) for p in Permutations(n))
        True

        sage: n = ZZ.random_element(200)
        sage: p = Permutations(n).random_element()
        sage: is_fine = True if p == f(p) else p ; is_fine
        True

    Both tableaux must be of the same shape::

        sage: RSK_inverse(Tableau([[1,2,3]]), Tableau([[1,2]]))
        Traceback (most recent call last):
        ...
        ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape
    """
    if insertion == 'hecke':
        return hecke_insertion_reverse(p, q, output)

    if p.shape() != q.shape():
        raise ValueError("p(=%s) and q(=%s) must have the same shape"%(p, q))
    from sage.combinat.tableau import SemistandardTableaux
    if p not in SemistandardTableaux():
        raise ValueError("p(=%s) must be a semistandard tableau"%p)

    from bisect import bisect_left
    # Make a copy of p since this is destructive to it
    p_copy = [list(row) for row in p]

    if q.is_standard():
        rev_word = [] # This will be our word in reverse
        d = dict((qij,i) for i, Li in enumerate(q) for qij in Li)
        # d is now a dictionary which assigns to each integer k the
        # number of the row of q containing k.

        use_EG = (insertion == 'EG')

        for i in reversed(d.values()): # Delete last entry from i-th row of p_copy
            x = p_copy[i].pop() # Always the right-most entry
            for row in reversed(p_copy[:i]):
                y_pos = bisect_left(row,x) - 1
                if use_EG and row[y_pos] == x - 1 and y_pos < len(row)-1 and row[y_pos+1] == x:
                    # Nothing to do except decrement x by 1.
                    # (Case 1 on p. 74 of Edelman-Greene [EG1987]_.)
                    x -= 1
                else:
                    # switch x and y
                    x, row[y_pos] = row[y_pos], x
            rev_word.append(x)

        if use_EG:
            return list(reversed(rev_word))
        if output == 'word':
            from sage.combinat.words.word import Word
            return Word(reversed(rev_word))
        if output == 'matrix':
            return to_matrix(list(range(1, len(rev_word)+1)), list(reversed(rev_word)))
        if output == 'array':
            return [list(range(1, len(rev_word)+1)), list(reversed(rev_word))]
        if output == 'permutation':
            if not p.is_standard():
                raise TypeError("p must be standard to have a valid permutation as output")
            from sage.combinat.permutation import Permutation
            return Permutation(reversed(rev_word))
        raise ValueError("invalid output option")

    # Checks
    if insertion != 'RSK':
        raise NotImplementedError("only RSK is implemented for non-standard q")
    if q not in SemistandardTableaux():
        raise ValueError("q(=%s) must be a semistandard tableau"%q)

    upper_row = []
    lower_row = []
    #upper_row and lower_row will be the upper and lower rows of the
    #generalized permutation we get as a result, but both reversed.
    d = {}
    for row, Li in enumerate(q):
        for col, val in enumerate(Li):
            if val in d:
                d[val][col] = row
            else:
                d[val] = {col: row}
    #d is now a double family such that for every integers k and j,
    #the value d[k][j] is the row i such that the (i, j)-th cell of
    #q is filled with k.
    for value, row_dict in reversed(d.items()):
        for i in reversed(row_dict.values()):
            x = p_copy[i].pop() # Always the right-most entry
            for row in reversed(p_copy[:i]):
                y = bisect_left(row,x) - 1
                x, row[y] = row[y], x
            upper_row.append(value)
            lower_row.append(x)

    if output == 'matrix':
        return to_matrix(list(reversed(upper_row)), list(reversed(lower_row)))
    if output == 'array':
        return [list(reversed(upper_row)), list(reversed(lower_row))]
    if output in ['permutation', 'word']:
        raise TypeError("q must be standard to have a %s as valid output"%output)
    raise ValueError("invalid output option")
예제 #12
0
def RSK_inverse(p, q, output='array', insertion='RSK'):
    r"""
    Return the generalized permutation corresponding to the pair of tableaux
    `(p,q)` under the inverse of the Robinson-Schensted-Knuth algorithm.

    For more information on the bijeciton, see :func:`RSK`.

    INPUT:

    - ``p``, ``q`` -- Two semi-standard tableaux of the same shape

    - ``output`` -- (Default: ``'array'``) if ``q`` is semi-standard:

      - ``'array'`` -- as a two-line array (i.e. generalized permutation or
        biword)
      -  ``'matrix'`` -- as an integer matrix

      and if ``q`` is standard, we can have the output:

      - ``'word'`` -- as a word

      and additionally if ``p`` is standard, we can also have the output:

      - ``'permutation'`` -- as a permutation

    - ``insertion`` -- (Default: ``RSK``) The insertion algorithm used in the
      bijection. Currently only ``'RSK'`` and ``'EG'`` (Edelman-Greene) are
      supported.

    EXAMPLES:

    If both ``p`` and ``q`` are standard::

        sage: t1 = Tableau([[1, 2, 5], [3], [4]])
        sage: t2 = Tableau([[1, 2, 3], [4], [5]])
        sage: RSK_inverse(t1, t2)
        [[1, 2, 3, 4, 5], [1, 4, 5, 3, 2]]
        sage: RSK_inverse(t1, t2, 'word')
        word: 14532
        sage: RSK_inverse(t1, t2, 'matrix')
        [1 0 0 0 0]
        [0 0 0 1 0]
        [0 0 0 0 1]
        [0 0 1 0 0]
        [0 1 0 0 0]
        sage: RSK_inverse(t1, t2, 'permutation')
        [1, 4, 5, 3, 2]
        sage: RSK_inverse(t1, t1, 'permutation')
        [1, 4, 3, 2, 5]
        sage: RSK_inverse(t2, t2, 'permutation')
        [1, 2, 5, 4, 3]
        sage: RSK_inverse(t2, t1, 'permutation')
        [1, 5, 4, 2, 3]

    If the first tableau is semistandard::

        sage: p = Tableau([[1,2,2],[3]]); q = Tableau([[1,2,4],[3]])
        sage: ret = RSK_inverse(p, q); ret
        [[1, 2, 3, 4], [1, 3, 2, 2]]
        sage: RSK_inverse(p, q, 'word')
        word: 1322

    In general::

        sage: p = Tableau([[1,2,2],[2]]); q = Tableau([[1,3,3],[2]])
        sage: RSK_inverse(p, q)
        [[1, 2, 3, 3], [2, 1, 2, 2]]
        sage: RSK_inverse(p, q, 'matrix')
        [0 1]
        [1 0]
        [0 2]

    Using the Edelman-Greene insertion::

        sage: RSK([2,1,2,3,2], insertion='RSK')
        [[[1, 2, 2], [2, 3]], [[1, 3, 4], [2, 5]]]
        sage: RSK_inverse(*_, insertion='EG')
        word: 21232

    .. NOTE::

        Currently the constructor of ``Tableau`` accept as input lists
        that are not even tableaux but only filling of a partition diagram.
        This feature should not be used with ``RSK_inverse``.

    TESTS:

    From empty tableaux::

        sage: RSK_inverse(Tableau([]), Tableau([]))
        [[], []]

    :func:`RSK_inverse` is the inverse of :func:`RSK`::

        sage: f = lambda p: RSK_inverse(*RSK(p), output='permutation')
        sage: all(p == f(p) for n in range(7) for p in Permutations(n))
        True

        sage: n = ZZ.random_element(200)
        sage: p = Permutations(n).random_element()
        sage: is_fine = True if p == f(p) else p ; is_fine
        True

    Same for Edelman-Greene (but we are checking only the reduced words that
    can be obtained using the ``reduced_word()`` method from permutations)::

        sage: g = lambda p: RSK_inverse(*RSK(p.reduced_word(), insertion='EG'), insertion='EG', output='permutation')
        sage: all(p == f(p) for n in range(7) for p in Permutations(n))
        True

        sage: n = ZZ.random_element(200)
        sage: p = Permutations(n).random_element()
        sage: is_fine = True if p == f(p) else p ; is_fine
        True

    Both tableaux must be of the same shape::

        sage: RSK_inverse(Tableau([[1,2,3]]), Tableau([[1,2]]))
        Traceback (most recent call last):
        ...
        ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape
    """
    if p.shape() != q.shape():
        raise ValueError("p(=%s) and q(=%s) must have the same shape" % (p, q))
    from sage.combinat.tableau import SemistandardTableaux
    if p not in SemistandardTableaux():
        raise ValueError("p(=%s) must be a semistandard tableau" % p)

    from bisect import bisect_left
    # Make a copy of p since this is destructive to it
    p_copy = [row[:] for row in p]

    if q.is_standard():
        permutation = []  #This will be our word in reverse. Despite
        #the name, it doesn't have to be a permutation.
        d = dict((qij, i) for i, Li in enumerate(q) for qij in Li)
        #d is now a dictionary which assigns to each integer k the
        #number of the row of q containing k.

        use_EG = (insertion == 'EG')

        for i in reversed(
                d.values()):  #Delete last entry from i-th row of p_copy
            x = p_copy[i].pop()  # Always the right-most entry
            for row in reversed(p_copy[:i]):
                y_pos = bisect_left(row, x) - 1
                if use_EG and row[y_pos] == x - 1 and y_pos < len(
                        row) - 1 and row[y_pos + 1] == x:
                    # Nothing to do except decrement x by 1.
                    # (Case 1 on p. 74 of Edelman-Greene [EG1987]_.)
                    x -= 1
                else:
                    # switch x and y
                    x, row[y_pos] = row[y_pos], x
            permutation.append(x)

        if output == 'word' or use_EG:
            from sage.combinat.words.word import Word
            return Word(reversed(permutation))
        if output == 'matrix':
            return to_matrix(list(range(1,
                                        len(permutation) + 1)),
                             list(reversed(permutation)))
        if output == 'array':
            return [
                list(range(1,
                           len(permutation) + 1)),
                list(reversed(permutation))
            ]
        if output == 'permutation':
            if not p.is_standard():
                raise TypeError(
                    "p must be standard to have a valid permutation as output")
            from sage.combinat.permutation import Permutation
            return Permutation(reversed(permutation))
        raise ValueError("Invalid output option")

    # Checks
    if insertion != 'RSK':
        raise NotImplementedError("Only RSK is implemented for non-standard Q")
    if q not in SemistandardTableaux():
        raise ValueError("q(=%s) must be a semistandard tableau" % q)

    upper_row = []
    lower_row = []
    #upper_row and lower_row will be the upper and lower rows of the
    #generalized permutation we get as a result, but both reversed.
    d = {}
    for row, Li in enumerate(q):
        for col, val in enumerate(Li):
            if val in d:
                d[val][col] = row
            else:
                d[val] = {col: row}
    #d is now a double family such that for every integers k and j,
    #the value d[k][j] is the row i such that the (i, j)-th cell of
    #q is filled with k.
    for value, row_dict in reversed(d.items()):
        for i in reversed(row_dict.values()):
            x = p_copy[i].pop()  # Always the right-most entry
            for row in reversed(p_copy[:i]):
                y = bisect_left(row, x) - 1
                x, row[y] = row[y], x
            upper_row.append(value)
            lower_row.append(x)

    if output == 'matrix':
        return to_matrix(list(reversed(upper_row)), list(reversed(lower_row)))
    if output == 'array':
        return [list(reversed(upper_row)), list(reversed(lower_row))]
    if output in ['permutation', 'word']:
        raise TypeError("q must be standard to have a %s as valid output" %
                        output)
    raise ValueError("Invalid output option")