def __init__(self, cartan_type, shapes): """ Construct the crystal of all tableaux of the given shapes INPUT: - ``cartan_type`` - (data coercible into) a cartan type - ``shapes`` - a list (or iterable) of shapes - ``shape` ` - a shape shapes themselves are lists (or iterable) of integers EXAMPLES:: sage: T = CrystalOfTableaux(['A',3], shape = [2,2]) sage: TestSuite(T).run() """ # super(CrystalOfTableaux, self).__init__(category = FiniteEnumeratedSets()) Parent.__init__(self, category = ClassicalCrystals()) self.letters = CrystalOfLetters(cartan_type) self.module_generators = tuple(self.module_generator(la) for la in shapes) self.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in shapes)))
class CrystalOfTableaux(CrystalOfWords): r""" A class for crystals of tableaux with integer valued shapes INPUT: - ``cartan_type`` -- a Cartan type - ``shape`` -- a partition of length at most ``cartan_type.rank()`` - ``shapes`` -- a list of such partitions This constructs a classical crystal with the given Cartan type and highest weight(s) corresponding to the given shape(s). If the type is `D_r`, the shape is permitted to have a negative value in the `r`-th position. Thus if shape=`[s_1,\dots,s_r]` then `s_r` may be negative but in any case `s_1 \ge \cdots \ge s_{r-1} \ge |s_r|`. This crystal is related to that of shape `[s_1,\dots,|s_r|]` by the outer automorphism of `SO(2r)`. If the type is `D_r` or `B_r`, the shape is permitted to be of length `r` with all parts of half integer value. This corresponds to having one spin column at the beginning of the tableau. If several shapes are provided, they currently should all or none have this property. Crystals of tableaux are constructed using an embedding into tensor products following Kashiwara and Nakashima [Kashiwara, Masaki; Nakashima, Toshiki, *Crystal graphs for representations of the q-analogue of classical Lie algebras*, J. Algebra 165 (1994), no. 2, 295-345.]. Sage's tensor product rule for crystals differs from that of Kashiwara and Nakashima by reversing the order of the tensor factors. Sage produces the same crystals of tableaux as Kashiwara and Nakashima. With Sage's convention, the tensor product of crystals is the same as the monoid operation on tableaux and hence the plactic monoid. .. seealso:: :mod:`sage.combinat.crystals.crystals` for general help on crystals, and in particular plotting and LaTeX output. EXAMPLES: We create the crystal of tableaux for type `A_2`, with highest weight given by the partition [2,1,1]:: sage: T = CrystalOfTableaux(['A',3], shape = [2,1,1]) Here is the list of its elements:: sage: T.list() [[[1, 1], [2], [3]], [[1, 2], [2], [3]], [[1, 3], [2], [3]], [[1, 4], [2], [3]], [[1, 4], [2], [4]], [[1, 4], [3], [4]], [[2, 4], [3], [4]], [[1, 1], [2], [4]], [[1, 2], [2], [4]], [[1, 3], [2], [4]], [[1, 3], [3], [4]], [[2, 3], [3], [4]], [[1, 1], [3], [4]], [[1, 2], [3], [4]], [[2, 2], [3], [4]]] Internally, a tableau of a given Cartan type is represented as a tensor product of letters of the same type. The order in which the tensor factors appear is by reading the columns of the tableaux left to right, top to bottom (in French notation). As an example:: sage: T = CrystalOfTableaux(['A',2], shape = [3,2]) sage: T.module_generators[0] [[1, 1, 1], [2, 2]] sage: T.module_generators[0]._list [2, 1, 2, 1, 1] To create a tableau, one can use:: sage: Tab = CrystalOfTableaux(['A',3], shape = [2,2]) sage: Tab(rows=[[1,2],[3,4]]) [[1, 2], [3, 4]] sage: Tab(columns=[[3,1],[4,2]]) [[1, 2], [3, 4]] FIXME: - do we want to specify the columns increasingly or decreasingly. That is, should this be Tab(columns = [[1,3],[2,4]]) - make this fully consistent with :func:`~sage.combinat.tableau.Tableau`! We illustrate the use of a shape with a negative last entry in type `D`:: sage: T = CrystalOfTableaux(['D',4],shape=[1,1,1,-1]) sage: T.cardinality() 35 sage: TestSuite(T).run() We illustrate the construction of crystals of spin tableaux when the partitions have half integer values in type `B` and `D`:: sage: T = CrystalOfTableaux(['B',3],shape=[3/2,1/2,1/2]); T The crystal of tableaux of type ['B', 3] and shape(s) [[3/2, 1/2, 1/2]] sage: T.cardinality() 48 sage: T.module_generators [[+++, [[1]]]] sage: TestSuite(T).run() sage: T = CrystalOfTableaux(['D',3],shape=[3/2,1/2,-1/2]); T The crystal of tableaux of type ['D', 3] and shape(s) [[3/2, 1/2, -1/2]] sage: T.cardinality() 20 sage: T.module_generators [[++-, [[1]]]] sage: TestSuite(T).run() TESTS: Base cases:: sage: T = CrystalOfTableaux(['A',2], shape = []) sage: T.list() [[]] sage: TestSuite(T).run() sage: T = CrystalOfTableaux(['C',2], shape = [1]) sage: T.list() [[[1]], [[2]], [[-2]], [[-1]]] sage: TestSuite(T).run() sage: T = CrystalOfTableaux(['A',2], shapes = [[],[1],[2]]) sage: T.list() [[], [[1]], [[2]], [[3]], [[1, 1]], [[1, 2]], [[2, 2]], [[1, 3]], [[2, 3]], [[3, 3]]] sage: T.module_generators ([], [[1]], [[1, 1]]) sage: T = CrystalOfTableaux(['B',2], shape=[3]) sage: T(rows=[[1,1,0]]) [[1, 1, 0]] Input tests:: sage: T = CrystalOfTableaux(['A',3], shape = [2,2]) sage: C = T.letters sage: Tab(rows = [[1,2],[3,4]])._list == [C(3),C(1),C(4),C(2)] True sage: Tab(columns = [[3,1],[4,2]])._list == [C(3),C(1),C(4),C(2)] True For compatibility with :func:`TensorProductOfCrystals` we need to accept as input the internal list or sequence of elements:: sage: Tab(list = [3,1,4,2])._list == [C(3),C(1),C(4),C(2)] True sage: Tab(3,1,4,2)._list == [C(3),C(1),C(4),C(2)] True The next example checks whether a given tableau is in fact a valid type `C` tableau or not:: sage: T = CrystalOfTableaux(['C',3], shape = [2,2,2]) sage: Tab = T(rows=[[1,3],[2,-3],[3,-1]]) sage: Tab in T.list() True sage: Tab = T(rows=[[2,3],[3,-3],[-3,-2]]) sage: Tab in T.list() False """ @staticmethod def __classcall_private__(cls, cartan_type, shapes = None, shape = None): """ Normalizes the input arguments to ensure unique representation, and to delegate the construction of spin tableaux. EXAMPLES:: sage: T1 = CrystalOfTableaux(CartanType(['A',3]), shape = [2,2]) sage: T2 = CrystalOfTableaux(['A',3], shape = (2,2)) sage: T3 = CrystalOfTableaux(['A',3], shapes = ([2,2],)) sage: T2 is T1, T3 is T1 (True, True) """ cartan_type = CartanType(cartan_type) n = cartan_type.rank() # standardize shape/shapes input into a tuple of tuples assert operator.xor(shape is not None, shapes is not None) if shape is not None: shapes = (shape,) spin_shapes = tuple( tuple(shape) for shape in shapes ) try: shapes = tuple( tuple(trunc(i) for i in shape) for shape in spin_shapes ) except StandardError: raise ValueError("shapes should all be partitions or half-integer partitions") if spin_shapes == shapes: return super(CrystalOfTableaux, cls).__classcall__(cls, cartan_type, shapes) # Handle the construction of a crystals of spin tableaux # Caveat: this currently only supports all shapes being half # integer partitions of length the rank for type B and D. In # particular, for type D, the spins all have to be plus or all # minus spins assert all(len(sh) == n for sh in shapes), \ "the length of all half-integer partition shapes should be the rank" assert all(2*i % 2 == 1 for shape in spin_shapes for i in shape), \ "shapes should be either all partitions or all half-integer partitions" if cartan_type.type() == 'D': if all( i >= 0 for shape in spin_shapes for i in shape): S = CrystalOfSpinsPlus(cartan_type) elif all(shape[-1]<0 for shape in spin_shapes): S = CrystalOfSpinsMinus(cartan_type) else: raise ValueError, "In type D spins should all be positive or negative" else: assert all( i >= 0 for shape in spin_shapes for i in shape), \ "shapes should all be partitions" S = CrystalOfSpins(cartan_type) B = CrystalOfTableaux(cartan_type, shapes = shapes) T = TensorProductOfCrystals(S,B, generators=[[S.module_generators[0],x] for x in B.module_generators]) T.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in spin_shapes))) T.shapes = spin_shapes return T def __init__(self, cartan_type, shapes): """ Construct the crystal of all tableaux of the given shapes INPUT: - ``cartan_type`` - (data coercible into) a cartan type - ``shapes`` - a list (or iterable) of shapes - ``shape` ` - a shape shapes themselves are lists (or iterable) of integers EXAMPLES:: sage: T = CrystalOfTableaux(['A',3], shape = [2,2]) sage: TestSuite(T).run() """ # super(CrystalOfTableaux, self).__init__(category = FiniteEnumeratedSets()) Parent.__init__(self, category = ClassicalCrystals()) self.letters = CrystalOfLetters(cartan_type) self.shapes = shapes self.module_generators = tuple(self.module_generator(la) for la in shapes) self.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in shapes))) def cartan_type(self): """ Returns the Cartan type of the associated crystal EXAMPLES:: sage: T = CrystalOfTableaux(['A',3], shape = [2,2]) sage: T.cartan_type() ['A', 3] """ return self.letters.cartan_type() def module_generator(self, shape): """ This yields the module generator (or highest weight element) of a classical crystal of given shape. The module generator is the unique tableau with equal shape and content. EXAMPLE:: sage: T = CrystalOfTableaux(['D',3], shape = [1,1]) sage: T.module_generator([1,1]) [[1], [2]] sage: T = CrystalOfTableaux(['D',4],shape=[2,2,2,-2]) sage: T.module_generator(tuple([2,2,2,-2])) [[1, 1], [2, 2], [3, 3], [-4, -4]] sage: T.cardinality() 294 sage: T = CrystalOfTableaux(['D',4],shape=[2,2,2,2]) sage: T.module_generator(tuple([2,2,2,2])) [[1, 1], [2, 2], [3, 3], [4, 4]] sage: T.cardinality() 294 """ type = self.cartan_type() if type[0] == 'D' and len(shape) == type[1] and shape[type[1]-1] < 0: invert = True shape = shape[:-1]+(-shape[type[1]-1],) else: invert = False p = Partition(shape).conjugate() # The column canonical tableau, read by columns module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(len(p))]) if invert: f = lambda x : -x if x == type[1] else x module_generator = map(f, module_generator) return self(list=[self.letters(x) for x in module_generator]) def _element_constructor_(self, *args, **options): """ Returns a CrystalOfTableauxElement EXAMPLES:: sage: T = CrystalOfTableaux(['A',3], shape = [2,2]) sage: T(rows=[[1,2],[3,4]]) [[1, 2], [3, 4]] sage: T(columns=[[3,1],[4,2]]) [[1, 2], [3, 4]] """ return self.element_class(self, *args, **options)
class CrystalOfTableaux(CrystalOfWords): r""" A class for crystals of tableaux with integer valued shapes INPUT: - ``cartan_type`` -- a Cartan type - ``shape`` -- a partition of length at most ``cartan_type.rank()`` - ``shapes`` -- a list of such partitions This constructs a classical crystal with the given Cartan type and highest weight(s) corresponding to the given shape(s). If the type is `D_r`, the shape is permitted to have a negative value in the `r`-th position. Thus if shape=`[s_1,\dots,s_r]` then `s_r` may be negative but in any case `s_1 \ge \cdots \ge s_{r-1} \ge |s_r|`. This crystal is related to that of shape `[s_1,\dots,|s_r|]` by the outer automorphism of `SO(2r)`. If the type is `D_r` or `B_r`, the shape is permitted to be of length `r` with all parts of half integer value. This corresponds to having one spin column at the beginning of the tableau. If several shapes are provided, they currently should all or none have this property. Crystals of tableaux are constructed using an embedding into tensor products following Kashiwara and Nakashima [Kashiwara, Masaki; Nakashima, Toshiki, *Crystal graphs for representations of the q-analogue of classical Lie algebras*, J. Algebra 165 (1994), no. 2, 295-345.]. Sage's tensor product rule for crystals differs from that of Kashiwara and Nakashima by reversing the order of the tensor factors. Sage produces the same crystals of tableaux as Kashiwara and Nakashima. With Sage's convention, the tensor product of crystals is the same as the monoid operation on tableaux and hence the plactic monoid. .. seealso:: :mod:`sage.combinat.crystals.crystals` for general help on crystals, and in particular plotting and LaTeX output. EXAMPLES: We create the crystal of tableaux for type `A_2`, with highest weight given by the partition [2,1,1]:: sage: T = CrystalOfTableaux(['A',3], shape = [2,1,1]) Here is the list of its elements:: sage: T.list() [[[1, 1], [2], [3]], [[1, 2], [2], [3]], [[1, 3], [2], [3]], [[1, 4], [2], [3]], [[1, 4], [2], [4]], [[1, 4], [3], [4]], [[2, 4], [3], [4]], [[1, 1], [2], [4]], [[1, 2], [2], [4]], [[1, 3], [2], [4]], [[1, 3], [3], [4]], [[2, 3], [3], [4]], [[1, 1], [3], [4]], [[1, 2], [3], [4]], [[2, 2], [3], [4]]] Internally, a tableau of a given Cartan type is represented as a tensor product of letters of the same type. The order in which the tensor factors appear is by reading the columns of the tableaux left to right, top to bottom (in French notation). As an example:: sage: T = CrystalOfTableaux(['A',2], shape = [3,2]) sage: T.module_generators[0] [[1, 1, 1], [2, 2]] sage: T.module_generators[0]._list [2, 1, 2, 1, 1] To create a tableau, one can use:: sage: Tab = CrystalOfTableaux(['A',3], shape = [2,2]) sage: Tab(rows=[[1,2],[3,4]]) [[1, 2], [3, 4]] sage: Tab(columns=[[3,1],[4,2]]) [[1, 2], [3, 4]] FIXME: - do we want to specify the columns increasingly or decreasingly. That is, should this be Tab(columns = [[1,3],[2,4]]) - make this fully consistent with :func:`~sage.combinat.tableau.Tableau`! We illustrate the use of a shape with a negative last entry in type `D`:: sage: T = CrystalOfTableaux(['D',4],shape=[1,1,1,-1]) sage: T.cardinality() 35 sage: TestSuite(T).run() We illustrate the construction of crystals of spin tableaux when the partitions have half integer values in type `B` and `D`:: sage: T = CrystalOfTableaux(['B',3],shape=[3/2,1/2,1/2]); T The crystal of tableaux of type ['B', 3] and shape(s) [[3/2, 1/2, 1/2]] sage: T.cardinality() 48 sage: T.module_generators [[+++, [[1]]]] sage: TestSuite(T).run() sage: T = CrystalOfTableaux(['D',3],shape=[3/2,1/2,-1/2]); T The crystal of tableaux of type ['D', 3] and shape(s) [[3/2, 1/2, -1/2]] sage: T.cardinality() 20 sage: T.module_generators [[++-, [[1]]]] sage: TestSuite(T).run() TESTS: Base cases:: sage: T = CrystalOfTableaux(['A',2], shape = []) sage: T.list() [[]] sage: TestSuite(T).run() sage: T = CrystalOfTableaux(['C',2], shape = [1]) sage: T.list() [[[1]], [[2]], [[-2]], [[-1]]] sage: TestSuite(T).run() sage: T = CrystalOfTableaux(['A',2], shapes = [[],[1],[2]]) sage: T.list() [[], [[1]], [[2]], [[3]], [[1, 1]], [[1, 2]], [[2, 2]], [[1, 3]], [[2, 3]], [[3, 3]]] sage: T.module_generators ([], [[1]], [[1, 1]]) sage: T = CrystalOfTableaux(['B',2], shape=[3]) sage: T(rows=[[1,1,0]]) [[1, 1, 0]] Input tests:: sage: T = CrystalOfTableaux(['A',3], shape = [2,2]) sage: C = T.letters sage: Tab(rows = [[1,2],[3,4]])._list == [C(3),C(1),C(4),C(2)] True sage: Tab(columns = [[3,1],[4,2]])._list == [C(3),C(1),C(4),C(2)] True For compatibility with :func:`TensorProductOfCrystals` we need to accept as input the internal list or sequence of elements:: sage: Tab(list = [3,1,4,2])._list == [C(3),C(1),C(4),C(2)] True sage: Tab(3,1,4,2)._list == [C(3),C(1),C(4),C(2)] True The next example checks whether a given tableau is in fact a valid type `C` tableau or not:: sage: T = CrystalOfTableaux(['C',3], shape = [2,2,2]) sage: Tab = T(rows=[[1,3],[2,-3],[3,-1]]) sage: Tab in T.list() True sage: Tab = T(rows=[[2,3],[3,-3],[-3,-2]]) sage: Tab in T.list() False """ @staticmethod def __classcall_private__(cls, cartan_type, shapes = None, shape = None): """ Normalizes the input arguments to ensure unique representation, and to delegate the construction of spin tableaux. EXAMPLES:: sage: T1 = CrystalOfTableaux(CartanType(['A',3]), shape = [2,2]) sage: T2 = CrystalOfTableaux(['A',3], shape = (2,2)) sage: T3 = CrystalOfTableaux(['A',3], shapes = ([2,2],)) sage: T2 is T1, T3 is T1 (True, True) """ cartan_type = CartanType(cartan_type) n = cartan_type.rank() # standardize shape/shapes input into a tuple of tuples assert operator.xor(shape is not None, shapes is not None) if shape is not None: shapes = (shape,) spin_shapes = tuple( tuple(shape) for shape in shapes ) try: shapes = tuple( tuple(trunc(i) for i in shape) for shape in spin_shapes ) except: raise ValueError("shapes should all be partitions or half-integer partitions") if spin_shapes == shapes: return super(CrystalOfTableaux, cls).__classcall__(cls, cartan_type, shapes) # Handle the construction of a crystals of spin tableaux # Caveat: this currently only supports all shapes being half # integer partitions of length the rank for type B and D. In # particular, for type D, the spins all have to be plus or all # minus spins assert all(len(sh) == n for sh in shapes), \ "the length of all half-integer partition shapes should be the rank" assert all(2*i % 2 == 1 for shape in spin_shapes for i in shape), \ "shapes should be either all partitions or all half-integer partitions" if cartan_type.type() == 'D': if all( i >= 0 for shape in spin_shapes for i in shape): S = CrystalOfSpinsPlus(cartan_type) elif all(shape[-1]<0 for shape in spin_shapes): S = CrystalOfSpinsMinus(cartan_type) else: raise ValueError, "In type D spins should all be positive or negative" else: assert all( i >= 0 for shape in spin_shapes for i in shape), \ "shapes should all be partitions" S = CrystalOfSpins(cartan_type) B = CrystalOfTableaux(cartan_type, shapes = shapes) T = TensorProductOfCrystals(S,B, generators=[[S.module_generators[0],x] for x in B.module_generators]) T.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in spin_shapes))) return T def __init__(self, cartan_type, shapes): """ Construct the crystal of all tableaux of the given shapes INPUT: - ``cartan_type`` - (data coercible into) a cartan type - ``shapes`` - a list (or iterable) of shapes - ``shape` ` - a shape shapes themselves are lists (or iterable) of integers EXAMPLES:: sage: T = CrystalOfTableaux(['A',3], shape = [2,2]) sage: TestSuite(T).run() """ # super(CrystalOfTableaux, self).__init__(category = FiniteEnumeratedSets()) Parent.__init__(self, category = ClassicalCrystals()) self.letters = CrystalOfLetters(cartan_type) self.module_generators = tuple(self.module_generator(la) for la in shapes) self.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in shapes))) def cartan_type(self): """ Returns the Cartan type of the associated crystal EXAMPLES:: sage: T = CrystalOfTableaux(['A',3], shape = [2,2]) sage: T.cartan_type() ['A', 3] """ return self.letters.cartan_type() def module_generator(self, shape): """ This yields the module generator (or highest weight element) of a classical crystal of given shape. The module generator is the unique tableau with equal shape and content. EXAMPLE:: sage: T = CrystalOfTableaux(['D',3], shape = [1,1]) sage: T.module_generator([1,1]) [[1], [2]] """ type = self.cartan_type() if type[0] == 'D' and len(shape) == type[1] and shape[type[1]-1] < 0: invert = True shape = shape[:-1]+(-shape[type[1]-1],) else: invert = False p = Partition(shape).conjugate() # The column canonical tableau, read by columns module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(len(p))]) if invert: for i in range(type[1]): if module_generator[i] == type[1]: module_generator[i] = -type[1] return self(list=[self.letters(x) for x in module_generator]) def _element_constructor_(self, *args, **options): """ Returns a CrystalOfTableauxElement EXAMPLES:: sage: T = CrystalOfTableaux(['A',3], shape = [2,2]) sage: T(rows=[[1,2],[3,4]]) [[1, 2], [3, 4]] sage: T(columns=[[3,1],[4,2]]) [[1, 2], [3, 4]] """ return self.element_class(self, *args, **options)