Path: blob/master/sage/combinat/crystals/tensor_product.py
4072 views
"""1Tensor Products of Crystals23Main entry points:45- :func:`TensorProductOfCrystals`6- :class:`CrystalOfTableaux`78"""9#*****************************************************************************10# Copyright (C) 2007 Anne Schilling <anne at math.ucdavis.edu>11# Nicolas Thiery <nthiery at users.sf.net>12#13# Distributed under the terms of the GNU General Public License (GPL)14#15# This code is distributed in the hope that it will be useful,16# but WITHOUT ANY WARRANTY; without even the implied warranty of17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU18# General Public License for more details.19#20# The full text of the GPL is available at:21#22# http://www.gnu.org/licenses/23#****************************************************************************2425import operator26from sage.misc.latex import latex27from sage.misc.cachefunc import cached_method28from sage.structure.parent import Parent29from sage.structure.element import Element, parent30from sage.categories.category import Category31from sage.categories.classical_crystals import ClassicalCrystals32from sage.combinat.root_system.cartan_type import CartanType33from sage.combinat.cartesian_product import CartesianProduct34from sage.combinat.combinat import CombinatorialObject35from sage.combinat.partition import Partition36from sage.combinat.tableau import Tableau37from sage.combinat.tableau import Tableau_class38from letters import CrystalOfLetters39from spins import CrystalOfSpins, CrystalOfSpinsMinus, CrystalOfSpinsPlus40from sage.misc.flatten import flatten41from sage.rings.integer import Integer4243##############################################################################44# Until trunc gets implemented in sage.function.other4546from sage.functions.other import floor, ceil47def trunc(i):48"""49Truncates to the integer closer to zero5051EXAMPLES::5253sage: from sage.combinat.crystals.tensor_product import trunc54sage: trunc(-3/2), trunc(-1), trunc(-1/2), trunc(0), trunc(1/2), trunc(1), trunc(3/2)55(-1, -1, 0, 0, 0, 1, 1)56sage: isinstance(trunc(3/2), Integer)57True58"""59if i>= 0:60return floor(i)61else:62return ceil(i)6364##############################################################################65# Support classes66##############################################################################6768from sage.structure.unique_representation import UniqueRepresentation6970class TestParent(UniqueRepresentation, Parent):71def _repr_(self):72return "A parent for tests"7374class ImmutableListWithParent(CombinatorialObject, Element):75r"""76A class for lists having a parent7778Specification: any subclass C should implement __init__ which79accepts the following form C(parent, list = list)8081EXAMPLES: We create an immutable list whose parent is the class82list::8384sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent85sage: l = ImmutableListWithParent(TestParent(), [1,2,3])86sage: l._list87[1, 2, 3]88sage: l.parent()89A parent for tests90sage: l.sibling([2,1]) == ImmutableListWithParent(TestParent(), [2,1])91True92sage: l.reversed()93[3, 2, 1]94sage: l.set_index(1,4)95[1, 4, 3]96"""9798def __init__(self, parent, list):99"""100EXAMPLES::101102sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent103sage: l = ImmutableListWithParent(TestParent(), [1,2,3])104sage: l.parent()105A parent for tests106sage: parent(l)107A parent for tests108sage: TestSuite(l).run(skip = "_test_category")109"""110Element.__init__(self, parent)111CombinatorialObject.__init__(self, list)112113def _repr_(self):114"""115EXAMPLES::116117sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent118sage: l = ImmutableListWithParent(TestParent(), [1,2,3])119sage: l._repr_()120'[1, 2, 3]'121"""122return "%s"%self._list123124def __eq__(self, other):125"""126EXAMPLES::127128sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent129sage: l = ImmutableListWithParent(TestParent(), [1,2,3])130sage: m = ImmutableListWithParent(ZZ, [1,2,3])131sage: n = ImmutableListWithParent(ZZ, [2,3,4])132sage: l == l133True134sage: l == m135False136sage: m == n137False138"""139return self.__class__ is other.__class__ and \140self.parent() == other.parent() and \141self._list == other._list142143def __ne__(self, other):144return not self.__eq__(other)145146# Should go in Element? Sets.ElementMethods?147# How to define them conditionally, only of __lt__ is defined?148def __le__(self, other):149if self == other:150return True151else:152return self.__le__(other)153154def __gt__(self, other):155if parent(self) is not parent(other):156return NotImplemented157return other.__lt__(self)158159def __ge__(self, other):160if self == other:161return True162else:163return self.__le__(other)164165def sibling(self, l):166"""167Returns an ImmutableListWithParent object whose list is l and whose168parent is the same as self's parent.169170Note that the implementation of this function makes an assumption171about the constructor for subclasses.172173EXAMPLES::174175sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent176sage: l = ImmutableListWithParent(TestParent(), [1,2,3])177sage: m = l.sibling([2,3,4]); m178[2, 3, 4]179sage: m.parent()180A parent for tests181"""182return self.__class__(self.parent(), list=l)183184def reversed(self):185"""186Returns the sibling of self which is obtained by reversing the187elements of self.188189EXAMPLES::190191sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent192sage: l = ImmutableListWithParent(TestParent(), [1,2,3])193sage: l.reversed()194[3, 2, 1]195"""196return self.sibling([ i for i in reversed(self._list)])197198def set_index(self, k, value):199"""200Returns the sibling of self obtained by setting the201`k^{th}` entry of self to value.202203EXAMPLES::204205sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent206sage: l = ImmutableListWithParent(TestParent(), [1,2,3])207sage: l.set_index(0,2)208[2, 2, 3]209sage: l.set_index(1,4)210[1, 4, 3]211sage: _.parent()212A parent for tests213"""214l = [i for i in self._list]215l[k] = value216return self.sibling(l)217218def TensorProductOfCrystals(*crystals, **options):219r"""220Tensor product of crystals.221222Given two crystals `B` and `B'` of the same type,223one can form the tensor product `B \otimes B'`. As a set224`B \otimes B'` is the Cartesian product225`B \times B'`. The crystal operators `f_i` and226`e_i` act on `b \otimes b' \in B \otimes B'` as227follows:228229.. math::230231f_i(b \otimes b') = \begin{cases}232f_i(b) \otimes b' & \text{if $\varepsilon_i(b) \ge \varphi_i(b')$}\\233b \otimes f_i(b') & \text{otherwise}234\end{cases}235236and237238.. math::239240e_i(b \otimes b') = \begin{cases}241b \otimes e_i(b') & \text{if $\varepsilon_i(b) \le \varphi_i(b')$}\\242e_i(b) \otimes b' & \text{otherwise.}243\end{cases}244245Note that this is the opposite of Kashiwara's convention for tensor246products of crystals.247248EXAMPLES:249250We construct the type `A_2`-crystal generated by `2 \otimes 1 \otimes 1`::251252sage: C = CrystalOfLetters(['A',2])253sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)]])254255It has `8` elements::256257sage: T.list()258[[2, 1, 1], [2, 1, 2], [2, 1, 3], [3, 1, 3], [3, 2, 3], [3, 1, 1], [3, 1, 2], [3, 2, 2]]259260One can also check the Cartan type of the crystal::261262sage: T.cartan_type()263['A', 2]264265Other examples include crystals of tableaux (which internally are represented as tensor products266obtained by reading the tableaux columnwise)::267268sage: C = CrystalOfTableaux(['A',3], shape=[1,1,0])269sage: D = CrystalOfTableaux(['A',3], shape=[1,0,0])270sage: T = TensorProductOfCrystals(C,D, generators=[[C(rows=[[1], [2]]), D(rows=[[1]])], [C(rows=[[2], [3]]), D(rows=[[1]])]])271sage: T.cardinality()27224273sage: TestSuite(T).run()274sage: T.module_generators275[[[[1], [2]], [[1]]], [[[2], [3]], [[1]]]]276sage: [x.weight() for x in T.module_generators]277[(2, 1, 0, 0), (1, 1, 1, 0)]278279If no module generators are specified, we obtain the full tensor280product::281282sage: C=CrystalOfLetters(['A',2])283sage: T=TensorProductOfCrystals(C,C)284sage: T.list()285[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]286sage: T.cardinality()2879288289For a tensor product of crystals without module generators, the290default implementation of module_generators contains all elements291in the tensor product of the crystals. If there is a subset of292elements in the tensor product that still generates the crystal,293this needs to be implemented for the specific crystal separately::294295sage: T.module_generators.list()296[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]297298For classical highest weight crystals, it is also possible to list299all highest weight elements::300301sage: C = CrystalOfLetters(['A',2])302sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)],[C(1),C(2),C(1)]])303sage: T.highest_weight_vectors()304[[2, 1, 1], [1, 2, 1]]305"""306crystals = tuple(crystals)307if "cartan_type" in options:308cartan_type = CartanType(options["cartan_type"])309else:310if len(crystals) == 0:311raise ValueError, "you need to specify the Cartan type if the tensor product list is empty"312else:313cartan_type = crystals[0].cartan_type()314315if "generators" in options:316generators = tuple(tuple(x) if isinstance(x, list) else x for x in options["generators"])317return TensorProductOfCrystalsWithGenerators(crystals, generators=generators, cartan_type = cartan_type)318else:319return FullTensorProductOfCrystals(crystals, cartan_type = cartan_type)320321322class CrystalOfWords(UniqueRepresentation, Parent):323"""324Auxiliary class to provide a call method to create tensor product elements.325This class is shared with several tensor product classes and is also used in CrystalOfTableaux to allow326tableaux of different tensor product structures in column-reading (and hence different shapes)327to be considered elements in the same crystal.328"""329def _element_constructor_(self, *crystalElements):330"""331EXAMPLES::332333sage: C = CrystalOfLetters(['A',2])334sage: T = TensorProductOfCrystals(C,C)335sage: T(1,1)336[1, 1]337sage: _.parent()338Full tensor product of the crystals [The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]]339sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)]])340sage: T(C(2), C(1), C(1))341[2, 1, 1]342"""343return self.element_class(self, list(crystalElements))344345346class TensorProductOfCrystalsWithGenerators(CrystalOfWords):347348def __init__(self, crystals, generators, cartan_type):349"""350EXAMPLES::351352sage: C = CrystalOfLetters(['A',2])353sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)]])354sage: TestSuite(T).run()355"""356assert isinstance(crystals, tuple)357assert isinstance(generators, tuple)358category = Category.meet([crystal.category() for crystal in crystals])359Parent.__init__(self, category = category)360self.rename("The tensor product of the crystals %s"%(crystals,))361self.crystals = crystals362self._cartan_type = cartan_type363self.module_generators = [ self(*x) for x in generators ]364365366class FullTensorProductOfCrystals(CrystalOfWords):367def __init__(self, crystals, **options):368"""369TESTS::370371sage: from sage.combinat.crystals.tensor_product import FullTensorProductOfCrystals372sage: C = CrystalOfLetters(['A',2])373sage: T = TensorProductOfCrystals(C,C)374sage: isinstance(T, FullTensorProductOfCrystals)375True376sage: TestSuite(T).run()377"""378crystals = list(crystals)379category = Category.meet([crystal.category() for crystal in crystals])380Parent.__init__(self, category = category)381self.rename("Full tensor product of the crystals %s"%(crystals,))382self.crystals = crystals383if options.has_key('cartan_type'):384self._cartan_type = CartanType(options['cartan_type'])385else:386if len(crystals) == 0:387raise ValueError, "you need to specify the Cartan type if the tensor product list is empty"388else:389self._cartan_type = crystals[0].cartan_type()390self.cartesian_product = CartesianProduct(*self.crystals)391self.module_generators = self392393# TODO: __iter__ and cardinality should be inherited from EnumeratedSets().CartesianProducts()394def __iter__(self):395"""396EXAMPLES::397398sage: C = CrystalOfLetters(['A',2])399sage: T = TensorProductOfCrystals(C,C)400sage: list(T)401[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]402sage: _[0].parent()403Full tensor product of the crystals [The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]]404"""405for x in self.cartesian_product:406yield self(*x)407408# list = CombinatorialClass._CombinatorialClass__list_from_iterator409410def cardinality(self):411"""412EXAMPLES::413414sage: C = CrystalOfLetters(['A',2])415sage: T = TensorProductOfCrystals(C,C)416sage: T.cardinality()4179418"""419return self.cartesian_product.cardinality()420421422423class TensorProductOfCrystalsElement(ImmutableListWithParent):424r"""425A class for elements of tensor products of crystals426"""427428def __lt__(self, other):429"""430Non elements of the crystal are incomparable with elements of the crystal431(or should it return NotImplemented?)432433Comparison of two elements of this crystal:434- different length: incomparable435- otherwise lexicographicaly, considering self[i] and other[i]436as incomparable if self[i] < other[i] returns NotImplemented437"""438if parent(self) is not parent(other):439return False440if len(self) != len(other):441return False442for i in range(len(self)):443if (self[i] < other[i]) == True:444return True445if (other[i] < self[i]) == True:446return False447return False448449def e(self, i):450"""451Returns the action of `e_i` on self.452453EXAMPLES::454455sage: C = CrystalOfLetters(['A',5])456sage: T = TensorProductOfCrystals(C,C)457sage: T(C(1),C(2)).e(1) == T(C(1),C(1))458True459sage: T(C(2),C(1)).e(1) == None460True461sage: T(C(2),C(2)).e(1) == T(C(1),C(2))462True463"""464assert i in self.index_set()465position = self.positions_of_unmatched_plus(i)466if position == []:467return None468k = position[0]469return self.set_index(k, self[k].e(i))470471def weight(self):472"""473Returns the weight of self.474475EXAMPLES::476477sage: C = CrystalOfLetters(['A',3])478sage: T = TensorProductOfCrystals(C,C)479sage: T(C(1),C(2)).weight()480(1, 1, 0, 0)481sage: T=CrystalOfTableaux(['D',4],shape=[])482sage: T.list()[0].weight()483(0, 0, 0, 0)484"""485return sum((self[j].weight() for j in range(len(self))), self.parent().weight_lattice_realization().zero())486487def f(self, i):488"""489Returns the action of `f_i` on self.490491EXAMPLES::492493sage: C = CrystalOfLetters(['A',5])494sage: T = TensorProductOfCrystals(C,C)495sage: T(C(1),C(1)).f(1)496[1, 2]497sage: T(C(1),C(2)).f(1)498[2, 2]499sage: T(C(2),C(1)).f(1) is None500True501"""502assert i in self.index_set()503position = self.positions_of_unmatched_minus(i)504if position == []:505return None506k = position[len(position)-1]507return self.set_index(k, self[k].f(i))508509def phi(self, i):510"""511EXAMPLES::512513sage: C = CrystalOfLetters(['A',5])514sage: T = TensorProductOfCrystals(C,C)515sage: T(C(1),C(1)).phi(1)5162517sage: T(C(1),C(2)).phi(1)5181519sage: T(C(2),C(1)).phi(1)5200521"""522self = self.reversed()523height = 0524for j in range(len(self)):525plus = self[j].epsilon(i)526minus = self[j].phi(i)527if height-plus < 0:528height = minus529else:530height = height - plus + minus531return height532533def epsilon(self, i):534"""535EXAMPLES::536537sage: C = CrystalOfLetters(['A',5])538sage: T = TensorProductOfCrystals(C,C)539sage: T(C(1),C(1)).epsilon(1)5400541sage: T(C(1),C(2)).epsilon(1)5421543sage: T(C(2),C(1)).epsilon(1)5440545"""546height = 0547for j in range(len(self)):548minus = self[j].phi(i)549plus = self[j].epsilon(i)550if height-minus < 0:551height = plus552else:553height = height - minus + plus554return height555556def positions_of_unmatched_minus(self, i, dual=False, reverse=False):557"""558EXAMPLES::559560sage: C = CrystalOfLetters(['A',5])561sage: T = TensorProductOfCrystals(C,C)562sage: T(C(2),C(1)).positions_of_unmatched_minus(1)563[]564sage: T(C(1),C(2)).positions_of_unmatched_minus(1)565[0]566"""567unmatched_plus = []568height = 0569if reverse == True:570self = self.reversed()571if dual == False:572for j in range(len(self)):573minus = self[j].phi(i)574plus = self[j].epsilon(i)575if height-minus < 0:576unmatched_plus.append(j)577height = plus578else:579height = height - minus + plus580else:581for j in range(len(self)):582plus = self[j].epsilon(i)583minus = self[j].phi(i)584if height-plus < 0:585unmatched_plus.append(j)586height = minus587else:588height = height - plus + minus589return unmatched_plus590591def positions_of_unmatched_plus(self, i):592"""593EXAMPLES::594595sage: C = CrystalOfLetters(['A',5])596sage: T = TensorProductOfCrystals(C,C)597sage: T(C(2),C(1)).positions_of_unmatched_plus(1)598[]599sage: T(C(1),C(2)).positions_of_unmatched_plus(1)600[1]601"""602l = self.positions_of_unmatched_minus(i, dual=True, reverse=True)603l.reverse()604return [len(self)-1-l[j] for j in range(len(l))]605606def _latex_(self):607"""608Returns latex code for self.609610EXAMPLES::611612sage: C = CrystalOfLetters(["A",2])613sage: D = CrystalOfTableaux(["A",2], shape=[2])614sage: E = TensorProductOfCrystals(C,D)615sage: E.module_generators[0]._latex_()616'1\\otimes{\\def\\lr#1{\\multicolumn{1}{|@{\\hspace{.6ex}}c@{\\hspace{.6ex}}|}{\\raisebox{-.3ex}{$#1$}}}\n\\raisebox{-.6ex}{$\\begin{array}[b]{cc}\n\\cline{1-1}\\cline{2-2}\n\\lr{1}&\\lr{1}\\\\\n\\cline{1-1}\\cline{2-2}\n\\end{array}$}\n}'617"""618return '\otimes'.join(latex(c) for c in self)619620def energy_function(self):621r"""622Returns the energy function of `self`.623624INPUT:625626- ``self`` -- an element of a tensor product of perfect Kirillov-Reshetkhin crystals of the same level.627628OUTPUT: an integer629630The energy is only defined when ``self`` is an element of a tensor product of affine Kirillov-Reshetikhin crystals.631In this implementation, it is assumed that ``self`` is an element of a tensor product of perfect crystals of the632same level, see Theorem 7.5 in [SchillingTingley2011]_.633634REFERENCES:635636.. [SchillingTingley2011] A. Schilling, P. Tingley.637Demazure crystals, Kirillov-Reshetikhin crystals, and the energy function.638preprint arXiv:1104.2359639640EXAMPLES::641642sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)643sage: T = TensorProductOfCrystals(K,K,K)644sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]645sage: for b in hw:646... print b, b.energy_function()647...648[[[1]], [[1]], [[1]]] 0649[[[1]], [[2]], [[1]]] 2650[[[2]], [[1]], [[1]]] 1651[[[3]], [[2]], [[1]]] 3652653sage: K = KirillovReshetikhinCrystal(['C',2,1],1,2)654sage: T = TensorProductOfCrystals(K,K)655sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]656sage: for b in hw: # long time (5s on sage.math, 2011)657... print b, b.energy_function()658...659[[], []] 4660[[], [[1, 1]]] 1661[[[1, 1]], []] 3662[[[1, 1]], [[1, 1]]] 0663[[[1, 2]], [[1, 1]]] 1664[[[2, 2]], [[1, 1]]] 2665[[[-1, -1]], [[1, 1]]] 2666[[[1, -1]], [[1, 1]]] 2667[[[2, -1]], [[1, 1]]] 2668669sage: K = KirillovReshetikhinCrystal(['C',2,1],1,1)670sage: T = TensorProductOfCrystals(K)671sage: t = T.module_generators[0]672sage: t.energy_function()673Traceback (most recent call last):674...675AssertionError: All crystals in the tensor product need to be perfect of the same level676"""677C = self.parent().crystals[0]678ell = ceil(C.s()/C.cartan_type().c()[C.r()])679assert all(ell == K.s()/K.cartan_type().c()[K.r()] for K in self.parent().crystals), \680"All crystals in the tensor product need to be perfect of the same level"681t = self.parent()(*[K.module_generator() for K in self.parent().crystals])682d = t.affine_grading()683return d - self.affine_grading()684685def affine_grading(self):686r"""687Returns the affine grading of `self`.688689INPUT:690691- ``self`` -- an element of a tensor product of Kirillov-Reshetikhin crystals.692693OUTPUT: an integer694695The affine grading is only defined when ``self`` is an element of a tensor product of affine Kirillov-Reshetikhin crystals.696It is calculated by finding a path from ``self`` to a ground state path using the helper method697:meth:`e_string_to_ground_state` and counting the number of affine Kashiwara operators `e_0` applied on the way.698699EXAMPLES::700701sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)702sage: T = TensorProductOfCrystals(K,K)703sage: t = T.module_generators[0]704sage: t.affine_grading()7051706707sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)708sage: T = TensorProductOfCrystals(K,K,K)709sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]710sage: for b in hw:711... print b, b.affine_grading()712...713[[[1]], [[1]], [[1]]] 3714[[[1]], [[2]], [[1]]] 1715[[[2]], [[1]], [[1]]] 2716[[[3]], [[2]], [[1]]] 0717718sage: K = KirillovReshetikhinCrystal(['C',2,1],1,1)719sage: T = TensorProductOfCrystals(K,K,K)720sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]721sage: for b in hw:722... print b, b.affine_grading()723...724[[[1]], [[1]], [[1]]] 2725[[[1]], [[2]], [[1]]] 1726[[[1]], [[-1]], [[1]]] 0727[[[2]], [[1]], [[1]]] 1728[[[-2]], [[2]], [[1]]] 0729[[[-1]], [[1]], [[1]]] 1730"""731return self.e_string_to_ground_state().count(0)732733@cached_method734def e_string_to_ground_state(self):735r"""736Returns a string of integers in the index set `(i_1,\ldots,i_k)` such that `e_{i_k} \cdots e_{i_1} self` is737the ground state.738739INPUT:740741- ``self`` -- an element of a tensor product of Kirillov-Reshetikhin crystals.742743OUTPUT: a tuple of integers `(i_1,\ldots,i_k)`744745This method is only defined when ``self`` is an element of a tensor product of affine Kirillov-Reshetikhin crystals.746It calculates a path from ``self`` to a ground state path using Demazure arrows as defined in747Lemma 7.3 in [SchillingTingley2011]_.748749EXAMPLES::750751sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)752sage: T = TensorProductOfCrystals(K,K)753sage: t = T.module_generators[0]754sage: t.e_string_to_ground_state()755(0, 2)756757sage: K = KirillovReshetikhinCrystal(['C',2,1],1,1)758sage: T = TensorProductOfCrystals(K,K)759sage: t = T.module_generators[0]; t760[[[1]], [[1]]]761sage: t.e_string_to_ground_state()762(0,)763sage: x=t.e(0)764sage: x.e_string_to_ground_state()765()766sage: y=t.f_string([1,2,1,1,0]); y767[[[2]], [[1]]]768sage: y.e_string_to_ground_state()769()770"""771assert self.parent().crystals[0].__module__ == 'sage.combinat.crystals.kirillov_reshetikhin', \772"All crystals in the tensor product need to be Kirillov-Reshetikhin crystals"773I = self.index_set()774I.remove(0)775ell = max(ceil(K.s()/K.cartan_type().c()[K.r()]) for K in self.parent().crystals)776for i in I:777if self.epsilon(i) > 0:778return (i,) + (self.e(i)).e_string_to_ground_state()779if self.epsilon(0) > ell:780return (0,) + (self.e(0)).e_string_to_ground_state()781return ()782783CrystalOfWords.Element = TensorProductOfCrystalsElement784785786class CrystalOfTableaux(CrystalOfWords):787r"""788A class for crystals of tableaux with integer valued shapes789790INPUT:791792- ``cartan_type`` -- a Cartan type793- ``shape`` -- a partition of length at most ``cartan_type.rank()``794- ``shapes`` -- a list of such partitions795796This constructs a classical crystal with the given Cartan type and797highest weight(s) corresponding to the given shape(s).798799If the type is `D_r`, the shape is permitted to have a negative800value in the `r`-th position. Thus if shape=`[s_1,\dots,s_r]` then801`s_r` may be negative but in any case `s_1 \ge \cdots \ge s_{r-1}802\ge |s_r|`. This crystal is related to that of shape803`[s_1,\dots,|s_r|]` by the outer automorphism of `SO(2r)`.804805If the type is `D_r` or `B_r`, the shape is permitted to be of806length `r` with all parts of half integer value. This corresponds807to having one spin column at the beginning of the tableau. If808several shapes are provided, they currently should all or none809have this property.810811Crystals of tableaux are constructed using an embedding into812tensor products following Kashiwara and Nakashima [Kashiwara,813Masaki; Nakashima, Toshiki, *Crystal graphs for representations of814the q-analogue of classical Lie algebras*, J. Algebra 165 (1994),815no. 2, 295-345.]. Sage's tensor product rule for crystals differs816from that of Kashiwara and Nakashima by reversing the order of the817tensor factors. Sage produces the same crystals of tableaux as818Kashiwara and Nakashima. With Sage's convention, the tensor819product of crystals is the same as the monoid operation on820tableaux and hence the plactic monoid.821822.. seealso:: :mod:`sage.combinat.crystals.crystals` for general help on823crystals, and in particular plotting and LaTeX output.824825EXAMPLES:826827We create the crystal of tableaux for type `A_2`, with828highest weight given by the partition [2,1,1]::829830sage: T = CrystalOfTableaux(['A',3], shape = [2,1,1])831832Here is the list of its elements::833834sage: T.list()835[[[1, 1], [2], [3]], [[1, 2], [2], [3]], [[1, 3], [2], [3]],836[[1, 4], [2], [3]], [[1, 4], [2], [4]], [[1, 4], [3], [4]],837[[2, 4], [3], [4]], [[1, 1], [2], [4]], [[1, 2], [2], [4]],838[[1, 3], [2], [4]], [[1, 3], [3], [4]], [[2, 3], [3], [4]],839[[1, 1], [3], [4]], [[1, 2], [3], [4]], [[2, 2], [3], [4]]]840841Internally, a tableau of a given Cartan type is represented as a842tensor product of letters of the same type. The order in which the843tensor factors appear is by reading the columns of the tableaux844left to right, top to bottom (in French notation). As an example::845846sage: T = CrystalOfTableaux(['A',2], shape = [3,2])847sage: T.module_generators[0]848[[1, 1, 1], [2, 2]]849sage: T.module_generators[0]._list850[2, 1, 2, 1, 1]851852To create a tableau, one can use::853854sage: Tab = CrystalOfTableaux(['A',3], shape = [2,2])855sage: Tab(rows=[[1,2],[3,4]])856[[1, 2], [3, 4]]857sage: Tab(columns=[[3,1],[4,2]])858[[1, 2], [3, 4]]859860FIXME:861862- do we want to specify the columns increasingly or863decreasingly. That is, should this be Tab(columns = [[1,3],[2,4]])864- make this fully consistent with :func:`~sage.combinat.tableau.Tableau`!865866We illustrate the use of a shape with a negative last entry in867type `D`::868869sage: T = CrystalOfTableaux(['D',4],shape=[1,1,1,-1])870sage: T.cardinality()87135872sage: TestSuite(T).run()873874We illustrate the construction of crystals of spin tableaux when875the partitions have half integer values in type `B` and `D`::876877sage: T = CrystalOfTableaux(['B',3],shape=[3/2,1/2,1/2]); T878The crystal of tableaux of type ['B', 3] and shape(s) [[3/2, 1/2, 1/2]]879sage: T.cardinality()88048881sage: T.module_generators882[[+++, [[1]]]]883sage: TestSuite(T).run()884885sage: T = CrystalOfTableaux(['D',3],shape=[3/2,1/2,-1/2]); T886The crystal of tableaux of type ['D', 3] and shape(s) [[3/2, 1/2, -1/2]]887sage: T.cardinality()88820889sage: T.module_generators890[[++-, [[1]]]]891sage: TestSuite(T).run()892893TESTS:894895Base cases::896897sage: T = CrystalOfTableaux(['A',2], shape = [])898sage: T.list()899[[]]900sage: TestSuite(T).run()901902sage: T = CrystalOfTableaux(['C',2], shape = [1])903sage: T.list()904[[[1]], [[2]], [[-2]], [[-1]]]905sage: TestSuite(T).run()906907sage: T = CrystalOfTableaux(['A',2], shapes = [[],[1],[2]])908sage: T.list()909[[], [[1]], [[2]], [[3]], [[1, 1]], [[1, 2]], [[2, 2]], [[1, 3]], [[2, 3]], [[3, 3]]]910sage: T.module_generators911([], [[1]], [[1, 1]])912913sage: T = CrystalOfTableaux(['B',2], shape=[3])914sage: T(rows=[[1,1,0]])915[[1, 1, 0]]916917Input tests::918919sage: T = CrystalOfTableaux(['A',3], shape = [2,2])920sage: C = T.letters921sage: Tab(rows = [[1,2],[3,4]])._list == [C(3),C(1),C(4),C(2)]922True923sage: Tab(columns = [[3,1],[4,2]])._list == [C(3),C(1),C(4),C(2)]924True925926For compatibility with :func:`TensorProductOfCrystals` we need to927accept as input the internal list or sequence of elements::928929sage: Tab(list = [3,1,4,2])._list == [C(3),C(1),C(4),C(2)]930True931sage: Tab(3,1,4,2)._list == [C(3),C(1),C(4),C(2)]932True933934The next example checks whether a given tableau is in fact a valid935type `C` tableau or not::936937sage: T = CrystalOfTableaux(['C',3], shape = [2,2,2])938sage: Tab = T(rows=[[1,3],[2,-3],[3,-1]])939sage: Tab in T.list()940True941sage: Tab = T(rows=[[2,3],[3,-3],[-3,-2]])942sage: Tab in T.list()943False944"""945946@staticmethod947def __classcall_private__(cls, cartan_type, shapes = None, shape = None):948"""949Normalizes the input arguments to ensure unique representation,950and to delegate the construction of spin tableaux.951952EXAMPLES::953954sage: T1 = CrystalOfTableaux(CartanType(['A',3]), shape = [2,2])955sage: T2 = CrystalOfTableaux(['A',3], shape = (2,2))956sage: T3 = CrystalOfTableaux(['A',3], shapes = ([2,2],))957sage: T2 is T1, T3 is T1958(True, True)959"""960cartan_type = CartanType(cartan_type)961n = cartan_type.rank()962# standardize shape/shapes input into a tuple of tuples963assert operator.xor(shape is not None, shapes is not None)964if shape is not None:965shapes = (shape,)966spin_shapes = tuple( tuple(shape) for shape in shapes )967try:968shapes = tuple( tuple(trunc(i) for i in shape) for shape in spin_shapes )969except:970raise ValueError("shapes should all be partitions or half-integer partitions")971if spin_shapes == shapes:972return super(CrystalOfTableaux, cls).__classcall__(cls, cartan_type, shapes)973974# Handle the construction of a crystals of spin tableaux975# Caveat: this currently only supports all shapes being half976# integer partitions of length the rank for type B and D. In977# particular, for type D, the spins all have to be plus or all978# minus spins979assert all(len(sh) == n for sh in shapes), \980"the length of all half-integer partition shapes should be the rank"981assert all(2*i % 2 == 1 for shape in spin_shapes for i in shape), \982"shapes should be either all partitions or all half-integer partitions"983if cartan_type.type() == 'D':984if all( i >= 0 for shape in spin_shapes for i in shape):985S = CrystalOfSpinsPlus(cartan_type)986elif all(shape[-1]<0 for shape in spin_shapes):987S = CrystalOfSpinsMinus(cartan_type)988else:989raise ValueError, "In type D spins should all be positive or negative"990else:991assert all( i >= 0 for shape in spin_shapes for i in shape), \992"shapes should all be partitions"993S = CrystalOfSpins(cartan_type)994B = CrystalOfTableaux(cartan_type, shapes = shapes)995T = TensorProductOfCrystals(S,B, generators=[[S.module_generators[0],x] for x in B.module_generators])996T.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in spin_shapes)))997return T9989991000def __init__(self, cartan_type, shapes):1001"""1002Construct the crystal of all tableaux of the given shapes10031004INPUT:1005- ``cartan_type`` - (data coercible into) a cartan type1006- ``shapes`` - a list (or iterable) of shapes1007- ``shape` ` - a shape10081009shapes themselves are lists (or iterable) of integers10101011EXAMPLES::10121013sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1014sage: TestSuite(T).run()1015"""1016# super(CrystalOfTableaux, self).__init__(category = FiniteEnumeratedSets())1017Parent.__init__(self, category = ClassicalCrystals())1018self.letters = CrystalOfLetters(cartan_type)1019self.module_generators = tuple(self.module_generator(la) for la in shapes)1020self.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in shapes)))10211022def cartan_type(self):1023"""1024Returns the Cartan type of the associated crystal10251026EXAMPLES::10271028sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1029sage: T.cartan_type()1030['A', 3]1031"""1032return self.letters.cartan_type()10331034def module_generator(self, shape):1035"""1036This yields the module generator (or highest weight element) of a classical1037crystal of given shape. The module generator is the unique tableau with equal1038shape and content.10391040EXAMPLE::10411042sage: T = CrystalOfTableaux(['D',3], shape = [1,1])1043sage: T.module_generator([1,1])1044[[1], [2]]1045"""1046type = self.cartan_type()1047if type[0] == 'D' and len(shape) == type[1] and shape[type[1]-1] < 0:1048invert = True1049shape = shape[:-1]+(-shape[type[1]-1],)1050else:1051invert = False1052p = Partition(shape).conjugate()1053# The column canonical tableau, read by columns1054module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(len(p))])1055if invert:1056for i in range(type[1]):1057if module_generator[i] == type[1]:1058module_generator[i] = -type[1]1059return self(list=[self.letters(x) for x in module_generator])10601061def _element_constructor_(self, *args, **options):1062"""1063Returns a CrystalOfTableauxElement10641065EXAMPLES::10661067sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1068sage: T(rows=[[1,2],[3,4]])1069[[1, 2], [3, 4]]1070sage: T(columns=[[3,1],[4,2]])1071[[1, 2], [3, 4]]1072"""1073return self.element_class(self, *args, **options)1074107510761077class CrystalOfTableauxElement(TensorProductOfCrystalsElement):1078def __init__(self, parent, *args, **options):1079"""1080There are several ways to input tableaux, by rows,1081by columns, as the list of column elements, or as a sequence of numbers1082in column reading.10831084EXAMPLES::10851086sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1087sage: t = T(rows=[[1,2],[3,4]])1088sage: t1089[[1, 2], [3, 4]]1090sage: TestSuite(t).run()10911092sage: t = T(columns=[[3,1],[4,2]])1093sage: t1094[[1, 2], [3, 4]]1095sage: TestSuite(t).run()10961097sage: t = T(list=[3,1,4,2])1098sage: t1099[[1, 2], [3, 4]]11001101sage: t = T(3,1,4,2)1102sage: t1103[[1, 2], [3, 4]]11041105Currently inputting the empty tableau as an empty sequence is broken due to a bug in1106the generic __call__ method (see trac ticket #8648)11071108EXAMPLES::11091110sage: T = CrystalOfTableaux(['A',3], shape=[])1111sage: t = T()1112sage: t._list1113[0]1114"""1115if len(args) == 1:1116if isinstance(args[0], Tableau_class):1117options['rows'] = args[0]1118if options.has_key('list'):1119list = options['list']1120elif options.has_key('rows'):1121rows=options['rows']1122# list=Tableau(rows).to_word_by_column()1123rows=Tableau(rows).conjugate()1124list=[]1125for col in rows:1126col.reverse()1127list+=col1128elif options.has_key('columns'):1129columns=options['columns']1130list=[]1131for col in columns:1132list+=col1133else:1134list = [i for i in args]1135if list != [] and type(list[0]) is Integer:1136list=[parent.letters(x) for x in list]1137TensorProductOfCrystalsElement.__init__(self, parent, list)11381139def _repr_(self):1140"""1141EXAMPLES::11421143sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1144sage: t = T(rows=[[1,2],[3,4]])1145sage: t._repr_()1146'[[1, 2], [3, 4]]'1147"""1148return repr(self.to_tableau())11491150def pp(self):1151"""1152EXAMPLES::11531154sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1155sage: t = T(rows=[[1,2],[3,4]])1156sage: t.pp()11571 211583 41159"""1160return self.to_tableau().pp()11611162def _latex_(self):1163r"""1164EXAMPLES::11651166sage: T = CrystalOfTableaux(['A',3], shape = [4,2])1167sage: t = T(rows=[[1,1,2,3],[2,3]])1168sage: latex(t) # indirect doctest1169{\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}}1170\raisebox{-.6ex}{$\begin{array}[b]{cccc}1171\cline{1-1}\cline{2-2}\cline{3-3}\cline{4-4}1172\lr{1}&\lr{1}&\lr{2}&\lr{3}\\1173\cline{1-1}\cline{2-2}\cline{3-3}\cline{4-4}1174\lr{2}&\lr{3}\\1175\cline{1-1}\cline{2-2}1176\end{array}$}1177}1178"""1179return latex(self.to_tableau())11801181@cached_method1182def to_tableau(self):1183"""1184Returns the Tableau object corresponding to self.11851186EXAMPLES::11871188sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1189sage: t = T(rows=[[1,2],[3,4]]).to_tableau(); t1190[[1, 2], [3, 4]]1191sage: type(t)1192<class 'sage.combinat.tableau.Tableau_class'>1193sage: type(t[0][0])1194<type 'sage.rings.integer.Integer'>1195sage: T = CrystalOfTableaux(['D',3], shape = [1,1])1196sage: t=T(rows=[[-3],[3]]).to_tableau(); t1197[[-3], [3]]1198sage: t=T(rows=[[3],[-3]]).to_tableau(); t1199[[3], [-3]]1200sage: T = CrystalOfTableaux(['B',2], shape = [1,1])1201sage: t = T(rows=[[0],[0]]).to_tableau(); t1202[[0], [0]]1203"""1204if self._list == []:1205return Tableau([])1206tab = [ [self[0].value] ]1207for i in range(1,len(self)):1208if self[i-1] < self[i] or (self[i-1].value != 0 and self[i-1] == self[i]):1209tab.append([self[i].value])1210else:1211l = len(tab)-11212tab[l].append(self[i].value)1213for x in tab:1214x.reverse()1215return Tableau(tab).conjugate()12161217def promotion(self):1218"""1219Promotion for type A crystals of tableaux of rectangular shape12201221Returns the result of applying promotion on this tableau.12221223This method only makes sense in type A with rectangular shapes.12241225EXAMPLES::12261227sage: C = CrystalOfTableaux(["A",3], shape = [3,3,3])1228sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]]))1229sage: t1230[[1, 1, 1], [2, 2, 3], [3, 4, 4]]1231sage: t.promotion()1232[[1, 1, 2], [2, 2, 3], [3, 4, 4]]1233sage: t.promotion().parent()1234The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]]1235"""1236crystal = self.parent()1237cartan_type = crystal.cartan_type()1238assert cartan_type.type() == 'A'1239return crystal(self.to_tableau().promotion(cartan_type.rank()))12401241def promotion_inverse(self):1242"""1243Inverse promotion for type A crystals of tableaux of rectangular shape12441245Returns the result of applying inverse promotion on this tableau.12461247This method only makes sense in type A with rectangular shapes.12481249EXAMPLES::12501251sage: C = CrystalOfTableaux(["A",3], shape = [3,3,3])1252sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]]))1253sage: t1254[[1, 1, 1], [2, 2, 3], [3, 4, 4]]1255sage: t.promotion_inverse()1256[[1, 1, 2], [2, 3, 3], [4, 4, 4]]1257sage: t.promotion_inverse().parent()1258The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]]1259"""1260crystal = self.parent()1261cartan_type = crystal.cartan_type()1262assert cartan_type.type() == 'A'1263return crystal(self.to_tableau().promotion_inverse(cartan_type.rank()))12641265CrystalOfTableaux.Element = CrystalOfTableauxElement126612671268