Path: blob/master/src/sage/combinat/crystals/tensor_product.py
8817 views
"""1Tensor Products of Crystals23Main entry points:45- :class:`TensorProductOfCrystals`6- :class:`CrystalOfTableaux`78AUTHORS:910- Anne Schilling, Nicolas Thiery (2007): Initial version11- Ben Salisbury, Travis Scrimshaw (2013): Refactored tensor products to handle12non-regular crystals and created new subclass to take advantage of13the regularity14"""15#*****************************************************************************16# Copyright (C) 2007 Anne Schilling <anne at math.ucdavis.edu>17# Nicolas Thiery <nthiery at users.sf.net>18#19# Distributed under the terms of the GNU General Public License (GPL)20#21# This code is distributed in the hope that it will be useful,22# but WITHOUT ANY WARRANTY; without even the implied warranty of23# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU24# General Public License for more details.25#26# The full text of the GPL is available at:27#28# http://www.gnu.org/licenses/29#****************************************************************************3031import operator32from sage.misc.latex import latex33from sage.misc.cachefunc import cached_method, cached_in_parent_method34from sage.structure.parent import Parent35from sage.structure.element import Element, parent36from sage.structure.global_options import GlobalOptions37from sage.categories.category import Category38from sage.categories.classical_crystals import ClassicalCrystals39from sage.categories.regular_crystals import RegularCrystals40from sage.combinat.root_system.cartan_type import CartanType41from sage.combinat.cartesian_product import CartesianProduct42from sage.combinat.combinat import CombinatorialObject43from sage.combinat.partition import Partition44from sage.combinat.tableau import Tableau45from letters import CrystalOfLetters46from spins import CrystalOfSpins, CrystalOfSpinsMinus, CrystalOfSpinsPlus47from sage.misc.flatten import flatten48from sage.rings.integer import Integer4950##############################################################################51# Until trunc gets implemented in sage.function.other5253from sage.functions.other import floor, ceil54def trunc(i):55"""56Truncates to the integer closer to zero5758EXAMPLES::5960sage: from sage.combinat.crystals.tensor_product import trunc61sage: trunc(-3/2), trunc(-1), trunc(-1/2), trunc(0), trunc(1/2), trunc(1), trunc(3/2)62(-1, -1, 0, 0, 0, 1, 1)63sage: isinstance(trunc(3/2), Integer)64True65"""66if i>= 0:67return floor(i)68else:69return ceil(i)7071##############################################################################72# Support classes73##############################################################################7475from sage.structure.unique_representation import UniqueRepresentation7677class TestParent(UniqueRepresentation, Parent):78def _repr_(self):79return "A parent for tests"8081class ImmutableListWithParent(CombinatorialObject, Element):82r"""83A class for lists having a parent8485Specification: any subclass C should implement __init__ which86accepts the following form C(parent, list = list)8788EXAMPLES: We create an immutable list whose parent is the class89list::9091sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent92sage: l = ImmutableListWithParent(TestParent(), [1,2,3])93sage: l._list94[1, 2, 3]95sage: l.parent()96A parent for tests97sage: l.sibling([2,1]) == ImmutableListWithParent(TestParent(), [2,1])98True99sage: l.reversed()100[3, 2, 1]101sage: l.set_index(1,4)102[1, 4, 3]103"""104105def __init__(self, parent, list):106"""107EXAMPLES::108109sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent110sage: l = ImmutableListWithParent(TestParent(), [1,2,3])111sage: l.parent()112A parent for tests113sage: parent(l)114A parent for tests115sage: TestSuite(l).run(skip = "_test_category")116"""117Element.__init__(self, parent)118CombinatorialObject.__init__(self, list)119120def _repr_(self):121"""122EXAMPLES::123124sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent125sage: l = ImmutableListWithParent(TestParent(), [1,2,3])126sage: l._repr_()127'[1, 2, 3]'128"""129return "%s"%self._list130131def __eq__(self, other):132"""133EXAMPLES::134135sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent136sage: l = ImmutableListWithParent(TestParent(), [1,2,3])137sage: m = ImmutableListWithParent(ZZ, [1,2,3])138sage: n = ImmutableListWithParent(ZZ, [2,3,4])139sage: l == l140True141sage: l == m142False143sage: m == n144False145"""146return self.__class__ is other.__class__ and \147self.parent() == other.parent() and \148self._list == other._list149150def __ne__(self, other):151return not self.__eq__(other)152153# Should go in Element? Sets.ElementMethods?154# How to define them conditionally, only of __lt__ is defined?155def __le__(self, other):156if self == other:157return True158else:159return self.__le__(other)160161def __gt__(self, other):162if parent(self) is not parent(other):163return NotImplemented164return other.__lt__(self)165166def __ge__(self, other):167if self == other:168return True169else:170return self.__le__(other)171172def sibling(self, l):173"""174Returns an ImmutableListWithParent object whose list is l and whose175parent is the same as self's parent.176177Note that the implementation of this function makes an assumption178about the constructor for subclasses.179180EXAMPLES::181182sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent183sage: l = ImmutableListWithParent(TestParent(), [1,2,3])184sage: m = l.sibling([2,3,4]); m185[2, 3, 4]186sage: m.parent()187A parent for tests188"""189return self.__class__(self.parent(), list=l)190191def reversed(self):192"""193Returns the sibling of self which is obtained by reversing the194elements of self.195196EXAMPLES::197198sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent199sage: l = ImmutableListWithParent(TestParent(), [1,2,3])200sage: l.reversed()201[3, 2, 1]202"""203return self.sibling([ i for i in reversed(self._list)])204205def set_index(self, k, value):206"""207Returns the sibling of self obtained by setting the208`k^{th}` entry of self to value.209210EXAMPLES::211212sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent213sage: l = ImmutableListWithParent(TestParent(), [1,2,3])214sage: l.set_index(0,2)215[2, 2, 3]216sage: l.set_index(1,4)217[1, 4, 3]218sage: _.parent()219A parent for tests220"""221l = [i for i in self._list]222l[k] = value223return self.sibling(l)224225class CrystalOfWords(UniqueRepresentation, Parent):226"""227Auxiliary class to provide a call method to create tensor product elements.228This class is shared with several tensor product classes and is also used in CrystalOfTableaux to allow229tableaux of different tensor product structures in column-reading (and hence different shapes)230to be considered elements in the same crystal.231"""232def _element_constructor_(self, *crystalElements):233"""234EXAMPLES::235236sage: C = CrystalOfLetters(['A',2])237sage: T = TensorProductOfCrystals(C,C)238sage: T(1,1)239[1, 1]240sage: _.parent()241Full tensor product of the crystals [The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]]242sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)]])243sage: T(C(2), C(1), C(1))244[2, 1, 1]245"""246return self.element_class(self, list(crystalElements))247248def one_dimensional_configuration_sum(self, q = None, group_components = True):249r"""250Computes the one-dimensional configuration sum.251252INPUT:253254- ``q`` -- (default: ``None``) a variable or ``None``; if ``None``,255a variable `q` is set in the code256- ``group_components`` -- (default: ``True``) boolean; if ``True``,257then the terms are grouped by classical component258259The one-dimensional configuration sum is the sum of the weights of all elements in the crystal260weighted by the energy function.261262EXAMPLES::263264sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)265sage: T = TensorProductOfCrystals(K,K)266sage: T.one_dimensional_configuration_sum()267B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]] + (q+1)*B[Lambda[1] - Lambda[2]]268+ B[2*Lambda[1]] + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]]269sage: R.<t> = ZZ[]270sage: T.one_dimensional_configuration_sum(t, False)271B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + (t+1)*B[Lambda[1] - Lambda[2]]272+ B[2*Lambda[1]] + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]]273274sage: R = RootSystem(['A',2,1])275sage: La = R.weight_space().basis()276sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1])277sage: LS.one_dimensional_configuration_sum() == T.one_dimensional_configuration_sum()278True279280TESTS::281282sage: K1 = KirillovReshetikhinCrystal(['A',2,1],1,1)283sage: K2 = KirillovReshetikhinCrystal(['A',2,1],2,1)284sage: T = TensorProductOfCrystals(K1,K2)285sage: T.one_dimensional_configuration_sum() == T.one_dimensional_configuration_sum(group_components=False)286True287"""288if q is None:289from sage.rings.all import QQ290q = QQ['q'].gens()[0]291P0 = self.weight_lattice_realization().classical()292B = P0.algebra(q.parent())293if group_components:294G = self.digraph(index_set = self.cartan_type().classical().index_set())295C = G.connected_components()296return sum(q**(c[0].energy_function())*B.sum(B(P0(b.weight())) for b in c) for c in C)297return B.sum(q**(b.energy_function())*B(P0(b.weight())) for b in self)298299TensorProductOfCrystalsOptions=GlobalOptions(name='tensor_product_of_crystals',300doc=r"""301Sets the global options for tensor products of crystals. The default is to302use the anti-Kashiwara convention.303304There are two conventions for how `e_i` and `f_i` act on tensor products,305and the difference between the two is the order of the tensor factors306are reversed. This affects both the input and output. See the example307below.308""",309end_doc=r"""310311.. NOTE::312313Changing the ``convention`` also changes how the input is handled.314315.. WARNING::316317Internally, the crystals are always stored using the anti-Kashiwara318convention.319320If no parameters are set, then the function returns a copy of the321options dictionary.322323EXAMPLES::324325sage: C = CrystalOfLetters(['A',2])326sage: T = TensorProductOfCrystals(C,C)327sage: elt = T(C(1), C(2)); elt328[1, 2]329sage: TensorProductOfCrystals.global_options['convention'] = "Kashiwara"330sage: elt331[2, 1]332sage: T(C(1), C(2)) == elt333False334sage: T(C(2), C(1)) == elt335True336sage: TensorProductOfCrystals.global_options.reset()337""",338convention=dict(default="antiKashiwara",339description='Sets the convention used for displaying/inputting tensor product of crystals',340values=dict(antiKashiwara='use the anti-Kashiwara convention',341Kashiwara='use the Kashiwara convention'),342alias=dict(anti="antiKashiwara", opposite="antiKashiwara"),343case_sensitive=False)344)345346class TensorProductOfCrystals(CrystalOfWords):347r"""348Tensor product of crystals.349350Given two crystals `B` and `B'` of the same Cartan type,351one can form the tensor product `B \otimes B^{\prime}`. As a set352`B \otimes B^{\prime}` is the Cartesian product353`B \times B^{\prime}`. The crystal operators `f_i` and354`e_i` act on `b \otimes b^{\prime} \in B \otimes B^{\prime}` as355follows:356357.. MATH::358359f_i(b \otimes b^{\prime}) = \begin{cases}360f_i(b) \otimes b^{\prime} & \text{if } \varepsilon_i(b) \geq361\varphi_i(b^{\prime}) \\362b \otimes f_i(b^{\prime}) & \text{otherwise}363\end{cases}364365and366367.. MATH::368369e_i(b \otimes b^{\prime}) = \begin{cases}370e_i(b) \otimes b^{\prime} & \text{if } \varepsilon_i(b) >371\varphi_i(b^{\prime}) \\372b \otimes e_i(b^{\prime}) & \text{otherwise.}373\end{cases}374375We also define:376377.. MATH::378379\begin{aligned}380\varphi_i(b \otimes b^{\prime}) & = \max\left( \varphi_i(b),381\varphi_i(b) + \varphi_i(b^{\prime}) - \varepsilon_i(b) \right)382\\ \varepsilon_i(b \otimes b^{\prime}) & = \max\left(383\varepsilon_i(b^{\prime}), \varepsilon_i(b^{\prime}) +384\varepsilon_i(b) - \varphi_i(b^{\prime}) \right).385\end{aligned}386387.. NOTE::388389This is the opposite of Kashiwara's convention for tensor390products of crystals.391392Since tensor products are associative `(\mathcal{B} \otimes \mathcal{C})393\otimes \mathcal{D} \cong \mathcal{B} \otimes (\mathcal{C} \otimes394\mathcal{D})` via the natural isomorphism `(b \otimes c) \otimes d \mapsto395b \otimes (c \otimes d)`, we can generalizing this to arbitrary tensor396products. Thus consider `B_N \otimes \cdots \otimes B_1`, where each397`B_k` is an abstract crystal. The underlying set of the tensor product is398`B_N \times \cdots \times B_1`, while the crystal structure is given399as follows. Let `I` be the index set, and fix some `i \in I` and `b_N400\otimes \cdots \otimes b_1 \in B_N \otimes \cdots \otimes B_1`. Define401402.. MATH::403404a_i(k) := \varepsilon_i(b_k) - \sum_{j=1}^{k-1} \langle405\alpha_i^{\vee}, \mathrm{wt}(b_j) \rangle.406407Then408409.. MATH::410411\begin{aligned}412\mathrm{wt}(b_N \otimes \cdots \otimes b_1) &=413\mathrm{wt}(b_N) + \cdots + \mathrm{wt}(b_1),414\\ \varepsilon_i(b_N \otimes \cdots \otimes b_1) &= \max_{1 \leq k415\leq n}\left( \sum_{j=1}^k \varepsilon_i(b_j) - \sum_{j=1}^{k-1}416\varphi_i(b_j) \right)417\\ & = \max_{1 \leq k \leq N}\bigl( a_i(k) \bigr),418\\ \varphi_i(b_N \otimes \cdots \otimes b_1) &= \max_{1 \leq k \leq N}419\left( \varphi_i(b_N) + \sum_{j=k}^{N-1} \big( \varphi_i(b_j) -420\varepsilon_i(b_{j+1}) \big) \right)421\\ & = \max_{1 \leq k \leq N}\bigl( \lambda_i + a_i(k) \bigr)422\end{aligned}423424where `\lambda_i = \langle \alpha_i^{\vee}, \mathrm{wt}(b_N \otimes \cdots425\otimes b_1) \rangle`. Then for `k = 1, \ldots, N` the action of the426Kashiwara operators is determined as follows.427428- If `a_i(k) > a_i(j)` for `1 \leq j < k` and `a_i(k) \geq a_i(j)`429for `k < j \leq N`:430431.. MATH::432433e_i(b_N \otimes \cdots \otimes b_1) = b_N \otimes \cdots \otimes434e_i b_k \otimes \cdots \otimes b_1.435436- If `a_i(k) \geq a_i(j)` for `1 \leq j < k` and `a_i(k) > a_i(j)`437for `k < j \leq N`:438439.. MATH::440441f_i(b_N \otimes \cdots \otimes b_1) = b_N \otimes \cdots \otimes442f_i b_k \otimes \cdots \otimes b_1.443444Note that this is just recursively applying the definition of the tensor445product on two crystals. Recall that `\langle \alpha_i^{\vee},446\mathrm{wt}(b_j) \rangle = \varphi_i(b_j) - \varepsilon_i(b_j)` by the447definition of a crystal.448449.. RUBRIC:: Regular crystals450451Now if all crystals `B_k` are regular crystals, all `\varepsilon_i` and452`\varphi_i` are non-negative and we can453define tensor product by the *signature rule*. We start by writing a word454in `+` and `-` as follows:455456.. MATH::457458\underbrace{- \cdots -}_{\varphi_i(b_N) \text{ times}} \quad459\underbrace{+ \cdots +}_{\varepsilon_i(b_N) \text{ times}}460\quad \cdots \quad461\underbrace{- \cdots -}_{\varphi_i(b_1) \text{ times}} \quad462\underbrace{+ \cdots +}_{\varepsilon_i(b_1) \text{ times}},463464and then canceling ordered pairs of `+-` until the word is in the reduced465form:466467.. MATH::468469\underbrace{- \cdots -}_{\varphi_i \text{ times}} \quad470\underbrace{+ \cdots +}_{\varepsilon_i \text{ times}}.471472Here `e_i` acts on the factor corresponding to the leftmost `+` and `f_i`473on the factor corresponding to the rightmost `-`. If there is no `+` or474`-` respectively, then the result is `0` (``None``).475476EXAMPLES:477478We construct the type `A_2`-crystal generated by `2 \otimes 1 \otimes 1`::479480sage: C = CrystalOfLetters(['A',2])481sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)]])482483It has `8` elements::484485sage: T.list()486[[2, 1, 1], [2, 1, 2], [2, 1, 3], [3, 1, 3], [3, 2, 3], [3, 1, 1], [3, 1, 2], [3, 2, 2]]487488One can also check the Cartan type of the crystal::489490sage: T.cartan_type()491['A', 2]492493Other examples include crystals of tableaux (which internally are494represented as tensor products obtained by reading the tableaux495columnwise)::496497sage: C = CrystalOfTableaux(['A',3], shape=[1,1,0])498sage: D = CrystalOfTableaux(['A',3], shape=[1,0,0])499sage: T = TensorProductOfCrystals(C,D, generators=[[C(rows=[[1], [2]]), D(rows=[[1]])], [C(rows=[[2], [3]]), D(rows=[[1]])]])500sage: T.cardinality()50124502sage: TestSuite(T).run()503sage: T.module_generators504[[[[1], [2]], [[1]]], [[[2], [3]], [[1]]]]505sage: [x.weight() for x in T.module_generators]506[(2, 1, 0, 0), (1, 1, 1, 0)]507508If no module generators are specified, we obtain the full tensor509product::510511sage: C = CrystalOfLetters(['A',2])512sage: T = TensorProductOfCrystals(C,C)513sage: T.list()514[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]515sage: T.cardinality()5169517518For a tensor product of crystals without module generators, the519default implementation of ``module_generators`` contains all elements520in the tensor product of the crystals. If there is a subset of521elements in the tensor product that still generates the crystal,522this needs to be implemented for the specific crystal separately::523524sage: T.module_generators.list()525[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]526527For classical highest weight crystals, it is also possible to list528all highest weight elements::529530sage: C = CrystalOfLetters(['A',2])531sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)],[C(1),C(2),C(1)]])532sage: T.highest_weight_vectors()533[[2, 1, 1], [1, 2, 1]]534535Examples with non-regular and infinite crystals (these did not work536before :trac:`14402`)::537538sage: B = InfinityCrystalOfTableaux(['D',10])539sage: T = TensorProductOfCrystals(B,B)540sage: T541Full tensor product of the crystals542[The infinity crystal of tableaux of type ['D', 10],543The infinity crystal of tableaux of type ['D', 10]]544545sage: B = InfinityCrystalOfGeneralizedYoungWalls(15)546sage: T = TensorProductOfCrystals(B,B,B)547sage: T548Full tensor product of the crystals549[Crystal of generalized Young walls of type ['A', 15, 1],550Crystal of generalized Young walls of type ['A', 15, 1],551Crystal of generalized Young walls of type ['A', 15, 1]]552553sage: La = RootSystem(['A',2,1]).weight_lattice().fundamental_weights()554sage: B = CrystalOfGeneralizedYoungWalls(2,La[0]+La[1])555sage: C = CrystalOfGeneralizedYoungWalls(2,2*La[2])556sage: D = CrystalOfGeneralizedYoungWalls(2,3*La[0]+La[2])557sage: T = TensorProductOfCrystals(B,C,D)558sage: T559Full tensor product of the crystals560[Highest weight crystal of generalized Young walls of Cartan type ['A', 2, 1] and highest weight Lambda[0] + Lambda[1].,561Highest weight crystal of generalized Young walls of Cartan type ['A', 2, 1] and highest weight 2*Lambda[2].,562Highest weight crystal of generalized Young walls of Cartan type ['A', 2, 1] and highest weight 3*Lambda[0] + Lambda[2].]563564There is also a global option for setting the convention (by default Sage565uses anti-Kashiwara)::566567sage: C = CrystalOfLetters(['A',2])568sage: T = TensorProductOfCrystals(C,C)569sage: elt = T(C(1), C(2)); elt570[1, 2]571sage: TensorProductOfCrystals.global_options['convention'] = "Kashiwara"572sage: elt573[2, 1]574sage: TensorProductOfCrystals.global_options.reset()575"""576@staticmethod577def __classcall_private__(cls, *crystals, **options):578"""579Create the correct parent object.580581EXAMPLES::582583sage: C = CrystalOfLetters(['A',2])584sage: T = TensorProductOfCrystals(C, C)585sage: T2 = TensorProductOfCrystals(C, C)586sage: T is T2587True588sage: B = InfinityCrystalOfTableaux(['A',2])589sage: T = TensorProductOfCrystals(B, B)590"""591crystals = tuple(crystals)592if "cartan_type" in options:593cartan_type = CartanType(options["cartan_type"])594else:595if len(crystals) == 0:596raise ValueError("you need to specify the Cartan type if the tensor product list is empty")597else:598cartan_type = crystals[0].cartan_type()599600if "generators" in options:601generators = tuple(tuple(x) if isinstance(x, list) else x for x in options["generators"])602603if all(c in RegularCrystals() for c in crystals):604return TensorProductOfRegularCrystalsWithGenerators(crystals, generators, cartan_type)605return TensorProductOfCrystalsWithGenerators(crystals, generators, cartan_type)606607if all(c in RegularCrystals() for c in crystals):608return FullTensorProductOfRegularCrystals(crystals, cartan_type=cartan_type)609return FullTensorProductOfCrystals(crystals, cartan_type=cartan_type)610611global_options = TensorProductOfCrystalsOptions612613def _element_constructor_(self, *crystalElements):614"""615EXAMPLES::616617sage: C = CrystalOfLetters(['A',2])618sage: T = TensorProductOfCrystals(C,C)619sage: T(1,1)620[1, 1]621sage: _.parent()622Full tensor product of the crystals [The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]]623sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)]])624sage: T(C(2), C(1), C(1))625[2, 1, 1]626"""627if self.global_options['convention'] == "Kashiwara":628crystalElements = reversed(crystalElements)629return self.element_class(self, list(crystalElements))630631class TensorProductOfCrystalsWithGenerators(TensorProductOfCrystals):632"""633Tensor product of crystals with a generating set.634"""635def __init__(self, crystals, generators, cartan_type):636"""637EXAMPLES::638639sage: C = CrystalOfLetters(['A',2])640sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)]])641sage: TestSuite(T).run()642"""643assert isinstance(crystals, tuple)644assert isinstance(generators, tuple)645category = Category.meet([crystal.category() for crystal in crystals])646Parent.__init__(self, category = category)647self.crystals = crystals648self._cartan_type = cartan_type649self.module_generators = [ self(*x) for x in generators ]650651def _repr_(self):652"""653Return a string representation of ``self``.654655EXAMPLES::656657sage: C = CrystalOfLetters(['A',2])658sage: TensorProductOfCrystals(C,C,generators=[[C(2),C(1)]])659The tensor product of the crystals [The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]]660"""661if self.global_options['convention'] == "Kashiwara":662st = repr(list(reversed(self.crystals)))663else:664st = repr(list(self.crystals))665return "The tensor product of the crystals %s"%st666667class FullTensorProductOfCrystals(TensorProductOfCrystals):668"""669Full tensor product of crystals.670"""671def __init__(self, crystals, **options):672"""673TESTS::674675sage: from sage.combinat.crystals.tensor_product import FullTensorProductOfCrystals676sage: C = CrystalOfLetters(['A',2])677sage: T = TensorProductOfCrystals(C,C)678sage: isinstance(T, FullTensorProductOfCrystals)679True680sage: TestSuite(T).run()681"""682crystals = list(crystals)683category = Category.meet([crystal.category() for crystal in crystals])684Parent.__init__(self, category = category)685self.crystals = crystals686if options.has_key('cartan_type'):687self._cartan_type = CartanType(options['cartan_type'])688else:689if len(crystals) == 0:690raise ValueError, "you need to specify the Cartan type if the tensor product list is empty"691else:692self._cartan_type = crystals[0].cartan_type()693self.cartesian_product = CartesianProduct(*self.crystals)694self.module_generators = self695696def _repr_(self):697"""698Return a string representation of ``self``.699700EXAMPLES::701702sage: C = CrystalOfLetters(['A',2])703sage: TensorProductOfCrystals(C,C)704Full tensor product of the crystals [The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]]705"""706if self.global_options['convention'] == "Kashiwara":707st = repr(reversed(self.crystals))708else:709st = repr(self.crystals)710return "Full tensor product of the crystals %s"%st711712# TODO: __iter__ and cardinality should be inherited from EnumeratedSets().CartesianProducts()713def __iter__(self):714"""715EXAMPLES::716717sage: C = CrystalOfLetters(['A',2])718sage: T = TensorProductOfCrystals(C,C)719sage: list(T)720[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]721sage: _[0].parent()722Full tensor product of the crystals [The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]]723"""724for x in self.cartesian_product:725yield self(*x)726727# list = CombinatorialClass._CombinatorialClass__list_from_iterator728729def cardinality(self):730"""731Return the cardinality of ``self``.732733EXAMPLES::734735sage: C = CrystalOfLetters(['A',2])736sage: T = TensorProductOfCrystals(C,C)737sage: T.cardinality()7389739"""740return self.cartesian_product.cardinality()741742class TensorProductOfCrystalsElement(ImmutableListWithParent):743r"""744A class for elements of tensor products of crystals.745"""746def _repr_(self):747"""748Return a string representation of ``self``.749750EXAMPLES::751752sage: C = CrystalOfLetters(['A',3])753sage: T = TensorProductOfCrystals(C,C)754sage: T(C(1),C(2))755[1, 2]756"""757if self.parent().global_options['convention'] == "Kashiwara":758return repr(list(reversed(self._list)))759return repr(self._list)760761def _latex_(self):762r"""763Return latex code for ``self``.764765EXAMPLES::766767sage: C = CrystalOfLetters(["A",2])768sage: D = CrystalOfTableaux(["A",2], shape=[2])769sage: E = TensorProductOfCrystals(C,D)770sage: latex(E.module_generators[0])7711 \otimes {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}}772\raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\cline{1-2}773\lr{1}&\lr{1}\\\cline{1-2}774\end{array}$}775}776"""777return ' \otimes '.join(latex(c) for c in self)778779def _ascii_art_(self):780"""781Return an ASCII art representation of ``self``.782783EXAMPLES::784785sage: KT = TensorProductOfKirillovReshetikhinTableaux(['D',4,1],[[3,3],[2,1],[1,2]])786sage: ascii_art(KT.module_generators[0])7871 1 17882 2 2 # 1 # 1 17893 3 3 2790-4 -4 -4791"""792from sage.misc.ascii_art import ascii_art, AsciiArt793s = ascii_art(self[0])794s._baseline = s._h // 2795ret = s796for tableau in self[1:]:797s = ascii_art(tableau)798s._baseline = s._h // 2799ret += AsciiArt([" # "]) + s800return ret801802def __lt__(self, other):803"""804Non elements of the crystal are incomparable with elements of the crystal805(or should it return NotImplemented?).806807Comparison of two elements of this crystal:808809- different length: incomparable810- otherwise lexicographicaly, considering ``self[i]`` and ``other[i]``811as incomparable if ``self[i] < other[i]`` returns NotImplemented812"""813if parent(self) is not parent(other):814return False815if len(self) != len(other):816return False817for i in range(len(self)):818if (self[i] < other[i]) == True:819return True820if (other[i] < self[i]) == True:821return False822return False823824def weight(self):825r"""826Return the weight of ``self``.827828EXAMPLES::829830sage: B = InfinityCrystalOfTableaux("A3")831sage: T = TensorProductOfCrystals(B,B)832sage: b1 = B.highest_weight_vector().f_string([2,1,3])833sage: b2 = B.highest_weight_vector().f(1)834sage: t = T(b2, b1)835sage: t836[[[1, 1, 1, 2], [2, 2], [3]], [[1, 1, 1, 1, 2], [2, 2, 4], [3]]]837sage: t.weight()838(-2, 1, 0, 1)839"""840return sum(self[i].weight() for i in range(len(self)))841842def epsilon(self, i):843r"""844Return `\varepsilon_i` of ``self``.845846INPUT:847848- ``i`` -- An element of the index set849850EXAMPLES::851852sage: B = InfinityCrystalOfTableaux("G2")853sage: T = TensorProductOfCrystals(B,B)854sage: b1 = B.highest_weight_vector().f(2)855sage: b2 = B.highest_weight_vector().f_string([2,2,1])856sage: t = T(b2, b1)857sage: [t.epsilon(i) for i in B.index_set()]858[0, 3]859"""860return max(self._sig(i, k) for k in range(1, len(self)+1))861862def phi(self, i):863r"""864Return `\varphi_i` of ``self``.865866INPUT:867868- ``i`` -- An element of the index set869870EXAMPLES::871872sage: La = RootSystem(['A',2,1]).weight_lattice().fundamental_weights()873sage: B = CrystalOfGeneralizedYoungWalls(2,La[0]+La[1])874sage: T = TensorProductOfCrystals(B,B)875sage: b1 = B.highest_weight_vector().f_string([1,0])876sage: b2 = B.highest_weight_vector().f_string([0,1])877sage: t = T(b2, b1)878sage: [t.phi(i) for i in B.index_set()]879[1, 1, 4]880881TESTS:882883Check that :trac:`15462` is fixed::884885sage: B = CrystalOfTableaux(['A',2], shape=[2,1])886sage: La = RootSystem(['A',2]).ambient_space().fundamental_weights()887sage: T = TensorProductOfCrystals(TCrystal(['A',2], La[1]+La[2]), B)888sage: t = T.an_element()889sage: t.phi(1)8902891sage: t.phi(2)8922893"""894P = self[-1].parent().weight_lattice_realization()895h = P.simple_coroots()896omega = P(self.weight()).scalar(h[i])897return max([omega + self._sig(i, k) for k in range(1, len(self)+1)])898899@cached_in_parent_method900def _sig(self,i,k):901r"""902Return `a_i(k)` of ``self``.903904The value `a_i(k)` of a crystal `b = b_N \otimes \cdots \otimes b_1`905is defined as:906907.. MATH::908909a_i(k) = \varepsilon_i(b_k) - \sum_{j=1}^{k-1} \langle h_i,910\mathrm{wt}(b_j) \rangle911912where `\mathrm{wt}` is the :meth:`weight` of `b_j`.913914INPUT:915916- ``i`` -- An element of the index set917918- ``k`` -- The (1-based) index of the tensor factor of ``self``919920EXAMPLES::921922sage: B = InfinityCrystalOfGeneralizedYoungWalls(3)923sage: T = TensorProductOfCrystals(B,B)924sage: b1 = B.highest_weight_vector().f_string([0,3,1])925sage: b2 = B.highest_weight_vector().f_string([3,2,1,0,2,3])926sage: t = T(b1, b2)927sage: [[t._sig(i,k) for k in range(1,len(t)+1)] for i in B.index_set()]928[[0, -1], [0, 0], [0, 1], [1, 2]]929"""930if k == 1:931return self[-1].epsilon(i)932return self._sig(i, k-1) + self[-k].epsilon(i) - self[-k+1].phi(i)933934def e(self,i):935r"""936Return the action of `e_i` on ``self``.937938INPUT:939940- ``i`` -- An element of the index set941942EXAMPLES::943944sage: B = InfinityCrystalOfTableaux("D4")945sage: T = TensorProductOfCrystals(B,B)946sage: b1 = B.highest_weight_vector().f_string([1,4,3])947sage: b2 = B.highest_weight_vector().f_string([2,2,3,1,4])948sage: t = T(b2, b1)949sage: t.e(1)950[[[1, 1, 1, 1, 1], [2, 2, 3, -3], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]]951sage: t.e(2)952sage: t.e(3)953[[[1, 1, 1, 1, 1, 2], [2, 2, 3, -4], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]]954sage: t.e(4)955[[[1, 1, 1, 1, 1, 2], [2, 2, 3, 4], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]]956"""957N = len(self) + 1958for k in range(1, N):959if all(self._sig(i,k) > self._sig(i,j) for j in range(1, k)) and \960all(self._sig(i,k) >= self._sig(i,j) for j in range(k+1, N)):961crystal = self[-k].e(i)962if crystal is None:963return None964return self.set_index(-k, crystal)965return None966967def f(self,i):968r"""969Return the action of `f_i` on ``self``.970971INPUT:972973- ``i`` -- An element of the index set974975EXAMPLES::976977sage: La = RootSystem(['A',3,1]).weight_lattice().fundamental_weights()978sage: B = CrystalOfGeneralizedYoungWalls(3,La[0])979sage: T = TensorProductOfCrystals(B,B,B)980sage: b1 = B.highest_weight_vector().f_string([0,3])981sage: b2 = B.highest_weight_vector().f_string([0])982sage: b3 = B.highest_weight_vector()983sage: t = T(b3, b2, b1)984sage: t.f(0)985[[[0]], [[0]], [[0, 3]]]986sage: t.f(1)987[[], [[0]], [[0, 3], [1]]]988sage: t.f(2)989[[], [[0]], [[0, 3, 2]]]990sage: t.f(3)991[[], [[0, 3]], [[0, 3]]]992"""993N = len(self) + 1994for k in range(1, N):995if all(self._sig(i,k) >= self._sig(i,j) for j in range(1, k)) and \996all(self._sig(i,k) > self._sig(i,j) for j in range(k+1, N)):997crystal = self[-k].f(i)998if crystal is None:999return None1000return self.set_index(-k, crystal)1001return None10021003class TensorProductOfRegularCrystalsElement(TensorProductOfCrystalsElement):1004"""1005Element class for a tensor product of regular crystals.10061007TESTS::10081009sage: C = CrystalOfLetters(['A',2])1010sage: T = TensorProductOfCrystals(C, C)1011sage: elt = T(C(1), C(2))1012sage: from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement1013sage: isinstance(elt, TensorProductOfRegularCrystalsElement)1014True1015"""1016def e(self, i):1017"""1018Return the action of `e_i` on ``self``.10191020EXAMPLES::10211022sage: C = CrystalOfLetters(['A',5])1023sage: T = TensorProductOfCrystals(C,C)1024sage: T(C(1),C(2)).e(1) == T(C(1),C(1))1025True1026sage: T(C(2),C(1)).e(1) == None1027True1028sage: T(C(2),C(2)).e(1) == T(C(1),C(2))1029True1030"""1031if i not in self.index_set():1032raise ValueError("i must be in the index set")1033position = self.positions_of_unmatched_plus(i)1034if position == []:1035return None1036k = position[0]1037return self.set_index(k, self[k].e(i))10381039def weight(self):1040"""1041Return the weight of ``self``.10421043EXAMPLES::10441045sage: C = CrystalOfLetters(['A',3])1046sage: T = TensorProductOfCrystals(C,C)1047sage: T(C(1),C(2)).weight()1048(1, 1, 0, 0)1049sage: T=CrystalOfTableaux(['D',4],shape=[])1050sage: T.list()[0].weight()1051(0, 0, 0, 0)1052"""1053return sum((self[j].weight() for j in range(len(self))), self.parent().weight_lattice_realization().zero())10541055def f(self, i):1056"""1057Return the action of `f_i` on ``self``.10581059EXAMPLES::10601061sage: C = CrystalOfLetters(['A',5])1062sage: T = TensorProductOfCrystals(C,C)1063sage: T(C(1),C(1)).f(1)1064[1, 2]1065sage: T(C(1),C(2)).f(1)1066[2, 2]1067sage: T(C(2),C(1)).f(1) is None1068True1069"""1070if i not in self.index_set():1071raise ValueError("i must be in the index set")1072position = self.positions_of_unmatched_minus(i)1073if position == []:1074return None1075k = position[len(position)-1]1076return self.set_index(k, self[k].f(i))10771078def phi(self, i):1079r"""1080Return `\varphi_i` of ``self``.10811082EXAMPLES::10831084sage: C = CrystalOfLetters(['A',5])1085sage: T = TensorProductOfCrystals(C,C)1086sage: T(C(1),C(1)).phi(1)108721088sage: T(C(1),C(2)).phi(1)108911090sage: T(C(2),C(1)).phi(1)109101092"""1093self = self.reversed()1094height = 01095for j in range(len(self)):1096plus = self[j].epsilon(i)1097minus = self[j].phi(i)1098if height-plus < 0:1099height = minus1100else:1101height = height - plus + minus1102return height11031104def epsilon(self, i):1105r"""1106Return `\varepsilon_i` of ``self``.11071108EXAMPLES::11091110sage: C = CrystalOfLetters(['A',5])1111sage: T = TensorProductOfCrystals(C,C)1112sage: T(C(1),C(1)).epsilon(1)111301114sage: T(C(1),C(2)).epsilon(1)111511116sage: T(C(2),C(1)).epsilon(1)111701118"""1119height = 01120for j in range(len(self)):1121minus = self[j].phi(i)1122plus = self[j].epsilon(i)1123if height-minus < 0:1124height = plus1125else:1126height = height - minus + plus1127return height11281129def positions_of_unmatched_minus(self, i, dual=False, reverse=False):1130"""1131EXAMPLES::11321133sage: C = CrystalOfLetters(['A',5])1134sage: T = TensorProductOfCrystals(C,C)1135sage: T(C(2),C(1)).positions_of_unmatched_minus(1)1136[]1137sage: T(C(1),C(2)).positions_of_unmatched_minus(1)1138[0]1139"""1140unmatched_plus = []1141height = 01142if reverse == True:1143self = self.reversed()1144if dual == False:1145for j in range(len(self)):1146minus = self[j].phi(i)1147plus = self[j].epsilon(i)1148if height-minus < 0:1149unmatched_plus.append(j)1150height = plus1151else:1152height = height - minus + plus1153else:1154for j in range(len(self)):1155plus = self[j].epsilon(i)1156minus = self[j].phi(i)1157if height-plus < 0:1158unmatched_plus.append(j)1159height = minus1160else:1161height = height - plus + minus1162return unmatched_plus11631164def positions_of_unmatched_plus(self, i):1165"""1166EXAMPLES::11671168sage: C = CrystalOfLetters(['A',5])1169sage: T = TensorProductOfCrystals(C,C)1170sage: T(C(2),C(1)).positions_of_unmatched_plus(1)1171[]1172sage: T(C(1),C(2)).positions_of_unmatched_plus(1)1173[1]1174"""1175l = self.positions_of_unmatched_minus(i, dual=True, reverse=True)1176l.reverse()1177return [len(self)-1-l[j] for j in range(len(l))]11781179def energy_function(self):1180r"""1181Return the energy function of ``self``.11821183INPUT:11841185- ``self`` -- an element of a tensor product of perfect Kirillov-Reshetkhin crystals of the same level.11861187OUTPUT: an integer11881189The energy is only defined when ``self`` is an element of a tensor product of affine Kirillov-Reshetikhin crystals.1190In this implementation, it is assumed that ``self`` is an element of a tensor product of perfect crystals of the1191same level, see Theorem 7.5 in [SchillingTingley2011]_.11921193REFERENCES:11941195.. [SchillingTingley2011] A. Schilling, P. Tingley.1196Demazure crystals, Kirillov-Reshetikhin crystals, and the energy1197function. Electronic Journal of Combinatorics. **19(2)**. 2012.1198:arXiv:`1104.2359`11991200EXAMPLES::12011202sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)1203sage: T = TensorProductOfCrystals(K,K,K)1204sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]1205sage: for b in hw:1206... print b, b.energy_function()1207...1208[[[1]], [[1]], [[1]]] 01209[[[1]], [[2]], [[1]]] 21210[[[2]], [[1]], [[1]]] 11211[[[3]], [[2]], [[1]]] 312121213sage: K = KirillovReshetikhinCrystal(['C',2,1],1,2)1214sage: T = TensorProductOfCrystals(K,K)1215sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]1216sage: for b in hw: # long time (5s on sage.math, 2011)1217... print b, b.energy_function()1218...1219[[], []] 41220[[], [[1, 1]]] 11221[[[1, 1]], []] 31222[[[1, 1]], [[1, 1]]] 01223[[[1, 2]], [[1, 1]]] 11224[[[2, 2]], [[1, 1]]] 21225[[[-1, -1]], [[1, 1]]] 21226[[[1, -1]], [[1, 1]]] 21227[[[2, -1]], [[1, 1]]] 212281229sage: K = KirillovReshetikhinCrystal(['C',2,1],1,1)1230sage: T = TensorProductOfCrystals(K)1231sage: t = T.module_generators[0]1232sage: t.energy_function()1233Traceback (most recent call last):1234...1235ValueError: All crystals in the tensor product need to be perfect of the same level1236"""1237C = self.parent().crystals[0]1238ell = ceil(C.s()/C.cartan_type().c()[C.r()])1239if any(ell != K.s()/K.cartan_type().c()[K.r()] for K in self.parent().crystals):1240raise ValueError("All crystals in the tensor product need to be perfect of the same level")1241t = self.parent()(*[K.module_generator() for K in self.parent().crystals])1242d = t.affine_grading()1243return d - self.affine_grading()12441245def affine_grading(self):1246r"""1247Returns the affine grading of `self`.12481249INPUT:12501251- ``self`` -- an element of a tensor product of Kirillov-Reshetikhin crystals.12521253OUTPUT: an integer12541255The affine grading is only defined when ``self`` is an element of a tensor product of affine Kirillov-Reshetikhin crystals.1256It is calculated by finding a path from ``self`` to a ground state path using the helper method1257:meth:`e_string_to_ground_state` and counting the number of affine Kashiwara operators `e_0` applied on the way.12581259EXAMPLES::12601261sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)1262sage: T = TensorProductOfCrystals(K,K)1263sage: t = T.module_generators[0]1264sage: t.affine_grading()1265112661267sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)1268sage: T = TensorProductOfCrystals(K,K,K)1269sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]1270sage: for b in hw:1271... print b, b.affine_grading()1272...1273[[[1]], [[1]], [[1]]] 31274[[[1]], [[2]], [[1]]] 11275[[[2]], [[1]], [[1]]] 21276[[[3]], [[2]], [[1]]] 012771278sage: K = KirillovReshetikhinCrystal(['C',2,1],1,1)1279sage: T = TensorProductOfCrystals(K,K,K)1280sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]1281sage: for b in hw:1282... print b, b.affine_grading()1283...1284[[[1]], [[1]], [[1]]] 21285[[[1]], [[2]], [[1]]] 11286[[[1]], [[-1]], [[1]]] 01287[[[2]], [[1]], [[1]]] 11288[[[-2]], [[2]], [[1]]] 01289[[[-1]], [[1]], [[1]]] 11290"""1291return self.e_string_to_ground_state().count(0)12921293@cached_method1294def e_string_to_ground_state(self):1295r"""1296Returns a string of integers in the index set `(i_1,\ldots,i_k)` such that `e_{i_k} \cdots e_{i_1} self` is1297the ground state.12981299INPUT:13001301- ``self`` -- an element of a tensor product of Kirillov-Reshetikhin crystals.13021303OUTPUT: a tuple of integers `(i_1,\ldots,i_k)`13041305This method is only defined when ``self`` is an element of a tensor product of affine Kirillov-Reshetikhin crystals.1306It calculates a path from ``self`` to a ground state path using Demazure arrows as defined in1307Lemma 7.3 in [SchillingTingley2011]_.13081309EXAMPLES::13101311sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)1312sage: T = TensorProductOfCrystals(K,K)1313sage: t = T.module_generators[0]1314sage: t.e_string_to_ground_state()1315(0, 2)13161317sage: K = KirillovReshetikhinCrystal(['C',2,1],1,1)1318sage: T = TensorProductOfCrystals(K,K)1319sage: t = T.module_generators[0]; t1320[[[1]], [[1]]]1321sage: t.e_string_to_ground_state()1322(0,)1323sage: x=t.e(0)1324sage: x.e_string_to_ground_state()1325()1326sage: y=t.f_string([1,2,1,1,0]); y1327[[[2]], [[1]]]1328sage: y.e_string_to_ground_state()1329()1330"""1331from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux1332if self.parent().crystals[0].__module__ != 'sage.combinat.crystals.kirillov_reshetikhin' and \1333not isinstance(self.parent().crystals[0], KirillovReshetikhinTableaux):1334raise ValueError("All crystals in the tensor product need to be Kirillov-Reshetikhin crystals")1335I = self.cartan_type().classical().index_set()1336ell = max(ceil(K.s()/K.cartan_type().c()[K.r()]) for K in self.parent().crystals)1337for i in I:1338if self.epsilon(i) > 0:1339return (i,) + (self.e(i)).e_string_to_ground_state()1340if self.epsilon(0) > ell:1341return (0,) + (self.e(0)).e_string_to_ground_state()1342return ()13431344CrystalOfWords.Element = TensorProductOfCrystalsElement13451346class FullTensorProductOfRegularCrystals(FullTensorProductOfCrystals):1347"""1348Full tensor product of regular crystals.1349"""1350Element = TensorProductOfRegularCrystalsElement13511352class TensorProductOfRegularCrystalsWithGenerators(TensorProductOfCrystalsWithGenerators):1353"""1354Tensor product of regular crystals with a generating set.1355"""1356Element = TensorProductOfRegularCrystalsElement13571358#########################################################1359## Crystal of tableaux13601361class CrystalOfTableaux(CrystalOfWords):1362r"""1363A class for crystals of tableaux with integer valued shapes13641365INPUT:13661367- ``cartan_type`` -- a Cartan type1368- ``shape`` -- a partition of length at most ``cartan_type.rank()``1369- ``shapes`` -- a list of such partitions13701371This constructs a classical crystal with the given Cartan type and1372highest weight(s) corresponding to the given shape(s).13731374If the type is `D_r`, the shape is permitted to have a negative1375value in the `r`-th position. Thus if the shape equals `[s_1,\ldots,s_r]`,1376then `s_r` may be negative but in any case `s_1 \geq \cdots \geq s_{r-1}1377\geq |s_r|`. This crystal is related to that of shape1378`[s_1,\ldots,|s_r|]` by the outer automorphism of `SO(2r)`.13791380If the type is `D_r` or `B_r`, the shape is permitted to be of1381length `r` with all parts of half integer value. This corresponds1382to having one spin column at the beginning of the tableau. If1383several shapes are provided, they currently should all or none1384have this property.13851386Crystals of tableaux are constructed using an embedding into1387tensor products following Kashiwara and Nakashima [KN94]_. Sage's tensor1388product rule for crystals differs from that of Kashiwara and Nakashima1389by reversing the order of the tensor factors. Sage produces the same1390crystals of tableaux as Kashiwara and Nakashima. With Sage's convention,1391the tensor product of crystals is the same as the monoid operation on1392tableaux and hence the plactic monoid.13931394.. SEEALSO::13951396:mod:`sage.combinat.crystals.crystals` for general help on1397crystals, and in particular plotting and `\LaTeX` output.13981399EXAMPLES:14001401We create the crystal of tableaux for type `A_2`, with1402highest weight given by the partition `[2,1,1]`::14031404sage: T = CrystalOfTableaux(['A',3], shape = [2,1,1])14051406Here is the list of its elements::14071408sage: T.list()1409[[[1, 1], [2], [3]], [[1, 2], [2], [3]], [[1, 3], [2], [3]],1410[[1, 4], [2], [3]], [[1, 4], [2], [4]], [[1, 4], [3], [4]],1411[[2, 4], [3], [4]], [[1, 1], [2], [4]], [[1, 2], [2], [4]],1412[[1, 3], [2], [4]], [[1, 3], [3], [4]], [[2, 3], [3], [4]],1413[[1, 1], [3], [4]], [[1, 2], [3], [4]], [[2, 2], [3], [4]]]14141415Internally, a tableau of a given Cartan type is represented as a1416tensor product of letters of the same type. The order in which the1417tensor factors appear is by reading the columns of the tableaux1418left to right, top to bottom (in French notation). As an example::14191420sage: T = CrystalOfTableaux(['A',2], shape = [3,2])1421sage: T.module_generators[0]1422[[1, 1, 1], [2, 2]]1423sage: T.module_generators[0]._list1424[2, 1, 2, 1, 1]14251426To create a tableau, one can use::14271428sage: Tab = CrystalOfTableaux(['A',3], shape = [2,2])1429sage: Tab(rows=[[1,2],[3,4]])1430[[1, 2], [3, 4]]1431sage: Tab(columns=[[3,1],[4,2]])1432[[1, 2], [3, 4]]14331434.. TODO::14351436FIXME:14371438- Do we want to specify the columns increasingly or1439decreasingly? That is, should this be1440``Tab(columns = [[1,3],[2,4]])``?1441- Make this fully consistent with1442:func:`~sage.combinat.tableau.Tableau`!14431444We illustrate the use of a shape with a negative last entry in1445type `D`::14461447sage: T = CrystalOfTableaux(['D',4],shape=[1,1,1,-1])1448sage: T.cardinality()1449351450sage: TestSuite(T).run()14511452We illustrate the construction of crystals of spin tableaux when1453the partitions have half integer values in type `B` and `D`::14541455sage: T = CrystalOfTableaux(['B',3],shape=[3/2,1/2,1/2]); T1456The crystal of tableaux of type ['B', 3] and shape(s) [[3/2, 1/2, 1/2]]1457sage: T.cardinality()1458481459sage: T.module_generators1460[[+++, [[1]]]]1461sage: TestSuite(T).run()14621463sage: T = CrystalOfTableaux(['D',3],shape=[3/2,1/2,-1/2]); T1464The crystal of tableaux of type ['D', 3] and shape(s) [[3/2, 1/2, -1/2]]1465sage: T.cardinality()1466201467sage: T.module_generators1468[[++-, [[1]]]]1469sage: TestSuite(T).run()14701471TESTS:14721473Base cases::14741475sage: T = CrystalOfTableaux(['A',2], shape = [])1476sage: T.list()1477[[]]1478sage: TestSuite(T).run()14791480sage: T = CrystalOfTableaux(['C',2], shape = [1])1481sage: T.list()1482[[[1]], [[2]], [[-2]], [[-1]]]1483sage: TestSuite(T).run()14841485sage: T = CrystalOfTableaux(['A',2], shapes = [[],[1],[2]])1486sage: T.list()1487[[], [[1]], [[2]], [[3]], [[1, 1]], [[1, 2]], [[2, 2]], [[1, 3]], [[2, 3]], [[3, 3]]]1488sage: T.module_generators1489([], [[1]], [[1, 1]])14901491sage: T = CrystalOfTableaux(['B',2], shape=[3])1492sage: T(rows=[[1,1,0]])1493[[1, 1, 0]]14941495Input tests::14961497sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1498sage: C = T.letters1499sage: Tab(rows = [[1,2],[3,4]])._list == [C(3),C(1),C(4),C(2)]1500True1501sage: Tab(columns = [[3,1],[4,2]])._list == [C(3),C(1),C(4),C(2)]1502True15031504For compatibility with :func:`TensorProductOfCrystals` we need to1505accept as input the internal list or sequence of elements::15061507sage: Tab(list = [3,1,4,2])._list == [C(3),C(1),C(4),C(2)]1508True1509sage: Tab(3,1,4,2)._list == [C(3),C(1),C(4),C(2)]1510True15111512The next example checks whether a given tableau is in fact a valid1513type `C` tableau or not::15141515sage: T = CrystalOfTableaux(['C',3], shape = [2,2,2])1516sage: Tab = T(rows=[[1,3],[2,-3],[3,-1]])1517sage: Tab in T.list()1518True1519sage: Tab = T(rows=[[2,3],[3,-3],[-3,-2]])1520sage: Tab in T.list()1521False1522"""15231524@staticmethod1525def __classcall_private__(cls, cartan_type, shapes = None, shape = None):1526"""1527Normalizes the input arguments to ensure unique representation,1528and to delegate the construction of spin tableaux.15291530EXAMPLES::15311532sage: T1 = CrystalOfTableaux(CartanType(['A',3]), shape = [2,2])1533sage: T2 = CrystalOfTableaux(['A',3], shape = (2,2))1534sage: T3 = CrystalOfTableaux(['A',3], shapes = ([2,2],))1535sage: T2 is T1, T3 is T11536(True, True)1537"""1538cartan_type = CartanType(cartan_type)1539n = cartan_type.rank()1540# standardize shape/shapes input into a tuple of tuples1541assert operator.xor(shape is not None, shapes is not None)1542if shape is not None:1543shapes = (shape,)1544spin_shapes = tuple( tuple(shape) for shape in shapes )1545try:1546shapes = tuple( tuple(trunc(i) for i in shape) for shape in spin_shapes )1547except Exception:1548raise ValueError("shapes should all be partitions or half-integer partitions")1549if spin_shapes == shapes:1550return super(CrystalOfTableaux, cls).__classcall__(cls, cartan_type, shapes)15511552# Handle the construction of a crystals of spin tableaux1553# Caveat: this currently only supports all shapes being half1554# integer partitions of length the rank for type B and D. In1555# particular, for type D, the spins all have to be plus or all1556# minus spins1557if any(len(sh) != n for sh in shapes):1558raise ValueError("the length of all half-integer partition shapes should be the rank")1559if any(2*i % 2 != 1 for shape in spin_shapes for i in shape):1560raise ValueError("shapes should be either all partitions or all half-integer partitions")1561if cartan_type.type() == 'D':1562if all( i >= 0 for shape in spin_shapes for i in shape):1563S = CrystalOfSpinsPlus(cartan_type)1564elif all(shape[-1]<0 for shape in spin_shapes):1565S = CrystalOfSpinsMinus(cartan_type)1566else:1567raise ValueError("In type D spins should all be positive or negative")1568else:1569if any( i < 0 for shape in spin_shapes for i in shape):1570raise ValueError("shapes should all be partitions")1571S = CrystalOfSpins(cartan_type)1572B = CrystalOfTableaux(cartan_type, shapes = shapes)1573T = TensorProductOfCrystals(S,B, generators=[[S.module_generators[0],x] for x in B.module_generators])1574T.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in spin_shapes)))1575T.shapes = spin_shapes1576return T157715781579def __init__(self, cartan_type, shapes):1580"""1581Construct the crystal of all tableaux of the given shapes15821583INPUT:15841585- ``cartan_type`` - (data coercible into) a cartan type1586- ``shapes`` - a list (or iterable) of shapes1587- ``shape` ` - a shape15881589shapes themselves are lists (or iterable) of integers15901591EXAMPLES::15921593sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1594sage: TestSuite(T).run()1595"""1596# super(CrystalOfTableaux, self).__init__(category = FiniteEnumeratedSets())1597Parent.__init__(self, category = ClassicalCrystals())1598self.letters = CrystalOfLetters(cartan_type)1599self.shapes = shapes1600self.module_generators = tuple(self.module_generator(la) for la in shapes)1601self.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in shapes)))16021603def cartan_type(self):1604"""1605Returns the Cartan type of the associated crystal16061607EXAMPLES::16081609sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1610sage: T.cartan_type()1611['A', 3]1612"""1613return self.letters.cartan_type()16141615def module_generator(self, shape):1616"""1617This yields the module generator (or highest weight element) of a classical1618crystal of given shape. The module generator is the unique tableau with equal1619shape and content.16201621EXAMPLE::16221623sage: T = CrystalOfTableaux(['D',3], shape = [1,1])1624sage: T.module_generator([1,1])1625[[1], [2]]16261627sage: T = CrystalOfTableaux(['D',4],shape=[2,2,2,-2])1628sage: T.module_generator(tuple([2,2,2,-2]))1629[[1, 1], [2, 2], [3, 3], [-4, -4]]1630sage: T.cardinality()16312941632sage: T = CrystalOfTableaux(['D',4],shape=[2,2,2,2])1633sage: T.module_generator(tuple([2,2,2,2]))1634[[1, 1], [2, 2], [3, 3], [4, 4]]1635sage: T.cardinality()16362941637"""1638type = self.cartan_type()1639if type[0] == 'D' and len(shape) == type[1] and shape[type[1]-1] < 0:1640invert = True1641shape = shape[:-1]+(-shape[type[1]-1],)1642else:1643invert = False1644p = Partition(shape).conjugate()1645# The column canonical tableau, read by columns1646module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(len(p))])1647if invert:1648f = lambda x : -x if x == type[1] else x1649module_generator = map(f, module_generator)1650return self(list=[self.letters(x) for x in module_generator])16511652def _element_constructor_(self, *args, **options):1653"""1654Returns a CrystalOfTableauxElement16551656EXAMPLES::16571658sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1659sage: T(rows=[[1,2],[3,4]])1660[[1, 2], [3, 4]]1661sage: T(columns=[[3,1],[4,2]])1662[[1, 2], [3, 4]]1663"""1664return self.element_class(self, *args, **options)1665166616671668class CrystalOfTableauxElement(TensorProductOfRegularCrystalsElement):1669"""1670Element in a crystal of tableaux.1671"""1672def __init__(self, parent, *args, **options):1673"""1674There are several ways to input tableaux, by rows,1675by columns, as the list of column elements, or as a sequence of numbers1676in column reading.16771678EXAMPLES::16791680sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1681sage: t = T(rows=[[1,2],[3,4]])1682sage: t1683[[1, 2], [3, 4]]1684sage: TestSuite(t).run()16851686sage: t = T(columns=[[3,1],[4,2]])1687sage: t1688[[1, 2], [3, 4]]1689sage: TestSuite(t).run()16901691sage: t = T(list=[3,1,4,2])1692sage: t1693[[1, 2], [3, 4]]16941695sage: t = T(3,1,4,2)1696sage: t1697[[1, 2], [3, 4]]16981699Currently inputting the empty tableau as an empty sequence is broken due to a bug in1700the generic __call__ method (see trac ticket #8648)17011702EXAMPLES::17031704sage: T = CrystalOfTableaux(['A',3], shape=[])1705sage: t = T()1706sage: t._list1707[0]17081709TESTS:17101711Integer types that are not a Sage ``Integer`` (such as a Python ``int``1712and typically arise from compiled code) were not converted into a1713letter. This caused certain functions to fail. This is fixed in1714:trac:`13204`::17151716sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1717sage: t = T(list=[int(3),1,4,2])1718sage: type(t[0])1719<type 'sage.combinat.crystals.letters.Crystal_of_letters_type_A_element'>1720sage: t = T(list=[3,int(1),4,2])1721sage: type(t[1])1722<type 'sage.combinat.crystals.letters.Crystal_of_letters_type_A_element'>1723sage: C = KirillovReshetikhinCrystal(['A',int(3),1], 1,1)1724sage: C[0].e(0)1725[[4]]1726"""1727if len(args) == 1:1728if isinstance(args[0], Tableau):1729options['rows'] = args[0]1730if options.has_key('list'):1731list = options['list']1732elif options.has_key('rows'):1733rows=options['rows']1734# list=Tableau(rows).to_word_by_column()1735rows=Tableau(rows).conjugate()1736list=[]1737for col in rows:1738col.reverse()1739list+=col1740elif options.has_key('columns'):1741columns=options['columns']1742list=[]1743for col in columns:1744list+=col1745else:1746list = [i for i in args]1747TensorProductOfRegularCrystalsElement.__init__(self, parent, map(parent.letters, list))17481749def _repr_(self):1750"""1751EXAMPLES::17521753sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1754sage: t = T(rows=[[1,2],[3,4]])1755sage: t._repr_()1756'[[1, 2], [3, 4]]'1757"""1758return repr(self.to_tableau())17591760def pp(self):1761"""1762EXAMPLES::17631764sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1765sage: t = T(rows=[[1,2],[3,4]])1766sage: t.pp()17671 217683 41769"""1770return self.to_tableau().pp()17711772def _latex_(self):1773r"""1774EXAMPLES::17751776sage: T = CrystalOfTableaux(['A',3], shape = [4,2])1777sage: t = T(rows=[[1,1,2,3],[2,3]])1778sage: latex(t) # indirect doctest1779{\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}}1780\raisebox{-.6ex}{$\begin{array}[b]{*{4}c}\cline{1-4}1781\lr{1}&\lr{1}&\lr{2}&\lr{3}\\\cline{1-4}1782\lr{2}&\lr{3}\\\cline{1-2}1783\end{array}$}1784}1785"""1786from sage.combinat.output import tex_from_array1787# Modified version of to_tableau() to have the entrys be letters1788# rather than their values1789if self._list == []:1790return "{\\emptyset}"17911792tab = [ [self[0]] ]1793for i in range(1,len(self)):1794if self[i-1] < self[i] or (self[i-1].value != 0 and self[i-1] == self[i]):1795tab.append([self[i]])1796else:1797l = len(tab)-11798tab[l].append(self[i])1799for x in tab:1800x.reverse()1801T = Tableau(tab).conjugate()1802return tex_from_array([[letter._latex_() for letter in row] for row in T])18031804@cached_method1805def to_tableau(self):1806"""1807Returns the Tableau object corresponding to self.18081809EXAMPLES::18101811sage: T = CrystalOfTableaux(['A',3], shape = [2,2])1812sage: t = T(rows=[[1,2],[3,4]]).to_tableau(); t1813[[1, 2], [3, 4]]1814sage: type(t)1815<class 'sage.combinat.tableau.Tableaux_all_with_category.element_class'>1816sage: type(t[0][0])1817<type 'int'>1818sage: T = CrystalOfTableaux(['D',3], shape = [1,1])1819sage: t=T(rows=[[-3],[3]]).to_tableau(); t1820[[-3], [3]]1821sage: t=T(rows=[[3],[-3]]).to_tableau(); t1822[[3], [-3]]1823sage: T = CrystalOfTableaux(['B',2], shape = [1,1])1824sage: t = T(rows=[[0],[0]]).to_tableau(); t1825[[0], [0]]1826"""1827if self._list == []:1828return Tableau([])1829tab = [ [self[0].value] ]1830for i in range(1,len(self)):1831if self[i-1] < self[i] or (self[i-1].value != 0 and self[i-1] == self[i]):1832tab.append([self[i].value])1833else:1834l = len(tab)-11835tab[l].append(self[i].value)1836for x in tab:1837x.reverse()1838return Tableau(tab).conjugate()18391840def promotion(self):1841"""1842Promotion for type A crystals of tableaux of rectangular shape18431844Returns the result of applying promotion on this tableau.18451846This method only makes sense in type A with rectangular shapes.18471848EXAMPLES::18491850sage: C = CrystalOfTableaux(["A",3], shape = [3,3,3])1851sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]]))1852sage: t1853[[1, 1, 1], [2, 2, 3], [3, 4, 4]]1854sage: t.promotion()1855[[1, 1, 2], [2, 2, 3], [3, 4, 4]]1856sage: t.promotion().parent()1857The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]]1858"""1859crystal = self.parent()1860cartan_type = crystal.cartan_type()1861assert cartan_type.type() == 'A'1862return crystal(self.to_tableau().promotion(cartan_type.rank()))18631864def promotion_inverse(self):1865"""1866Inverse promotion for type A crystals of tableaux of rectangular shape18671868Returns the result of applying inverse promotion on this tableau.18691870This method only makes sense in type A with rectangular shapes.18711872EXAMPLES::18731874sage: C = CrystalOfTableaux(["A",3], shape = [3,3,3])1875sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]]))1876sage: t1877[[1, 1, 1], [2, 2, 3], [3, 4, 4]]1878sage: t.promotion_inverse()1879[[1, 1, 2], [2, 3, 3], [4, 4, 4]]1880sage: t.promotion_inverse().parent()1881The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]]1882"""1883crystal = self.parent()1884cartan_type = crystal.cartan_type()1885assert cartan_type.type() == 'A'1886return crystal(self.to_tableau().promotion_inverse(cartan_type.rank()))18871888CrystalOfTableaux.Element = CrystalOfTableauxElement188918901891