Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/combinat/crystals/littelmann_path.py
8817 views
1
r"""
2
Littelmann paths
3
4
AUTHORS:
5
6
- Mark Shimozono, Anne Schilling (2012): Initial version
7
- Anne Schilling (2013): Implemented :class:`CrystalOfProjectedLevelZeroLSPaths`
8
"""
9
#****************************************************************************
10
# Copyright (C) 2012 Mark Shimozono
11
# Anne Schilling
12
#
13
# Distributed under the terms of the GNU General Public License (GPL)
14
#
15
# This code is distributed in the hope that it will be useful,
16
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
# General Public License for more details.
19
#
20
# The full text of the GPL is available at:
21
#
22
# http://www.gnu.org/licenses/
23
#****************************************************************************
24
25
from sage.misc.cachefunc import cached_in_parent_method
26
from sage.structure.unique_representation import UniqueRepresentation
27
from sage.structure.element_wrapper import ElementWrapper
28
from sage.structure.parent import Parent
29
from sage.categories.crystals import Crystals
30
from sage.categories.highest_weight_crystals import HighestWeightCrystals
31
from sage.categories.regular_crystals import RegularCrystals
32
from sage.categories.finite_crystals import FiniteCrystals
33
from sage.categories.classical_crystals import ClassicalCrystals
34
from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets
35
from sage.combinat.root_system.cartan_type import CartanType
36
from sage.combinat.root_system.weyl_group import WeylGroup
37
from sage.rings.integer import Integer
38
from sage.rings.rational_field import QQ
39
from sage.combinat.root_system.root_system import RootSystem
40
from sage.functions.other import floor
41
from sage.misc.latex import latex
42
43
44
class CrystalOfLSPaths(UniqueRepresentation, Parent):
45
r"""
46
Crystal graph of LS paths generated from the straight-line path to a given weight.
47
48
INPUT:
49
50
- ``cartan_type`` -- (optional) the Cartan type of a finite or affine root system
51
- ``starting_weight`` -- a weight; if ``cartan_type`` is given, then the weight should
52
be given as a list of coefficients of the fundamental weights, otherwise it should
53
be given in the ``weight_space`` basis; for affine highest weight crystals, one needs
54
to use the extended weight space.
55
56
The crystal class of piecewise linear paths in the weight space,
57
generated from a straight-line path from the origin to a given
58
element of the weight lattice.
59
60
OUTPUT:
61
62
- a tuple of weights defining the directions of the piecewise linear segments
63
64
EXAMPLES::
65
66
sage: R = RootSystem(['A',2,1])
67
sage: La = R.weight_space(extended = True).basis()
68
sage: B = CrystalOfLSPaths(La[2]-La[0]); B
69
The crystal of LS paths of type ['A', 2, 1] and weight -Lambda[0] + Lambda[2]
70
71
sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1]); C
72
The crystal of LS paths of type ['A', 2, 1] and weight -Lambda[0] + Lambda[2]
73
sage: B == C
74
True
75
sage: c = C.module_generators[0]; c
76
(-Lambda[0] + Lambda[2],)
77
sage: [c.f(i) for i in C.index_set()]
78
[None, None, (Lambda[1] - Lambda[2],)]
79
80
sage: R = C.R; R
81
Root system of type ['A', 2, 1]
82
sage: Lambda = R.weight_space().basis(); Lambda
83
Finite family {0: Lambda[0], 1: Lambda[1], 2: Lambda[2]}
84
sage: b=C(tuple([-Lambda[0]+Lambda[2]]))
85
sage: b==c
86
True
87
sage: b.f(2)
88
(Lambda[1] - Lambda[2],)
89
90
For classical highest weight crystals we can also compare the results with the tableaux implementation::
91
92
sage: C = CrystalOfLSPaths(['A',2],[1,1])
93
sage: list(set(C.list()))
94
[(-Lambda[1] - Lambda[2],), (-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2]), (-Lambda[1] + 2*Lambda[2],),
95
(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2]), (Lambda[1] - 2*Lambda[2],), (-2*Lambda[1] + Lambda[2],),
96
(2*Lambda[1] - Lambda[2],), (Lambda[1] + Lambda[2],)]
97
sage: C.cardinality()
98
8
99
sage: B = CrystalOfTableaux(['A',2],shape=[2,1])
100
sage: B.cardinality()
101
8
102
sage: B.digraph().is_isomorphic(C.digraph())
103
True
104
105
Make sure you use the weight space and not the weight lattice for your weights::
106
107
sage: R = RootSystem(['A',2,1])
108
sage: La = R.weight_lattice(extended = True).basis()
109
sage: B = CrystalOfLSPaths(La[2]); B
110
Traceback (most recent call last):
111
...
112
ValueError: Please use the weight space, rather than weight lattice for your weights
113
114
REFERENCES:
115
116
.. [Littelmann95] P. Littelmann, Paths and root operators in representation
117
theory. Ann. of Math. (2) 142 (1995), no. 3, 499-525.
118
"""
119
120
@staticmethod
121
def __classcall_private__(cls, starting_weight, cartan_type = None):
122
"""
123
Classcall to mend the input.
124
125
Internally, the CrystalOfLSPaths code works with a ``starting_weight`` that
126
is in the ``weight_space`` associated to the crystal. The user can, however,
127
also input a ``cartan_type`` and the coefficients of the fundamental weights
128
as ``starting_weight``. This code transforms the input into the right
129
format (also necessary for UniqueRepresentation).
130
131
TESTS::
132
133
sage: CrystalOfLSPaths(['A',2,1],[-1,0,1])
134
The crystal of LS paths of type ['A', 2, 1] and weight -Lambda[0] + Lambda[2]
135
136
sage: R = RootSystem(['B',2,1])
137
sage: La = R.weight_space().basis()
138
sage: C = CrystalOfLSPaths(['B',2,1],[0,0,1])
139
sage: B = CrystalOfLSPaths(La[2])
140
sage: B is C
141
True
142
"""
143
if cartan_type is not None:
144
cartan_type, starting_weight = CartanType(starting_weight), cartan_type
145
if cartan_type.is_affine():
146
extended = True
147
else:
148
extended = False
149
150
R = RootSystem(cartan_type)
151
P = R.weight_space(extended = extended)
152
Lambda = P.basis()
153
offset = R.index_set()[Integer(0)]
154
starting_weight = P.sum(starting_weight[j-offset]*Lambda[j] for j in R.index_set())
155
156
return super(CrystalOfLSPaths, cls).__classcall__(cls, starting_weight)
157
158
def __init__(self, starting_weight):
159
"""
160
EXAMPLES::
161
162
sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1]); C
163
The crystal of LS paths of type ['A', 2, 1] and weight -Lambda[0] + Lambda[2]
164
sage: C.R
165
Root system of type ['A', 2, 1]
166
sage: C.weight
167
-Lambda[0] + Lambda[2]
168
sage: C.weight.parent()
169
Extended weight space over the Rational Field of the Root system of type ['A', 2, 1]
170
sage: C.module_generators
171
[(-Lambda[0] + Lambda[2],)]
172
173
TESTS::
174
175
sage: C = CrystalOfLSPaths(['A',2,1], [-1,0,1])
176
sage: TestSuite(C).run()
177
sage: C = CrystalOfLSPaths(['E',6], [1,0,0,0,0,0])
178
sage: TestSuite(C).run()
179
"""
180
cartan_type = starting_weight.parent().cartan_type()
181
self.R = RootSystem(cartan_type)
182
self.weight = starting_weight
183
if not self.weight.parent().base_ring().has_coerce_map_from(QQ):
184
raise ValueError, "Please use the weight space, rather than weight lattice for your weights"
185
self._cartan_type = cartan_type
186
self._name = "The crystal of LS paths of type %s and weight %s"%(cartan_type,starting_weight)
187
if cartan_type.is_affine():
188
if all(i>=0 for i in starting_weight.coefficients()):
189
Parent.__init__( self, category = (RegularCrystals(),
190
HighestWeightCrystals(),
191
InfiniteEnumeratedSets()) )
192
elif starting_weight.parent().is_extended():
193
Parent.__init__(self, category = (RegularCrystals(), InfiniteEnumeratedSets()))
194
else:
195
Parent.__init__(self, category = (RegularCrystals(), FiniteCrystals()))
196
else:
197
Parent.__init__(self, category = ClassicalCrystals())
198
199
if starting_weight == starting_weight.parent().zero():
200
initial_element = self(tuple([]))
201
else:
202
initial_element = self(tuple([starting_weight]))
203
self.module_generators = [initial_element]
204
205
def _repr_(self):
206
"""
207
EXAMPLES::
208
209
sage: CrystalOfLSPaths(['B',3],[1,1,0]) # indirect doctest
210
The crystal of LS paths of type ['B', 3] and weight Lambda[1] + Lambda[2]
211
"""
212
return self._name
213
214
class Element(ElementWrapper):
215
"""
216
TESTS::
217
218
sage: C = CrystalOfLSPaths(['E',6],[1,0,0,0,0,0])
219
sage: c=C.an_element()
220
sage: TestSuite(c).run()
221
"""
222
223
def endpoint(self):
224
r"""
225
Computes the endpoint of the path.
226
227
EXAMPLES::
228
229
sage: C = CrystalOfLSPaths(['A',2],[1,1])
230
sage: b = C.module_generators[0]
231
sage: b.endpoint()
232
Lambda[1] + Lambda[2]
233
sage: b.f_string([1,2,2,1])
234
(-Lambda[1] - Lambda[2],)
235
sage: b.f_string([1,2,2,1]).endpoint()
236
-Lambda[1] - Lambda[2]
237
sage: b.f_string([1,2])
238
(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])
239
sage: b.f_string([1,2]).endpoint()
240
0
241
sage: b = C([])
242
sage: b.endpoint()
243
0
244
"""
245
if len(self.value) > 0:
246
return sum(self.value)
247
return self.parent().weight.parent().zero()
248
#return self.parent().R.weight_space(extended = self.parent().extended).zero()
249
250
def compress(self):
251
r"""
252
Merges consecutive positively parallel steps present in the path.
253
254
EXAMPLES::
255
256
sage: C = CrystalOfLSPaths(['A',2],[1,1])
257
sage: Lambda = C.R.weight_space().fundamental_weights(); Lambda
258
Finite family {1: Lambda[1], 2: Lambda[2]}
259
sage: c = C(tuple([1/2*Lambda[1]+1/2*Lambda[2], 1/2*Lambda[1]+1/2*Lambda[2]]))
260
sage: c.compress()
261
(Lambda[1] + Lambda[2],)
262
"""
263
def positively_parallel_weights(v, w):
264
"""
265
Checks whether the vectors ``v`` and ``w`` are positive scalar multiples of each other.
266
"""
267
supp = v.support()
268
if len(supp) > 0:
269
i = supp[0]
270
if v[i]*w[i] > 0 and v[i]*w == w[i]*v:
271
return True
272
return False
273
274
if len(self.value) == 0:
275
return self
276
q = []
277
curr = self.value[0]
278
for i in range(1,len(self.value)):
279
if positively_parallel_weights(curr,self.value[i]):
280
curr = curr + self.value[i]
281
else:
282
q.append(curr)
283
curr = self.value[i]
284
q.append(curr)
285
return self.parent()(tuple(q))
286
287
def split_step(self, which_step, r):
288
r"""
289
Splits indicated step into two parallel steps of relative lengths `r` and `1-r`.
290
291
INPUT:
292
293
- ``which_step`` -- a position in the tuple ``self``
294
- ``r`` -- a rational number between 0 and 1
295
296
EXAMPLES::
297
298
sage: C = CrystalOfLSPaths(['A',2],[1,1])
299
sage: b = C.module_generators[0]
300
sage: b.split_step(0,1/3)
301
(1/3*Lambda[1] + 1/3*Lambda[2], 2/3*Lambda[1] + 2/3*Lambda[2])
302
"""
303
assert which_step in range(len(self.value))
304
v = self.value[which_step]
305
return self.parent()(self.value[:which_step]+tuple([r*v,(1-r)*v])+self.value[which_step+1:])
306
307
def reflect_step(self, which_step, i):
308
r"""
309
Apply the `i`-th simple reflection to the indicated step in ``self``.
310
311
EXAMPLES::
312
313
sage: C = CrystalOfLSPaths(['A',2],[1,1])
314
sage: b = C.module_generators[0]
315
sage: b.reflect_step(0,1)
316
(-Lambda[1] + 2*Lambda[2],)
317
sage: b.reflect_step(0,2)
318
(2*Lambda[1] - Lambda[2],)
319
"""
320
assert i in self.index_set()
321
assert which_step in range(len(self.value))
322
return self.parent()(self.value[:which_step]+tuple([self.value[which_step].simple_reflection(i)])+self.value[which_step+1:])
323
324
def _string_data(self, i):
325
r"""
326
Computes the `i`-string data of ``self``.
327
328
TESTS::
329
330
sage: C = CrystalOfLSPaths(['A',2],[1,1])
331
sage: b = C.module_generators[0]
332
sage: b._string_data(1)
333
()
334
sage: b._string_data(2)
335
()
336
sage: b.f(1)._string_data(1)
337
((0, -1, -1),)
338
sage: b.f(1).f(2)._string_data(2)
339
((0, -1, -1),)
340
"""
341
if len(self.value) == 0:
342
return ()
343
# get the i-th simple coroot
344
alv = self.value[0].parent().alphacheck()[i]
345
# Compute the i-heights of the steps of vs
346
steps = [v.scalar(alv) for v in self.value]
347
# Get the wet step data
348
minima_pos = []
349
ps = 0
350
psmin = 0
351
for ix in range(len(steps)):
352
ps = ps + steps[ix]
353
if ps < psmin:
354
minima_pos.append((ix,ps,steps[ix]))
355
psmin = ps
356
return tuple(minima_pos)
357
358
def epsilon(self, i):
359
r"""
360
Returns the distance to the beginning of the `i`-string.
361
362
This method overrides the generic implementation in the category of crystals
363
since this computation is more efficient.
364
365
EXAMPLES::
366
367
sage: C = CrystalOfLSPaths(['A',2],[1,1])
368
sage: [c.epsilon(1) for c in C]
369
[0, 1, 0, 0, 1, 0, 1, 2]
370
sage: [c.epsilon(2) for c in C]
371
[0, 0, 1, 2, 1, 1, 0, 0]
372
"""
373
return self.e(i,length_only=True)
374
375
def phi(self, i):
376
r"""
377
Returns the distance to the end of the `i`-string.
378
379
This method overrides the generic implementation in the category of crystals
380
since this computation is more efficient.
381
382
EXAMPLES::
383
384
sage: C = CrystalOfLSPaths(['A',2],[1,1])
385
sage: [c.phi(1) for c in C]
386
[1, 0, 0, 1, 0, 2, 1, 0]
387
sage: [c.phi(2) for c in C]
388
[1, 2, 1, 0, 0, 0, 0, 1]
389
"""
390
return self.f(i,length_only=True)
391
392
def e(self, i, power=1, to_string_end=False, length_only=False):
393
r"""
394
Returns the `i`-th crystal raising operator on ``self``.
395
396
INPUT:
397
398
- ``i`` -- element of the index set of the underlying root system
399
- ``power`` -- positive integer; specifies the power of the raising operator
400
to be applied (default: 1)
401
- ``to_string_end`` -- boolean; if set to True, returns the dominant end of the
402
`i`-string of ``self``. (default: False)
403
- ``length_only`` -- boolean; if set to True, returns the distance to the dominant
404
end of the `i`-string of ``self``.
405
406
EXAMPLES::
407
408
sage: C = CrystalOfLSPaths(['A',2],[1,1])
409
sage: c = C[2]; c
410
(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])
411
sage: c.e(1)
412
sage: c.e(2)
413
(-Lambda[1] + 2*Lambda[2],)
414
sage: c.e(2,to_string_end=True)
415
(-Lambda[1] + 2*Lambda[2],)
416
sage: c.e(1,to_string_end=True)
417
(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])
418
sage: c.e(1,length_only=True)
419
0
420
"""
421
assert i in self.index_set()
422
data = self._string_data(i)
423
# compute the minimum i-height M on the path
424
if len(data) == 0:
425
M = 0
426
else:
427
M = data[-1][1]
428
max_raisings = floor(-M)
429
if length_only:
430
return max_raisings
431
# set the power of e_i to apply
432
if to_string_end:
433
p = max_raisings
434
else:
435
p = power
436
if p > max_raisings:
437
return None
438
439
# copy the vector sequence into a working vector sequence ws
440
#!!! ws only needs to be the actual vector sequence, not some
441
#!!! fancy crystal graph element
442
ws = self.parent()(self.value)
443
444
ix = len(data)-1
445
while ix >= 0 and data[ix][1] < M + p:
446
# get the index of the current step to be processed
447
j = data[ix][0]
448
# find the i-height where the current step might need to be split
449
if ix == 0:
450
prev_ht = M + p
451
else:
452
prev_ht = min(data[ix-1][1],M+p)
453
# if necessary split the step. Then reflect the wet part.
454
if data[ix][1] - data[ix][2] > prev_ht:
455
ws = ws.split_step(j,1-(prev_ht-data[ix][1])/(-data[ix][2]))
456
ws = ws.reflect_step(j+1,i)
457
else:
458
ws = ws.reflect_step(j,i)
459
ix = ix - 1
460
#!!! at this point we should return the fancy crystal graph element
461
#!!! corresponding to the humble vector sequence ws
462
return self.parent()(ws.compress())
463
464
def dualize(self):
465
r"""
466
Returns dualized path.
467
468
EXAMPLES::
469
470
sage: C = CrystalOfLSPaths(['A',2],[1,1])
471
sage: for c in C:
472
... print c, c.dualize()
473
...
474
(Lambda[1] + Lambda[2],) (-Lambda[1] - Lambda[2],)
475
(-Lambda[1] + 2*Lambda[2],) (Lambda[1] - 2*Lambda[2],)
476
(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2]) (1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])
477
(Lambda[1] - 2*Lambda[2],) (-Lambda[1] + 2*Lambda[2],)
478
(-Lambda[1] - Lambda[2],) (Lambda[1] + Lambda[2],)
479
(2*Lambda[1] - Lambda[2],) (-2*Lambda[1] + Lambda[2],)
480
(-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2]) (-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2])
481
(-2*Lambda[1] + Lambda[2],) (2*Lambda[1] - Lambda[2],)
482
"""
483
if len(self.value) == 0:
484
return self
485
dual_path = [-v for v in self.value]
486
dual_path.reverse()
487
return self.parent()(tuple(dual_path))
488
489
def f(self, i, power=1, to_string_end=False, length_only=False):
490
r"""
491
Returns the `i`-th crystal lowering operator on ``self``.
492
493
INPUT:
494
495
- ``i`` -- element of the index set of the underlying root system
496
- ``power`` -- positive integer; specifies the power of the lowering operator
497
to be applied (default: 1)
498
- ``to_string_end`` -- boolean; if set to True, returns the anti-dominant end of the
499
`i`-string of ``self``. (default: False)
500
- ``length_only`` -- boolean; if set to True, returns the distance to the anti-dominant
501
end of the `i`-string of ``self``.
502
503
EXAMPLES::
504
505
sage: C = CrystalOfLSPaths(['A',2],[1,1])
506
sage: c = C.module_generators[0]
507
sage: c.f(1)
508
(-Lambda[1] + 2*Lambda[2],)
509
sage: c.f(1,power=2)
510
sage: c.f(2)
511
(2*Lambda[1] - Lambda[2],)
512
sage: c.f(2,to_string_end=True)
513
(2*Lambda[1] - Lambda[2],)
514
sage: c.f(2,length_only=True)
515
1
516
517
sage: C = CrystalOfLSPaths(['A',2,1],[-1,-1,2])
518
sage: c = C.module_generators[0]
519
sage: c.f(2,power=2)
520
(Lambda[0] + Lambda[1] - 2*Lambda[2],)
521
"""
522
dual_path = self.dualize()
523
dual_path = dual_path.e(i, power, to_string_end, length_only)
524
if length_only:
525
return dual_path
526
if dual_path == None:
527
return None
528
return dual_path.dualize()
529
530
def s(self, i):
531
r"""
532
Computes the reflection of ``self`` along the `i`-string.
533
534
This method is more efficient than the generic implementation since it uses
535
powers of `e` and `f` in the Littelmann model directly.
536
537
EXAMPLES::
538
539
sage: C = CrystalOfLSPaths(['A',2],[1,1])
540
sage: c = C.module_generators[0]
541
sage: c.s(1)
542
(-Lambda[1] + 2*Lambda[2],)
543
sage: c.s(2)
544
(2*Lambda[1] - Lambda[2],)
545
546
sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1])
547
sage: c = C.module_generators[0]; c
548
(-Lambda[0] + Lambda[2],)
549
sage: c.s(2)
550
(Lambda[1] - Lambda[2],)
551
sage: c.s(1)
552
(-Lambda[0] + Lambda[2],)
553
sage: c.f(2).s(1)
554
(Lambda[0] - Lambda[1],)
555
"""
556
ph = self.phi(i)
557
ep = self.epsilon(i)
558
diff = ph - ep
559
if diff >= 0:
560
return self.f(i, power=diff)
561
else:
562
return self.e(i, power=-diff)
563
564
def _latex_(self):
565
r"""
566
Latex method for ``self``.
567
568
EXAMPLES::
569
570
sage: C = CrystalOfLSPaths(['A',2],[1,1])
571
sage: c = C.module_generators[0]
572
sage: c._latex_()
573
[\Lambda_{1} + \Lambda_{2}]
574
"""
575
return [latex(p) for p in self.value]
576
577
578
class CrystalOfProjectedLevelZeroLSPaths(CrystalOfLSPaths):
579
r"""
580
Crystal of projected level zero LS paths.
581
582
INPUT:
583
584
- ``weight`` -- a dominant weight of the weight space of an affine Kac-Moody root system
585
586
When ``weight`` is just a single fundamental weight `\Lambda_r`, this crystal is
587
isomorphic to a Kirillov-Reshetikhin (KR) crystal, see also
588
:meth:`sage.combinat.crystals.kirillov_reshetikhin.KirillovReshetikhinCrystalFromLSPaths`.
589
For general weights, it is isomorphic to a tensor product of single-column KR crystals.
590
591
EXAMPLES::
592
593
sage: R = RootSystem(['C',3,1])
594
sage: La = R.weight_space().basis()
595
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])
596
sage: LS.cardinality()
597
84
598
sage: GLS = LS.digraph()
599
600
sage: K1 = KirillovReshetikhinCrystal(['C',3,1],1,1)
601
sage: K3 = KirillovReshetikhinCrystal(['C',3,1],3,1)
602
sage: T = TensorProductOfCrystals(K3,K1)
603
sage: T.cardinality()
604
84
605
sage: GT = T.digraph() # long time
606
sage: GLS.is_isomorphic(GT, edge_labels = True) # long time
607
True
608
609
TESTS::
610
611
sage: ct = CartanType(['A',4,2]).dual()
612
sage: P = RootSystem(ct).weight_space()
613
sage: La = P.fundamental_weights()
614
sage: C = CrystalOfProjectedLevelZeroLSPaths(La[1])
615
sage: C.list()
616
[(-Lambda[0] + Lambda[1],),
617
(Lambda[0] - Lambda[1],),
618
(Lambda[1] - 2*Lambda[2],),
619
(-Lambda[1] + 2*Lambda[2],),
620
(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])]
621
"""
622
623
@staticmethod
624
def __classcall_private__(cls, weight):
625
"""
626
Classcall to mend the input.
627
628
Internally, the CrystalOfProjectedLevelZeroLSPaths uses a level zero weight,
629
which is passed on to CrystalOfLSPaths. ``weight`` is first coerced to
630
a level zero weight.
631
632
TESTS::
633
634
sage: R = RootSystem(['C',3,1])
635
sage: La = R.weight_space().basis()
636
sage: C = CrystalOfProjectedLevelZeroLSPaths(La[1] + La[2])
637
sage: C2 = CrystalOfProjectedLevelZeroLSPaths(La[1] + La[2])
638
sage: C is C2
639
True
640
"""
641
La = weight.parent().basis()
642
weight = weight - (weight.level())*La[0]/(La[0].level())
643
return super(CrystalOfLSPaths, cls).__classcall__(cls, weight)
644
645
def one_dimensional_configuration_sum(self, q = None, group_components = True):
646
r"""
647
Compute the one-dimensional configuration sum.
648
649
INPUT:
650
651
- ``q`` -- (default: ``None``) a variable or ``None``; if ``None``,
652
a variable ``q`` is set in the code
653
- ``group_components`` -- (default: ``True``) boolean; if ``True``,
654
then the terms are grouped by classical component
655
656
The one-dimensional configuration sum is the sum of the weights of all elements in the crystal
657
weighted by the energy function. For untwisted types it uses the parabolic quantum Bruhat graph, see [LNSSS2013]_.
658
In the dual-of-untwisted case, the parabolic quantum Bruhat graph is defined by
659
exchanging the roles of roots and coroots (which is still conjectural at this point).
660
661
EXAMPLES::
662
663
sage: R = RootSystem(['A',2,1])
664
sage: La = R.weight_space().basis()
665
sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1])
666
sage: LS.one_dimensional_configuration_sum()
667
B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]]
668
+ (q+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]]
669
sage: R.<t> = ZZ[]
670
sage: LS.one_dimensional_configuration_sum(t, False)
671
B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + (t+1)*B[Lambda[1] - Lambda[2]]
672
+ B[2*Lambda[1]] + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]]
673
674
TESTS::
675
676
sage: R = RootSystem(['B',3,1])
677
sage: La = R.weight_space().basis()
678
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[2])
679
sage: LS.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum(group_components=False) # long time
680
True
681
sage: K1 = KirillovReshetikhinCrystal(['B',3,1],1,1)
682
sage: K2 = KirillovReshetikhinCrystal(['B',3,1],2,1)
683
sage: T = TensorProductOfCrystals(K2,K1)
684
sage: T.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum()
685
True
686
687
sage: R = RootSystem(['D',4,2])
688
sage: La = R.weight_space().basis()
689
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[2])
690
sage: K1 = KirillovReshetikhinCrystal(['D',4,2],1,1)
691
sage: K2 = KirillovReshetikhinCrystal(['D',4,2],2,1)
692
sage: T = TensorProductOfCrystals(K2,K1)
693
sage: T.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum()
694
True
695
696
sage: R = RootSystem(['A',5,2])
697
sage: La = R.weight_space().basis()
698
sage: LS = CrystalOfProjectedLevelZeroLSPaths(3*La[1])
699
sage: K1 = KirillovReshetikhinCrystal(['A',5,2],1,1)
700
sage: T = TensorProductOfCrystals(K1,K1,K1)
701
sage: T.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum()
702
True
703
"""
704
if q is None:
705
from sage.rings.all import QQ
706
q = QQ['q'].gens()[0]
707
P0 = self.weight_lattice_realization().classical()
708
B = P0.algebra(q.parent())
709
if group_components:
710
G = self.digraph(index_set = self.cartan_type().classical().index_set())
711
C = G.connected_components()
712
return sum(q**(c[0].energy_function())*B.sum(B(P0(b.weight())) for b in c) for c in C)
713
return B.sum(q**(b.energy_function())*B(P0(b.weight())) for b in self)
714
715
def is_perfect(self, level=1):
716
r"""
717
Checks whether the crystal ``self`` is perfect (of level ``level``).
718
719
INPUT:
720
721
- ``level`` -- (default: 1) positive integer
722
723
A crystal `\mathcal{B}` is perfect of level `\ell` if:
724
725
#. `\mathcal{B}` is isomorphic to the crystal graph of a finite-dimensional `U_q^{'}(\mathfrak{g})`-module.
726
#. `\mathcal{B}\otimes \mathcal{B}` is connected.
727
#. There exists a `\lambda\in X`, such that `\mathrm{wt}(\mathcal{B}) \subset \lambda
728
+ \sum_{i\in I} \mathbb{Z}_{\le 0} \alpha_i` and there is a unique element in `\mathcal{B}` of classical
729
weight `\lambda`.
730
#. `\forall b \in \mathcal{B}, \mathrm{level}(\varepsilon (b)) \geq \ell`.
731
#. `\forall \Lambda` dominant weights of level `\ell`, there exist unique elements
732
`b_{\Lambda}, b^{\Lambda} \in \mathcal{B}`,
733
such that `\varepsilon ( b_{\Lambda}) = \Lambda = \varphi( b^{\Lambda})`.
734
735
Points (1)-(3) are known to hold. This method checks points (4) and (5).
736
737
EXAMPLES::
738
739
sage: C = CartanType(['C',2,1])
740
sage: R = RootSystem(C)
741
sage: La = R.weight_space().basis()
742
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1])
743
sage: LS.is_perfect()
744
False
745
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[2])
746
sage: LS.is_perfect()
747
True
748
749
sage: C = CartanType(['E',6,1])
750
sage: R = RootSystem(C)
751
sage: La = R.weight_space().basis()
752
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1])
753
sage: LS.is_perfect()
754
True
755
sage: LS.is_perfect(2)
756
False
757
758
sage: C = CartanType(['D',4,1])
759
sage: R = RootSystem(C)
760
sage: La = R.weight_space().basis()
761
sage: all(CrystalOfProjectedLevelZeroLSPaths(La[i]).is_perfect() for i in [1,2,3,4])
762
True
763
764
sage: C = CartanType(['A',6,2])
765
sage: R = RootSystem(C)
766
sage: La = R.weight_space().basis()
767
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[2])
768
sage: LS.is_perfect()
769
True
770
sage: LS.is_perfect(2)
771
False
772
"""
773
MPhi = []
774
for b in self:
775
p = b.Phi().level()
776
assert p == b.Epsilon().level()
777
if p < level:
778
return False
779
if p == level:
780
MPhi += [b]
781
weights = []
782
I = self.index_set()
783
rank = len(I)
784
La = self.weight_lattice_realization().basis()
785
from sage.combinat.integer_vector import IntegerVectors
786
for n in range(1,level+1):
787
for c in IntegerVectors(n, rank):
788
w = sum(c[i]*La[i] for i in I)
789
if w.level() == level:
790
weights.append(w)
791
return sorted([b.Phi() for b in MPhi]) == sorted(weights)
792
793
class Element(CrystalOfLSPaths.Element):
794
"""
795
Element of a crystal of projected level zero LS paths.
796
"""
797
798
@cached_in_parent_method
799
def scalar_factors(self):
800
r"""
801
Obtain the scalar factors for ``self``.
802
803
Each LS path (or ``self``) can be written as a piecewise linear map
804
805
.. MATH::
806
807
\pi(t) = \sum_{u'=1}^{u-1} (\sigma_{u'} - \sigma_{u'-1}) \nu_{u'} + (t-\sigma_{u-1}) \nu_{u}
808
809
for `0<\sigma_1<\sigma_2<\cdots<\sigma_s=1` and `\sigma_{u-1} \le t \le \sigma_{u}` and `1 \le u \le s`.
810
This method returns the tuple of `(\sigma_1,\ldots,\sigma_s)`.
811
812
EXAMPLES::
813
814
sage: R = RootSystem(['C',3,1])
815
sage: La = R.weight_space().basis()
816
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])
817
sage: b = LS.module_generators[0]
818
sage: b.scalar_factors()
819
[1]
820
sage: c = b.f(1).f(3).f(2)
821
sage: c.scalar_factors()
822
[1/3, 1]
823
"""
824
weight = self.parent().weight
825
l = []
826
s = 0
827
for c in self.value:
828
supp = c.support()
829
if len(supp) > 0:
830
for w in weight.orbit():
831
i = supp[0]
832
# Check whether the vectors c and w are positive scalar multiples of each other
833
if i in w.support() and c[i]*w[i] > 0 and c[i]*w == w[i]*c:
834
s += c[i] / w[i]
835
l += [s]
836
break
837
return l
838
839
@cached_in_parent_method
840
def weyl_group_representation(self):
841
r"""
842
Transforms the weights in the LS path ``self`` to elements in the Weyl group.
843
844
Each LS path can be written as the piecewise linear map:
845
846
.. MATH::
847
848
\pi(t) = \sum_{u'=1}^{u-1} (\sigma_{u'} - \sigma_{u'-1}) \nu_{u'} + (t-\sigma_{u-1}) \nu_{u}
849
850
for `0<\sigma_1<\sigma_2<\cdots<\sigma_s=1` and `\sigma_{u-1} \le t \le \sigma_{u}` and `1 \le u \le s`.
851
Each weight `\nu_u` is also associated to a Weyl group element. This method returns the list
852
of Weyl group elements associated to the `\nu_u` for `1\le u\le s`.
853
854
EXAMPLES::
855
856
sage: R = RootSystem(['C',3,1])
857
sage: La = R.weight_space().basis()
858
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])
859
sage: b = LS.module_generators[0]
860
sage: c = b.f(1).f(3).f(2)
861
sage: c.weyl_group_representation()
862
[s2*s3*s1, s3*s1]
863
"""
864
cartan = self.parent().weight.parent().cartan_type().classical()
865
I = cartan.index_set()
866
W = WeylGroup(cartan,prefix='s')
867
return [W.from_reduced_word(x.to_dominant_chamber(index_set=I, reduced_word=True)[1]) for x in self.value]
868
869
@cached_in_parent_method
870
def energy_function(self):
871
r"""
872
Return the energy function of ``self``.
873
874
The energy function `D(\pi)` of the level zero LS path `\pi \in \mathbb{B}_\mathrm{cl}(\lambda)`
875
requires a series of definitions; for simplicity the root system is assumed to be untwisted affine.
876
877
The LS path `\pi` is a piecewise linear map from the unit interval `[0,1]` to the weight lattice.
878
It is specified by "times" `0=\sigma_0<\sigma_1<\dotsm<\sigma_s=1` and "direction vectors"
879
`x_u \lambda` where `x_u \in W/W_J` for `1\le u\le s`, and `W_J` is the
880
stabilizer of `\lambda` in the finite Weyl group `W`. Precisely,
881
882
.. MATH::
883
884
\pi(t)=\sum_{u'=1}^{u-1} (\sigma_{u'}-\sigma_{u'-1})x_{u'}\lambda+(t-\sigma_{u-1})x_{u}\lambda
885
886
for `1\le u\le s` and `\sigma_{u-1} \le t \le \sigma_{u}`.
887
888
For any `x,y\in W/W_J` let
889
890
.. MATH::
891
892
d: x= w_{0} \stackrel{\beta_{1}}{\leftarrow}
893
w_{1} \stackrel{\beta_{2}}{\leftarrow} \cdots
894
\stackrel{\beta_{n}}{\leftarrow} w_{n}=y
895
896
be a shortest directed path in the parabolic quantum Bruhat graph. Define
897
898
.. MATH::
899
900
\mathrm{wt}(d):=\sum_{\substack{1\le k\le n \\ \ell(w_{k-1})<\ell(w_k)}}
901
\beta_{k}^{\vee}
902
903
It can be shown that `\mathrm{wt}(d)` depends only on `x,y`;
904
call its value `\mathrm{wt}(x,y)`. The energy function `D(\pi)` is defined by
905
906
.. MATH::
907
908
D(\pi)=-\sum_{u=1}^{s-1} (1-\sigma_{u}) \langle \lambda,\mathrm{wt}(x_u,x_{u+1}) \rangle
909
910
For more information, see [LNSSS2013]_.
911
912
REFERENCES:
913
914
.. [LNSSS2013] C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono,
915
A uniform model for Kirillov-Reshetikhin crystals. Extended abstract.
916
DMTCS proc, to appear ( {{{:arXiv:`1211.6019`}}} )
917
918
.. NOTE::
919
920
In the dual-of-untwisted case the parabolic quantum Bruhat graph that is used is obtained by
921
exchanging the roles of roots and coroots. Moreover, in the computation of the
922
pairing the short roots must be doubled (or tripled for type `G`). This factor
923
is determined by the translation factor of the corresponding root.
924
Type `BC` is viewed as untwisted type, whereas the dual of `BC` is viewed as twisted.
925
Except for the untwisted cases, these formulas are currently still conjectural.
926
927
EXAMPLES::
928
929
sage: R = RootSystem(['C',3,1])
930
sage: La = R.weight_space().basis()
931
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])
932
sage: b = LS.module_generators[0]
933
sage: c = b.f(1).f(3).f(2)
934
sage: c.energy_function()
935
0
936
sage: c=b.e(0)
937
sage: c.energy_function()
938
1
939
940
sage: R = RootSystem(['A',2,1])
941
sage: La = R.weight_space().basis()
942
sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1])
943
sage: b = LS.module_generators[0]
944
sage: c = b.e(0)
945
sage: c.energy_function()
946
1
947
sage: [c.energy_function() for c in sorted(LS.list())]
948
[0, 1, 0, 0, 0, 1, 0, 1, 0]
949
950
The next test checks that the energy function is constant on classically connected components::
951
952
sage: R = RootSystem(['A',2,1])
953
sage: La = R.weight_space().basis()
954
sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2])
955
sage: G = LS.digraph(index_set=[1,2])
956
sage: C = G.connected_components()
957
sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C]
958
[True, True, True, True]
959
960
sage: R = RootSystem(['D',4,2])
961
sage: La = R.weight_space().basis()
962
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[2])
963
sage: J = R.cartan_type().classical().index_set()
964
sage: hw = [x for x in LS if x.is_highest_weight(J)]
965
sage: [(x.weight(), x.energy_function()) for x in hw]
966
[(-2*Lambda[0] + Lambda[2], 0), (-2*Lambda[0] + Lambda[1], 1), (0, 2)]
967
sage: G = LS.digraph(index_set=J)
968
sage: C = G.connected_components()
969
sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C]
970
[True, True, True]
971
972
sage: R = RootSystem(CartanType(['G',2,1]).dual())
973
sage: La = R.weight_space().basis()
974
sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[2])
975
sage: G = LS.digraph(index_set=[1,2])
976
sage: C = G.connected_components()
977
sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] # long time
978
[True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]
979
980
sage: ct = CartanType(['BC',2,2]).dual()
981
sage: R = RootSystem(ct)
982
sage: La = R.weight_space().basis()
983
sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2])
984
sage: G = LS.digraph(index_set=R.cartan_type().classical().index_set())
985
sage: C = G.connected_components()
986
sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] # long time
987
[True, True, True, True, True, True, True, True, True, True, True]
988
989
sage: R = RootSystem(['BC',2,2])
990
sage: La = R.weight_space().basis()
991
sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2])
992
sage: G = LS.digraph(index_set=R.cartan_type().classical().index_set())
993
sage: C = G.connected_components()
994
sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] # long time
995
[True, True, True, True, True, True, True, True, True, True, True, True, True, True, True,
996
True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]
997
"""
998
weight = self.parent().weight
999
P = weight.parent()
1000
c_weight = P.classical()(weight)
1001
ct = P.cartan_type()
1002
cartan = ct.classical()
1003
Qv = RootSystem(cartan).coroot_lattice()
1004
W = WeylGroup(cartan,prefix='s')
1005
J = tuple(weight.weyl_stabilizer())
1006
L = self.weyl_group_representation()
1007
if ct.is_untwisted_affine() or ct.type() == 'BC':
1008
untwisted = True
1009
G = W.quantum_bruhat_graph(J)
1010
else:
1011
untwisted = False
1012
cartan_dual = cartan.dual()
1013
Wd = WeylGroup(cartan_dual, prefix='s')
1014
G = Wd.quantum_bruhat_graph(J)
1015
Qd = RootSystem(cartan_dual).root_lattice()
1016
dualize = lambda x: Qv.from_vector(x.to_vector())
1017
L = [Wd.from_reduced_word(x.reduced_word()) for x in L]
1018
def stretch_short_root(a):
1019
# stretches roots by translation factor
1020
if ct.dual().type() == 'BC':
1021
return ct.c()[a.to_simple_root()]*a
1022
return ct.dual().c()[a.to_simple_root()]*a
1023
#if a.is_short_root():
1024
# if cartan_dual.type() == 'G':
1025
# return 3*a
1026
# else:
1027
# return 2*a
1028
#return a
1029
paths = [G.shortest_path(L[i+1],L[i]) for i in range(len(L)-1)]
1030
paths_labels = [[G.edge_label(p[i],p[i+1]) for i in range(len(p)-1) if p[i].length()+1 != p[i+1].length()] for p in paths]
1031
scalars = self.scalar_factors()
1032
if untwisted:
1033
s = sum((1-scalars[i])*c_weight.scalar( Qv.sum(root.associated_coroot()
1034
for root in paths_labels[i]) ) for i in range(len(paths_labels)))
1035
if ct.type() == 'BC':
1036
return 2*s
1037
else:
1038
return s
1039
else:
1040
s = sum((1-scalars[i])*c_weight.scalar( dualize (Qd.sum(stretch_short_root(root) for root in paths_labels[i])) ) for i in range(len(paths_labels)))
1041
if ct.dual().type() == 'BC':
1042
return s/2
1043
else:
1044
return s
1045
1046