Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/crystals/tensor_product.py
4072 views
1
"""
2
Tensor Products of Crystals
3
4
Main entry points:
5
6
- :func:`TensorProductOfCrystals`
7
- :class:`CrystalOfTableaux`
8
9
"""
10
#*****************************************************************************
11
# Copyright (C) 2007 Anne Schilling <anne at math.ucdavis.edu>
12
# Nicolas Thiery <nthiery at users.sf.net>
13
#
14
# Distributed under the terms of the GNU General Public License (GPL)
15
#
16
# This code is distributed in the hope that it will be useful,
17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
# General Public License for more details.
20
#
21
# The full text of the GPL is available at:
22
#
23
# http://www.gnu.org/licenses/
24
#****************************************************************************
25
26
import operator
27
from sage.misc.latex import latex
28
from sage.misc.cachefunc import cached_method
29
from sage.structure.parent import Parent
30
from sage.structure.element import Element, parent
31
from sage.categories.category import Category
32
from sage.categories.classical_crystals import ClassicalCrystals
33
from sage.combinat.root_system.cartan_type import CartanType
34
from sage.combinat.cartesian_product import CartesianProduct
35
from sage.combinat.combinat import CombinatorialObject
36
from sage.combinat.partition import Partition
37
from sage.combinat.tableau import Tableau
38
from sage.combinat.tableau import Tableau_class
39
from letters import CrystalOfLetters
40
from spins import CrystalOfSpins, CrystalOfSpinsMinus, CrystalOfSpinsPlus
41
from sage.misc.flatten import flatten
42
from sage.rings.integer import Integer
43
44
##############################################################################
45
# Until trunc gets implemented in sage.function.other
46
47
from sage.functions.other import floor, ceil
48
def trunc(i):
49
"""
50
Truncates to the integer closer to zero
51
52
EXAMPLES::
53
54
sage: from sage.combinat.crystals.tensor_product import trunc
55
sage: trunc(-3/2), trunc(-1), trunc(-1/2), trunc(0), trunc(1/2), trunc(1), trunc(3/2)
56
(-1, -1, 0, 0, 0, 1, 1)
57
sage: isinstance(trunc(3/2), Integer)
58
True
59
"""
60
if i>= 0:
61
return floor(i)
62
else:
63
return ceil(i)
64
65
##############################################################################
66
# Support classes
67
##############################################################################
68
69
from sage.structure.unique_representation import UniqueRepresentation
70
71
class TestParent(UniqueRepresentation, Parent):
72
def _repr_(self):
73
return "A parent for tests"
74
75
class ImmutableListWithParent(CombinatorialObject, Element):
76
r"""
77
A class for lists having a parent
78
79
Specification: any subclass C should implement __init__ which
80
accepts the following form C(parent, list = list)
81
82
EXAMPLES: We create an immutable list whose parent is the class
83
list::
84
85
sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent
86
sage: l = ImmutableListWithParent(TestParent(), [1,2,3])
87
sage: l._list
88
[1, 2, 3]
89
sage: l.parent()
90
A parent for tests
91
sage: l.sibling([2,1]) == ImmutableListWithParent(TestParent(), [2,1])
92
True
93
sage: l.reversed()
94
[3, 2, 1]
95
sage: l.set_index(1,4)
96
[1, 4, 3]
97
"""
98
99
def __init__(self, parent, list):
100
"""
101
EXAMPLES::
102
103
sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent
104
sage: l = ImmutableListWithParent(TestParent(), [1,2,3])
105
sage: l.parent()
106
A parent for tests
107
sage: parent(l)
108
A parent for tests
109
sage: TestSuite(l).run(skip = "_test_category")
110
"""
111
Element.__init__(self, parent)
112
CombinatorialObject.__init__(self, list)
113
114
def _repr_(self):
115
"""
116
EXAMPLES::
117
118
sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent
119
sage: l = ImmutableListWithParent(TestParent(), [1,2,3])
120
sage: l._repr_()
121
'[1, 2, 3]'
122
"""
123
return "%s"%self._list
124
125
def __eq__(self, other):
126
"""
127
EXAMPLES::
128
129
sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent
130
sage: l = ImmutableListWithParent(TestParent(), [1,2,3])
131
sage: m = ImmutableListWithParent(ZZ, [1,2,3])
132
sage: n = ImmutableListWithParent(ZZ, [2,3,4])
133
sage: l == l
134
True
135
sage: l == m
136
False
137
sage: m == n
138
False
139
"""
140
return self.__class__ is other.__class__ and \
141
self.parent() == other.parent() and \
142
self._list == other._list
143
144
def __ne__(self, other):
145
return not self.__eq__(other)
146
147
# Should go in Element? Sets.ElementMethods?
148
# How to define them conditionally, only of __lt__ is defined?
149
def __le__(self, other):
150
if self == other:
151
return True
152
else:
153
return self.__le__(other)
154
155
def __gt__(self, other):
156
if parent(self) is not parent(other):
157
return NotImplemented
158
return other.__lt__(self)
159
160
def __ge__(self, other):
161
if self == other:
162
return True
163
else:
164
return self.__le__(other)
165
166
def sibling(self, l):
167
"""
168
Returns an ImmutableListWithParent object whose list is l and whose
169
parent is the same as self's parent.
170
171
Note that the implementation of this function makes an assumption
172
about the constructor for subclasses.
173
174
EXAMPLES::
175
176
sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent
177
sage: l = ImmutableListWithParent(TestParent(), [1,2,3])
178
sage: m = l.sibling([2,3,4]); m
179
[2, 3, 4]
180
sage: m.parent()
181
A parent for tests
182
"""
183
return self.__class__(self.parent(), list=l)
184
185
def reversed(self):
186
"""
187
Returns the sibling of self which is obtained by reversing the
188
elements of self.
189
190
EXAMPLES::
191
192
sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent
193
sage: l = ImmutableListWithParent(TestParent(), [1,2,3])
194
sage: l.reversed()
195
[3, 2, 1]
196
"""
197
return self.sibling([ i for i in reversed(self._list)])
198
199
def set_index(self, k, value):
200
"""
201
Returns the sibling of self obtained by setting the
202
`k^{th}` entry of self to value.
203
204
EXAMPLES::
205
206
sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent
207
sage: l = ImmutableListWithParent(TestParent(), [1,2,3])
208
sage: l.set_index(0,2)
209
[2, 2, 3]
210
sage: l.set_index(1,4)
211
[1, 4, 3]
212
sage: _.parent()
213
A parent for tests
214
"""
215
l = [i for i in self._list]
216
l[k] = value
217
return self.sibling(l)
218
219
def TensorProductOfCrystals(*crystals, **options):
220
r"""
221
Tensor product of crystals.
222
223
Given two crystals `B` and `B'` of the same type,
224
one can form the tensor product `B \otimes B'`. As a set
225
`B \otimes B'` is the Cartesian product
226
`B \times B'`. The crystal operators `f_i` and
227
`e_i` act on `b \otimes b' \in B \otimes B'` as
228
follows:
229
230
.. math::
231
232
f_i(b \otimes b') = \begin{cases}
233
f_i(b) \otimes b' & \text{if $\varepsilon_i(b) \ge \varphi_i(b')$}\\
234
b \otimes f_i(b') & \text{otherwise}
235
\end{cases}
236
237
and
238
239
.. math::
240
241
e_i(b \otimes b') = \begin{cases}
242
b \otimes e_i(b') & \text{if $\varepsilon_i(b) \le \varphi_i(b')$}\\
243
e_i(b) \otimes b' & \text{otherwise.}
244
\end{cases}
245
246
Note that this is the opposite of Kashiwara's convention for tensor
247
products of crystals.
248
249
EXAMPLES:
250
251
We construct the type `A_2`-crystal generated by `2 \otimes 1 \otimes 1`::
252
253
sage: C = CrystalOfLetters(['A',2])
254
sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)]])
255
256
It has `8` elements::
257
258
sage: T.list()
259
[[2, 1, 1], [2, 1, 2], [2, 1, 3], [3, 1, 3], [3, 2, 3], [3, 1, 1], [3, 1, 2], [3, 2, 2]]
260
261
One can also check the Cartan type of the crystal::
262
263
sage: T.cartan_type()
264
['A', 2]
265
266
Other examples include crystals of tableaux (which internally are represented as tensor products
267
obtained by reading the tableaux columnwise)::
268
269
sage: C = CrystalOfTableaux(['A',3], shape=[1,1,0])
270
sage: D = CrystalOfTableaux(['A',3], shape=[1,0,0])
271
sage: T = TensorProductOfCrystals(C,D, generators=[[C(rows=[[1], [2]]), D(rows=[[1]])], [C(rows=[[2], [3]]), D(rows=[[1]])]])
272
sage: T.cardinality()
273
24
274
sage: TestSuite(T).run()
275
sage: T.module_generators
276
[[[[1], [2]], [[1]]], [[[2], [3]], [[1]]]]
277
sage: [x.weight() for x in T.module_generators]
278
[(2, 1, 0, 0), (1, 1, 1, 0)]
279
280
If no module generators are specified, we obtain the full tensor
281
product::
282
283
sage: C=CrystalOfLetters(['A',2])
284
sage: T=TensorProductOfCrystals(C,C)
285
sage: T.list()
286
[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
287
sage: T.cardinality()
288
9
289
290
For a tensor product of crystals without module generators, the
291
default implementation of module_generators contains all elements
292
in the tensor product of the crystals. If there is a subset of
293
elements in the tensor product that still generates the crystal,
294
this needs to be implemented for the specific crystal separately::
295
296
sage: T.module_generators.list()
297
[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
298
299
For classical highest weight crystals, it is also possible to list
300
all highest weight elements::
301
302
sage: C = CrystalOfLetters(['A',2])
303
sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)],[C(1),C(2),C(1)]])
304
sage: T.highest_weight_vectors()
305
[[2, 1, 1], [1, 2, 1]]
306
"""
307
crystals = tuple(crystals)
308
if "cartan_type" in options:
309
cartan_type = CartanType(options["cartan_type"])
310
else:
311
if len(crystals) == 0:
312
raise ValueError, "you need to specify the Cartan type if the tensor product list is empty"
313
else:
314
cartan_type = crystals[0].cartan_type()
315
316
if "generators" in options:
317
generators = tuple(tuple(x) if isinstance(x, list) else x for x in options["generators"])
318
return TensorProductOfCrystalsWithGenerators(crystals, generators=generators, cartan_type = cartan_type)
319
else:
320
return FullTensorProductOfCrystals(crystals, cartan_type = cartan_type)
321
322
323
class CrystalOfWords(UniqueRepresentation, Parent):
324
"""
325
Auxiliary class to provide a call method to create tensor product elements.
326
This class is shared with several tensor product classes and is also used in CrystalOfTableaux to allow
327
tableaux of different tensor product structures in column-reading (and hence different shapes)
328
to be considered elements in the same crystal.
329
"""
330
def _element_constructor_(self, *crystalElements):
331
"""
332
EXAMPLES::
333
334
sage: C = CrystalOfLetters(['A',2])
335
sage: T = TensorProductOfCrystals(C,C)
336
sage: T(1,1)
337
[1, 1]
338
sage: _.parent()
339
Full tensor product of the crystals [The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]]
340
sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)]])
341
sage: T(C(2), C(1), C(1))
342
[2, 1, 1]
343
"""
344
return self.element_class(self, list(crystalElements))
345
346
347
class TensorProductOfCrystalsWithGenerators(CrystalOfWords):
348
349
def __init__(self, crystals, generators, cartan_type):
350
"""
351
EXAMPLES::
352
353
sage: C = CrystalOfLetters(['A',2])
354
sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)]])
355
sage: TestSuite(T).run()
356
"""
357
assert isinstance(crystals, tuple)
358
assert isinstance(generators, tuple)
359
category = Category.meet([crystal.category() for crystal in crystals])
360
Parent.__init__(self, category = category)
361
self.rename("The tensor product of the crystals %s"%(crystals,))
362
self.crystals = crystals
363
self._cartan_type = cartan_type
364
self.module_generators = [ self(*x) for x in generators ]
365
366
367
class FullTensorProductOfCrystals(CrystalOfWords):
368
def __init__(self, crystals, **options):
369
"""
370
TESTS::
371
372
sage: from sage.combinat.crystals.tensor_product import FullTensorProductOfCrystals
373
sage: C = CrystalOfLetters(['A',2])
374
sage: T = TensorProductOfCrystals(C,C)
375
sage: isinstance(T, FullTensorProductOfCrystals)
376
True
377
sage: TestSuite(T).run()
378
"""
379
crystals = list(crystals)
380
category = Category.meet([crystal.category() for crystal in crystals])
381
Parent.__init__(self, category = category)
382
self.rename("Full tensor product of the crystals %s"%(crystals,))
383
self.crystals = crystals
384
if options.has_key('cartan_type'):
385
self._cartan_type = CartanType(options['cartan_type'])
386
else:
387
if len(crystals) == 0:
388
raise ValueError, "you need to specify the Cartan type if the tensor product list is empty"
389
else:
390
self._cartan_type = crystals[0].cartan_type()
391
self.cartesian_product = CartesianProduct(*self.crystals)
392
self.module_generators = self
393
394
# TODO: __iter__ and cardinality should be inherited from EnumeratedSets().CartesianProducts()
395
def __iter__(self):
396
"""
397
EXAMPLES::
398
399
sage: C = CrystalOfLetters(['A',2])
400
sage: T = TensorProductOfCrystals(C,C)
401
sage: list(T)
402
[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]]
403
sage: _[0].parent()
404
Full tensor product of the crystals [The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]]
405
"""
406
for x in self.cartesian_product:
407
yield self(*x)
408
409
# list = CombinatorialClass._CombinatorialClass__list_from_iterator
410
411
def cardinality(self):
412
"""
413
EXAMPLES::
414
415
sage: C = CrystalOfLetters(['A',2])
416
sage: T = TensorProductOfCrystals(C,C)
417
sage: T.cardinality()
418
9
419
"""
420
return self.cartesian_product.cardinality()
421
422
423
424
class TensorProductOfCrystalsElement(ImmutableListWithParent):
425
r"""
426
A class for elements of tensor products of crystals
427
"""
428
429
def __lt__(self, other):
430
"""
431
Non elements of the crystal are incomparable with elements of the crystal
432
(or should it return NotImplemented?)
433
434
Comparison of two elements of this crystal:
435
- different length: incomparable
436
- otherwise lexicographicaly, considering self[i] and other[i]
437
as incomparable if self[i] < other[i] returns NotImplemented
438
"""
439
if parent(self) is not parent(other):
440
return False
441
if len(self) != len(other):
442
return False
443
for i in range(len(self)):
444
if (self[i] < other[i]) == True:
445
return True
446
if (other[i] < self[i]) == True:
447
return False
448
return False
449
450
def e(self, i):
451
"""
452
Returns the action of `e_i` on self.
453
454
EXAMPLES::
455
456
sage: C = CrystalOfLetters(['A',5])
457
sage: T = TensorProductOfCrystals(C,C)
458
sage: T(C(1),C(2)).e(1) == T(C(1),C(1))
459
True
460
sage: T(C(2),C(1)).e(1) == None
461
True
462
sage: T(C(2),C(2)).e(1) == T(C(1),C(2))
463
True
464
"""
465
assert i in self.index_set()
466
position = self.positions_of_unmatched_plus(i)
467
if position == []:
468
return None
469
k = position[0]
470
return self.set_index(k, self[k].e(i))
471
472
def weight(self):
473
"""
474
Returns the weight of self.
475
476
EXAMPLES::
477
478
sage: C = CrystalOfLetters(['A',3])
479
sage: T = TensorProductOfCrystals(C,C)
480
sage: T(C(1),C(2)).weight()
481
(1, 1, 0, 0)
482
sage: T=CrystalOfTableaux(['D',4],shape=[])
483
sage: T.list()[0].weight()
484
(0, 0, 0, 0)
485
"""
486
return sum((self[j].weight() for j in range(len(self))), self.parent().weight_lattice_realization().zero())
487
488
def f(self, i):
489
"""
490
Returns the action of `f_i` on self.
491
492
EXAMPLES::
493
494
sage: C = CrystalOfLetters(['A',5])
495
sage: T = TensorProductOfCrystals(C,C)
496
sage: T(C(1),C(1)).f(1)
497
[1, 2]
498
sage: T(C(1),C(2)).f(1)
499
[2, 2]
500
sage: T(C(2),C(1)).f(1) is None
501
True
502
"""
503
assert i in self.index_set()
504
position = self.positions_of_unmatched_minus(i)
505
if position == []:
506
return None
507
k = position[len(position)-1]
508
return self.set_index(k, self[k].f(i))
509
510
def phi(self, i):
511
"""
512
EXAMPLES::
513
514
sage: C = CrystalOfLetters(['A',5])
515
sage: T = TensorProductOfCrystals(C,C)
516
sage: T(C(1),C(1)).phi(1)
517
2
518
sage: T(C(1),C(2)).phi(1)
519
1
520
sage: T(C(2),C(1)).phi(1)
521
0
522
"""
523
self = self.reversed()
524
height = 0
525
for j in range(len(self)):
526
plus = self[j].epsilon(i)
527
minus = self[j].phi(i)
528
if height-plus < 0:
529
height = minus
530
else:
531
height = height - plus + minus
532
return height
533
534
def epsilon(self, i):
535
"""
536
EXAMPLES::
537
538
sage: C = CrystalOfLetters(['A',5])
539
sage: T = TensorProductOfCrystals(C,C)
540
sage: T(C(1),C(1)).epsilon(1)
541
0
542
sage: T(C(1),C(2)).epsilon(1)
543
1
544
sage: T(C(2),C(1)).epsilon(1)
545
0
546
"""
547
height = 0
548
for j in range(len(self)):
549
minus = self[j].phi(i)
550
plus = self[j].epsilon(i)
551
if height-minus < 0:
552
height = plus
553
else:
554
height = height - minus + plus
555
return height
556
557
def positions_of_unmatched_minus(self, i, dual=False, reverse=False):
558
"""
559
EXAMPLES::
560
561
sage: C = CrystalOfLetters(['A',5])
562
sage: T = TensorProductOfCrystals(C,C)
563
sage: T(C(2),C(1)).positions_of_unmatched_minus(1)
564
[]
565
sage: T(C(1),C(2)).positions_of_unmatched_minus(1)
566
[0]
567
"""
568
unmatched_plus = []
569
height = 0
570
if reverse == True:
571
self = self.reversed()
572
if dual == False:
573
for j in range(len(self)):
574
minus = self[j].phi(i)
575
plus = self[j].epsilon(i)
576
if height-minus < 0:
577
unmatched_plus.append(j)
578
height = plus
579
else:
580
height = height - minus + plus
581
else:
582
for j in range(len(self)):
583
plus = self[j].epsilon(i)
584
minus = self[j].phi(i)
585
if height-plus < 0:
586
unmatched_plus.append(j)
587
height = minus
588
else:
589
height = height - plus + minus
590
return unmatched_plus
591
592
def positions_of_unmatched_plus(self, i):
593
"""
594
EXAMPLES::
595
596
sage: C = CrystalOfLetters(['A',5])
597
sage: T = TensorProductOfCrystals(C,C)
598
sage: T(C(2),C(1)).positions_of_unmatched_plus(1)
599
[]
600
sage: T(C(1),C(2)).positions_of_unmatched_plus(1)
601
[1]
602
"""
603
l = self.positions_of_unmatched_minus(i, dual=True, reverse=True)
604
l.reverse()
605
return [len(self)-1-l[j] for j in range(len(l))]
606
607
def _latex_(self):
608
"""
609
Returns latex code for self.
610
611
EXAMPLES::
612
613
sage: C = CrystalOfLetters(["A",2])
614
sage: D = CrystalOfTableaux(["A",2], shape=[2])
615
sage: E = TensorProductOfCrystals(C,D)
616
sage: E.module_generators[0]._latex_()
617
'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}'
618
"""
619
return '\otimes'.join(latex(c) for c in self)
620
621
def energy_function(self):
622
r"""
623
Returns the energy function of `self`.
624
625
INPUT:
626
627
- ``self`` -- an element of a tensor product of perfect Kirillov-Reshetkhin crystals of the same level.
628
629
OUTPUT: an integer
630
631
The energy is only defined when ``self`` is an element of a tensor product of affine Kirillov-Reshetikhin crystals.
632
In this implementation, it is assumed that ``self`` is an element of a tensor product of perfect crystals of the
633
same level, see Theorem 7.5 in [SchillingTingley2011]_.
634
635
REFERENCES:
636
637
.. [SchillingTingley2011] A. Schilling, P. Tingley.
638
Demazure crystals, Kirillov-Reshetikhin crystals, and the energy function.
639
preprint arXiv:1104.2359
640
641
EXAMPLES::
642
643
sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)
644
sage: T = TensorProductOfCrystals(K,K,K)
645
sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]
646
sage: for b in hw:
647
... print b, b.energy_function()
648
...
649
[[[1]], [[1]], [[1]]] 0
650
[[[1]], [[2]], [[1]]] 2
651
[[[2]], [[1]], [[1]]] 1
652
[[[3]], [[2]], [[1]]] 3
653
654
sage: K = KirillovReshetikhinCrystal(['C',2,1],1,2)
655
sage: T = TensorProductOfCrystals(K,K)
656
sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]
657
sage: for b in hw: # long time (5s on sage.math, 2011)
658
... print b, b.energy_function()
659
...
660
[[], []] 4
661
[[], [[1, 1]]] 1
662
[[[1, 1]], []] 3
663
[[[1, 1]], [[1, 1]]] 0
664
[[[1, 2]], [[1, 1]]] 1
665
[[[2, 2]], [[1, 1]]] 2
666
[[[-1, -1]], [[1, 1]]] 2
667
[[[1, -1]], [[1, 1]]] 2
668
[[[2, -1]], [[1, 1]]] 2
669
670
sage: K = KirillovReshetikhinCrystal(['C',2,1],1,1)
671
sage: T = TensorProductOfCrystals(K)
672
sage: t = T.module_generators[0]
673
sage: t.energy_function()
674
Traceback (most recent call last):
675
...
676
AssertionError: All crystals in the tensor product need to be perfect of the same level
677
"""
678
C = self.parent().crystals[0]
679
ell = ceil(C.s()/C.cartan_type().c()[C.r()])
680
assert all(ell == K.s()/K.cartan_type().c()[K.r()] for K in self.parent().crystals), \
681
"All crystals in the tensor product need to be perfect of the same level"
682
t = self.parent()(*[K.module_generator() for K in self.parent().crystals])
683
d = t.affine_grading()
684
return d - self.affine_grading()
685
686
def affine_grading(self):
687
r"""
688
Returns the affine grading of `self`.
689
690
INPUT:
691
692
- ``self`` -- an element of a tensor product of Kirillov-Reshetikhin crystals.
693
694
OUTPUT: an integer
695
696
The affine grading is only defined when ``self`` is an element of a tensor product of affine Kirillov-Reshetikhin crystals.
697
It is calculated by finding a path from ``self`` to a ground state path using the helper method
698
:meth:`e_string_to_ground_state` and counting the number of affine Kashiwara operators `e_0` applied on the way.
699
700
EXAMPLES::
701
702
sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)
703
sage: T = TensorProductOfCrystals(K,K)
704
sage: t = T.module_generators[0]
705
sage: t.affine_grading()
706
1
707
708
sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)
709
sage: T = TensorProductOfCrystals(K,K,K)
710
sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]
711
sage: for b in hw:
712
... print b, b.affine_grading()
713
...
714
[[[1]], [[1]], [[1]]] 3
715
[[[1]], [[2]], [[1]]] 1
716
[[[2]], [[1]], [[1]]] 2
717
[[[3]], [[2]], [[1]]] 0
718
719
sage: K = KirillovReshetikhinCrystal(['C',2,1],1,1)
720
sage: T = TensorProductOfCrystals(K,K,K)
721
sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])]
722
sage: for b in hw:
723
... print b, b.affine_grading()
724
...
725
[[[1]], [[1]], [[1]]] 2
726
[[[1]], [[2]], [[1]]] 1
727
[[[1]], [[-1]], [[1]]] 0
728
[[[2]], [[1]], [[1]]] 1
729
[[[-2]], [[2]], [[1]]] 0
730
[[[-1]], [[1]], [[1]]] 1
731
"""
732
return self.e_string_to_ground_state().count(0)
733
734
@cached_method
735
def e_string_to_ground_state(self):
736
r"""
737
Returns a string of integers in the index set `(i_1,\ldots,i_k)` such that `e_{i_k} \cdots e_{i_1} self` is
738
the ground state.
739
740
INPUT:
741
742
- ``self`` -- an element of a tensor product of Kirillov-Reshetikhin crystals.
743
744
OUTPUT: a tuple of integers `(i_1,\ldots,i_k)`
745
746
This method is only defined when ``self`` is an element of a tensor product of affine Kirillov-Reshetikhin crystals.
747
It calculates a path from ``self`` to a ground state path using Demazure arrows as defined in
748
Lemma 7.3 in [SchillingTingley2011]_.
749
750
EXAMPLES::
751
752
sage: K = KirillovReshetikhinCrystal(['A',2,1],1,1)
753
sage: T = TensorProductOfCrystals(K,K)
754
sage: t = T.module_generators[0]
755
sage: t.e_string_to_ground_state()
756
(0, 2)
757
758
sage: K = KirillovReshetikhinCrystal(['C',2,1],1,1)
759
sage: T = TensorProductOfCrystals(K,K)
760
sage: t = T.module_generators[0]; t
761
[[[1]], [[1]]]
762
sage: t.e_string_to_ground_state()
763
(0,)
764
sage: x=t.e(0)
765
sage: x.e_string_to_ground_state()
766
()
767
sage: y=t.f_string([1,2,1,1,0]); y
768
[[[2]], [[1]]]
769
sage: y.e_string_to_ground_state()
770
()
771
"""
772
assert self.parent().crystals[0].__module__ == 'sage.combinat.crystals.kirillov_reshetikhin', \
773
"All crystals in the tensor product need to be Kirillov-Reshetikhin crystals"
774
I = self.index_set()
775
I.remove(0)
776
ell = max(ceil(K.s()/K.cartan_type().c()[K.r()]) for K in self.parent().crystals)
777
for i in I:
778
if self.epsilon(i) > 0:
779
return (i,) + (self.e(i)).e_string_to_ground_state()
780
if self.epsilon(0) > ell:
781
return (0,) + (self.e(0)).e_string_to_ground_state()
782
return ()
783
784
CrystalOfWords.Element = TensorProductOfCrystalsElement
785
786
787
class CrystalOfTableaux(CrystalOfWords):
788
r"""
789
A class for crystals of tableaux with integer valued shapes
790
791
INPUT:
792
793
- ``cartan_type`` -- a Cartan type
794
- ``shape`` -- a partition of length at most ``cartan_type.rank()``
795
- ``shapes`` -- a list of such partitions
796
797
This constructs a classical crystal with the given Cartan type and
798
highest weight(s) corresponding to the given shape(s).
799
800
If the type is `D_r`, the shape is permitted to have a negative
801
value in the `r`-th position. Thus if shape=`[s_1,\dots,s_r]` then
802
`s_r` may be negative but in any case `s_1 \ge \cdots \ge s_{r-1}
803
\ge |s_r|`. This crystal is related to that of shape
804
`[s_1,\dots,|s_r|]` by the outer automorphism of `SO(2r)`.
805
806
If the type is `D_r` or `B_r`, the shape is permitted to be of
807
length `r` with all parts of half integer value. This corresponds
808
to having one spin column at the beginning of the tableau. If
809
several shapes are provided, they currently should all or none
810
have this property.
811
812
Crystals of tableaux are constructed using an embedding into
813
tensor products following Kashiwara and Nakashima [Kashiwara,
814
Masaki; Nakashima, Toshiki, *Crystal graphs for representations of
815
the q-analogue of classical Lie algebras*, J. Algebra 165 (1994),
816
no. 2, 295-345.]. Sage's tensor product rule for crystals differs
817
from that of Kashiwara and Nakashima by reversing the order of the
818
tensor factors. Sage produces the same crystals of tableaux as
819
Kashiwara and Nakashima. With Sage's convention, the tensor
820
product of crystals is the same as the monoid operation on
821
tableaux and hence the plactic monoid.
822
823
.. seealso:: :mod:`sage.combinat.crystals.crystals` for general help on
824
crystals, and in particular plotting and LaTeX output.
825
826
EXAMPLES:
827
828
We create the crystal of tableaux for type `A_2`, with
829
highest weight given by the partition [2,1,1]::
830
831
sage: T = CrystalOfTableaux(['A',3], shape = [2,1,1])
832
833
Here is the list of its elements::
834
835
sage: T.list()
836
[[[1, 1], [2], [3]], [[1, 2], [2], [3]], [[1, 3], [2], [3]],
837
[[1, 4], [2], [3]], [[1, 4], [2], [4]], [[1, 4], [3], [4]],
838
[[2, 4], [3], [4]], [[1, 1], [2], [4]], [[1, 2], [2], [4]],
839
[[1, 3], [2], [4]], [[1, 3], [3], [4]], [[2, 3], [3], [4]],
840
[[1, 1], [3], [4]], [[1, 2], [3], [4]], [[2, 2], [3], [4]]]
841
842
Internally, a tableau of a given Cartan type is represented as a
843
tensor product of letters of the same type. The order in which the
844
tensor factors appear is by reading the columns of the tableaux
845
left to right, top to bottom (in French notation). As an example::
846
847
sage: T = CrystalOfTableaux(['A',2], shape = [3,2])
848
sage: T.module_generators[0]
849
[[1, 1, 1], [2, 2]]
850
sage: T.module_generators[0]._list
851
[2, 1, 2, 1, 1]
852
853
To create a tableau, one can use::
854
855
sage: Tab = CrystalOfTableaux(['A',3], shape = [2,2])
856
sage: Tab(rows=[[1,2],[3,4]])
857
[[1, 2], [3, 4]]
858
sage: Tab(columns=[[3,1],[4,2]])
859
[[1, 2], [3, 4]]
860
861
FIXME:
862
863
- do we want to specify the columns increasingly or
864
decreasingly. That is, should this be Tab(columns = [[1,3],[2,4]])
865
- make this fully consistent with :func:`~sage.combinat.tableau.Tableau`!
866
867
We illustrate the use of a shape with a negative last entry in
868
type `D`::
869
870
sage: T = CrystalOfTableaux(['D',4],shape=[1,1,1,-1])
871
sage: T.cardinality()
872
35
873
sage: TestSuite(T).run()
874
875
We illustrate the construction of crystals of spin tableaux when
876
the partitions have half integer values in type `B` and `D`::
877
878
sage: T = CrystalOfTableaux(['B',3],shape=[3/2,1/2,1/2]); T
879
The crystal of tableaux of type ['B', 3] and shape(s) [[3/2, 1/2, 1/2]]
880
sage: T.cardinality()
881
48
882
sage: T.module_generators
883
[[+++, [[1]]]]
884
sage: TestSuite(T).run()
885
886
sage: T = CrystalOfTableaux(['D',3],shape=[3/2,1/2,-1/2]); T
887
The crystal of tableaux of type ['D', 3] and shape(s) [[3/2, 1/2, -1/2]]
888
sage: T.cardinality()
889
20
890
sage: T.module_generators
891
[[++-, [[1]]]]
892
sage: TestSuite(T).run()
893
894
TESTS:
895
896
Base cases::
897
898
sage: T = CrystalOfTableaux(['A',2], shape = [])
899
sage: T.list()
900
[[]]
901
sage: TestSuite(T).run()
902
903
sage: T = CrystalOfTableaux(['C',2], shape = [1])
904
sage: T.list()
905
[[[1]], [[2]], [[-2]], [[-1]]]
906
sage: TestSuite(T).run()
907
908
sage: T = CrystalOfTableaux(['A',2], shapes = [[],[1],[2]])
909
sage: T.list()
910
[[], [[1]], [[2]], [[3]], [[1, 1]], [[1, 2]], [[2, 2]], [[1, 3]], [[2, 3]], [[3, 3]]]
911
sage: T.module_generators
912
([], [[1]], [[1, 1]])
913
914
sage: T = CrystalOfTableaux(['B',2], shape=[3])
915
sage: T(rows=[[1,1,0]])
916
[[1, 1, 0]]
917
918
Input tests::
919
920
sage: T = CrystalOfTableaux(['A',3], shape = [2,2])
921
sage: C = T.letters
922
sage: Tab(rows = [[1,2],[3,4]])._list == [C(3),C(1),C(4),C(2)]
923
True
924
sage: Tab(columns = [[3,1],[4,2]])._list == [C(3),C(1),C(4),C(2)]
925
True
926
927
For compatibility with :func:`TensorProductOfCrystals` we need to
928
accept as input the internal list or sequence of elements::
929
930
sage: Tab(list = [3,1,4,2])._list == [C(3),C(1),C(4),C(2)]
931
True
932
sage: Tab(3,1,4,2)._list == [C(3),C(1),C(4),C(2)]
933
True
934
935
The next example checks whether a given tableau is in fact a valid
936
type `C` tableau or not::
937
938
sage: T = CrystalOfTableaux(['C',3], shape = [2,2,2])
939
sage: Tab = T(rows=[[1,3],[2,-3],[3,-1]])
940
sage: Tab in T.list()
941
True
942
sage: Tab = T(rows=[[2,3],[3,-3],[-3,-2]])
943
sage: Tab in T.list()
944
False
945
"""
946
947
@staticmethod
948
def __classcall_private__(cls, cartan_type, shapes = None, shape = None):
949
"""
950
Normalizes the input arguments to ensure unique representation,
951
and to delegate the construction of spin tableaux.
952
953
EXAMPLES::
954
955
sage: T1 = CrystalOfTableaux(CartanType(['A',3]), shape = [2,2])
956
sage: T2 = CrystalOfTableaux(['A',3], shape = (2,2))
957
sage: T3 = CrystalOfTableaux(['A',3], shapes = ([2,2],))
958
sage: T2 is T1, T3 is T1
959
(True, True)
960
"""
961
cartan_type = CartanType(cartan_type)
962
n = cartan_type.rank()
963
# standardize shape/shapes input into a tuple of tuples
964
assert operator.xor(shape is not None, shapes is not None)
965
if shape is not None:
966
shapes = (shape,)
967
spin_shapes = tuple( tuple(shape) for shape in shapes )
968
try:
969
shapes = tuple( tuple(trunc(i) for i in shape) for shape in spin_shapes )
970
except:
971
raise ValueError("shapes should all be partitions or half-integer partitions")
972
if spin_shapes == shapes:
973
return super(CrystalOfTableaux, cls).__classcall__(cls, cartan_type, shapes)
974
975
# Handle the construction of a crystals of spin tableaux
976
# Caveat: this currently only supports all shapes being half
977
# integer partitions of length the rank for type B and D. In
978
# particular, for type D, the spins all have to be plus or all
979
# minus spins
980
assert all(len(sh) == n for sh in shapes), \
981
"the length of all half-integer partition shapes should be the rank"
982
assert all(2*i % 2 == 1 for shape in spin_shapes for i in shape), \
983
"shapes should be either all partitions or all half-integer partitions"
984
if cartan_type.type() == 'D':
985
if all( i >= 0 for shape in spin_shapes for i in shape):
986
S = CrystalOfSpinsPlus(cartan_type)
987
elif all(shape[-1]<0 for shape in spin_shapes):
988
S = CrystalOfSpinsMinus(cartan_type)
989
else:
990
raise ValueError, "In type D spins should all be positive or negative"
991
else:
992
assert all( i >= 0 for shape in spin_shapes for i in shape), \
993
"shapes should all be partitions"
994
S = CrystalOfSpins(cartan_type)
995
B = CrystalOfTableaux(cartan_type, shapes = shapes)
996
T = TensorProductOfCrystals(S,B, generators=[[S.module_generators[0],x] for x in B.module_generators])
997
T.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in spin_shapes)))
998
return T
999
1000
1001
def __init__(self, cartan_type, shapes):
1002
"""
1003
Construct the crystal of all tableaux of the given shapes
1004
1005
INPUT:
1006
- ``cartan_type`` - (data coercible into) a cartan type
1007
- ``shapes`` - a list (or iterable) of shapes
1008
- ``shape` ` - a shape
1009
1010
shapes themselves are lists (or iterable) of integers
1011
1012
EXAMPLES::
1013
1014
sage: T = CrystalOfTableaux(['A',3], shape = [2,2])
1015
sage: TestSuite(T).run()
1016
"""
1017
# super(CrystalOfTableaux, self).__init__(category = FiniteEnumeratedSets())
1018
Parent.__init__(self, category = ClassicalCrystals())
1019
self.letters = CrystalOfLetters(cartan_type)
1020
self.module_generators = tuple(self.module_generator(la) for la in shapes)
1021
self.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in shapes)))
1022
1023
def cartan_type(self):
1024
"""
1025
Returns the Cartan type of the associated crystal
1026
1027
EXAMPLES::
1028
1029
sage: T = CrystalOfTableaux(['A',3], shape = [2,2])
1030
sage: T.cartan_type()
1031
['A', 3]
1032
"""
1033
return self.letters.cartan_type()
1034
1035
def module_generator(self, shape):
1036
"""
1037
This yields the module generator (or highest weight element) of a classical
1038
crystal of given shape. The module generator is the unique tableau with equal
1039
shape and content.
1040
1041
EXAMPLE::
1042
1043
sage: T = CrystalOfTableaux(['D',3], shape = [1,1])
1044
sage: T.module_generator([1,1])
1045
[[1], [2]]
1046
"""
1047
type = self.cartan_type()
1048
if type[0] == 'D' and len(shape) == type[1] and shape[type[1]-1] < 0:
1049
invert = True
1050
shape = shape[:-1]+(-shape[type[1]-1],)
1051
else:
1052
invert = False
1053
p = Partition(shape).conjugate()
1054
# The column canonical tableau, read by columns
1055
module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(len(p))])
1056
if invert:
1057
for i in range(type[1]):
1058
if module_generator[i] == type[1]:
1059
module_generator[i] = -type[1]
1060
return self(list=[self.letters(x) for x in module_generator])
1061
1062
def _element_constructor_(self, *args, **options):
1063
"""
1064
Returns a CrystalOfTableauxElement
1065
1066
EXAMPLES::
1067
1068
sage: T = CrystalOfTableaux(['A',3], shape = [2,2])
1069
sage: T(rows=[[1,2],[3,4]])
1070
[[1, 2], [3, 4]]
1071
sage: T(columns=[[3,1],[4,2]])
1072
[[1, 2], [3, 4]]
1073
"""
1074
return self.element_class(self, *args, **options)
1075
1076
1077
1078
class CrystalOfTableauxElement(TensorProductOfCrystalsElement):
1079
def __init__(self, parent, *args, **options):
1080
"""
1081
There are several ways to input tableaux, by rows,
1082
by columns, as the list of column elements, or as a sequence of numbers
1083
in column reading.
1084
1085
EXAMPLES::
1086
1087
sage: T = CrystalOfTableaux(['A',3], shape = [2,2])
1088
sage: t = T(rows=[[1,2],[3,4]])
1089
sage: t
1090
[[1, 2], [3, 4]]
1091
sage: TestSuite(t).run()
1092
1093
sage: t = T(columns=[[3,1],[4,2]])
1094
sage: t
1095
[[1, 2], [3, 4]]
1096
sage: TestSuite(t).run()
1097
1098
sage: t = T(list=[3,1,4,2])
1099
sage: t
1100
[[1, 2], [3, 4]]
1101
1102
sage: t = T(3,1,4,2)
1103
sage: t
1104
[[1, 2], [3, 4]]
1105
1106
Currently inputting the empty tableau as an empty sequence is broken due to a bug in
1107
the generic __call__ method (see trac ticket #8648)
1108
1109
EXAMPLES::
1110
1111
sage: T = CrystalOfTableaux(['A',3], shape=[])
1112
sage: t = T()
1113
sage: t._list
1114
[0]
1115
"""
1116
if len(args) == 1:
1117
if isinstance(args[0], Tableau_class):
1118
options['rows'] = args[0]
1119
if options.has_key('list'):
1120
list = options['list']
1121
elif options.has_key('rows'):
1122
rows=options['rows']
1123
# list=Tableau(rows).to_word_by_column()
1124
rows=Tableau(rows).conjugate()
1125
list=[]
1126
for col in rows:
1127
col.reverse()
1128
list+=col
1129
elif options.has_key('columns'):
1130
columns=options['columns']
1131
list=[]
1132
for col in columns:
1133
list+=col
1134
else:
1135
list = [i for i in args]
1136
if list != [] and type(list[0]) is Integer:
1137
list=[parent.letters(x) for x in list]
1138
TensorProductOfCrystalsElement.__init__(self, parent, list)
1139
1140
def _repr_(self):
1141
"""
1142
EXAMPLES::
1143
1144
sage: T = CrystalOfTableaux(['A',3], shape = [2,2])
1145
sage: t = T(rows=[[1,2],[3,4]])
1146
sage: t._repr_()
1147
'[[1, 2], [3, 4]]'
1148
"""
1149
return repr(self.to_tableau())
1150
1151
def pp(self):
1152
"""
1153
EXAMPLES::
1154
1155
sage: T = CrystalOfTableaux(['A',3], shape = [2,2])
1156
sage: t = T(rows=[[1,2],[3,4]])
1157
sage: t.pp()
1158
1 2
1159
3 4
1160
"""
1161
return self.to_tableau().pp()
1162
1163
def _latex_(self):
1164
r"""
1165
EXAMPLES::
1166
1167
sage: T = CrystalOfTableaux(['A',3], shape = [4,2])
1168
sage: t = T(rows=[[1,1,2,3],[2,3]])
1169
sage: latex(t) # indirect doctest
1170
{\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}}
1171
\raisebox{-.6ex}{$\begin{array}[b]{cccc}
1172
\cline{1-1}\cline{2-2}\cline{3-3}\cline{4-4}
1173
\lr{1}&\lr{1}&\lr{2}&\lr{3}\\
1174
\cline{1-1}\cline{2-2}\cline{3-3}\cline{4-4}
1175
\lr{2}&\lr{3}\\
1176
\cline{1-1}\cline{2-2}
1177
\end{array}$}
1178
}
1179
"""
1180
return latex(self.to_tableau())
1181
1182
@cached_method
1183
def to_tableau(self):
1184
"""
1185
Returns the Tableau object corresponding to self.
1186
1187
EXAMPLES::
1188
1189
sage: T = CrystalOfTableaux(['A',3], shape = [2,2])
1190
sage: t = T(rows=[[1,2],[3,4]]).to_tableau(); t
1191
[[1, 2], [3, 4]]
1192
sage: type(t)
1193
<class 'sage.combinat.tableau.Tableau_class'>
1194
sage: type(t[0][0])
1195
<type 'sage.rings.integer.Integer'>
1196
sage: T = CrystalOfTableaux(['D',3], shape = [1,1])
1197
sage: t=T(rows=[[-3],[3]]).to_tableau(); t
1198
[[-3], [3]]
1199
sage: t=T(rows=[[3],[-3]]).to_tableau(); t
1200
[[3], [-3]]
1201
sage: T = CrystalOfTableaux(['B',2], shape = [1,1])
1202
sage: t = T(rows=[[0],[0]]).to_tableau(); t
1203
[[0], [0]]
1204
"""
1205
if self._list == []:
1206
return Tableau([])
1207
tab = [ [self[0].value] ]
1208
for i in range(1,len(self)):
1209
if self[i-1] < self[i] or (self[i-1].value != 0 and self[i-1] == self[i]):
1210
tab.append([self[i].value])
1211
else:
1212
l = len(tab)-1
1213
tab[l].append(self[i].value)
1214
for x in tab:
1215
x.reverse()
1216
return Tableau(tab).conjugate()
1217
1218
def promotion(self):
1219
"""
1220
Promotion for type A crystals of tableaux of rectangular shape
1221
1222
Returns the result of applying promotion on this tableau.
1223
1224
This method only makes sense in type A with rectangular shapes.
1225
1226
EXAMPLES::
1227
1228
sage: C = CrystalOfTableaux(["A",3], shape = [3,3,3])
1229
sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]]))
1230
sage: t
1231
[[1, 1, 1], [2, 2, 3], [3, 4, 4]]
1232
sage: t.promotion()
1233
[[1, 1, 2], [2, 2, 3], [3, 4, 4]]
1234
sage: t.promotion().parent()
1235
The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]]
1236
"""
1237
crystal = self.parent()
1238
cartan_type = crystal.cartan_type()
1239
assert cartan_type.type() == 'A'
1240
return crystal(self.to_tableau().promotion(cartan_type.rank()))
1241
1242
def promotion_inverse(self):
1243
"""
1244
Inverse promotion for type A crystals of tableaux of rectangular shape
1245
1246
Returns the result of applying inverse promotion on this tableau.
1247
1248
This method only makes sense in type A with rectangular shapes.
1249
1250
EXAMPLES::
1251
1252
sage: C = CrystalOfTableaux(["A",3], shape = [3,3,3])
1253
sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]]))
1254
sage: t
1255
[[1, 1, 1], [2, 2, 3], [3, 4, 4]]
1256
sage: t.promotion_inverse()
1257
[[1, 1, 2], [2, 3, 3], [4, 4, 4]]
1258
sage: t.promotion_inverse().parent()
1259
The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]]
1260
"""
1261
crystal = self.parent()
1262
cartan_type = crystal.cartan_type()
1263
assert cartan_type.type() == 'A'
1264
return crystal(self.to_tableau().promotion_inverse(cartan_type.rank()))
1265
1266
CrystalOfTableaux.Element = CrystalOfTableauxElement
1267
1268