Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/categories/crystals.py
4068 views
1
r"""
2
Crystals
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2010 Anne Schilling <anne at math.ucdavis.edu>
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
# http://www.gnu.org/licenses/
9
#******************************************************************************
10
11
from sage.misc.cachefunc import cached_method
12
from sage.misc.cachefunc import CachedFunction
13
from sage.misc.abstract_method import abstract_method
14
from sage.categories.category import Category
15
from sage.categories.category_singleton import Category_singleton
16
from sage.categories.enumerated_sets import EnumeratedSets
17
from sage.misc.latex import latex
18
from sage.combinat import ranker
19
from sage.combinat.subset import Subsets
20
from sage.graphs.dot2tex_utils import have_dot2tex
21
from sage.rings.integer import Integer
22
23
class Crystals(Category_singleton):
24
"""
25
The category of crystals
26
27
See :mod:`sage.combinat.crystals` for an introduction to crystals.
28
29
EXAMPLES::
30
31
sage: C = Crystals()
32
sage: C
33
Category of crystals
34
sage: C.super_categories()
35
[Category of... enumerated sets]
36
sage: C.example()
37
Highest weight crystal of type A_3 of highest weight omega_1
38
39
Parents in this category should implement the following methods:
40
41
- either a method ``cartan_type`` or an attribute ``_cartan_type``
42
43
- ``module_generators``: a list (or container) of distinct elements
44
which generate the crystal using `f_i`
45
46
Furthermore, their elements should implement the following methods:
47
48
- x.e(i) (returning `e_i(x)`)
49
50
- x.f(i) (returning `f_i(x)`)
51
52
EXAMPLES::
53
54
sage: from sage.misc.abstract_method import abstract_methods_of_class
55
sage: abstract_methods_of_class(Crystals().element_class)
56
{'required': ['e', 'f'], 'optional': []}
57
58
TESTS::
59
60
sage: TestSuite(C).run()
61
sage: B = Crystals().example()
62
sage: TestSuite(B).run(verbose = True)
63
running ._test_an_element() . . . pass
64
running ._test_category() . . . pass
65
running ._test_elements() . . .
66
Running the test suite of self.an_element()
67
running ._test_category() . . . pass
68
running ._test_eq() . . . pass
69
running ._test_not_implemented_methods() . . . pass
70
running ._test_pickling() . . . pass
71
running ._test_stembridge_local_axioms() . . . pass
72
pass
73
running ._test_elements_eq() . . . pass
74
running ._test_enumerated_set_contains() . . . pass
75
running ._test_enumerated_set_iter_cardinality() . . . pass
76
running ._test_enumerated_set_iter_list() . . . pass
77
running ._test_eq() . . . pass
78
running ._test_fast_iter() . . . pass
79
running ._test_not_implemented_methods() . . . pass
80
running ._test_pickling() . . . pass
81
running ._test_some_elements() . . . pass
82
running ._test_stembridge_local_axioms() . . . pass
83
"""
84
85
def super_categories(self):
86
r"""
87
EXAMPLES::
88
89
sage: Crystals().super_categories()
90
[Category of enumerated sets]
91
"""
92
return [EnumeratedSets()]
93
94
def example(self, choice="highwt", **kwds):
95
r"""
96
Returns an example of a crystal, as per
97
:meth:`Category.example()
98
<sage.categories.category.Category.example>`.
99
100
INPUT::
101
102
- ``choice`` -- str [default: 'highwt']. Can be either 'highwt'
103
for the highest weight crystal of type A, or 'naive' for an
104
example of a broken crystal.
105
106
- ``**kwds`` -- keyword arguments passed onto the constructor for the
107
chosen crystal.
108
109
EXAMPLES::
110
111
sage: Crystals().example(choice='highwt', n=5)
112
Highest weight crystal of type A_5 of highest weight omega_1
113
sage: Crystals().example(choice='naive')
114
A broken crystal, defined by digraph, of dimension five.
115
"""
116
import sage.categories.examples.crystals as examples
117
if choice == "naive":
118
return examples.NaiveCrystal(**kwds)
119
else:
120
if isinstance(choice, Integer):
121
return examples.HighestWeightCrystalOfTypeA(n=choice, **kwds)
122
else:
123
return examples.HighestWeightCrystalOfTypeA(**kwds)
124
125
class ParentMethods:
126
127
def an_element(self):
128
"""
129
Returns an element of ``self``
130
131
sage: C = CrystalOfLetters(['A', 5])
132
sage: C.an_element()
133
1
134
"""
135
return self.first()
136
137
def weight_lattice_realization(self):
138
"""
139
Returns the weight lattice realization for the root system
140
associated to ``self``. This default implementation uses
141
the ambient space of the root system.
142
143
EXAMPLES::
144
145
sage: C = CrystalOfLetters(['A', 5])
146
sage: C.weight_lattice_realization()
147
Ambient space of the Root system of type ['A', 5]
148
sage: K = KirillovReshetikhinCrystal(['A',2,1], 1, 1)
149
sage: K.weight_lattice_realization()
150
Weight lattice of the Root system of type ['A', 2, 1]
151
"""
152
F = self.cartan_type().root_system()
153
if F.ambient_space() is None:
154
return F.weight_lattice()
155
else:
156
return F.ambient_space()
157
158
def cartan_type(self):
159
"""
160
Returns the Cartan type of the crystal
161
162
EXAMPLES::
163
sage: C = CrystalOfLetters(['A',2])
164
sage: C.cartan_type()
165
['A', 2]
166
"""
167
return self._cartan_type
168
169
def index_set(self):
170
"""
171
Returns the index set of the Dynkin diagram underlying the crystal
172
173
EXAMPLES:
174
sage: C = CrystalOfLetters(['A', 5])
175
sage: C.index_set()
176
[1, 2, 3, 4, 5]
177
"""
178
return self.cartan_type().index_set()
179
180
def Lambda(self):
181
"""
182
Returns the fundamentals weights in the weight lattice
183
realization for the root system associated the crystal
184
185
EXAMPLES::
186
187
sage: C = CrystalOfLetters(['A', 5])
188
sage: C.Lambda()
189
Finite family {1: (1, 0, 0, 0, 0, 0), 2: (1, 1, 0, 0, 0, 0), 3: (1, 1, 1, 0, 0, 0), 4: (1, 1, 1, 1, 0, 0), 5: (1, 1, 1, 1, 1, 0)}
190
"""
191
return self.weight_lattice_realization().fundamental_weights()
192
193
def crystal_morphism(self, g, index_set = None, automorphism = lambda i : i, direction = 'down', direction_image = 'down',
194
similarity_factor = None, similarity_factor_domain = None, cached = False, acyclic = True):
195
"""
196
Constructs a morphism from the crystal ``self`` to another crystal.
197
The input `g` can either be a function of a (sub)set of elements of self to
198
element in another crystal or a dictionary between certain elements.
199
Usually one would map highest weight elements or crystal generators to each
200
other using g.
201
Specifying index_set gives the opportunity to define the morphism as `I`-crystals
202
where `I =` index_set. If index_set is not specified, the index set of self is used.
203
It is also possible to define twisted-morphisms by specifying an automorphism on the
204
nodes in te Dynkin diagram (or the index_set).
205
The option direction and direction_image indicate whether to use `f_i` or `e_i` in
206
self or the image crystal to construct the morphism, depending on whether the direction
207
is set to 'down' or 'up'.
208
It is also possible to set a similarity_factor. This should be a dictionary between
209
the elements in the index set and positive integers. The crystal operator `f_i` then gets
210
mapped to `f_i^{m_i}` where `m_i =` similarity_factor[i].
211
Setting similarity_factor_domain to a dictionary between the index set and positive integers
212
has the effect that `f_i^{m_i}` gets mapped to `f_i` where `m_i =` similarity_factor_domain[i].
213
Finally, it is possible to set the option `acyclic = False`. This calculates an isomorphism
214
for cyclic crystals (for example finite affine crystals). In this case the input function `g`
215
is supposed to be given as a dictionary.
216
217
EXAMPLES::
218
219
sage: C2 = CrystalOfLetters(['A',2])
220
sage: C3 = CrystalOfLetters(['A',3])
221
sage: g = {C2.module_generators[0] : C3.module_generators[0]}
222
sage: g_full = C2.crystal_morphism(g)
223
sage: g_full(C2(1))
224
1
225
sage: g_full(C2(2))
226
2
227
sage: g = {C2(1) : C2(3)}
228
sage: g_full = C2.crystal_morphism(g, automorphism = lambda i : 3-i, direction_image = 'up')
229
sage: [g_full(b) for b in C2]
230
[3, 2, 1]
231
sage: T = CrystalOfTableaux(['A',2], shape = [2])
232
sage: g = {C2(1) : T(rows=[[1,1]])}
233
sage: g_full = C2.crystal_morphism(g, similarity_factor = {1:2, 2:2})
234
sage: [g_full(b) for b in C2]
235
[[[1, 1]], [[2, 2]], [[3, 3]]]
236
sage: g = {T(rows=[[1,1]]) : C2(1)}
237
sage: g_full = T.crystal_morphism(g, similarity_factor_domain = {1:2, 2:2})
238
sage: g_full(T(rows=[[2,2]]))
239
2
240
241
sage: B1=KirillovReshetikhinCrystal(['A',2,1],1,1)
242
sage: B2=KirillovReshetikhinCrystal(['A',2,1],1,2)
243
sage: T=TensorProductOfCrystals(B1,B2)
244
sage: T1=TensorProductOfCrystals(B2,B1)
245
sage: La = T.weight_lattice_realization().fundamental_weights()
246
sage: t = [b for b in T if b.weight() == -3*La[0] + 3*La[1]][0]
247
sage: t1 = [b for b in T1 if b.weight() == -3*La[0] + 3*La[1]][0]
248
sage: g={t:t1}
249
sage: f=T.crystal_morphism(g,acyclic = False)
250
sage: [[b,f(b)] for b in T]
251
[[[[[1]], [[1, 1]]], [[[1, 1]], [[1]]]],
252
[[[[1]], [[1, 2]]], [[[1, 1]], [[2]]]],
253
[[[[1]], [[2, 2]]], [[[1, 2]], [[2]]]],
254
[[[[1]], [[1, 3]]], [[[1, 1]], [[3]]]],
255
[[[[1]], [[2, 3]]], [[[1, 2]], [[3]]]],
256
[[[[1]], [[3, 3]]], [[[1, 3]], [[3]]]],
257
[[[[2]], [[1, 1]]], [[[1, 2]], [[1]]]],
258
[[[[2]], [[1, 2]]], [[[2, 2]], [[1]]]],
259
[[[[2]], [[2, 2]]], [[[2, 2]], [[2]]]],
260
[[[[2]], [[1, 3]]], [[[2, 3]], [[1]]]],
261
[[[[2]], [[2, 3]]], [[[2, 2]], [[3]]]],
262
[[[[2]], [[3, 3]]], [[[2, 3]], [[3]]]],
263
[[[[3]], [[1, 1]]], [[[1, 3]], [[1]]]],
264
[[[[3]], [[1, 2]]], [[[1, 3]], [[2]]]],
265
[[[[3]], [[2, 2]]], [[[2, 3]], [[2]]]],
266
[[[[3]], [[1, 3]]], [[[3, 3]], [[1]]]],
267
[[[[3]], [[2, 3]]], [[[3, 3]], [[2]]]],
268
[[[[3]], [[3, 3]]], [[[3, 3]], [[3]]]]]
269
"""
270
if index_set is None:
271
index_set = self.index_set()
272
if similarity_factor is None:
273
similarity_factor = dict( (i,1) for i in index_set )
274
if similarity_factor_domain is None:
275
similarity_factor_domain = dict( (i,1) for i in index_set )
276
if direction == 'down':
277
e_string = 'e_string'
278
else:
279
e_string = 'f_string'
280
if direction_image == 'down':
281
f_string = 'f_string'
282
else:
283
f_string = 'e_string'
284
285
if acyclic:
286
if type(g) == dict:
287
g = g.__getitem__
288
289
def morphism(b):
290
for i in index_set:
291
c = getattr(b, e_string)([i for k in range(similarity_factor_domain[i])])
292
if c is not None:
293
d = getattr(morphism(c), f_string)([automorphism(i) for k in range(similarity_factor[i])])
294
if d is not None:
295
return d
296
else:
297
raise ValueError, "This is not a morphism!"
298
#now we know that b is hw
299
return g(b)
300
301
else:
302
import copy
303
morphism = copy.copy(g)
304
known = set( g.keys() )
305
todo = copy.copy(known)
306
images = set( [g[x] for x in known] )
307
# Invariants:
308
# - images contains all known morphism(x)
309
# - known contains all elements x for which we know morphism(x)
310
# - todo contains all elements x for which we haven't propagated to each child
311
while todo <> set( [] ):
312
x = todo.pop()
313
for i in index_set:
314
eix = getattr(x, f_string)([i for k in range(similarity_factor_domain[i])])
315
eigx = getattr(morphism[x], f_string)([automorphism(i) for k in range(similarity_factor[i])])
316
if bool(eix is None) <> bool(eigx is None):
317
# This is not a crystal morphism!
318
raise ValueError, "This is not a morphism!" #, print("x="x,"g(x)="g(x),"i="i)
319
if (eix is not None) and (eix not in known):
320
todo.add(eix)
321
known.add(eix)
322
morphism[eix] = eigx
323
images.add(eigx)
324
# Check that the module generators are indeed module generators
325
assert(len(known) == self.cardinality())
326
# We may not want to systematically run those tests,
327
# to allow for non bijective crystal morphism
328
# Add an option CheckBijective?
329
if not ( len(known) == len(images) and len(images) == images.pop().parent().cardinality() ):
330
return(None)
331
return morphism.__getitem__
332
333
if cached:
334
return morphism
335
else:
336
return CachedFunction(morphism)
337
338
def digraph(self):
339
"""
340
Returns the DiGraph associated to self.
341
342
EXAMPLES::
343
344
sage: C = Crystals().example(5)
345
sage: C.digraph()
346
Digraph on 6 vertices
347
348
The edges of the crystal graph are by default colored using blue for edge 1, red for edge 2,
349
and green for edge 3::
350
351
sage: C = Crystals().example(3)
352
sage: G = C.digraph()
353
sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz
354
355
One may also overwrite the colors::
356
357
sage: C = Crystals().example(3)
358
sage: G = C.digraph()
359
sage: G.set_latex_options(color_by_label = {1:"red", 2:"purple", 3:"blue"})
360
sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz
361
362
Or one may add colors to yet unspecified edges::
363
364
sage: C = Crystals().example(4)
365
sage: G = C.digraph()
366
sage: C.cartan_type()._index_set_coloring[4]="purple"
367
sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz
368
369
TODO: add more tests
370
"""
371
from sage.graphs.all import DiGraph
372
d = {}
373
for x in self:
374
d[x] = {}
375
for i in self.index_set():
376
child = x.f(i)
377
if child is None:
378
continue
379
d[x][child]=i
380
G = DiGraph(d)
381
if have_dot2tex():
382
G.set_latex_options(format="dot2tex", edge_labels = True, color_by_label = self.cartan_type()._index_set_coloring,
383
edge_options = lambda (u,v,label): ({"backward":label ==0}))
384
return G
385
386
def latex_file(self, filename):
387
r"""
388
Exports a file, suitable for pdflatex, to 'filename'. This requires
389
a proper installation of ``dot2tex`` in sage-python. For more
390
information see the documentation for ``self.latex()``.
391
392
EXAMPLES::
393
394
sage: C = CrystalOfLetters(['A', 5])
395
sage: C.latex_file('/tmp/test.tex') #optional - dot2tex
396
"""
397
header = r"""\documentclass{article}
398
\usepackage[x11names, rgb]{xcolor}
399
\usepackage[utf8]{inputenc}
400
\usepackage{tikz}
401
\usetikzlibrary{snakes,arrows,shapes}
402
\usepackage{amsmath}
403
\usepackage[active,tightpage]{preview}
404
\newenvironment{bla}{}{}
405
\PreviewEnvironment{bla}
406
407
\begin{document}
408
\begin{bla}"""
409
410
footer = r"""\end{bla}
411
\end{document}"""
412
413
f = open(filename, 'w')
414
f.write(header + self.latex() + footer)
415
f.close()
416
417
def _latex_(self, **options):
418
r"""
419
Returns the crystal graph as a latex string. This can be exported
420
to a file with self.latex_file('filename').
421
422
EXAMPLES::
423
424
sage: T = CrystalOfTableaux(['A',2],shape=[1])
425
sage: T._latex_() #optional - dot2tex
426
...
427
sage: view(T, pdflatex = True, tightpage = True) #optional - dot2tex graphviz
428
429
One can for example also color the edges using the following options::
430
431
sage: T = CrystalOfTableaux(['A',2],shape=[1])
432
sage: T._latex_(color_by_label = {0:"black", 1:"red", 2:"blue"}) #optional - dot2tex graphviz
433
...
434
"""
435
if not have_dot2tex():
436
print "dot2tex not available. Install after running \'sage -sh\'"
437
return
438
G=self.digraph()
439
G.set_latex_options(format="dot2tex", edge_labels = True,
440
edge_options = lambda (u,v,label): ({"backward":label ==0}), **options)
441
return G._latex_()
442
443
latex = _latex_
444
445
def metapost(self, filename, thicklines=False, labels=True, scaling_factor=1.0, tallness=1.0):
446
r"""
447
Use C.metapost("filename.mp",[options]), where options can be:
448
449
thicklines = True (for thicker edges) labels = False (to suppress
450
labeling of the vertices) scaling_factor=value, where value is a
451
floating point number, 1.0 by default. Increasing or decreasing the
452
scaling factor changes the size of the image. tallness=1.0.
453
Increasing makes the image taller without increasing the width.
454
455
Root operators e(1) or f(1) move along red lines, e(2) or f(2)
456
along green. The highest weight is in the lower left. Vertices with
457
the same weight are kept close together. The concise labels on the
458
nodes are strings introduced by Berenstein and Zelevinsky and
459
Littelmann; see Littelmann's paper Cones, Crystals, Patterns,
460
sections 5 and 6.
461
462
For Cartan types B2 or C2, the pattern has the form
463
464
a2 a3 a4 a1
465
466
where c\*a2 = a3 = 2\*a4 =0 and a1=0, with c=2 for B2, c=1 for C2.
467
Applying e(2) a1 times, e(1) a2 times, e(2) a3 times, e(1) a4 times
468
returns to the highest weight. (Observe that Littelmann writes the
469
roots in opposite of the usual order, so our e(1) is his e(2) for
470
these Cartan types.) For type A2, the pattern has the form
471
472
a3 a2 a1
473
474
where applying e(1) a1 times, e(2) a2 times then e(3) a1 times
475
returns to the highest weight. These data determine the vertex and
476
may be translated into a Gelfand-Tsetlin pattern or tableau.
477
478
EXAMPLES::
479
480
sage: C = CrystalOfLetters(['A', 2])
481
sage: C.metapost('/tmp/test.mp') #optional
482
483
::
484
485
sage: C = CrystalOfLetters(['A', 5])
486
sage: C.metapost('/tmp/test.mp')
487
Traceback (most recent call last):
488
...
489
NotImplementedError
490
"""
491
# FIXME: those tests are not robust
492
# Should use instead self.cartan_type() == CartanType(['B',2])
493
if self.cartan_type()[0] == 'B' and self.cartan_type()[1] == 2:
494
word = [2,1,2,1]
495
elif self.cartan_type()[0] == 'C' and self.cartan_type()[1] == 2:
496
word = [2,1,2,1]
497
elif self.cartan_type()[0] == 'A' and self.cartan_type()[1] == 2:
498
word = [1,2,1]
499
else:
500
raise NotImplementedError
501
size = self.cardinality()
502
string_data = []
503
for i in range(size):
504
turtle = self.list()[i]
505
string_datum = []
506
for j in word:
507
turtlewalk = 0
508
while not turtle.e(j) == None:
509
turtle = turtle.e(j)
510
turtlewalk += 1
511
string_datum.append(turtlewalk)
512
string_data.append(string_datum)
513
514
if self.cartan_type()[0] == 'A':
515
if labels:
516
c0 = int(55*scaling_factor)
517
c1 = int(-25*scaling_factor)
518
c2 = int(45*tallness*scaling_factor)
519
c3 = int(-12*scaling_factor)
520
c4 = int(-12*scaling_factor)
521
else:
522
c0 = int(45*scaling_factor)
523
c1 = int(-20*scaling_factor)
524
c2 = int(35*tallness*scaling_factor)
525
c3 = int(12*scaling_factor)
526
c4 = int(-12*scaling_factor)
527
outstring = "verbatimtex\n\\magnification=600\netex\n\nbeginfig(-1);\nsx:=35; sy:=30;\n\nz1000=(%d,0);\nz1001=(%d,%d);\nz1002=(%d,%d);\nz2001=(-3,3);\nz2002=(3,3);\nz2003=(0,-3);\nz2004=(7,0);\nz2005=(0,7);\nz2006=(-7,0);\nz2007=(0,7);\n\n"%(c0,c1,c2,c3,c4)
528
else:
529
if labels:
530
outstring = "verbatimtex\n\\magnification=600\netex\n\nbeginfig(-1);\n\nsx := %d;\nsy=%d;\n\nz1000=(2*sx,0);\nz1001=(-sx,sy);\nz1002=(-16,-10);\n\nz2001=(0,-3);\nz2002=(-5,3);\nz2003=(0,3);\nz2004=(5,3);\nz2005=(10,1);\nz2006=(0,10);\nz2007=(-10,1);\nz2008=(0,-8);\n\n"%(int(scaling_factor*40),int(tallness*scaling_factor*40))
531
else:
532
outstring = "beginfig(-1);\n\nsx := %d;\nsy := %d;\n\nz1000=(2*sx,0);\nz1001=(-sx,sy);\nz1002=(-5,-5);\n\nz1003=(10,10);\n\n"%(int(scaling_factor*35),int(tallness*scaling_factor*35))
533
for i in range(size):
534
if self.cartan_type()[0] == 'A':
535
[a1,a2,a3] = string_data[i]
536
else:
537
[a1,a2,a3,a4] = string_data[i]
538
shift = 0
539
for j in range(i):
540
if self.cartan_type()[0] == 'A':
541
[b1,b2,b3] = string_data[j]
542
if b1+b3 == a1+a3 and b2 == a2:
543
shift += 1
544
else:
545
[b1,b2,b3,b4] = string_data[j]
546
if b1+b3 == a1+a3 and b2+b4 == a2+a4:
547
shift += 1
548
if self.cartan_type()[0] == 'A':
549
outstring = outstring +"z%d=%d*z1000+%d*z1001+%d*z1002;\n"%(i,a1+a3,a2,shift)
550
else:
551
outstring = outstring +"z%d=%d*z1000+%d*z1001+%d*z1002;\n"%(i,a1+a3,a2+a4,shift)
552
outstring = outstring + "\n"
553
if thicklines:
554
outstring = outstring +"pickup pencircle scaled 2\n\n"
555
for i in range(size):
556
for j in range(1,3):
557
dest = self.list()[i].f(j)
558
if not dest == None:
559
dest = self.list().index(dest)
560
if j == 1:
561
col = "red;"
562
else:
563
col = "green; "
564
if self.cartan_type()[0] == 'A':
565
[a1,a2,a3] = string_data[i] # included to facilitate hand editing of the .mp file
566
outstring = outstring+"draw z%d--z%d withcolor %s %% %d %d %d\n"%(i,dest,col,a1,a2,a3)
567
else:
568
[a1,a2,a3,a4] = string_data[i]
569
outstring = outstring+"draw z%d--z%d withcolor %s %% %d %d %d %d\n"%(i,dest,col,a1,a2,a3,a4)
570
outstring += "\npickup pencircle scaled 3;\n\n"
571
for i in range(self.cardinality()):
572
if labels:
573
if self.cartan_type()[0] == 'A':
574
outstring = outstring+"pickup pencircle scaled 15;\nfill z%d+z2004..z%d+z2006..z%d+z2006..z%d+z2007..cycle withcolor white;\nlabel(btex %d etex, z%d+z2001);\nlabel(btex %d etex, z%d+z2002);\nlabel(btex %d etex, z%d+z2003);\npickup pencircle scaled .5;\ndraw z%d+z2004..z%d+z2006..z%d+z2006..z%d+z2007..cycle;\n"%(i,i,i,i,string_data[i][2],i,string_data[i][1],i,string_data[i][0],i,i,i,i,i)
575
else:
576
outstring = outstring+"%%%d %d %d %d\npickup pencircle scaled 1;\nfill z%d+z2005..z%d+z2006..z%d+z2007..z%d+z2008..cycle withcolor white;\nlabel(btex %d etex, z%d+z2001);\nlabel(btex %d etex, z%d+z2002);\nlabel(btex %d etex, z%d+z2003);\nlabel(btex %d etex, z%d+z2004);\npickup pencircle scaled .5;\ndraw z%d+z2005..z%d+z2006..z%d+z2007..z%d+z2008..cycle;\n\n"%(string_data[i][0],string_data[i][1],string_data[i][2],string_data[i][3],i,i,i,i,string_data[i][0],i,string_data[i][1],i,string_data[i][2],i,string_data[i][3],i,i,i,i,i)
577
else:
578
outstring += "drawdot z%d;\n"%i
579
outstring += "\nendfig;\n\nend;\n\n"
580
581
f = open(filename, 'w')
582
f.write(outstring)
583
f.close()
584
585
def dot_tex(self):
586
r"""
587
Returns a dot_tex string representation of ``self``.
588
589
EXAMPLES::
590
591
sage: C = CrystalOfLetters(['A',2])
592
sage: C.dot_tex()
593
'digraph G { \n node [ shape=plaintext ];\n N_0 [ label = " ", texlbl = "$1$" ];\n N_1 [ label = " ", texlbl = "$2$" ];\n N_2 [ label = " ", texlbl = "$3$" ];\n N_0 -> N_1 [ label = " ", texlbl = "1" ];\n N_1 -> N_2 [ label = " ", texlbl = "2" ];\n}'
594
"""
595
import re
596
rank = ranker.from_list(self.list())[0]
597
vertex_key = lambda x: "N_"+str(rank(x))
598
599
# To do: check the regular expression
600
# Removing %-style comments, newlines, quotes
601
# This should probably be moved to sage.misc.latex
602
quoted_latex = lambda x: re.sub("\"|\r|(%[^\n]*)?\n","", latex(x))
603
604
result = "digraph G { \n node [ shape=plaintext ];\n"
605
606
for x in self:
607
result += " " + vertex_key(x) + " [ label = \" \", texlbl = \"$"+quoted_latex(x)+"$\" ];\n"
608
for x in self:
609
for i in self.index_set():
610
child = x.f(i)
611
if child is None:
612
continue
613
# result += " " + vertex_key(x) + " -> "+vertex_key(child)+ " [ label = \" \", texlbl = \""+quoted_latex(i)+"\" ];\n"
614
if i == 0:
615
option = "dir = back, "
616
(source, target) = (child, x)
617
else:
618
option = ""
619
(source, target) = (x, child)
620
result += " " + vertex_key(source) + " -> "+vertex_key(target)+ " [ "+option+"label = \" \", texlbl = \""+quoted_latex(i)+"\" ];\n"
621
result+="}"
622
return result
623
624
def plot(self, **options):
625
"""
626
Returns the plot of self as a directed graph.
627
628
EXAMPLES::
629
630
sage: C = CrystalOfLetters(['A', 5])
631
sage: show_default(False) #do not show the plot by default
632
sage: C.plot()
633
Graphics object consisting of 17 graphics primitives
634
"""
635
return self.digraph().plot(edge_labels=True,vertex_size=0,**options)
636
637
def plot3d(self, **options):
638
"""
639
Returns the 3-dimensional plot of self as a directed graph.
640
641
EXAMPLES::
642
643
sage: C = KirillovReshetikhinCrystal(['A',3,1],2,1)
644
sage: C.plot3d()
645
Graphics3d Object
646
"""
647
G = self.digraph(**options)
648
return G.plot3d()
649
650
class ElementMethods:
651
652
def index_set(self):
653
"""
654
EXAMPLES::
655
656
sage: C = CrystalOfLetters(['A',5])
657
sage: C(1).index_set()
658
[1, 2, 3, 4, 5]
659
"""
660
return self.parent().index_set()
661
662
def cartan_type(self):
663
"""
664
Returns the cartan type associated to ``self``
665
666
EXAMPLES::
667
668
sage: C = CrystalOfLetters(['A', 5])
669
sage: C(1).cartan_type()
670
['A', 5]
671
"""
672
return self.parent().cartan_type()
673
674
def weight(self):
675
"""
676
Returns the weight of this crystal element
677
678
EXAMPLES::
679
680
sage: C = CrystalOfLetters(['A',5])
681
sage: C(1).weight()
682
(1, 0, 0, 0, 0, 0)
683
"""
684
return self.Phi() - self.Epsilon()
685
686
@abstract_method
687
def e(self, i):
688
r"""
689
Returns `e_i(x)` if it exists or ``None`` otherwise.
690
691
This method should be implemented by the element class of
692
the crystal.
693
694
EXAMPLES::
695
696
sage: C = Crystals().example(5)
697
sage: x = C[2]; x
698
3
699
sage: x.e(1), x.e(2), x.e(3)
700
(None, 2, None)
701
"""
702
703
@abstract_method
704
def f(self, i):
705
r"""
706
Returns `f_i(x)` if it exists or ``None`` otherwise.
707
708
This method should be implemented by the element class of
709
the crystal.
710
711
EXAMPLES::
712
713
sage: C = Crystals().example(5)
714
sage: x = C[1]; x
715
2
716
sage: x.f(1), x.f(2), x.f(3)
717
(None, 3, None)
718
"""
719
720
def epsilon(self, i):
721
r"""
722
EXAMPLES::
723
724
sage: C = CrystalOfLetters(['A',5])
725
sage: C(1).epsilon(1)
726
0
727
sage: C(2).epsilon(1)
728
1
729
"""
730
assert i in self.index_set()
731
x = self
732
eps = 0
733
while True:
734
x = x.e(i)
735
if x is None:
736
break
737
eps = eps+1
738
return eps
739
740
def phi(self, i):
741
r"""
742
EXAMPLES::
743
744
sage: C = CrystalOfLetters(['A',5])
745
sage: C(1).phi(1)
746
1
747
sage: C(2).phi(1)
748
0
749
"""
750
assert i in self.index_set()
751
x = self
752
phi = 0
753
while True:
754
x = x.f(i)
755
if x is None:
756
break
757
phi = phi+1
758
return phi
759
760
def phi_minus_epsilon(self, i):
761
"""
762
Returns `\phi_i - \epsilon_i` of self. There are sometimes
763
better implementations using the weight for this. It is used
764
for reflections along a string.
765
766
EXAMPLES::
767
768
sage: C = CrystalOfLetters(['A',5])
769
sage: C(1).phi_minus_epsilon(1)
770
1
771
"""
772
return self.phi(i) - self.epsilon(i)
773
774
def Epsilon(self):
775
"""
776
EXAMPLES::
777
778
sage: C = CrystalOfLetters(['A',5])
779
sage: C(0).Epsilon()
780
(0, 0, 0, 0, 0, 0)
781
sage: C(1).Epsilon()
782
(0, 0, 0, 0, 0, 0)
783
sage: C(2).Epsilon()
784
(1, 0, 0, 0, 0, 0)
785
"""
786
Lambda = self.parent().Lambda()
787
return sum(self.epsilon(i) * Lambda[i] for i in self.index_set())
788
789
def Phi(self):
790
"""
791
EXAMPLES::
792
793
sage: C = CrystalOfLetters(['A',5])
794
sage: C(0).Phi()
795
(0, 0, 0, 0, 0, 0)
796
sage: C(1).Phi()
797
(1, 0, 0, 0, 0, 0)
798
sage: C(2).Phi()
799
(1, 1, 0, 0, 0, 0)
800
"""
801
Lambda = self.parent().Lambda()
802
return sum(self.phi(i) * Lambda[i] for i in self.index_set())
803
804
def f_string(self, list):
805
r"""
806
Applies `f_{i_r} ... f_{i_1}` to self for `list = [i_1, ..., i_r]`
807
808
EXAMPLES::
809
810
sage: C = CrystalOfLetters(['A',3])
811
sage: b = C(1)
812
sage: b.f_string([1,2])
813
3
814
sage: b.f_string([2,1])
815
816
"""
817
b = self
818
for i in list:
819
b = b.f(i)
820
if b is None:
821
return None
822
return b
823
824
def e_string(self, list):
825
r"""
826
Applies `e_{i_r} ... e_{i_1}` to self for `list = [i_1, ..., i_r]`
827
828
EXAMPLES::
829
830
sage: C = CrystalOfLetters(['A',3])
831
sage: b = C(3)
832
sage: b.e_string([2,1])
833
1
834
sage: b.e_string([1,2])
835
836
"""
837
b = self
838
for i in list:
839
b = b.e(i)
840
if b is None:
841
return None
842
return b
843
844
def s(self, i):
845
r"""
846
Returns the reflection of ``self`` along its `i`-string
847
848
EXAMPLES::
849
850
sage: C = CrystalOfTableaux(['A',2], shape=[2,1])
851
sage: b=C(rows=[[1,1],[3]])
852
sage: b.s(1)
853
[[2, 2], [3]]
854
sage: b=C(rows=[[1,2],[3]])
855
sage: b.s(2)
856
[[1, 2], [3]]
857
sage: T=CrystalOfTableaux(['A',2],shape=[4])
858
sage: t=T(rows=[[1,2,2,2]])
859
sage: t.s(1)
860
[[1, 1, 1, 2]]
861
"""
862
d = self.phi_minus_epsilon(i)
863
b = self
864
if d > 0:
865
for j in range(d):
866
b = b.f(i)
867
else:
868
for j in range(-d):
869
b = b.e(i)
870
return b
871
872
def demazure_operator(self, i, truncated = False):
873
r"""
874
Returns the list of the elements one can obtain from
875
``self`` by application of `f_i`. If the option
876
"truncated" is set to True, then ``self`` is not included
877
in the list.
878
879
REFERENCES:
880
881
.. [L1995] Peter Littelmann, Crystal graphs and Young tableaux,
882
J. Algebra 175 (1995), no. 1, 65--87.
883
884
.. [K1993] Masaki Kashiwara, The crystal base and Littelmann's refined Demazure character formula,
885
Duke Math. J. 71 (1993), no. 3, 839--858.
886
887
EXAMPLES::
888
889
sage: T = CrystalOfTableaux(['A',2], shape=[2,1])
890
sage: t=T(rows=[[1,2],[2]])
891
sage: t.demazure_operator(2)
892
[[[1, 2], [2]], [[1, 3], [2]], [[1, 3], [3]]]
893
sage: t.demazure_operator(2, truncated = True)
894
[[[1, 3], [2]], [[1, 3], [3]]]
895
sage: t.demazure_operator(1, truncated = True)
896
[]
897
sage: t.demazure_operator(1)
898
[[[1, 2], [2]]]
899
900
sage: K = KirillovReshetikhinCrystal(['A',2,1],2,1)
901
sage: t = K(rows=[[3],[2]])
902
sage: t.demazure_operator(0)
903
[[[2, 3]], [[1, 2]]]
904
"""
905
if truncated:
906
l = []
907
else:
908
l = [self]
909
for k in range(self.phi(i)):
910
l.append(self.f_string([i for j in range(k+1)]))
911
return(l)
912
913
def stembridgeDelta_depth(self,i,j):
914
r"""
915
The `i`-depth of a crystal node `x` is ``-x.epsilon(i)``.
916
This function returns the difference in the `j`-depth of `x` and ``x.e(i)``,
917
where `i` and `j` are in the index set of the underlying crystal.
918
This function is useful for checking the Stembridge local axioms for crystal bases.
919
920
EXAMPLES::
921
922
sage: T = CrystalOfTableaux(['A',2], shape=[2,1])
923
sage: t=T(rows=[[1,2],[2]])
924
sage: t.stembridgeDelta_depth(1,2)
925
0
926
sage: s=T(rows=[[2,3],[3]])
927
sage: s.stembridgeDelta_depth(1,2)
928
-1
929
"""
930
if self.e(i) is None: return 0
931
return -self.e(i).epsilon(j) + self.epsilon(j)
932
933
def stembridgeDelta_rise(self,i,j):
934
r"""
935
The `i`-rise of a crystal node `x` is ``x.phi(i)``.
936
937
This function returns the difference in the `j`-rise of `x` and ``x.e(i)``,
938
where `i` and `j` are in the index set of the underlying crystal.
939
This function is useful for checking the Stembridge local axioms for crystal bases.
940
941
EXAMPLES::
942
943
sage: T = CrystalOfTableaux(['A',2], shape=[2,1])
944
sage: t=T(rows=[[1,2],[2]])
945
sage: t.stembridgeDelta_rise(1,2)
946
-1
947
sage: s=T(rows=[[2,3],[3]])
948
sage: s.stembridgeDelta_rise(1,2)
949
0
950
"""
951
if self.e(i) is None: return 0
952
return self.e(i).phi(j) - self.phi(j)
953
954
def stembridgeDel_depth(self,i,j):
955
r"""
956
The `i`-depth of a crystal node `x` is ``-x.epsilon(i)``.
957
This function returns the difference in the `j`-depth of `x` and ``x.f(i)``,
958
where `i` and `j` are in the index set of the underlying crystal.
959
This function is useful for checking the Stembridge local axioms for crystal bases.
960
961
EXAMPLES::
962
963
sage: T = CrystalOfTableaux(['A',2], shape=[2,1])
964
sage: t=T(rows=[[1,1],[2]])
965
sage: t.stembridgeDel_depth(1,2)
966
0
967
sage: s=T(rows=[[1,3],[3]])
968
sage: s.stembridgeDel_depth(1,2)
969
-1
970
"""
971
if self.f(i) is None: return 0
972
return -self.epsilon(j) + self.f(i).epsilon(j)
973
974
def stembridgeDel_rise(self,i,j):
975
r"""
976
The `i`-rise of a crystal node `x` is ``x.phi(i)``.
977
This function returns the difference in the `j`-rise of `x` and ``x.f(i)``,
978
where `i` and `j` are in the index set of the underlying crystal.
979
This function is useful for checking the Stembridge local axioms for crystal bases.
980
981
EXAMPLES::
982
983
sage: T = CrystalOfTableaux(['A',2], shape=[2,1])
984
sage: t=T(rows=[[1,1],[2]])
985
sage: t.stembridgeDel_rise(1,2)
986
-1
987
sage: s=T(rows=[[1,3],[3]])
988
sage: s.stembridgeDel_rise(1,2)
989
0
990
"""
991
if self.f(i) is None: return 0
992
return self.phi(j)-self.f(i).phi(j)
993
994
def stembridgeTriple(self,i,j):
995
r"""
996
Let `A` be the Cartan matrix of the crystal, `x` a crystal element,
997
and let `i` and `j` be in the index set of the crystal.
998
Further, set
999
``b=stembridgeDelta_depth(x,i,j)``, and
1000
``c=stembridgeDelta_rise(x,i,j))``.
1001
If ``x.e(i)`` is non-empty, this function returns the triple
1002
`( A_{ij}, b, c )`; otherwise it returns ``None``.
1003
By the Stembridge local characterization of crystal bases, one should have `A_{ij}=b+c`.
1004
1005
EXAMPLES::
1006
1007
sage: T = CrystalOfTableaux(['A',2], shape=[2,1])
1008
sage: t=T(rows=[[1,1],[2]])
1009
sage: t.stembridgeTriple(1,2)
1010
sage: s=T(rows=[[1,2],[2]])
1011
sage: s.stembridgeTriple(1,2)
1012
(-1, 0, -1)
1013
1014
sage: T = CrystalOfTableaux(['B',2], shape=[2,1])
1015
sage: t=T(rows=[[1,2],[2]])
1016
sage: t.stembridgeTriple(1,2)
1017
(-2, 0, -2)
1018
sage: s=T(rows=[[-1,-1],[0]])
1019
sage: s.stembridgeTriple(1,2)
1020
(-2, -2, 0)
1021
sage: u=T(rows=[[0,2],[1]])
1022
sage: u.stembridgeTriple(1,2)
1023
(-2, -1, -1)
1024
"""
1025
if self.e(i) is None: return None
1026
A=self.cartan_type().cartan_matrix()
1027
b=self.stembridgeDelta_depth(i,j)
1028
c=self.stembridgeDelta_rise(i,j)
1029
dd=self.cartan_type().dynkin_diagram()
1030
a=dd[j,i]
1031
return (a, b, c)
1032
1033
def _test_stembridge_local_axioms(self, index_set=None, verbose=False, **options):
1034
r"""
1035
This implements tests for the Stembridge local characterization on the element of a crystal ``self``.
1036
The current implementation only uses the axioms for simply-laced types. Crystals
1037
of other types should still pass the test, but in non-simply-laced types,
1038
passing is not a guarantee that the crystal arises from a representation.
1039
1040
One can specify an index set smaller than the full index set of the crystal,
1041
using the option ``index_set``.
1042
1043
Running with ``verbose=True`` will print warnings when a test fails.
1044
1045
REFERENCES::
1046
1047
.. [S2003] John R. Stembridge, A Local Characterization of Simply-Laced Crystals,
1048
Transactions of the American Mathematical Society, Vol. 355, No. 12 (Dec., 2003), pp. 4807-4823
1049
1050
1051
EXAMPLES::
1052
1053
sage: T = CrystalOfTableaux(['A',2], shape=[2,1])
1054
sage: t=T(rows=[[1,1],[2]])
1055
sage: t._test_stembridge_local_axioms()
1056
True
1057
sage: t._test_stembridge_local_axioms(index_set=[1,3])
1058
True
1059
sage: t._test_stembridge_local_axioms(verbose=True)
1060
True
1061
"""
1062
tester = self._tester(**options)
1063
goodness=True
1064
A=self.cartan_type().cartan_matrix()
1065
if index_set is None: index_set=self.index_set()
1066
1067
for (i,j) in Subsets(index_set, 2):
1068
if self.e(i) is not None and self.e(j) is not None:
1069
triple=self.stembridgeTriple(i,j)
1070
#Test axioms P3 and P4.
1071
if not triple[0]==triple[1]+triple[2] or triple[1]>0 or triple[2]>0:
1072
if verbose:
1073
print 'Warning: Failed axiom P3 or P4 at vector ', self, 'i,j=', i, j, 'Stembridge triple:', self.stembridgeTriple(i,j)
1074
goodness=False
1075
else:
1076
tester.fail()
1077
if self.stembridgeDelta_depth(i,j)==0:
1078
#check E_i E_j(x)= E_j E_i(x)
1079
if self.e(i).e(j)!=self.e(j).e(i) or self.e(i).e(j).stembridgeDel_rise(j, i)!=0:
1080
if complete:
1081
print 'Warning: Failed axiom P5 at: vector ', self, 'i,j=', i, j, 'Stembridge triple:', stembridgeTriple(x,i,j)
1082
goodness=False
1083
else:
1084
tester.fail()
1085
if self.stembridgeDelta_depth(i,j)==-1 and self.stembridgeDelta_depth(j,i)==-1:
1086
#check E_i E_j^2 E_i (x)= E_j E_i^2 E_j (x)
1087
y1=self.e(j).e(i).e(i).e(j)
1088
y2=self.e(j).e(i).e(i).e(j)
1089
a=y1.stembridgeDel_rise(j, i)
1090
b=y2.stembridgeDel_rise(i, j)
1091
if y1!=y2 or a!=-1 or b!=-1:
1092
if verbose:
1093
print 'Warning: Failed axiom P6 at: vector ', x, 'i,j=', i, j, 'Stembridge triple:', stembridgeTriple(x,i,j)
1094
goodness=False
1095
else:
1096
tester.fail()
1097
tester.assertTrue(goodness)
1098
return goodness
1099
1100
def is_highest_weight(self, index_set = None):
1101
r"""
1102
Returns ``True`` if ``self`` is a highest weight.
1103
Specifying the option ``index_set`` to be a subset `I` of the
1104
index set of the underlying crystal, finds all highest
1105
weight vectors for arrows in `I`.
1106
1107
EXAMPLES::
1108
1109
sage: C = CrystalOfLetters(['A',5])
1110
sage: C(1).is_highest_weight()
1111
True
1112
sage: C(2).is_highest_weight()
1113
False
1114
sage: C(2).is_highest_weight(index_set = [2,3,4,5])
1115
True
1116
"""
1117
if index_set is None:
1118
index_set = self.index_set()
1119
return all(self.e(i) is None for i in index_set)
1120
1121
def is_lowest_weight(self, index_set = None):
1122
r"""
1123
Returns ``True`` if ``self`` is a lowest weight.
1124
Specifying the option ``index_set`` to be a subset `I` of the
1125
index set of the underlying crystal, finds all lowest
1126
weight vectors for arrows in `I`.
1127
1128
EXAMPLES::
1129
1130
sage: C = CrystalOfLetters(['A',5])
1131
sage: C(1).is_lowest_weight()
1132
False
1133
sage: C(6).is_lowest_weight()
1134
True
1135
sage: C(4).is_lowest_weight(index_set = [1,3])
1136
True
1137
"""
1138
if index_set is None:
1139
index_set = self.index_set()
1140
return all(self.f(i) is None for i in index_set)
1141
1142
def to_highest_weight(self, index_set = None):
1143
r"""
1144
Yields the highest weight element `u` and a list `[i_1,...,i_k]`
1145
such that `self = f_{i_1} ... f_{i_k} u`, where `i_1,...,i_k` are
1146
elements in `index_set`. By default the index set is assumed to be
1147
the full index set of self.
1148
1149
EXAMPLES::
1150
1151
sage: T = CrystalOfTableaux(['A',3], shape = [1])
1152
sage: t = T(rows = [[3]])
1153
sage: t.to_highest_weight()
1154
[[[1]], [2, 1]]
1155
sage: T = CrystalOfTableaux(['A',3], shape = [2,1])
1156
sage: t = T(rows = [[1,2],[4]])
1157
sage: t.to_highest_weight()
1158
[[[1, 1], [2]], [1, 3, 2]]
1159
sage: t.to_highest_weight(index_set = [3])
1160
[[[1, 2], [3]], [3]]
1161
sage: K = KirillovReshetikhinCrystal(['A',3,1],2,1)
1162
sage: t = K(rows=[[2],[3]]); t.to_highest_weight(index_set=[1])
1163
[[[1], [3]], [1]]
1164
sage: t.to_highest_weight()
1165
Traceback (most recent call last):
1166
...
1167
ValueError: This is not a highest weight crystals!
1168
"""
1169
from sage.categories.highest_weight_crystals import HighestWeightCrystals
1170
if index_set is None:
1171
if HighestWeightCrystals() not in self.parent().categories():
1172
raise ValueError, "This is not a highest weight crystals!"
1173
index_set = self.index_set()
1174
for i in index_set:
1175
if self.epsilon(i) <> 0:
1176
self = self.e(i)
1177
hw = self.to_highest_weight(index_set = index_set)
1178
return [hw[0], [i] + hw[1]]
1179
return [self, []]
1180
1181
def to_lowest_weight(self, index_set = None):
1182
r"""
1183
Yields the lowest weight element `u` and a list `[i_1,...,i_k]`
1184
such that `self = e_{i_1} ... e_{i_k} u`, where `i_1,...,i_k` are
1185
elements in `index_set`. By default the index set is assumed to be
1186
the full index set of self.
1187
1188
EXAMPLES::
1189
1190
sage: T = CrystalOfTableaux(['A',3], shape = [1])
1191
sage: t = T(rows = [[3]])
1192
sage: t.to_lowest_weight()
1193
[[[4]], [3]]
1194
sage: T = CrystalOfTableaux(['A',3], shape = [2,1])
1195
sage: t = T(rows = [[1,2],[4]])
1196
sage: t.to_lowest_weight()
1197
[[[3, 4], [4]], [1, 2, 2, 3]]
1198
sage: t.to_lowest_weight(index_set = [3])
1199
[[[1, 2], [4]], []]
1200
sage: K = KirillovReshetikhinCrystal(['A',3,1],2,1)
1201
sage: t = K.module_generator(); t
1202
[[1], [2]]
1203
sage: t.to_lowest_weight(index_set=[1,2,3])
1204
[[[3], [4]], [2, 1, 3, 2]]
1205
sage: t.to_lowest_weight()
1206
Traceback (most recent call last):
1207
...
1208
ValueError: This is not a highest weight crystals!
1209
"""
1210
from sage.categories.highest_weight_crystals import HighestWeightCrystals
1211
if index_set is None:
1212
if HighestWeightCrystals() not in self.parent().categories():
1213
raise ValueError, "This is not a highest weight crystals!"
1214
index_set = self.index_set()
1215
for i in index_set:
1216
if self.phi(i) <> 0:
1217
self = self.f(i)
1218
lw = self.to_lowest_weight(index_set = index_set)
1219
return [lw[0], [i] + lw[1]]
1220
return [self, []]
1221
1222