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
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()
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))
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
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)
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))
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))
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)
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
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")
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")
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")