Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/combinat/crystals/alcove_path.py
8817 views
1
r"""
2
Alcove paths
3
4
AUTHORS:
5
6
- Brant Jones (2008): initial version
7
- Arthur Lubovsky (2013-03-07): rewritten to implement affine type
8
9
Special thanks to: Nicolas Borie, Anne Schilling, Travis Scrimshaw, and
10
Nicolas Thiery.
11
"""
12
#*****************************************************************************
13
# Copyright (C) 2008 Brant Jones <brant at math.ucdavis.edu>
14
# Copyright (C) 2013 Arthur Lubovsky <alubovsky at albany.edu>
15
#
16
# Distributed under the terms of the GNU General Public License (GPL)
17
#
18
# This code is distributed in the hope that it will be useful,
19
# but WITHOUT ANY WARRANTY; without even the implied warranty of
20
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
# General Public License for more details.
22
#
23
# The full text of the GPL is available at:
24
#
25
# http://www.gnu.org/licenses/
26
#****************************************************************************
27
28
from sage.structure.parent import Parent
29
from sage.structure.element import Element
30
from sage.structure.element_wrapper import ElementWrapper
31
from sage.structure.unique_representation import UniqueRepresentation
32
from sage.categories.crystals import Crystals
33
from sage.categories.finite_crystals import FiniteCrystals
34
from sage.graphs.all import DiGraph
35
from sage.combinat.root_system.cartan_type import CartanType
36
from sage.combinat.root_system.root_system import RootSystem
37
from sage.all import vector
38
from sage.rings.integer import Integer
39
from sage.rings.all import ZZ
40
from sage.combinat.root_system.weyl_group import WeylGroup
41
from sage.misc.misc_c import prod
42
from sage.categories.sets_cat import Sets
43
from sage.combinat.crystals.littelmann_path import CrystalOfLSPaths
44
from sage.misc.cachefunc import cached_method, cached_in_parent_method
45
from sage.categories.highest_weight_crystals import HighestWeightCrystals
46
from copy import copy
47
from sage.misc.latex import latex
48
49
# necessary for tests
50
from sage.combinat.partition import Partitions
51
from sage.combinat.crystals.all import CrystalOfTableaux
52
53
54
class CrystalOfAlcovePaths(UniqueRepresentation, Parent):
55
r"""
56
Crystal of alcove paths generated from a "straight-line" path to the
57
negative of a given dominant weight.
58
59
INPUT:
60
61
- ``cartan_type`` -- Cartan type of a finite or affine untwisted root
62
system.
63
64
- ``weight`` -- dominant weight as a list of (integral) coefficients of the
65
fundamental weights.
66
67
- ``highest_weight_crystal`` -- (Default: ``True``) If ``True``
68
returns the highest weight crystal. If ``False`` returns an
69
object which is close to being isomorphic to the tensor product
70
of Kirillov-Reshetikhin crystals of column shape in the
71
following sense: We get all the vertices, but only some of the
72
edges. We'll call the included edges pseudo-Demazure. They are
73
all non-zero edges and the 0-edges not at the end of a 0-string
74
of edges, i.e. not those with `f_{0}(b) = b'` with
75
`\phi_0(b) =1`. (Whereas Demazure 0-edges are those that
76
are not at the beginning of a zero string) In this case the
77
weight `[c_1, c_2, \ldots, c_k]` represents
78
`\sum_{i=1}^k c_i \omega_i`.
79
80
.. NOTE::
81
82
If ``highest_weight_crystal`` = ``False``, since we do not
83
get the full crystal, ``TestSuite`` will fail on the
84
Stembridge axioms.
85
86
.. SEEALSO::
87
88
- :class:`Crystals`
89
90
EXAMPLES:
91
92
The following example appears in Figure 2 of [LP2008]_::
93
94
sage: C = CrystalOfAlcovePaths(['G',2],[0,1])
95
sage: G = C.digraph()
96
sage: GG = DiGraph({
97
... () : {(0) : 2 },
98
... (0) : {(0,8) : 1 },
99
... (0,1) : {(0,1,7) : 2 },
100
... (0,1,2) : {(0,1,2,9) : 1 },
101
... (0,1,2,3) : {(0,1,2,3,4) : 2 },
102
... (0,1,2,6) : {(0,1,2,3) : 1 },
103
... (0,1,2,9) : {(0,1,2,6) : 1 },
104
... (0,1,7) : {(0,1,2) : 2 },
105
... (0,1,7,9) : {(0,1,2,9) : 2 },
106
... (0,5) : {(0,1) : 1, (0,5,7) : 2 },
107
... (0,5,7) : {(0,5,7,9) : 1 },
108
... (0,5,7,9) : {(0,1,7,9) : 1 },
109
... (0,8) : {(0,5) : 1 },
110
... })
111
sage: G.is_isomorphic(GG)
112
True
113
sage: for (u,v,i) in G.edges(): print (u.integer_sequence() , v.integer_sequence(), i)
114
([], [0], 2)
115
([0], [0, 8], 1)
116
([0, 1], [0, 1, 7], 2)
117
([0, 1, 2], [0, 1, 2, 9], 1)
118
([0, 1, 2, 3], [0, 1, 2, 3, 4], 2)
119
([0, 1, 2, 6], [0, 1, 2, 3], 1)
120
([0, 1, 2, 9], [0, 1, 2, 6], 1)
121
([0, 1, 7], [0, 1, 2], 2)
122
([0, 1, 7, 9], [0, 1, 2, 9], 2)
123
([0, 5], [0, 1], 1)
124
([0, 5], [0, 5, 7], 2)
125
([0, 5, 7], [0, 5, 7, 9], 1)
126
([0, 5, 7, 9], [0, 1, 7, 9], 1)
127
([0, 8], [0, 5], 1)
128
129
Alcove path crystals are a discrete version of Littelmann paths.
130
We verify that the alcove path crystal is isomorphic to the LS
131
path crystal::
132
133
sage: C1 = CrystalOfAlcovePaths(['C',3],[2,1,0])
134
sage: g1 = C1.digraph() #long time
135
sage: C2 = CrystalOfLSPaths(['C',3],[2,1,0])
136
sage: g2 = C2.digraph() #long time
137
sage: g1.is_isomorphic(g2, edge_labels=True) #long time
138
True
139
140
The preferred initialization method is via explicit weights rather than a Cartan type
141
and the coefficients of the fundamental weights::
142
143
sage: R = RootSystem(['C',3])
144
sage: P = R.weight_lattice()
145
sage: La = P.fundamental_weights()
146
sage: C = CrystalOfAlcovePaths(2*La[1]+La[2]); C
147
Highest weight crystal of alcove paths of type ['C', 3] and weight 2*Lambda[1] + Lambda[2]
148
sage: C1==C
149
True
150
151
We now explain the data structure::
152
153
sage: C = CrystalOfAlcovePaths(['A',2],[2,0]) ; C
154
Highest weight crystal of alcove paths of type ['A', 2] and weight 2*Lambda[1]
155
sage: C._R.lambda_chain()
156
[(alpha[1], 0), (alpha[1] + alpha[2], 0), (alpha[1], 1), (alpha[1] + alpha[2], 1)]
157
158
The previous list gives the initial "straight line" path from the
159
fundamental alcove `A_o` to its translation `A_o - \lambda` where
160
`\lambda = 2\omega_1` in this example. The initial path for weight
161
`\lambda` is called the `\lambda`-chain. This path is constructed from
162
the ordered pairs `(\beta, k)`, by crossing the hyperplane orthogonal to
163
`\beta` at height `-k`. We can view a plot of this path as follows::
164
165
sage: x=C( () )
166
sage: x.plot() # not tested - outputs a pdf
167
168
An element of the crystal is given by a subset of the `\lambda`-chain.
169
This subset indicates the hyperplanes where the initial path should be
170
folded. The highest weight element is given by the empty subset. ::
171
172
sage: x
173
()
174
sage: x.f(1).f(2)
175
((alpha[1], 1), (alpha[1] + alpha[2], 1))
176
sage: x.f(1).f(2).integer_sequence()
177
[2, 3]
178
sage: C([2,3])
179
((alpha[1], 1), (alpha[1] + alpha[2], 1))
180
sage: C([2,3]).is_admissible() #check if a valid vertex
181
True
182
sage: C([1,3]).is_admissible() #check if a valid vertex
183
False
184
185
Alcove path crystals now works in affine type (:trac:`14143`)::
186
187
sage: C = CrystalOfAlcovePaths(['A',2,1],[1,0,0]) ; C
188
Highest weight crystal of alcove paths of type ['A', 2, 1] and weight Lambda[0]
189
sage: x=C( () )
190
sage: x.f(0)
191
((alpha[0], 0),)
192
sage: C.R
193
Root system of type ['A', 2, 1]
194
sage: C.weight
195
Lambda[0]
196
197
Test that the tensor products of Kirillov-Reshetikhin crystals
198
minus non-pseudo-Demazure arrows is in bijection with alcove path
199
construction::
200
201
sage: K = KirillovReshetikhinCrystal(['B',3,1],2,1)
202
sage: T = TensorProductOfCrystals(K,K)
203
sage: g = T.digraph() #long time
204
sage: for e in g.edges(): #long time
205
....: if e[0].phi(0) == 1 and e[2] == 0: #long time
206
....: g.delete_edge(e) #long time
207
208
sage: C = CrystalOfAlcovePaths(['B',3,1],[0,2,0], highest_weight_crystal=False)
209
sage: g2 = C.digraph_fast() #long time
210
sage: g.is_isomorphic(g2, edge_labels = True) #long time
211
True
212
213
.. NOTE::
214
215
In type `C_n^{(1)}`, the Kirillov-Reshetikhin crystal is not connected
216
when restricted to pseudo-Demazure arrows, hence the previous example will
217
fail for type `C_n^{(1)}` crystals.
218
219
::
220
221
sage: R = RootSystem(['B',3])
222
sage: P = R.weight_lattice()
223
sage: La = P.fundamental_weights()
224
sage: D = CrystalOfAlcovePaths(2*La[2], highest_weight_crystal=False)
225
sage: C == D
226
True
227
228
.. WARNING:: Weights from finite root systems index non-highest weight crystals.
229
230
REFERENCES:
231
232
.. [LP2008] C. Lenart and A. Postnikov. *A combinatorial model for
233
crystals of Kac-Moody algebras*. Trans. Amer. Math. Soc. 360 (2008),
234
4349-4381.
235
"""
236
237
@staticmethod
238
def __classcall_private__(cls, starting_weight, cartan_type = None,
239
highest_weight_crystal=None):
240
"""
241
Classcall to mend the input.
242
243
Internally, the CrystalOfAlcovePaths code works with a ``starting_weight`` that
244
is in the ``weight_space`` associated to the crystal. The user can, however,
245
also input a ``cartan_type`` and the coefficients of the fundamental weights
246
as ``starting_weight``. This code transforms the input into the right
247
format (also necessary for UniqueRepresentation).
248
249
TESTS::
250
251
sage: C = CrystalOfAlcovePaths(['A',2,1], [1,0,0])
252
sage: C2 = CrystalOfAlcovePaths(CartanType(['A',2,1]), (1,0,0))
253
sage: C is C2
254
True
255
sage: R = RootSystem(['B',2,1])
256
sage: La = R.weight_space().basis()
257
sage: B1 = CrystalOfAlcovePaths(['B',2,1],[0,0,1])
258
sage: B2 = CrystalOfAlcovePaths(La[2])
259
sage: B1 is B2
260
True
261
"""
262
if isinstance(cartan_type, bool): # new style signature, optional arguments leak over
263
highest_weight_crystal = cartan_type
264
265
elif isinstance(cartan_type, list) or isinstance(cartan_type, tuple): #old style signature
266
#switch positional arguments
267
cartan_type, starting_weight = CartanType(starting_weight), cartan_type
268
269
if highest_weight_crystal == False:
270
cartan_type = cartan_type.classical()
271
272
R = RootSystem(cartan_type)
273
P = R.weight_space()
274
Lambda = P.basis()
275
offset = R.index_set()[Integer(0)]
276
starting_weight = P.sum(starting_weight[j-offset]*Lambda[j] for j in R.index_set())
277
278
#set defaults
279
if highest_weight_crystal == None:
280
highest_weight_crystal = True
281
282
if not starting_weight.is_dominant():
283
raise ValueError("{0} is not a dominant weight".format(starting_weight))
284
285
286
return super(CrystalOfAlcovePaths, cls).__classcall__(cls,
287
starting_weight, highest_weight_crystal)
288
289
290
def __init__(self, starting_weight, highest_weight_crystal):
291
r"""
292
Initialize ``self``.
293
294
TESTS::
295
296
sage: C = CrystalOfAlcovePaths(['G',2],[0,1])
297
sage: TestSuite(C).run()
298
299
sage: C = CrystalOfAlcovePaths(['A',2,1],[1,0,0])
300
sage: TestSuite(C).run() #long time
301
302
sage: C = CrystalOfAlcovePaths(['A',2,1],[1,0],False)
303
sage: TestSuite(C).run(skip="_test_stembridge_local_axioms") #long time
304
"""
305
##########################################################################
306
# NOTE:
307
# If cartan_type.is_affine() == True and highest weight crystal == False,
308
# since we only use the positive roots of the *finite* root system
309
# to get the crystal we set self._finite_cartan_type is true
310
#
311
# We want the indexing set to include 0 so use the affine type notation
312
# for the cartan type.
313
##########################################################################
314
cartan_type = starting_weight.parent().cartan_type()
315
316
self.weight = starting_weight
317
self.R = RootSystem(cartan_type)
318
self._highest_weight_crystal = highest_weight_crystal
319
self._cartan_type = cartan_type
320
321
322
if cartan_type.is_finite() and highest_weight_crystal == True:
323
Parent.__init__(self, category = FiniteCrystals() )
324
self._R = RootsWithHeight(starting_weight)
325
self._finite_cartan_type = True
326
elif cartan_type.is_finite() and highest_weight_crystal == False:
327
Parent.__init__(self, category = FiniteCrystals() )
328
self._R = RootsWithHeight(starting_weight)
329
self._finite_cartan_type = True
330
self._cartan_type = cartan_type.affine()
331
else:
332
Parent.__init__(self, category = HighestWeightCrystals())
333
self._R = RootsWithHeight(starting_weight)
334
self._finite_cartan_type = False
335
336
337
self.module_generators = ( self.element_class(self, ()), )
338
339
def _repr_(self):
340
"""
341
Return a string representation of ``self``.
342
343
EXAMPLES::
344
345
sage: C = CrystalOfAlcovePaths(['A',2,1], [1,0,0])
346
sage: C
347
Highest weight crystal of alcove paths of type ['A', 2, 1] and weight Lambda[0]
348
sage: C = CrystalOfAlcovePaths(['A',2,1], [1,0], False)
349
sage: C
350
Crystal of alcove paths of type ['A', 2, 1] and weight Lambda[1]
351
"""
352
if self._highest_weight_crystal:
353
return "Highest weight crystal of alcove paths of type %s and weight %s"%(self._cartan_type, self.weight)
354
return "Crystal of alcove paths of type %s and weight %s"%(self._cartan_type, self.weight)
355
356
def _element_constructor_(self, data):
357
"""
358
Construct an element of ``self`` from ``data``.
359
360
EXAMPLES::
361
362
sage: C = CrystalOfAlcovePaths(['A',2],[3,2])
363
sage: C([8,9])
364
((alpha[1], 2), (alpha[1] + alpha[2], 4))
365
"""
366
if isinstance(data, tuple):
367
return self.element_class(self, data)
368
elif isinstance(data, list):
369
lambda_chain = self._R.lambda_chain()
370
#data starts indexing at 0
371
return self.element_class(self, tuple(sorted([lambda_chain[i] for i in data])))
372
373
def vertices(self):
374
"""
375
Return a list of all the vertices of the crystal.
376
377
The vertices are represented as lists of integers recording the folding
378
positions.
379
380
One can compute all vertices of the crystal by finding all the
381
admissible subsets of the `\lambda`-chain (see method
382
is_admissible, for definition). We use the breath first
383
search algorithm.
384
385
.. WARNING::
386
387
This method is (currently) only useful for the case when
388
``highest_weight_crystal = False``, where you cannot always
389
reach all vertices of the crystal using crystal operators,
390
starting from the highest weight vertex. This method is
391
typically slower than generating the crystal graph using
392
crystal operators.
393
394
EXAMPLES::
395
396
sage: C = CrystalOfAlcovePaths(['C',2],[1,0])
397
sage: C.vertices()
398
[[], [0], [0, 1], [0, 1, 2]]
399
sage: C = CrystalOfAlcovePaths(['C',2,1],[2,1],False)
400
sage: len(C.vertices())
401
80
402
403
The number of elements reachable using the crystal operators from the
404
module generator::
405
406
sage: len(list(C))
407
55
408
"""
409
lambda_chain = self._R.lambda_chain()
410
len_lambda_chain = len(lambda_chain)
411
W = WeylGroup(self._R._cartan_type, prefix='s')
412
s = W.simple_reflections()
413
highest_weight_crystal = self._highest_weight_crystal
414
415
if highest_weight_crystal == True:
416
successors = 'bruhat_upper_covers'
417
else:
418
successors = 'quantum_bruhat_successors'
419
420
# lst contains ordered pairs (w,l) l= list of positions that get
421
# you to the word, it needs to be refreshed
422
423
#initialization
424
lst=[]
425
for i in range(len_lambda_chain):
426
associated_reflection = lambda_chain[i].root.associated_reflection()
427
if len(associated_reflection) == 1:
428
lst.append( (prod([ s[j] for j in associated_reflection ]), [i]) )
429
430
l=copy(lst)
431
432
while True:
433
lst2 = []
434
for x in lst:
435
suc = getattr(x[0], successors)()
436
for j in range(x[1][-1]+1, len_lambda_chain):
437
temp = x[0] * prod(
438
[ s[k] for k in lambda_chain[j].root.associated_reflection() ])
439
if temp in suc:
440
lst2.append((temp,x[1]+[j]))
441
l.append((temp,x[1]+[j]))
442
if lst2 == []:
443
break
444
else :
445
lst = lst2
446
447
return [ [] ] + [i[1] for i in l]
448
449
def digraph_fast(self, depth=None):
450
r"""
451
Return the crystal :class:`graph <DiGraph>` with maximum depth
452
``depth`` deep starting at the module generator. Significant speed up
453
for highest_weight_crystals of affine type.
454
455
EXAMPLES::
456
457
sage: CrystalOfAlcovePaths(['A',2], [1,1]).digraph_fast(depth=3)
458
Digraph on 7 vertices
459
460
TESTS:
461
462
The following example demonstrates the speed improvement.
463
The speedup in non-affine types is small however::
464
465
sage: cartan_type = ['A',2,1] #long time
466
sage: weight = [1,1,0] #long time
467
sage: depth = 5 #long time
468
sage: C = CrystalOfAlcovePaths(cartan_type, weight) #long time
469
sage: %timeit C.digraph_fast(depth) # not tested
470
10 loops, best of 3: 171 ms per loop
471
sage: %timeit C.digraph(subset=C.subcrystal(max_depth=depth, direction='lower')) #not tested
472
1 loops, best of 3: 19.7 s per loop
473
sage: G1 = C.digraph_fast(depth) #long time
474
sage: G2 = C.digraph(subset=C.subcrystal(max_depth=depth, direction='lower')) #long time
475
sage: G1.is_isomorphic(G2, edge_labels=True) #long time
476
True
477
478
"""
479
if not self._highest_weight_crystal:
480
return super(CrystalOfAlcovePaths, self).digraph()
481
482
if self._cartan_type.is_affine() and depth is None:
483
depth = 10
484
I = self.index_set()
485
486
rank = 0
487
G = { self.module_generators[0]: {} }
488
visited = { self.module_generators[0] }
489
490
while depth is None or rank < depth:
491
recently_visited = set()
492
for x in visited:
493
G.setdefault(x, {}) # does nothing if there's a default
494
for i in I:
495
xfi = x.f(i)
496
if xfi != None:
497
G[x][xfi] = i
498
recently_visited.add(xfi)
499
if len(recently_visited) == 0: # No new nodes, nothing more to do
500
break
501
rank += 1
502
visited = recently_visited
503
504
return DiGraph(G)
505
506
class CrystalOfAlcovePathsElement(ElementWrapper):
507
"""
508
Crystal of alcove paths element.
509
510
INPUT:
511
512
- ``data`` -- a list of folding positions in the lambda chain (indexing
513
starts at 0) or a tuple of :class:`RootsWithHeight` giving folding
514
positions in the lambda chain.
515
516
EXAMPLES::
517
518
sage: C = CrystalOfAlcovePaths(['A',2],[3,2])
519
sage: x = C ( () )
520
sage: x.f(1).f(2)
521
((alpha[1], 2), (alpha[1] + alpha[2], 4))
522
sage: x.f(1).f(2).integer_sequence()
523
[8, 9]
524
sage: C([8,9])
525
((alpha[1], 2), (alpha[1] + alpha[2], 4))
526
"""
527
def __iter__(self):
528
r"""
529
Initialize ``self``.
530
531
EXAMPLES::
532
533
sage: C = CrystalOfAlcovePaths(['A',2],[1,0])
534
sage: lst = list(C)
535
sage: for i in lst[2]: i
536
(alpha[1], 0)
537
(alpha[1] + alpha[2], 0)
538
"""
539
return iter(self.value)
540
541
def is_admissible(self):
542
r"""
543
Diagnostic test to check if ``self`` is a valid element of the crystal.
544
545
If ``self.value`` is given by
546
547
.. MATH::
548
549
(\beta_1, i_1), (\beta_2, i_2), \ldots, (\beta_k, i_k),
550
551
for highest weight crystals this checks if the sequence
552
553
.. MATH::
554
555
1 \rightarrow s_{\beta_1} \rightarrow
556
s_{\beta_1}s_{\beta_2} \rightarrow \cdots \rightarrow
557
s_{\beta_1}s_{\beta_2} \ldots s_{\beta_k}
558
559
is a path in the Bruhat graph. If ``highest_weight_crystal=False``,
560
then the method checks if the above sequence is a path in the quantum
561
Bruhat graph.
562
563
EXAMPLES::
564
565
sage: C = CrystalOfAlcovePaths(['A',2],[1,1]); C
566
Highest weight crystal of alcove paths of type ['A', 2] and weight Lambda[1] + Lambda[2]
567
sage: roots = sorted(list(C._R._root_lattice.positive_roots())); roots
568
[alpha[1], alpha[1] + alpha[2], alpha[2]]
569
sage: r1 = C._R(roots[0],0); r1
570
(alpha[1], 0)
571
sage: r2 = C._R(roots[2],0); r2
572
(alpha[2], 0)
573
sage: r3 = C._R(roots[1],1); r3
574
(alpha[1] + alpha[2], 1)
575
sage: x = C( ( r1,r2) )
576
sage: x.is_admissible()
577
True
578
sage: x = C( (r3,) ); x
579
((alpha[1] + alpha[2], 1),)
580
sage: x.is_admissible()
581
False
582
sage: C = CrystalOfAlcovePaths(['C',2,1],[2,1],False)
583
sage: C([7,8]).is_admissible()
584
True
585
sage: C = CrystalOfAlcovePaths(['A',2],[3,2])
586
sage: C([2,3]).is_admissible()
587
True
588
589
.. TODO:: Better doctest
590
"""
591
W = WeylGroup(self.parent()._R._cartan_type, prefix='s')
592
s = W.simple_reflections()
593
highest_weight_crystal = self.parent()._highest_weight_crystal
594
595
if highest_weight_crystal == True:
596
successors = 'bruhat_upper_covers'
597
else:
598
successors = 'quantum_bruhat_successors'
599
600
#start at the identity
601
w = W.unit()
602
for i in self:
603
t = prod( [ s[j] for j in i.root.associated_reflection() ] )
604
successor = w * t
605
if successor not in getattr(w, successors)():
606
return False
607
w = successor
608
return True
609
610
def _latex_(self):
611
r"""
612
Return a `\LaTeX` representation of ``self``.
613
614
EXAMPLES::
615
616
sage: C = CrystalOfAlcovePaths(['A',2],[1,1])
617
sage: C([1,2])._latex_()
618
[(\alpha_{1} + \alpha_{2}, 0), (\alpha_{1}, 0)]
619
"""
620
return [ (latex(i.root),i.height) for i in self.value ]
621
622
@cached_in_parent_method
623
def integer_sequence(self):
624
r"""
625
Return a list of integers corresponding to positions in
626
the `\lambda`-chain where it is folded.
627
628
.. TODO::
629
630
Incorporate this method into the ``_repr_`` for finite Cartan type.
631
632
.. NOTE::
633
634
Only works for finite Cartan types and indexing starts at 0.
635
636
EXAMPLES::
637
638
sage: C = CrystalOfAlcovePaths(['A',2],[3,2])
639
sage: x = C( () )
640
sage: x.f(1).f(2).integer_sequence()
641
[8, 9]
642
"""
643
lambda_chain = self.parent()._R.lambda_chain()
644
return [lambda_chain.index(j) for j in self.value]
645
646
def phi(self, i):
647
r"""
648
Return the distance to the end of the `i`-string.
649
650
This method overrides the generic implementation in the category of
651
crystals since this computation is more efficient.
652
653
EXAMPLES::
654
655
sage: C = CrystalOfAlcovePaths(['A',2],[1,1])
656
sage: [c.phi(1) for c in C]
657
[1, 2, 0, 1, 0, 0, 1, 0]
658
sage: [c.phi(2) for c in C]
659
[1, 0, 2, 0, 1, 1, 0, 0]
660
"""
661
highest_weight_crystal = self.parent()._highest_weight_crystal
662
positions, gi = self._gi(i)
663
664
m=max(gi)
665
666
if not highest_weight_crystal and i == 0:
667
raise NotImplementedError
668
# I think the M below should still work in this case
669
670
M = Integer(m)/2 - Integer(1)/2
671
return M
672
673
def epsilon(self, i):
674
r"""
675
Return the distance to the start of the `i`-string.
676
677
EXAMPLES::
678
679
sage: C = CrystalOfAlcovePaths(['A',2],[1,1])
680
sage: [c.epsilon(1) for c in C]
681
[0, 0, 1, 1, 0, 2, 0, 1]
682
sage: [c.epsilon(2) for c in C]
683
[0, 1, 0, 0, 1, 0, 2, 1]
684
"""
685
#crude but functional
686
j = 0
687
temp = self
688
temp = temp.e(i)
689
while temp != None:
690
j+=1
691
temp = temp.e(i)
692
693
return j
694
695
def weight(self):
696
"""
697
Returns the weight of self.
698
699
EXAMPLES::
700
701
sage: C = CrystalOfAlcovePaths(['A',2],[2,0])
702
sage: for i in C: i.weight()
703
2*Lambda[1]
704
Lambda[2]
705
Lambda[1] - Lambda[2]
706
-2*Lambda[1] + 2*Lambda[2]
707
-Lambda[1]
708
-2*Lambda[2]
709
710
711
"""
712
root_space = self.parent().R.root_space()
713
weight = -self.parent().weight
714
for i in self.value[::-1]:
715
root = root_space(i.root)
716
weight = -i.height*root + weight.reflection(root)
717
718
return -weight
719
720
#def __repr__(self):
721
#return str(self.integer_sequence())
722
723
def plot(self):
724
r"""
725
Return a plot ``self``.
726
727
.. NOTE::
728
729
Currently only implemented for types `A_2`, `B_2`, and `C_2`.
730
731
EXAMPLES::
732
733
sage: C = CrystalOfAlcovePaths(['A',2],[2,0])
734
sage: x = C( () ).f(1).f(2)
735
sage: x.plot() # Not tested - creates a pdf
736
"""
737
ct = self.parent()._R._cartan_type.dual()
738
word = self.parent()._R.word()
739
integer_sequence = self.integer_sequence()
740
foldings = [False for i in word]
741
for i in integer_sequence:
742
foldings[i] = True
743
affine_ambient_space = RootSystem(ct.affine()).ambient_space()
744
return affine_ambient_space.plot() + affine_ambient_space.plot_alcove_walk( word, foldings=foldings, labels=False)
745
746
def __eq__(self, other):
747
r"""
748
Test equality of ``self.value`` and ``other.value``.
749
750
EXAMPLES::
751
752
sage: C=CrystalOfAlcovePaths(['B',2],[1,0])
753
sage: lst=list(C)
754
sage: lst[2] == lst[2]
755
True
756
sage: lst[2] == lst[1]
757
False
758
"""
759
#note: may want to use _eq_ for coercion
760
try:
761
return self.value == other.value
762
except (NameError, AttributeError):
763
return False
764
765
def __lt__(self, other):
766
r"""
767
Test if ``self.value`` is less than ``other.value`` in dictionary order.
768
769
EXAMPLES::
770
771
sage: C = CrystalOfAlcovePaths(['A',2],[2,0])
772
sage: x = C( () )
773
sage: x.__lt__(x.f(1))
774
True
775
sage: a=x.f(1) ; b = x.f(1).f(1).f(2)
776
sage: a.__lt__(b)
777
False
778
"""
779
return self.value < other.value
780
781
def __gt__(self, other):
782
r"""
783
Test if ``self.value`` is greater than ``other.value`` in dictionary
784
order.
785
786
EXAMPLES::
787
788
sage: C = CrystalOfAlcovePaths(['A',2],[2,0])
789
sage: x = C( () )
790
sage: x.__gt__(x.f(1))
791
False
792
sage: a=x.f(1) ; b = x.f(1).f(1).f(2)
793
sage: a.__gt__(b)
794
True
795
"""
796
return self.value > other.value
797
798
def _folding_data(self, i):
799
r"""
800
Compute information needed to build the graph `g_{\alpha_i}`.
801
Results of this method are sent to _gi for further processing.
802
803
INPUT:
804
805
- ``i`` -- element of the index_set of the underlying root_system.
806
807
OUTPUT:
808
809
A dictionary where the keys are of type RootsWithHeight which record
810
positions where `\pm \alpha_i` shows up in the folded `\lambda` chain.
811
The values are `1` if `\alpha_i` is in the corresponding position in
812
the folded `\lambda`-chain, `-1` if `-\alpha_i` is in the corresponding
813
position in the folded `\lambda`-chain.
814
815
.. NOTE::
816
817
*infinity* is a special key that records the "sign at infinity".
818
819
::
820
821
sage: C=CrystalOfAlcovePaths(['A',2],[1,1])
822
sage: x=C( () ).f(1)
823
sage: x._folding_data(2)
824
{(alpha[1] + alpha[2], 1): 1, 'infinity': 1, (alpha[2], 0): 1}
825
"""
826
Parent = self.parent()
827
828
#self.value contains the admissible sequence as a tuple of Element
829
830
finite_cartan_type = Parent._finite_cartan_type # bool
831
J = list(self.value)
832
833
#NOTE: R is a RootsWithHeight object and NOT a RootSystem object
834
R = Parent._R
835
weight = Parent.weight
836
837
signs = {}
838
839
# 0 arrows in the case of finite cartan type
840
# always allow 0 arrows
841
if finite_cartan_type and i == 0:
842
Beta = R._root_lattice.highest_root()
843
elif i in self.index_set():
844
Beta = R._root_lattice.simple_root(i)
845
846
max_height_Beta = weight.scalar(Beta.associated_coroot())
847
848
if len(J) == 0:
849
for k in range( max_height_Beta ) :
850
x = R(Beta, k)
851
signs[x]=self._sign(Beta)
852
signs['infinity'] = self._sign(Beta)
853
854
elif len(J) > 0 :
855
#NOTE: we assume J is sorted by order on Element of RootsWithHeight
856
857
for k in range( max_height_Beta ):
858
x = R(Beta, k)
859
if x <= J[0]:
860
signs[x] = self._sign(Beta)
861
862
for j in range( len(J) ):
863
864
Beta = Beta.reflection(J[j].root)
865
sign_Beta = self._sign(Beta)
866
max_height_Beta = weight.scalar(
867
(sign_Beta * Beta).associated_coroot())
868
869
870
# some optimization so we don't initialize too many objects
871
# range(c1,c2) can be replaced by range(max_height_Beta) but it
872
# checks unnecessary extra things
873
874
c1 = J[j]._cmp_v[0] * max_height_Beta
875
if j == len(J) - 1:
876
c2 = max_height_Beta
877
else:
878
c2 = min (max_height_Beta, J[j+1]._cmp_v[0]*max_height_Beta + 1)
879
880
for k in range(c1,c2):
881
882
x=R( sign_Beta * Beta , k)
883
884
if (
885
( j < len(J) - 1 and J[j] < x <= J[j+1] ) or
886
( j == len(J) - 1 and J[j] < x)
887
):
888
signs[x] = sign_Beta
889
890
signs['infinity'] = sign_Beta # tail sign tells something about last step
891
# in g_alpha
892
893
if finite_cartan_type and i==0:
894
signs = { x : -signs[x] for x in signs.keys() }
895
896
return signs
897
898
def e(self, i):
899
r"""
900
Return the `i`-th crystal raising operator on ``self``.
901
902
INPUT:
903
904
- ``i`` -- element of the index set of the underlying root system.
905
906
EXAMPLES::
907
908
sage: C = CrystalOfAlcovePaths(['A',2],[2,0]); C
909
Highest weight crystal of alcove paths of type ['A', 2] and weight 2*Lambda[1]
910
sage: x = C( () )
911
sage: x.e(1)
912
sage: x.f(1) == x.f(1).f(2).e(2)
913
True
914
"""
915
Parent = self.parent()
916
finite_cartan_type = Parent._finite_cartan_type
917
918
J = list(self.value)
919
positions, gi = self._gi(i)
920
921
m=max(gi)
922
m_index = len(gi)-1-list(reversed(gi)).index(m) # last max in gi
923
924
925
if finite_cartan_type and i==0 :
926
M = Integer(m)/2 + Integer(1)/2
927
else:
928
M = Integer(m)/2 - Integer(1)/2
929
930
931
KR_test = finite_cartan_type and i==0 and m_index < len(gi) - 1
932
KR_test = KR_test and M >= 1
933
934
######################################################################
935
# NOTE:
936
# In the KR_case we want to insure that positions[m_index] is in J
937
# If m_index > 0 then it's always true
938
# If m_index == 0 then M >=1 guarantees this
939
######################################################################
940
941
if ( (not finite_cartan_type or i!=0) and m_index < len(gi)-1 # alpha_i is a simple root
942
) or KR_test:
943
944
945
J.remove(positions[m_index])
946
if m_index+1 < len(positions): # if m_index+1 != 'infinity'
947
# i.e. positions[m_index+1] makes sense
948
J.append(positions[m_index+1])
949
return_value = Parent ( tuple( sorted(J) ) )
950
951
# we attach to each admissible sequence a list
952
# which encodes a path (via root operators) from the () generator
953
# to the admissible sequence
954
# this is useful for investing the crystal
955
956
try:
957
return_value.i_string = self.i_string + [['e',i]]
958
except AttributeError:
959
return_value.i_string = [['e',i]]
960
961
return return_value
962
else:
963
return None
964
965
@cached_method
966
def _gi(self, i):
967
r"""
968
Compute information needed to build the graph `g_{\alpha_i}`.
969
This graph is used to apply the `i`-th crystal operator.
970
971
INPUT:
972
973
- ``i`` - element of the index_set of the underlying root_system.
974
975
OUTPUT:
976
977
A tuple ``(positions, gi)``:
978
979
- ``positions`` -- is a list of RootsWithHeight. These appear sorted in
980
their natural order, and record where `\pm \alpha_i` shows up in
981
the folded `\lambda`-chain.
982
983
- ``gi`` -- is a list of integers recording the height
984
(up to affine transformation) of `\pm \alpha_i`
985
in the folded `\lambda`-chain whose location is recorded by
986
``positions``.
987
988
.. NOTE::
989
990
- ``positions`` has length one less than ``gi`` since it does not
991
contain the position 'infinity'.
992
993
- To get the real `g_{\alpha_i}` one has to divide by 2 and add 1/2
994
or divide by 2 and subtract 1/2 depending on if
995
``self._finite_cartan_type==True and i == 0``
996
or not. This is done in crystal operator methods.
997
998
EXAMPLES::
999
1000
sage: C=CrystalOfAlcovePaths(['A',2],[1,1])
1001
sage: x=C( () ).f(1)
1002
sage: x._gi(2)
1003
([(alpha[2], 0), (alpha[1] + alpha[2], 1)], [1, 3, 5])
1004
"""
1005
signs = self._folding_data(i)
1006
positions = sorted( [ x for x in signs.keys() if x != 'infinity' ] )
1007
1008
if len(positions)==0 :
1009
return ( positions, [ signs['infinity'] ] )
1010
1011
gi = [ signs[ positions[0] ] ]
1012
for j in range(1,len(positions)):
1013
gi.append(
1014
gi[j-1] +
1015
signs[positions[j-1]]*self._eps(positions[j-1]) + signs[positions[j]] )
1016
gi.append( gi[-1] +
1017
signs[positions[-1]]*self._eps(positions[-1]) + signs['infinity'] )
1018
1019
return (positions, gi)
1020
1021
def f(self, i):
1022
r"""
1023
Returns the `i`-th crystal lowering operator on ``self``.
1024
1025
INPUT:
1026
1027
- ``i`` -- element of the index_set of the underlying root_system.
1028
1029
EXAMPLES::
1030
1031
sage: C=CrystalOfAlcovePaths(['B',2],[1,1])
1032
sage: x=C( () )
1033
sage: x.f(1)
1034
((alpha[1], 0),)
1035
sage: x.f(1).f(2)
1036
((alpha[1], 0), (alpha[1] + alpha[2], 2))
1037
1038
"""
1039
Parent = self.parent()
1040
finite_cartan_type = Parent._finite_cartan_type
1041
1042
# get a copy in a form of a list of self.value
1043
J = list(self.value)
1044
positions, gi = self._gi(i)
1045
1046
m=max(gi)
1047
m_index=gi.index(m)
1048
1049
1050
if finite_cartan_type and i==0 :
1051
1052
# python doesn't handle fractions natively
1053
M = Integer(m)/2 + Integer(1)/2
1054
else:
1055
M = Integer(m)/2 - Integer(1)/2
1056
1057
1058
# boolian determining when to move a folding in KR case
1059
KR_test = finite_cartan_type and i==0
1060
KR_test = KR_test and M > 1
1061
1062
# In the KR case, we return a value other than None when
1063
# `\alpha_i` is in position m_index - 1
1064
# (The following relies on a technical condition (C2) )
1065
# note insert reference
1066
#
1067
# if m_index - 1 == 0 then M > 1 and (C2) forces
1068
# `\alhpa_i` in positions[m_index - 1]
1069
#
1070
# otherwise if m_index - 1 > 0 then (C2) is enough
1071
1072
if ( (not finite_cartan_type or i!=0) and M > 0 # alpha_i is a simple root
1073
) or KR_test :# KR case
1074
1075
J.append(positions[m_index-1])
1076
if m_index < len(positions): # if m_index != 'infinity'
1077
# thus positions[m_index] makes sense
1078
J.remove(positions[m_index])
1079
return_value = Parent ( tuple( sorted(J) ) )
1080
1081
# we attach to each admissible sequence a list
1082
# which encodes a path (via root operators) from the generator ()
1083
1084
try:
1085
return_value.i_string = self.i_string + [['f',i]]
1086
except AttributeError:
1087
return_value.i_string = [['f',i]]
1088
1089
return return_value
1090
else:
1091
return None
1092
1093
@staticmethod
1094
def _sign(root):
1095
r"""
1096
Return `1` if root is a positive root, and `-1` if root is a negative
1097
root.
1098
1099
EXAMPLES::
1100
1101
sage: from sage.combinat.crystals.alcove_path import CrystalOfAlcovePathsElement
1102
sage: rl = RootSystem(['A',2]).root_lattice()
1103
sage: x = rl.from_vector(vector([0,1]))
1104
sage: CrystalOfAlcovePathsElement._sign(x)
1105
1
1106
"""
1107
if root.is_positive_root():
1108
return 1
1109
else:
1110
return -1
1111
1112
def _eps(self, root):
1113
r"""
1114
Return `-1` if root is in ``self.value``, otherwise return `1`.
1115
1116
EXAMPLES::
1117
1118
sage: C = CrystalOfAlcovePaths(['C',2],[3,2])
1119
sage: x = C( () ).f(1).f(2); x
1120
((alpha[1], 2), (2*alpha[1] + alpha[2], 4))
1121
sage: x._eps(x.value[0])
1122
-1
1123
sage: R = C._R
1124
sage: y = R ( x.value[0].root, 1 ); y
1125
(alpha[1], 1)
1126
sage: x._eps(y)
1127
1
1128
1129
"""
1130
if root in self.value:
1131
return -1
1132
else:
1133
return 1
1134
1135
CrystalOfAlcovePaths.Element = CrystalOfAlcovePathsElement
1136
#deprecate the old name
1137
from sage.misc.superseded import deprecated_function_alias
1138
ClassicalCrystalOfAlcovePaths = deprecated_function_alias(14143, CrystalOfAlcovePaths)
1139
1140
class RootsWithHeight(UniqueRepresentation, Parent):
1141
r"""
1142
Data structure of the ordered pairs `(\beta,k)`,
1143
where `\beta` is a positive root and `k` is a non-negative integer. A total
1144
order is implemented on this set, and depends on the weight.
1145
1146
INPUT:
1147
1148
- ``cartan_type`` -- Cartan type of a finite or affine untwisted root
1149
system
1150
1151
- ``weight`` -- dominant weight as a list of (integral) coefficients of
1152
the fundamental weights
1153
1154
EXAMPLES::
1155
1156
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1157
sage: R = RootsWithHeight(['A',2],[1,1]); R
1158
Roots with height of cartan type ['A', 2] and dominant weight Lambda[1] + Lambda[2]
1159
1160
sage: r1 = R._root_lattice.from_vector(vector([1,0])); r1
1161
alpha[1]
1162
sage: r2 = R._root_lattice.from_vector(vector([1,1])); r2
1163
alpha[1] + alpha[2]
1164
1165
sage: x = R(r1,0); x
1166
(alpha[1], 0)
1167
sage: y = R(r2,1); y
1168
(alpha[1] + alpha[2], 1)
1169
sage: x < y
1170
True
1171
"""
1172
1173
@staticmethod
1174
def __classcall_private__(cls, starting_weight, cartan_type = None):
1175
"""
1176
Classcall to mend the input.
1177
1178
Internally, the RootsWithHeight code works with a ``starting_weight`` that
1179
is in the ``weight_space`` associated to the crystal. The user can, however,
1180
also input a ``cartan_type`` and the coefficients of the fundamental weights
1181
as ``starting_weight``. This code transforms the input into the right
1182
format (also necessary for UniqueRepresentation).
1183
1184
TESTS::
1185
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1186
sage: R = RootsWithHeight(['A',2],[3,2])
1187
sage: S = RootsWithHeight(CartanType(['A',2]), (3,2))
1188
sage: R is S
1189
True
1190
1191
sage: R = RootSystem(['B',2,1])
1192
sage: La = R.weight_space().basis()
1193
sage: C = RootsWithHeight(['B',2,1],[0,0,1])
1194
sage: B = RootsWithHeight(La[2])
1195
sage: B is C
1196
True
1197
1198
"""
1199
if cartan_type is not None:
1200
cartan_type, starting_weight = CartanType(starting_weight), cartan_type
1201
1202
R = RootSystem(cartan_type)
1203
P = R.weight_space()
1204
Lambda = P.basis()
1205
offset = R.index_set()[Integer(0)]
1206
starting_weight = P.sum(starting_weight[j-offset]*Lambda[j] for j in R.index_set())
1207
1208
return super(RootsWithHeight, cls).__classcall__(cls, starting_weight)
1209
1210
1211
def __init__(self, weight):
1212
r"""
1213
Initialize ``self``.
1214
1215
EXAMPLES::
1216
1217
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1218
sage: R = RootsWithHeight(['A',2],[3,2])
1219
sage: TestSuite(R).run()
1220
"""
1221
Parent.__init__(self, category = Sets() )
1222
1223
cartan_type = weight.parent().cartan_type()
1224
self._cartan_type = cartan_type
1225
self._root_system = RootSystem(cartan_type)
1226
self._root_lattice = self._root_system.root_lattice()
1227
self._weight_lattice = self._root_system.weight_lattice()
1228
self.weight = weight
1229
1230
def _repr_(self):
1231
"""
1232
Return a string representation of ``self``.
1233
1234
EXAMPLES::
1235
1236
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1237
sage: RootsWithHeight(['A',2],[3,2])
1238
Roots with height of cartan type ['A', 2] and dominant weight 3*Lambda[1] + 2*Lambda[2]
1239
"""
1240
return "Roots with height of cartan type %s and dominant weight %s"%(
1241
self._root_system.cartan_type(), self.weight)
1242
1243
def _max_height(self, root):
1244
r"""
1245
If root is `\beta`, return `k = \langle \lambda, \beta^{\vee} \rangle`.
1246
1247
Only ordered pairs of the form `(\beta, l)` for `0 \leq l < k` are
1248
allowed.
1249
1250
EXAMPLES::
1251
1252
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1253
sage: C = RootsWithHeight(['A',3],[3,2,0])
1254
sage: x = C._root_lattice.from_vector(vector([1,1])); x
1255
alpha[1] + alpha[2]
1256
sage: C._max_height(x)
1257
5
1258
"""
1259
return self.weight.scalar(root.associated_coroot())
1260
1261
@cached_method
1262
def word(self):
1263
r"""
1264
Gives the initial alcove path (`\lambda`-chain) in terms of simple
1265
roots. Used for plotting the path.
1266
1267
.. NOTE::
1268
1269
Currently only implemented for finite Cartan types.
1270
1271
EXAMPLES::
1272
1273
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1274
sage: R = RootsWithHeight(['A',2],[3,2])
1275
sage: R.word()
1276
[2, 1, 2, 0, 1, 2, 1, 0, 1, 2]
1277
"""
1278
cartan_type = self._root_system.cartan_type()
1279
if not cartan_type.is_finite():
1280
raise NotImplementedError
1281
simple_roots = self._root_lattice.simple_roots().list()
1282
lambda_chain = [ x.root for x in self.lambda_chain() ]
1283
1284
coroot_lattice = RootSystem(cartan_type).coroot_lattice()
1285
cohighest_root = coroot_lattice.highest_root()
1286
1287
word = []
1288
for i in range(len(lambda_chain)):
1289
beta = lambda_chain[i]
1290
for j in reversed(range(i)):
1291
beta = beta.reflection(lambda_chain[j])
1292
#beta is now a simple root or the highest root
1293
1294
coroot = beta.associated_coroot()
1295
support = coroot.support() # the path is in dual affine space
1296
if len(support) == 1: # beta is a simple root
1297
word.append(support[0])
1298
elif coroot == -cohighest_root:
1299
word.append(0)
1300
else:
1301
assert False, 'should never get here'
1302
1303
return word
1304
1305
@cached_method
1306
def lambda_chain(self):
1307
r"""
1308
Return the unfolded `\lambda`-chain.
1309
1310
.. NOTE:: Only works in root systems of finite type.
1311
1312
EXAMPLES::
1313
1314
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1315
sage: R = RootsWithHeight(['A',2],[1,1]); R
1316
Roots with height of cartan type ['A', 2] and dominant weight Lambda[1] + Lambda[2]
1317
sage: R.lambda_chain()
1318
[(alpha[2], 0), (alpha[1] + alpha[2], 0), (alpha[1], 0), (alpha[1] + alpha[2], 1)]
1319
"""
1320
if not self._root_lattice.cartan_type().is_finite():
1321
raise ValueError("cartan type {0} is not finite".format(self._root_lattice.cartan_type()))
1322
1323
l=[]
1324
for i in self._root_lattice.positive_roots():
1325
for j in range(self._max_height(i)):
1326
l.append(self(i,j))
1327
1328
return sorted(l)
1329
1330
def _element_constructor_(self, root, height):
1331
r"""
1332
Construct a :class:`RootsWithHeightElement` with ``self`` as the parent.
1333
1334
EXAMPLES::
1335
1336
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1337
sage: rl = RootSystem(['A',2]).root_lattice()
1338
sage: x = rl.from_vector(vector([1,1])); x
1339
alpha[1] + alpha[2]
1340
sage: R = RootsWithHeight(['A',2],[1,1]); R
1341
Roots with height of cartan type ['A', 2] and dominant weight Lambda[1] + Lambda[2]
1342
sage: y = R(x,1); y
1343
(alpha[1] + alpha[2], 1)
1344
"""
1345
root = self._root_lattice.from_vector(vector(root))
1346
return self.element_class(self, root, height)
1347
1348
def _an_element_(self):
1349
r"""
1350
1351
EXAMPLES::
1352
1353
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1354
sage: R = RootsWithHeight(['A',2],[3,2])
1355
sage: R._an_element_()
1356
(alpha[1], 0)
1357
"""
1358
return self( self._root_lattice.from_vector(vector([1])), 0 )
1359
1360
class RootsWithHeightElement(Element):
1361
r"""
1362
Element of :class:`RootsWithHeight`.
1363
1364
INPUT:
1365
1366
- ``root`` -- A positive root `\beta` in our root system
1367
- ``height`` -- Is an integer, such that
1368
`0 \leq l \leq \langle \lambda, \beta^{\vee} \rangle`
1369
1370
EXAMPLES::
1371
1372
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1373
sage: rl = RootSystem(['A',2]).root_lattice()
1374
sage: x = rl.from_vector(vector([1,1])); x
1375
alpha[1] + alpha[2]
1376
sage: R = RootsWithHeight(['A',2],[1,1]); R
1377
Roots with height of cartan type ['A', 2] and dominant weight Lambda[1] + Lambda[2]
1378
sage: y = R(x, 1); y
1379
(alpha[1] + alpha[2], 1)
1380
"""
1381
def __init__(self, parent, root, height):
1382
r"""
1383
Initialize ``self``.
1384
1385
EXAMPLES::
1386
1387
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1388
sage: rl = RootSystem(['A',2]).root_lattice()
1389
sage: x = rl.from_vector(vector([1,1]))
1390
sage: R = RootsWithHeight(['A',2],[3,2])
1391
sage: y = R(x, 1); y
1392
(alpha[1] + alpha[2], 1)
1393
sage: TestSuite(x).run()
1394
"""
1395
Element.__init__(self, parent)
1396
max_height = parent._max_height(root)
1397
1398
# make sure the height is in the right range, this also catches negative
1399
# roots
1400
1401
if not 0 <= height < max_height:
1402
raise ValueError("%d out of allowed range [%d,%d)"%(height, 0, max_height))
1403
1404
v = [height/max_height]
1405
v.extend( [ x/max_height for x in root.associated_coroot().to_vector() ] )
1406
#v.insert(0, height/max_height)
1407
1408
# the map from (root, height) --> _cmp_v is injective
1409
1410
self._cmp_v = tuple(v)
1411
self.root = root
1412
self.height = height
1413
1414
def _repr_(self):
1415
r"""
1416
Return a string representation of ``self``.
1417
1418
EXAMPLES::
1419
1420
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1421
sage: R = RootsWithHeight(['A',2],[3,2])
1422
sage: rl = RootSystem(['A',2]).root_lattice()
1423
sage: vec = rl.from_vector(vector([1,1])); vec
1424
alpha[1] + alpha[2]
1425
sage: R(vec,1)
1426
(alpha[1] + alpha[2], 1)
1427
"""
1428
return "(%s, %s)" % (self.root, self.height)
1429
1430
def __hash__(self):
1431
r"""
1432
1433
EXAMPLES::
1434
1435
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1436
sage: R = RootsWithHeight(['A',2],[3,2])
1437
sage: rl = RootSystem(['A',2]).root_lattice()
1438
sage: root = rl.from_vector(vector([1,1]))
1439
sage: vec = R(root,0)
1440
sage: hash(vec) == hash(vec)
1441
True
1442
"""
1443
return hash(self._cmp_v)
1444
1445
def __eq__(self, other):
1446
r"""
1447
1448
EXAMPLES::
1449
1450
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1451
sage: R = RootsWithHeight(['A',2],[3,2])
1452
sage: rl = RootSystem(['A',2]).root_lattice()
1453
sage: v1 = rl.from_vector(vector([1,1]))
1454
sage: v2 = rl.from_vector(vector([1]))
1455
sage: x1 = R(v1,1) ; x2 = R(v1,0) ; x3 = R(v2,1)
1456
sage: x1.__eq__(x1)
1457
True
1458
sage: x1.__eq__(x2)
1459
False
1460
sage: x1.__eq__(x3)
1461
False
1462
"""
1463
try:
1464
return self._cmp_v == other._cmp_v
1465
except (NameError, AttributeError):
1466
return False
1467
1468
def __cmp__(self, other):
1469
r"""
1470
Define a total order on :class:`RootsWithHeightElement`. This defines
1471
the initial `\lambda`-chain.
1472
1473
EXAMPLES::
1474
1475
sage: from sage.combinat.crystals.alcove_path import RootsWithHeight
1476
sage: R = RootsWithHeight(['A',2],[3,2])
1477
sage: rl = RootSystem(['A',2]).root_lattice()
1478
sage: v1 = rl.from_vector(vector([1,1]))
1479
sage: v2 = rl.from_vector(vector([1]))
1480
sage: x1 = R(v1,1) ; x2 = R(v1,0) ; x3 = R(v2,1)
1481
sage: x1.__cmp__(x2)
1482
1
1483
sage: x1.__cmp__(x3)
1484
-1
1485
1486
"""
1487
# I suspect that if you redefine this method to produce a
1488
# different (valid) `\lambda`-chain the rest of the
1489
# code should still work.
1490
#todo: check if self and other have the same parent ?
1491
#assert self.parent() is other.parent(), "elements have different parents"
1492
return cmp(self._cmp_v, other._cmp_v)
1493
1494
RootsWithHeight.Element = RootsWithHeightElement
1495
1496
#####################################################################
1497
# Test code, by comparing with existing crystal implementations.
1498
#####################################################################
1499
1500
def _test_some_specific_examples(clss=CrystalOfAlcovePaths):
1501
r"""
1502
Test against some specific (finite type) examples.
1503
1504
EXAMPLES::
1505
1506
sage: from sage.combinat.crystals.alcove_path import _test_some_specific_examples
1507
sage: _test_some_specific_examples(CrystalOfAlcovePaths)
1508
G2 example passed.
1509
C3 example passed.
1510
B3 example 1 passed.
1511
B3 example 2 passed.
1512
True
1513
"""
1514
# This appears in Lenart.
1515
C = clss(['G',2],[0,1])
1516
G = C.digraph()
1517
1518
GT = DiGraph({
1519
() : {(0) : 2 },
1520
(0) : {(0,8) : 1 },
1521
(0,1) : {(0,1,7) : 2 },
1522
(0,1,2) : {(0,1,2,9) : 1 },
1523
(0,1,2,3) : {(0,1,2,3,4) : 2 },
1524
(0,1,2,6) : {(0,1,2,3) : 1 },
1525
(0,1,2,9) : {(0,1,2,6) : 1 },
1526
(0,1,7) : {(0,1,2) : 2 },
1527
(0,1,7,9) : {(0,1,2,9) : 2 },
1528
(0,5) : {(0,1) : 1, (0,5,7) : 2 },
1529
(0,5,7) : {(0,5,7,9) : 1 },
1530
(0,5,7,9) : {(0,1,7,9) : 1 },
1531
(0,8) : {(0,5) : 1 }
1532
})
1533
1534
if (G.is_isomorphic(GT) != True):
1535
return False
1536
else:
1537
print "G2 example passed."
1538
1539
# Some examples from Hong--Kang:
1540
1541
# type C, ex. 8.3.5, pg. 189
1542
C = clss(['C',3],[0,0,1])
1543
G = C.digraph()
1544
GT = DiGraph({
1545
():{ (0): 3},
1546
(0):{ (0, 6): 2},
1547
(0, 1):{ (0, 1, 3): 3, (0, 1, 7): 1},
1548
(0, 1, 2):{ (0, 1, 2, 3): 3},
1549
(0, 1, 2, 3):{ (0, 1, 2, 3, 8): 2},
1550
(0, 1, 2, 3, 4):{ (0, 1, 2, 3, 4, 5): 3},
1551
(0, 1, 2, 3, 8):{ (0, 1, 2, 3, 4): 2},
1552
(0, 1, 3):{ (0, 1, 3, 7): 1},
1553
(0, 1, 3, 7):{ (0, 1, 2, 3): 1, (0, 1, 3, 7, 8): 2},
1554
(0, 1, 3, 7, 8):{ (0, 1, 2, 3, 8): 1},
1555
(0, 1, 7):{ (0, 1, 2): 1, (0, 1, 3, 7): 3},
1556
(0, 6):{ (0, 1): 2, (0, 6, 7): 1},
1557
(0, 6, 7):{ (0, 1, 7): 2}
1558
})
1559
1560
if (G.is_isomorphic(GT) != True):
1561
return False
1562
else:
1563
print "C3 example passed."
1564
1565
# type B, fig. 8.1 pg. 172
1566
C = clss(['B',3],[2,0,0])
1567
G = C.digraph()
1568
1569
GT = DiGraph({
1570
():{ (6): 1},
1571
(0):{ (0, 7): 2},
1572
(0, 1):{ (0, 1, 11): 3},
1573
(0, 1, 2):{ (0, 1, 2, 9): 2},
1574
(0, 1, 2, 3):{ (0, 1, 2, 3, 10): 1},
1575
(0, 1, 2, 3, 10):{ (0, 1, 2, 3, 4): 1},
1576
(0, 1, 2, 9):{ (0, 1, 2, 3): 2, (0, 1, 2, 9, 10): 1},
1577
(0, 1, 2, 9, 10):{ (0, 1, 2, 3, 10): 2},
1578
(0, 1, 5):{ (0, 1, 2): 3, (0, 1, 5, 9): 2},
1579
(0, 1, 5, 9):{ (0, 1, 2, 9): 3, (0, 1, 5, 9, 10): 1},
1580
(0, 1, 5, 9, 10):{ (0, 1, 2, 9, 10): 3},
1581
(0, 1, 8):{ (0, 1, 5): 3},
1582
(0, 1, 8, 9):{ (0, 1, 5, 9): 3, (0, 1, 8, 9, 10): 1},
1583
(0, 1, 8, 9, 10):{ (0, 1, 5, 9, 10): 3},
1584
(0, 1, 11):{ (0, 1, 8): 3},
1585
(0, 7):{ (0, 1): 2, (0, 7, 11): 3},
1586
(0, 7, 8):{ (0, 7, 8, 9): 2},
1587
(0, 7, 8, 9):{ (0, 1, 8, 9): 2},
1588
(0, 7, 8, 9, 10):{ (0, 1, 8, 9, 10): 2},
1589
(0, 7, 11):{ (0, 1, 11): 2, (0, 7, 8): 3},
1590
(6):{ (0): 1, (6, 7): 2},
1591
(6, 7):{ (0, 7): 1, (6, 7, 11): 3},
1592
(6, 7, 8):{ (0, 7, 8): 1, (6, 7, 8, 9): 2},
1593
(6, 7, 8, 9):{ (6, 7, 8, 9, 10): 1},
1594
(6, 7, 8, 9, 10):{ (0, 7, 8, 9, 10): 1},
1595
(6, 7, 11):{ (0, 7, 11): 1, (6, 7, 8): 3}
1596
})
1597
1598
if (G.is_isomorphic(GT) != True):
1599
return False
1600
else:
1601
print "B3 example 1 passed."
1602
1603
C = clss(['B',3],[0,1,0])
1604
G = C.digraph()
1605
1606
GT = DiGraph({
1607
():{ (0): 2},
1608
(0):{ (0, 1): 1, (0, 7): 3},
1609
(0, 1):{ (0, 1, 7): 3},
1610
(0, 1, 2):{ (0, 1, 2, 8): 2},
1611
(0, 1, 2, 3):{ (0, 1, 2, 3, 5): 1, (0, 1, 2, 3, 9): 3},
1612
(0, 1, 2, 3, 4):{ (0, 1, 2, 3, 4, 5): 1},
1613
(0, 1, 2, 3, 4, 5):{ (0, 1, 2, 3, 4, 5, 6): 2},
1614
(0, 1, 2, 3, 5):{ (0, 1, 2, 3, 5, 9): 3},
1615
(0, 1, 2, 3, 5, 9):{ (0, 1, 2, 3, 4, 5): 3},
1616
(0, 1, 2, 3, 9):{ (0, 1, 2, 3, 4): 3, (0, 1, 2, 3, 5, 9): 1},
1617
(0, 1, 2, 5):{ (0, 1, 2, 3, 5): 2},
1618
(0, 1, 2, 8):{ (0, 1, 2, 3): 2},
1619
(0, 1, 2, 8, 9):{ (0, 1, 2, 3, 9): 2},
1620
(0, 1, 7):{ (0, 1, 2): 3, (0, 1, 7, 8): 2},
1621
(0, 1, 7, 8):{ (0, 1, 7, 8, 9): 3},
1622
(0, 1, 7, 8, 9):{ (0, 1, 2, 8, 9): 3},
1623
(0, 2):{ (0, 1, 2): 1, (0, 2, 5): 2},
1624
(0, 2, 5):{ (0, 2, 5, 8): 1},
1625
(0, 2, 5, 8):{ (0, 1, 2, 5): 1},
1626
(0, 7):{ (0, 1, 7): 1, (0, 2): 3}
1627
})
1628
1629
if (G.is_isomorphic(GT) != True):
1630
return False
1631
else:
1632
print "B3 example 2 passed."
1633
1634
# type B, fig. 8.3 pg. 174
1635
1636
return True
1637
1638
def compare_graphs(g1, g2, node1, node2):
1639
r"""
1640
Compare two edge-labeled :class:`graphs <DiGraph>` obtained from
1641
``Crystal.digraph()``, starting from the root nodes of each graph.
1642
1643
- ``g1`` -- :class:`graphs <DiGraph>`, first digraph
1644
- ``g2`` -- :class:`graphs <DiGraph>`, second digraph
1645
- ``node1`` -- element of ``g1``
1646
- ``node2`` -- element of ``g2``
1647
1648
Traverse ``g1`` starting at ``node1`` and compare this graph with
1649
the one obtained by traversing ``g2`` starting with ``node2``.
1650
If the graphs match (including labels) then return ``True``.
1651
Return ``False`` otherwise.
1652
1653
EXAMPLES::
1654
1655
sage: from sage.combinat.crystals.alcove_path import compare_graphs
1656
sage: G1 = sage.combinat.crystals.all.CrystalOfTableaux(['A',3], shape=[1,1]).digraph()
1657
sage: C = CrystalOfAlcovePaths(['A',3],[0,1,0])
1658
sage: G2 = C.digraph()
1659
sage: compare_graphs(G1, G2, C( () ), G2.vertices()[0])
1660
True
1661
"""
1662
for out_edge in g1.outgoing_edges( node1 ):
1663
matched = False
1664
for o2 in g2.outgoing_edges( node2 ):
1665
if o2[2] == out_edge[2]:
1666
if matched == True:
1667
print "ERROR: Two edges with the same label for ", out_edge, " exist."
1668
return False
1669
matched = True
1670
result = compare_graphs(g1, g2, out_edge[1], o2[1])
1671
if result == False:
1672
return False
1673
if matched == False:
1674
print "ERROR: No matching edge for ", out_edge, "."
1675
return False
1676
return True
1677
1678
def _test_against_tableaux(R, N, k, clss=CrystalOfAlcovePaths):
1679
r"""
1680
Tests :class:`CrystalOfAlcovePaths` against all of the tableaux crystals
1681
of type `R` in rank `N` with highest weight given by a partition of `k`.
1682
1683
EXAMPLES::
1684
1685
sage: from sage.combinat.crystals.alcove_path import _test_against_tableaux
1686
sage: _test_against_tableaux(['A',3], 3, 2)
1687
** Shape [2]
1688
T has 10 nodes.
1689
C weight [2, 0, 0]
1690
C has 10 nodes.
1691
Compare graphs: True
1692
** Shape [1, 1]
1693
T has 6 nodes.
1694
C weight [0, 1, 0]
1695
C has 6 nodes.
1696
Compare graphs: True
1697
"""
1698
shapes = Partitions(k).list()
1699
for shape in shapes:
1700
print "** Shape ", shape
1701
T = CrystalOfTableaux(R, shape = shape)
1702
ct = len(T.list())
1703
print " T has ", ct, " nodes."
1704
#T.digraph().show(edge_labels=True)
1705
H = T.digraph()
1706
weight = T.module_generators[0].weight()
1707
w = [ weight.scalar(RootSystem(R).ambient_space().simple_coroot(i)) for i in range(1,N+1) ]
1708
print " C weight ", w
1709
1710
C = clss(R , w)
1711
1712
cc = len(C.list())
1713
#C.digraph().show(edge_labels=True)
1714
G = C.digraph()
1715
print " C has ", cc, " nodes."
1716
if cc != ct:
1717
print "FAIL: number of nodes differ.", cc, ct
1718
return
1719
print " Compare graphs: ", compare_graphs(G, H, C(()), H.vertices()[0])
1720
1721
def _test_with_lspaths_crystal(cartan_type, weight, depth=10):
1722
r"""
1723
Test if the digraphs generated are isomorphic to the ones generated by
1724
lspath model.
1725
1726
INPUT:
1727
1728
- ``cartan_type`` -- Cartan type of a finite or affine untwisted root
1729
system.
1730
- ``weight`` -- dominant weight as a list of (integral) coefficients of the
1731
fundamental weights.
1732
- ``depth`` -- starting at the module generator how deep do you want to
1733
generate the crystal, useful for affine types.
1734
1735
EXAMPLES::
1736
1737
sage: from sage.combinat.crystals.alcove_path import _test_with_lspaths_crystal
1738
sage: _test_with_lspaths_crystal(['A',3,1],[1,0,0,0],10) #long time
1739
True
1740
sage: _test_with_lspaths_crystal(['G',2,1],[1,0,0,0,0],10) #long time
1741
True
1742
"""
1743
G1 = CrystalOfAlcovePaths(cartan_type, weight).digraph_fast(depth)
1744
C = CrystalOfLSPaths(cartan_type, weight)
1745
G2 = C.digraph(subset=C.subcrystal(max_depth=depth, direction='lower'))
1746
1747
return G1.is_isomorphic(G2, edge_labels=True)
1748
1749
1750