Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/combinat/diagram_algebras.py
8815 views
1
r"""
2
Diagram and Partition Algebras
3
4
AUTHORS:
5
6
- Mike Hansen (2007): Initial version
7
- Stephen Doty, Aaron Lauve, George H. Seelinger (2012): Implementation of
8
partition, Brauer, Temperley--Lieb, and ideal partition algebras
9
"""
10
11
#*****************************************************************************
12
# Copyright (C) 2007 Mike Hansen <[email protected]>,
13
# 2012 Stephen Doty <[email protected]>,
14
# Aaron Lauve <[email protected]>,
15
# George H. Seelinger <[email protected]>
16
#
17
# Distributed under the terms of the GNU General Public License (GPL)
18
# http://www.gnu.org/licenses/
19
#*****************************************************************************
20
21
from sage.categories.all import FiniteDimensionalAlgebrasWithBasis
22
from sage.structure.element import generic_power
23
from sage.combinat.free_module import (CombinatorialFreeModule,
24
CombinatorialFreeModuleElement)
25
from sage.combinat.set_partition import SetPartitions, SetPartition
26
from sage.sets.set import Set
27
from sage.graphs.graph import Graph
28
from sage.misc.cachefunc import cached_method
29
from sage.rings.all import ZZ
30
import math
31
32
def partition_diagrams(k):
33
r"""
34
Return a list of all partition diagrams of order ``k``.
35
36
A partition diagram of order `k \in \ZZ` to is a set partition of
37
`\{1, \dots, k, -1, \ldots, -k\}`. If we have `k - 1/2 \in ZZ`, then
38
a partition diagram of order `k \in 1/2 \ZZ` is a set partition of
39
`\{1, \ldots, k+1/2, -1, \ldots, -(k+1/2)\}` with `k+1/2` and `-(k+1/2)`
40
in the same block. See [HR2005]_.
41
42
INPUT:
43
44
- ``k`` -- the order of the partition diagrams
45
46
EXAMPLES::
47
48
sage: import sage.combinat.diagram_algebras as da
49
sage: da.partition_diagrams(2)
50
[{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, {{-2, -1, 1}, {2}},
51
{{-2}, {-1, 1, 2}}, {{-2, 1, 2}, {-1}}, {{-2, 1}, {-1, 2}},
52
{{-2, 2}, {-1, 1}}, {{-2, -1}, {1, 2}}, {{-2, -1}, {1}, {2}},
53
{{-2}, {-1, 2}, {1}}, {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 1}, {2}},
54
{{-2, 1}, {-1}, {2}}, {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}]
55
sage: da.partition_diagrams(3/2)
56
[{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, {{-2, 2}, {-1, 1}},
57
{{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}]
58
"""
59
if k in ZZ:
60
return SetPartitions( range(1, k+1) + [-j for j in range(1, k+1)] ).list()
61
# Else k in 1/2 ZZ
62
L = []
63
k += ZZ(1) / ZZ(2)
64
for sp in SetPartitions( range(1, k+1) + [-j for j in range(1, k)] ):
65
sp = list(sp)
66
for i in range(len(sp)):
67
if k in sp[i]:
68
sp[i] += Set([-k])
69
break
70
L.append(SetPartition(sp))
71
return L
72
73
def brauer_diagrams(k):
74
r"""
75
Return a list of all Brauer diagrams of order ``k``.
76
77
A Brauer diagram of order `k` is a partition diagram of order `k`
78
with block size 2.
79
80
INPUT:
81
82
- ``k`` -- the order of the Brauer diagrams
83
84
EXAMPLES::
85
86
sage: import sage.combinat.diagram_algebras as da
87
sage: da.brauer_diagrams(2)
88
[{{-2, 1}, {-1, 2}}, {{-2, 2}, {-1, 1}}, {{-2, -1}, {1, 2}}]
89
sage: da.brauer_diagrams(5/2)
90
[{{-3, 3}, {-2, 1}, {-1, 2}}, {{-3, 3}, {-2, 2}, {-1, 1}}, {{-3, 3}, {-2, -1}, {1, 2}}]
91
"""
92
if k in ZZ:
93
return [SetPartition(list(x)) for x in
94
SetPartitions( range(1,k+1) + [-j for j in range(1,k+1)],
95
[2 for j in range(1,k+1)] )]
96
# Else k in 1/2 ZZ
97
L = []
98
k += ZZ(1) / ZZ(2)
99
for i in SetPartitions( range(1, k) + [-j for j in range(1, k)],
100
[2 for j in range(1, k)] ):
101
L.append(SetPartition(list(i) + [Set([k, -k])]))
102
return L
103
104
def temperley_lieb_diagrams(k):
105
r"""
106
Return a list of all Temperley--Lieb diagrams of order ``k``.
107
108
A Temperley--Lieb diagram of order `k` is a partition diagram of order `k`
109
with block size 2 and is planar.
110
111
INPUT:
112
113
- ``k`` -- the order of the Temperley--Lieb diagrams
114
115
EXAMPLES::
116
117
sage: import sage.combinat.diagram_algebras as da
118
sage: da.temperley_lieb_diagrams(2)
119
[{{-2, 2}, {-1, 1}}, {{-2, -1}, {1, 2}}]
120
sage: da.temperley_lieb_diagrams(5/2)
121
[{{-3, 3}, {-2, 2}, {-1, 1}}, {{-3, 3}, {-2, -1}, {1, 2}}]
122
"""
123
B = brauer_diagrams(k)
124
T = []
125
for i in B:
126
if is_planar(i) == True:
127
T.append(i)
128
return T
129
130
def planar_diagrams(k):
131
r"""
132
Return a list of all planar diagrams of order ``k``.
133
134
A planar diagram of order `k` is a partition diagram of order `k`
135
that has no crossings.
136
137
EXAMPLES::
138
139
sage: import sage.combinat.diagram_algebras as da
140
sage: da.planar_diagrams(2)
141
[{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, {{-2, -1, 1}, {2}},
142
{{-2}, {-1, 1, 2}}, {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1, 1}},
143
{{-2, -1}, {1, 2}}, {{-2, -1}, {1}, {2}}, {{-2}, {-1, 2}, {1}},
144
{{-2, 2}, {-1}, {1}}, {{-2}, {-1, 1}, {2}}, {{-2, 1}, {-1}, {2}},
145
{{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}]
146
sage: da.planar_diagrams(3/2)
147
[{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, {{-2, 2}, {-1, 1}},
148
{{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}]
149
"""
150
A = partition_diagrams(k)
151
P = []
152
for i in A:
153
if is_planar(i) == True:
154
P.append(i)
155
return P
156
157
def ideal_diagrams(k):
158
r"""
159
Return a list of all "ideal" diagrams of order ``k``.
160
161
An ideal diagram of order `k` is a partition diagram of order `k` with
162
propagating number less than `k`.
163
164
EXAMPLES::
165
166
sage: import sage.combinat.diagram_algebras as da
167
sage: da.ideal_diagrams(2)
168
[{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, {{-2, -1, 1}, {2}}, {{-2}, {-1, 1, 2}},
169
{{-2, 1, 2}, {-1}}, {{-2, -1}, {1, 2}}, {{-2, -1}, {1}, {2}},
170
{{-2}, {-1, 2}, {1}}, {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 1}, {2}}, {{-2, 1},
171
{-1}, {2}}, {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}]
172
sage: da.ideal_diagrams(3/2)
173
[{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}]
174
"""
175
A = partition_diagrams(k)
176
I = []
177
for i in A:
178
if propagating_number(i) < k:
179
I.append(i)
180
return I
181
182
class DiagramAlgebra(CombinatorialFreeModule):
183
r"""
184
Abstract class for diagram algebras and is not designed to be used
185
directly. If used directly, the class could create an "algebra"
186
that is not actually an algebra.
187
188
TESTS::
189
190
sage: import sage.combinat.diagram_algebras as da
191
sage: R.<x> = QQ[]
192
sage: D = da.DiagramAlgebra(2, x, R, 'P', da.partition_diagrams)
193
sage: sorted(D.basis())
194
[P[{{-2}, {-1}, {1}, {2}}],
195
P[{{-2}, {-1}, {1, 2}}],
196
P[{{-2}, {-1, 1}, {2}}],
197
P[{{-2}, {-1, 1, 2}}],
198
P[{{-2}, {-1, 2}, {1}}],
199
P[{{-2, -1}, {1}, {2}}],
200
P[{{-2, -1}, {1, 2}}],
201
P[{{-2, -1, 1}, {2}}],
202
P[{{-2, -1, 1, 2}}],
203
P[{{-2, -1, 2}, {1}}],
204
P[{{-2, 1}, {-1}, {2}}],
205
P[{{-2, 1}, {-1, 2}}],
206
P[{{-2, 1, 2}, {-1}}],
207
P[{{-2, 2}, {-1}, {1}}],
208
P[{{-2, 2}, {-1, 1}}]]
209
"""
210
def __init__(self, k, q, base_ring, prefix, diagrams, category=None):
211
r"""
212
Initialize ``self``.
213
214
INPUT:
215
216
- ``k`` -- the rank
217
- ``q`` -- the deformation parameter
218
- ``base_ring`` -- the base ring
219
- ``prefix`` -- the prefix of our monomials
220
- ``diagrams`` -- the *function* which will generate all diagrams
221
(i.e. indices for the basis elements)
222
223
TESTS::
224
225
sage: import sage.combinat.diagram_algebras as da
226
sage: R.<x> = QQ[]
227
sage: D = da.DiagramAlgebra(2, x, R, 'P', da.partition_diagrams)
228
sage: TestSuite(D).run()
229
"""
230
self._prefix = prefix
231
self._q = base_ring(q)
232
self._k = k
233
if category is None:
234
category = FiniteDimensionalAlgebrasWithBasis(base_ring)
235
CombinatorialFreeModule.__init__(self, base_ring, diagrams(k),
236
category=category, prefix=prefix)
237
238
def _element_constructor_(self, set_partition):
239
r"""
240
Construct an element of ``self``.
241
242
TESTS::
243
244
sage: import sage.combinat.diagram_algebras as da
245
sage: R.<x> = QQ[]
246
sage: D = da.DiagramAlgebra(2, x, R, 'P', da.partition_diagrams)
247
sage: sp = da.to_set_partition( [[1,2], [-1,-2]] )
248
sage: b_elt = D(sp); b_elt
249
P[{{-2, -1}, {1, 2}}]
250
sage: b_elt in D
251
True
252
sage: D([[1,2],[-1,-2]]) == b_elt
253
True
254
sage: D([{1,2},{-1,-2}]) == b_elt
255
True
256
"""
257
if set_partition in self.basis().keys():
258
return CombinatorialFreeModule._element_constructor_(self, set_partition)
259
260
sp = SetPartition(set_partition) # attempt conversion
261
if sp in self.basis().keys():
262
return self.basis()[sp]
263
264
raise ValueError("invalid input of {0}".format(set_partition))
265
266
def __getitem__(self, i):
267
"""
268
Get the basis item of ``self`` indexed by ``i``.
269
270
EXAMPLES::
271
272
sage: import sage.combinat.diagram_algebras as da
273
sage: R.<x> = QQ[]
274
sage: D = da.DiagramAlgebra(2, x, R, 'P', da.partition_diagrams)
275
sage: sp = da.to_set_partition( [[1,2], [-1,-2]] )
276
sage: D[sp]
277
P[{{-2, -1}, {1, 2}}]
278
"""
279
i = to_set_partition(i)
280
if i in self.basis().keys():
281
return self.basis()[i]
282
raise ValueError("{0} is not an index of a basis element".format(i))
283
284
def order(self):
285
r"""
286
Return the order of ``self``.
287
288
The order of a partition algebra is defined as half of the number
289
of nodes in the diagrams.
290
291
EXAMPLES::
292
293
sage: q = var('q')
294
sage: PA = PartitionAlgebra(2, q)
295
sage: PA.order()
296
2
297
"""
298
return self._k
299
300
def set_partitions(self):
301
r"""
302
Return the collection of underlying set partitions indexing the
303
basis elements of a given diagram algebra.
304
305
TESTS::
306
307
sage: import sage.combinat.diagram_algebras as da
308
sage: R.<x> = QQ[]
309
sage: D = da.DiagramAlgebra(2, x, R, 'P', da.partition_diagrams)
310
sage: list(D.set_partitions()) == da.partition_diagrams(2)
311
True
312
"""
313
return self.basis().keys()
314
315
def product_on_basis(self, d1, d2):
316
r"""
317
Returns the product `D_{d_1} D_{d_2}` by two basis diagrams.
318
319
TESTS::
320
321
sage: import sage.combinat.diagram_algebras as da
322
sage: R.<x> = QQ[]
323
sage: D = da.DiagramAlgebra(2, x, R, 'P', da.partition_diagrams)
324
sage: sp = SetPartition([[1,2],[-1,-2]])
325
sage: D.product_on_basis(sp, sp)
326
x*P[{{-2, -1}, {1, 2}}]
327
"""
328
(composite_diagram, loops_removed) = set_partition_composition(d1, d2)
329
return self.term(composite_diagram, self._q**loops_removed)
330
331
@cached_method
332
def one_basis(self):
333
r"""
334
The following constructs the identity element of the diagram algebra.
335
336
It is not called directly; instead one should use ``DA.one()`` if
337
``DA`` is a defined diagram algebra.
338
339
EXAMPLES::
340
341
sage: import sage.combinat.diagram_algebras as da
342
sage: R.<x> = QQ[]
343
sage: D = da.DiagramAlgebra(2, x, R, 'P', da.partition_diagrams)
344
sage: D.one_basis()
345
{{-2, 2}, {-1, 1}}
346
"""
347
return identity_set_partition(self._k)
348
349
# The following subclass provides a few additional methods for
350
# partition algebra elements.
351
class Element(CombinatorialFreeModuleElement):
352
r"""
353
This subclass provides a few additional methods for
354
partition algebra elements. Most element methods are
355
already implemented elsewhere.
356
"""
357
def diagram(self):
358
r"""
359
Return the underlying diagram of ``self`` if ``self`` is a basis
360
element. Raises an error if ``self`` is not a basis element.
361
362
EXAMPLES::
363
364
sage: R.<x> = ZZ[]
365
sage: P = PartitionAlgebra(2, x, R)
366
sage: elt = 3*P([[1,2],[-2,-1]])
367
sage: elt.diagram()
368
{{-2, -1}, {1, 2}}
369
"""
370
if len(self) != 1:
371
raise ValueError("this is only defined for basis elements")
372
PA = self.parent()
373
ans = self.support_of_term()
374
if ans not in partition_diagrams(PA.order()):
375
raise ValueError("element should be keyed by a diagram")
376
return ans
377
378
def diagrams(self):
379
r"""
380
Return the diagrams in the support of ``self``.
381
382
EXAMPLES::
383
384
sage: R.<x> = ZZ[]
385
sage: P = PartitionAlgebra(2, x, R)
386
sage: elt = 3*P([[1,2],[-2,-1]]) + P([[1,2],[-2], [-1]])
387
sage: elt.diagrams()
388
[{{-2}, {-1}, {1, 2}}, {{-2, -1}, {1, 2}}]
389
"""
390
return self.support()
391
392
def _latex_(self):
393
r"""
394
Return `\LaTeX` representation of ``self`` to draw single
395
diagrams in latex using tikz.
396
397
EXAMPLES::
398
399
sage: R.<x> = ZZ[]
400
sage: P = PartitionAlgebra(2, x, R)
401
sage: latex(P([[1,2],[-2,-1]]))
402
\begin{tikzpicture}[scale = 0.9,thick]
403
\tikzstyle{vertex} = [shape = circle, minimum size = 9pt, inner sep = 1pt]
404
\node[vertex] (G--2) at (1.5, -1) [shape = circle, draw] {};
405
\node[vertex] (G--1) at (0.0, -1) [shape = circle, draw] {};
406
\node[vertex] (G-1) at (0.0, 1) [shape = circle, draw] {};
407
\node[vertex] (G-2) at (1.5, 1) [shape = circle, draw] {};
408
\draw (G--2) .. controls +(-0.5, 0.5) and +(0.5, 0.5) .. (G--1);
409
\draw (G-1) .. controls +(0.5, -0.5) and +(-0.5, -0.5) .. (G-2);
410
\end{tikzpicture}
411
"""
412
# these allow the view command to work (maybe move them somewhere more appropriate?)
413
from sage.misc.latex import latex
414
latex.add_to_mathjax_avoid_list('tikzpicture')
415
latex.add_package_to_preamble_if_available('tikz')
416
417
# Define the sign function
418
def sgn(x):
419
if x > 0:
420
return 1
421
if x < 0:
422
return -1
423
return 0
424
diagram = self.diagram()
425
l1 = [] #list of blocks
426
l2 = [] #lsit of nodes
427
for i in list(diagram):
428
l1.append(list(i))
429
for j in list(i):
430
l2.append(j)
431
#setup beginning of picture
432
output = "\\begin{tikzpicture}[scale = 0.9,thick] \n\\tikzstyle{vertex} = [shape = circle, minimum size = 9pt, inner sep = 1pt] \n"
433
for i in l2: #add nodes
434
output = output + "\\node[vertex] (G-%s) at (%s, %s) [shape = circle, draw] {}; \n" % (i, (abs(i)-1)*1.5, sgn(i))
435
for i in l1: #add edges
436
if (len(i) > 1):
437
l4 = list(i)
438
posList = []
439
negList = []
440
for i in l4: #sort list so rows are grouped together
441
if i > 0:
442
posList.append(i)
443
elif i < 0:
444
negList.append(i)
445
posList.sort()
446
negList.sort()
447
l4 = posList + negList
448
l5 = l4[:] #deep copy
449
for j in range(len(l5)):
450
l5[j-1] = l4[j] #create a permuted list
451
if (len(l4) == 2):
452
l4.pop()
453
l5.pop() #pops to prevent duplicating edges
454
for j in zip(l4, l5):
455
xdiff = abs(j[1])-abs(j[0])
456
y1 = sgn(j[0])
457
y2 = sgn(j[1])
458
if ((y2-y1) == 0 and abs(xdiff) < 5): #if nodes are close to each other on same row
459
diffCo = (0.5+0.1*(abs(xdiff)-1)) #gets bigger as nodes are farther apart; max value of 1; min value of 0.5.
460
outVec = (sgn(xdiff)*diffCo, -1*diffCo*y1)
461
inVec = (-1*diffCo*sgn(xdiff), -1*diffCo*y2)
462
elif ((y2-y1) != 0 and abs(xdiff) == 1): #if nodes are close enough curviness looks bad.
463
outVec = (sgn(xdiff)*0.75, -1*y1)
464
inVec = (-1*sgn(xdiff)*0.75, -1*y2)
465
else:
466
outVec = (sgn(xdiff)*1, -1*y1)
467
inVec = (-1*sgn(xdiff), -1*y2)
468
output = output + "\\draw (G-%s) .. controls +%s and +%s .. (G-%s); \n" % (j[0], outVec, inVec,j[1])
469
output = output + "\\end{tikzpicture}" #end picture
470
return output
471
472
class PartitionAlgebra(DiagramAlgebra):
473
r"""
474
A partition algebra.
475
476
The partition algebra of rank `k` is an algebra with basis indexed by the
477
collection of set partitions of `\{1, \dots, k, -1, \ldots, -k\}`. Each
478
such set partition is regarded as a graph on nodes `\{1, \ldots, k, -1,
479
\ldots, -k\}` arranged in two rows, with nodes `1, \dots, k` in the top
480
row from left to right and with nodes `-1, \ldots, -k` in the bottom row
481
from left to right, and an edge connecting two nodes if and only if the
482
nodes lie in the same subset of the set partition.
483
484
The partition algebra is regarded as an example of a "diagram algebra" due
485
to the fact that its natural basis is given by certain graphs often called
486
diagrams.
487
488
The product of two basis elements is given by the rule
489
`a \cdot b = q^N (a \circ b)`, where `a \circ b` is the composite set
490
partition obtained by placing diagram `a` above diagram `b`, identifying
491
the bottom row nodes of `a` with the top row nodes of `b`, and omitting
492
any closed "loops" in the middle. The number `N` is the number of
493
connected components of the omitted loops.
494
495
The parameter `q` is a deformation parameter. Taking `q = 1` produces the
496
semigroup algebra (over the base ring) of the partition monoid, in which
497
the product of two set partitions is simply given by their composition.
498
499
The Iwahori--Hecke algebra of type `A` (with a single parameter) is
500
naturally a subalgebra of the partition algebra.
501
502
An excellent reference for partition algebras and its various subalgebras
503
(Brauer algebra, Temperley--Lieb algebra, etc) is the paper [HR2005]_.
504
505
INPUT:
506
507
- ``k``-- rank of the algebra
508
509
- ``q``-- the deformation parameter `q`
510
511
OPTIONAL ARGUMENTS:
512
513
- ``base_ring``-- (default ``None``) a ring containing ``q``; if ``None``
514
then just takes the parent of ``q``
515
516
- ``prefix``-- (default ``"P"``) a label for the basis elements
517
518
EXAMPLES:
519
520
The following shorthand simultaneously define the univariate polynomial
521
ring over the rationals as well as the variable ``x``::
522
523
sage: R.<x> = PolynomialRing(QQ)
524
sage: R
525
Univariate Polynomial Ring in x over Rational Field
526
sage: x
527
x
528
sage: x.parent() is R
529
True
530
531
We now define the partition algebra of rank `2` with parameter ``x``
532
over `\ZZ`::
533
534
sage: R.<x> = ZZ[]
535
sage: P = PartitionAlgebra(2, x, R)
536
sage: P
537
Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Integer Ring
538
sage: P.basis().list()
539
[P[{{-2, -1, 1, 2}}], P[{{-2, -1, 2}, {1}}],
540
P[{{-2, -1, 1}, {2}}], P[{{-2}, {-1, 1, 2}}],
541
P[{{-2, 1, 2}, {-1}}], P[{{-2, 1}, {-1, 2}}],
542
P[{{-2, 2}, {-1, 1}}], P[{{-2, -1}, {1, 2}}],
543
P[{{-2, -1}, {1}, {2}}], P[{{-2}, {-1, 2}, {1}}],
544
P[{{-2, 2}, {-1}, {1}}], P[{{-2}, {-1, 1}, {2}}],
545
P[{{-2, 1}, {-1}, {2}}], P[{{-2}, {-1}, {1, 2}}],
546
P[{{-2}, {-1}, {1}, {2}}]]
547
sage: E = P([[1,2],[-2,-1]]); E
548
P[{{-2, -1}, {1, 2}}]
549
sage: E in P.basis()
550
True
551
sage: E^2
552
x*P[{{-2, -1}, {1, 2}}]
553
sage: E^5
554
x^4*P[{{-2, -1}, {1, 2}}]
555
sage: (P([[2,-2],[-1,1]]) - 2*P([[1,2],[-1,-2]]))^2
556
(4*x-4)*P[{{-2, -1}, {1, 2}}] + P[{{-2, 2}, {-1, 1}}]
557
558
One can work with partition algebras using a symbol for the parameter,
559
leaving the base ring unspecified. This implies that the underlying
560
base ring is Sage's symbolic ring.
561
562
::
563
564
sage: q = var('q')
565
sage: PA = PartitionAlgebra(2, q); PA
566
Partition Algebra of rank 2 with parameter q over Symbolic Ring
567
sage: PA([[1,2],[-2,-1]])^2 == q*PA([[1,2],[-2,-1]])
568
True
569
sage: (PA([[2, -2], [1, -1]]) - 2*PA([[-2, -1], [1, 2]]))^2 == (4*q-4)*PA([[1, 2], [-2, -1]]) + PA([[2, -2], [1, -1]])
570
True
571
572
The identity element of the partition algebra is the diagram whose set
573
partition is `\{\{1,-1\}, \{2,-2\}, \ldots, \{k,-k\}\}`::
574
575
sage: P = PA.basis().list()
576
sage: PA.one()
577
P[{{-2, 2}, {-1, 1}}]
578
sage: PA.one()*P[7] == P[7]
579
True
580
sage: P[7]*PA.one() == P[7]
581
True
582
583
We now give some further examples of the use of the other arguments.
584
One may wish to "specialize" the parameter to a chosen element of
585
the base ring::
586
587
sage: R.<q> = RR[]
588
sage: PA = PartitionAlgebra(2, q, R, prefix='B')
589
sage: PA
590
Partition Algebra of rank 2 with parameter q over
591
Univariate Polynomial Ring in q over Real Field with 53 bits of precision
592
sage: PA([[1,2],[-1,-2]])
593
1.00000000000000*B[{{-2, -1}, {1, 2}}]
594
sage: PA = PartitionAlgebra(2, 5, base_ring=ZZ, prefix='B')
595
sage: PA
596
Partition Algebra of rank 2 with parameter 5 over Integer Ring
597
sage: (PA([[2, -2], [1, -1]]) - 2*PA([[-2, -1], [1, 2]]))^2 == 16*PA([[-2, -1], [1, 2]]) + PA([[2, -2], [1, -1]])
598
True
599
600
REFERENCES:
601
602
.. [HR2005] Tom Halverson and Arun Ram, *Partition algebras*. European
603
Journal of Combinatorics **26** (2005), 869--921.
604
"""
605
@staticmethod
606
def __classcall_private__(cls, k, q, base_ring=None, prefix="P"):
607
r"""
608
Standardize the input by getting the base ring from the parent of
609
the parameter ``q`` if no ``base_ring`` is given.
610
611
TESTS::
612
613
sage: R.<q> = QQ[]
614
sage: PA1 = PartitionAlgebra(2, q)
615
sage: PA2 = PartitionAlgebra(2, q, R, 'P')
616
sage: PA1 is PA2
617
True
618
"""
619
if base_ring is None:
620
base_ring = q.parent()
621
return super(PartitionAlgebra, cls).__classcall__(cls, k, q, base_ring, prefix)
622
623
# The following is the basic constructor method for the class.
624
# The purpose of the "prefix" is to label the basis elements
625
def __init__(self, k, q, base_ring, prefix):
626
r"""
627
Initialize ``self``.
628
629
TESTS::
630
631
sage: R.<q> = QQ[]
632
sage: PA = PartitionAlgebra(2, q, R)
633
sage: TestSuite(PA).run()
634
"""
635
self._k = k
636
self._prefix = prefix
637
self._q = base_ring(q)
638
DiagramAlgebra.__init__(self, k, q, base_ring, prefix, partition_diagrams)
639
640
def _repr_(self):
641
"""
642
Return a string representation of ``self``.
643
644
EXAMPLES::
645
646
sage: R.<q> = QQ[]
647
sage: PartitionAlgebra(2, q, R)
648
Partition Algebra of rank 2 with parameter q over Univariate Polynomial Ring in q over Rational Field
649
"""
650
return "Partition Algebra of rank %s with parameter %s over %s"%(self._k,
651
self._q, self.base_ring())
652
653
class SubPartitionAlgebra(DiagramAlgebra):
654
"""
655
A subalgebra of the partition algebra indexed by a subset of the diagrams.
656
"""
657
def __init__(self, k, q, base_ring, prefix, diagrams, category=None):
658
"""
659
Initialize ``self`` by adding a coercion to the ambient space.
660
661
EXAMPLES::
662
663
sage: R.<x> = QQ[]
664
sage: BA = BrauerAlgebra(2, x, R)
665
sage: BA.ambient().has_coerce_map_from(BA)
666
True
667
"""
668
DiagramAlgebra.__init__(self, k, q, base_ring, prefix, diagrams, category)
669
amb = self.ambient()
670
self.module_morphism(self.lift, codomain=amb,
671
category=self.category()).register_as_coercion()
672
673
#These methods allow for a sub algebra to be correctly identified in a partition algebra
674
def ambient(self):
675
r"""
676
Return the partition algebra ``self`` is a sub-algebra of.
677
Generally, this method is not called directly.
678
679
EXAMPLES::
680
681
sage: x = var('x')
682
sage: BA = BrauerAlgebra(2, x)
683
sage: BA.ambient()
684
Partition Algebra of rank 2 with parameter x over Symbolic Ring
685
"""
686
return PartitionAlgebra(self._k, self._q, self.base_ring(), prefix=self._prefix)
687
688
def lift(self, x):
689
r"""
690
Lift a diagram subalgebra element to the corresponding element
691
in the ambient space. This method is not intended to be called
692
directly.
693
694
EXAMPLES::
695
696
sage: R.<x> = QQ[]
697
sage: BA = BrauerAlgebra(2, x, R)
698
sage: E = BA([[1,2],[-1,-2]])
699
sage: lifted = BA.lift(E); lifted
700
B[{{-2, -1}, {1, 2}}]
701
sage: lifted.parent() is BA.ambient()
702
True
703
"""
704
if x not in self:
705
raise ValueError("{0} is not in {1}".format(x, self))
706
monomial_indices = x.support()
707
monomial_coefficients = x.coefficients()
708
result = 0
709
for i in xrange(len(monomial_coefficients)):
710
result += monomial_coefficients[i]*self.ambient().monomial(monomial_indices[i])
711
return result
712
713
def retract(self, x):
714
r"""
715
Retract an appropriate partition algebra element to the
716
corresponding element in the partition subalgebra. This method
717
is not intended to be called directly.
718
719
EXAMPLES::
720
721
sage: R.<x> = QQ[]
722
sage: BA = BrauerAlgebra(2, x, R)
723
sage: PA = BA.ambient()
724
sage: E = PA([[1,2], [-1,-2]])
725
sage: BA.retract(E) in BA
726
True
727
"""
728
if x not in self.ambient() or not set(x.support()).issubset(set(self.basis().keys())):
729
raise ValueError("{0} cannot retract to {1}".format(x, self))
730
monomial_indices = x.support()
731
monomial_coefficients = x.coefficients()
732
result = self.zero()
733
for i in xrange(len(monomial_coefficients)):
734
result += monomial_coefficients[i]*self.monomial(monomial_indices[i])
735
return result
736
737
class BrauerAlgebra(SubPartitionAlgebra):
738
r"""
739
A Brauer algebra.
740
741
The Brauer algebra of rank `k` is an algebra with basis indexed by the
742
collection of set partitions of `\{1, \ldots, k, -1, \ldots, -k\}`
743
with block size 2.
744
745
This algebra is a subalgebra of the partition algebra.
746
For more information, see :class:`PartitionAlgebra`.
747
748
INPUT:
749
750
- ``k``-- rank of the algebra
751
752
- ``q``-- the deformation parameter `q`
753
754
OPTIONAL ARGUMENTS:
755
756
- ``base_ring``-- (default ``None``) a ring containing ``q``; if ``None``
757
then just takes the parent of ``q``
758
759
- ``prefix``-- (default ``"B"``) a label for the basis elements
760
761
EXAMPLES:
762
763
We now define the Brauer algebra of rank `2` with parameter ``x`` over
764
`\ZZ`::
765
766
sage: R.<x> = ZZ[]
767
sage: B = BrauerAlgebra(2, x, R)
768
sage: B
769
Brauer Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Integer Ring
770
sage: B.basis()
771
Finite family {{{-2, -1}, {1, 2}}: B[{{-2, -1}, {1, 2}}], {{-2, 1}, {-1, 2}}: B[{{-2, 1}, {-1, 2}}], {{-2, 2}, {-1, 1}}: B[{{-2, 2}, {-1, 1}}]}
772
sage: b = B.basis().list()
773
sage: b
774
[B[{{-2, 1}, {-1, 2}}], B[{{-2, 2}, {-1, 1}}], B[{{-2, -1}, {1, 2}}]]
775
sage: b[2]
776
B[{{-2, -1}, {1, 2}}]
777
sage: b[2]^2
778
x*B[{{-2, -1}, {1, 2}}]
779
sage: b[2]^5
780
x^4*B[{{-2, -1}, {1, 2}}]
781
"""
782
@staticmethod
783
def __classcall_private__(cls, k, q, base_ring=None, prefix="B"):
784
r"""
785
Standardize the input by getting the base ring from the parent of
786
the parameter ``q`` if no ``base_ring`` is given.
787
788
TESTS::
789
790
sage: R.<q> = QQ[]
791
sage: BA1 = BrauerAlgebra(2, q)
792
sage: BA2 = BrauerAlgebra(2, q, R, 'B')
793
sage: BA1 is BA2
794
True
795
"""
796
if base_ring is None:
797
base_ring = q.parent()
798
return super(BrauerAlgebra, cls).__classcall__(cls, k, q, base_ring, prefix)
799
800
def __init__(self, k, q, base_ring, prefix):
801
r"""
802
Initialize ``self``.
803
804
TESTS::
805
806
sage: R.<q> = QQ[]
807
sage: BA = BrauerAlgebra(2, q, R)
808
sage: TestSuite(BA).run()
809
"""
810
SubPartitionAlgebra.__init__(self, k, q, base_ring, prefix, brauer_diagrams)
811
812
def _repr_(self):
813
"""
814
Return a string representation of ``self``.
815
816
EXAMPLES::
817
818
sage: R.<q> = QQ[]
819
sage: BrauerAlgebra(2, q, R)
820
Brauer Algebra of rank 2 with parameter q over Univariate Polynomial Ring in q over Rational Field
821
"""
822
return "Brauer Algebra of rank %s with parameter %s over %s"%(self._k, self._q, self.base_ring())
823
824
def _element_constructor_(self, set_partition):
825
r"""
826
Construct an element of ``self``.
827
828
EXAMPLES::
829
830
sage: R.<q> = QQ[]
831
sage: BA = BrauerAlgebra(2, q, R)
832
sage: sp = SetPartition([[1,2], [-1,-2]])
833
sage: b_elt = BA(sp); b_elt
834
B[{{-2, -1}, {1, 2}}]
835
sage: b_elt in BA
836
True
837
sage: BA([[1,2],[-1,-2]]) == b_elt
838
True
839
sage: BA([{1,2},{-1,-2}]) == b_elt
840
True
841
"""
842
set_partition = to_Brauer_partition(set_partition, k = self.order())
843
return DiagramAlgebra._element_constructor_(self, set_partition)
844
845
class TemperleyLiebAlgebra(SubPartitionAlgebra):
846
r"""
847
A Temperley--Lieb algebra.
848
849
The Temperley--Lieb algebra of rank `k` is an algebra with basis indexed
850
by the collection of planar set partitions of `\{1, \ldots, k, -1,
851
\ldots, -k\}` with block size 2.
852
853
This algebra is thus a subalgebra of the partition algebra.
854
For more information, see :class:`PartitionAlgebra`.
855
856
INPUT:
857
858
- ``k``-- rank of the algebra
859
860
- ``q``-- the deformation parameter `q`
861
862
OPTIONAL ARGUMENTS:
863
864
- ``base_ring``-- (default ``None``) a ring containing ``q``; if ``None``
865
then just takes the parent of ``q``
866
867
- ``prefix``-- (default ``"T"``) a label for the basis elements
868
869
EXAMPLES:
870
871
We define the Temperley--Lieb algebra of rank `2` with parameter
872
`x` over `\ZZ`::
873
874
sage: R.<x> = ZZ[]
875
sage: T = TemperleyLiebAlgebra(2, x, R); T
876
Temperley-Lieb Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Integer Ring
877
sage: T.basis()
878
Finite family {{{-2, 2}, {-1, 1}}: T[{{-2, 2}, {-1, 1}}], {{-2, -1}, {1, 2}}: T[{{-2, -1}, {1, 2}}]}
879
sage: b = T.basis().list()
880
sage: b
881
[T[{{-2, 2}, {-1, 1}}], T[{{-2, -1}, {1, 2}}]]
882
sage: b[1]
883
T[{{-2, -1}, {1, 2}}]
884
sage: b[1]^2 == x*b[1]
885
True
886
sage: b[1]^5 == x^4*b[1]
887
True
888
"""
889
@staticmethod
890
def __classcall_private__(cls, k, q, base_ring=None, prefix="T"):
891
r"""
892
Standardize the input by getting the base ring from the parent of
893
the parameter ``q`` if no ``base_ring`` is given.
894
895
TESTS::
896
897
sage: R.<q> = QQ[]
898
sage: T1 = TemperleyLiebAlgebra(2, q)
899
sage: T2 = TemperleyLiebAlgebra(2, q, R, 'T')
900
sage: T1 is T2
901
True
902
"""
903
if base_ring is None:
904
base_ring = q.parent()
905
return super(TemperleyLiebAlgebra, cls).__classcall__(cls, k, q, base_ring, prefix)
906
907
def __init__(self, k, q, base_ring, prefix):
908
r"""
909
Initialize ``self``
910
911
TESTS::
912
913
sage: R.<q> = QQ[]
914
sage: TL = TemperleyLiebAlgebra(2, q, R)
915
sage: TestSuite(TL).run()
916
"""
917
SubPartitionAlgebra.__init__(self, k, q, base_ring, prefix, temperley_lieb_diagrams)
918
919
def _repr_(self):
920
"""
921
Return a string represetation of ``self``.
922
923
EXAMPLES::
924
925
sage: R.<q> = QQ[]
926
sage: TemperleyLiebAlgebra(2, q, R)
927
Temperley-Lieb Algebra of rank 2 with parameter q over Univariate Polynomial Ring in q over Rational Field
928
"""
929
return "Temperley-Lieb Algebra of rank %s with parameter %s over %s"%(self._k,
930
self._q, self.base_ring())
931
932
def _element_constructor_(self, set_partition):
933
r"""
934
Construct an element of ``self``.
935
936
EXAMPLES::
937
938
sage: R.<q> = QQ[]
939
sage: TL = TemperleyLiebAlgebra(2, q, R)
940
sage: sp = SetPartition([[1,2], [-1,-2]])
941
sage: b_elt = TL(sp); b_elt
942
T[{{-2, -1}, {1, 2}}]
943
sage: b_elt in TL
944
True
945
sage: TL([[1,2],[-1,-2]]) == b_elt
946
True
947
sage: TL([{1,2},{-1,-2}]) == b_elt
948
True
949
"""
950
set_partition = to_Brauer_partition(set_partition, k = self.order())
951
return SubPartitionAlgebra._element_constructor_(self, set_partition)
952
953
class PlanarAlgebra(SubPartitionAlgebra):
954
"""
955
A planar algebra.
956
957
The planar algebra of rank `k` is an algebra with basis indexed by the
958
collection of set partitions of `\{1, \ldots, k, -1, \ldots, -k\}`
959
where each set partition is planar.
960
961
This algebra is thus a subalgebra of the partition algebra. For more
962
information, see :class:`PartitionAlgebra`.
963
964
INPUT:
965
966
- ``k``-- rank of the algebra
967
968
- ``q``-- the deformation parameter `q`
969
970
OPTIONAL ARGUMENTS:
971
972
- ``base_ring``-- (default ``None``) a ring containing ``q``; if ``None``
973
then just takes the parent of ``q``
974
975
- ``prefix``-- (default ``"Pl"``) a label for the basis elements
976
977
EXAMPLES:
978
979
We now define the planar algebra of rank `2` with parameter
980
`x` over `\ZZ`::
981
982
sage: R.<x> = ZZ[]
983
sage: Pl = PlanarAlgebra(2, x, R); Pl
984
Planar Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Integer Ring
985
sage: Pl.basis().list()
986
[Pl[{{-2, -1, 1, 2}}], Pl[{{-2, -1, 2}, {1}}],
987
Pl[{{-2, -1, 1}, {2}}], Pl[{{-2}, {-1, 1, 2}}],
988
Pl[{{-2, 1, 2}, {-1}}], Pl[{{-2, 2}, {-1, 1}}],
989
Pl[{{-2, -1}, {1, 2}}], Pl[{{-2, -1}, {1}, {2}}],
990
Pl[{{-2}, {-1, 2}, {1}}], Pl[{{-2, 2}, {-1}, {1}}],
991
Pl[{{-2}, {-1, 1}, {2}}], Pl[{{-2, 1}, {-1}, {2}}],
992
Pl[{{-2}, {-1}, {1, 2}}], Pl[{{-2}, {-1}, {1}, {2}}]]
993
sage: E = Pl([[1,2],[-1,-2]])
994
sage: E^2 == x*E
995
True
996
sage: E^5 == x^4*E
997
True
998
"""
999
@staticmethod
1000
def __classcall_private__(cls, k, q, base_ring=None, prefix="Pl"):
1001
r"""
1002
Standardize the input by getting the base ring from the parent of
1003
the parameter ``q`` if no ``base_ring`` is given.
1004
1005
TESTS::
1006
1007
sage: R.<q> = QQ[]
1008
sage: Pl1 = PlanarAlgebra(2, q)
1009
sage: Pl2 = PlanarAlgebra(2, q, R, 'Pl')
1010
sage: Pl1 is Pl2
1011
True
1012
"""
1013
if base_ring is None:
1014
base_ring = q.parent()
1015
return super(PlanarAlgebra, cls).__classcall__(cls, k, q, base_ring, prefix)
1016
1017
def __init__(self, k, q, base_ring, prefix):
1018
r"""
1019
Initialize ``self``.
1020
1021
TESTS::
1022
1023
sage: R.<q> = QQ[]
1024
sage: PlA = PlanarAlgebra(2, q, R)
1025
sage: TestSuite(PlA).run()
1026
"""
1027
SubPartitionAlgebra.__init__(self, k, q, base_ring, prefix, planar_diagrams)
1028
1029
def _repr_(self):
1030
"""
1031
Return a string representation of ``self``.
1032
1033
EXAMPLES::
1034
1035
sage: R.<x> = ZZ[]
1036
sage: Pl = PlanarAlgebra(2, x, R); Pl
1037
Planar Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Integer Ring
1038
"""
1039
return "Planar Algebra of rank %s with parameter %s over %s"%(self._k,
1040
self._q, self.base_ring())
1041
1042
class PropagatingIdeal(SubPartitionAlgebra):
1043
r"""
1044
A propagating ideal.
1045
1046
The propagating ideal of rank `k` is a non-unital algebra with basis
1047
indexed by the collection of ideal set partitions of `\{1, \ldots, k, -1,
1048
\ldots, -k\}`. We say a set partition is *ideal* if its propagating
1049
number is less than `k`.
1050
1051
This algebra is a non-unital subalgebra and an ideal of the partition
1052
algebra.
1053
For more information, see :class:`PartitionAlgebra`.
1054
1055
EXAMPLES:
1056
1057
We now define the propagating ideal of rank `2` with parameter
1058
`x` over `\ZZ`::
1059
1060
sage: R.<x> = QQ[]
1061
sage: I = PropagatingIdeal(2, x, R); I
1062
Propagating Ideal of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field
1063
sage: I.basis().list()
1064
[I[{{-2, -1, 1, 2}}], I[{{-2, -1, 2}, {1}}],
1065
I[{{-2, -1, 1}, {2}}], I[{{-2}, {-1, 1, 2}}],
1066
I[{{-2, 1, 2}, {-1}}], I[{{-2, -1}, {1, 2}}],
1067
I[{{-2, -1}, {1}, {2}}], I[{{-2}, {-1, 2}, {1}}],
1068
I[{{-2, 2}, {-1}, {1}}], I[{{-2}, {-1, 1}, {2}}],
1069
I[{{-2, 1}, {-1}, {2}}], I[{{-2}, {-1}, {1, 2}}],
1070
I[{{-2}, {-1}, {1}, {2}}]]
1071
sage: E = I([[1,2],[-1,-2]])
1072
sage: E^2 == x*E
1073
True
1074
sage: E^5 == x^4*E
1075
True
1076
"""
1077
@staticmethod
1078
def __classcall_private__(cls, k, q, base_ring=None, prefix="I"):
1079
r"""
1080
Standardize the input by getting the base ring from the parent of
1081
the parameter ``q`` if no ``base_ring`` is given.
1082
1083
TESTS::
1084
1085
sage: R.<q> = QQ[]
1086
sage: IA1 = PropagatingIdeal(2, q)
1087
sage: IA2 = PropagatingIdeal(2, q, R, 'I')
1088
sage: IA1 is IA2
1089
True
1090
"""
1091
if base_ring is None:
1092
base_ring = q.parent()
1093
return super(PropagatingIdeal, cls).__classcall__(cls, k, q, base_ring, prefix)
1094
1095
def __init__(self, k, q, base_ring, prefix):
1096
r"""
1097
Initialize ``self``.
1098
1099
TESTS::
1100
1101
sage: R.<q> = QQ[]
1102
sage: I = PropagatingIdeal(2, q, R)
1103
sage: TestSuite(I).run() # Not tested -- needs non-unital algebras category
1104
"""
1105
# This should be the category of non-unital fin-dim algebras with basis
1106
category = FiniteDimensionalAlgebrasWithBasis(base_ring)
1107
SubPartitionAlgebra.__init__(self, k, q, base_ring, prefix, ideal_diagrams, category)
1108
1109
@cached_method
1110
def one_basis(self):
1111
r"""
1112
The propagating ideal is a non-unital algebra, i.e. it does not have a
1113
multiplicative identity.
1114
1115
EXAMPLES::
1116
1117
sage: R.<q> = QQ[]
1118
sage: I = PropagatingIdeal(2, q, R)
1119
sage: I.one_basis()
1120
Traceback (most recent call last):
1121
...
1122
ValueError: The ideal partition algebra is not unital
1123
sage: I.one()
1124
Traceback (most recent call last):
1125
...
1126
ValueError: The ideal partition algebra is not unital
1127
"""
1128
raise ValueError("The ideal partition algebra is not unital")
1129
#return identity_set_partition(self._k)
1130
1131
def _repr_(self):
1132
"""
1133
Return a string representation of ``self``.
1134
1135
EXAMPLES::
1136
1137
sage: R.<x> = QQ[]
1138
sage: PropagatingIdeal(2, x, R)
1139
Propagating Ideal of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field
1140
"""
1141
return "Propagating Ideal of rank %s with parameter %s over %s"%(self._k,
1142
self._q, self.base_ring())
1143
1144
class Element(SubPartitionAlgebra.Element):
1145
"""
1146
Need to take care of exponents since we are not unital.
1147
"""
1148
def __pow__(self, n):
1149
"""
1150
Return ``self`` to the `n`-th power.
1151
1152
INPUT::
1153
1154
- ``n`` -- a positive integer
1155
1156
EXAMPLES::
1157
1158
sage: R.<x> = QQ[]
1159
sage: I = PropagatingIdeal(2, x, R)
1160
sage: E = I([[1,2],[-1,-2]])
1161
sage: E^2
1162
x*I[{{-2, -1}, {1, 2}}]
1163
sage: E^0
1164
Traceback (most recent call last):
1165
...
1166
ValueError: can only take positive integer powers
1167
"""
1168
if n <= 0:
1169
raise ValueError("can only take positive integer powers")
1170
return generic_power(self, n)
1171
1172
#########################################################################
1173
# START BORROWED CODE
1174
#########################################################################
1175
# Borrowed from Mike Hansen's original code -- global methods for dealing
1176
# with partition diagrams, compositions of partition diagrams, and so on.
1177
# --> CHANGED 'identity' to 'identity_set_partition' for enhanced clarity.
1178
#########################################################################
1179
1180
def is_planar(sp):
1181
r"""
1182
Return ``True`` if the diagram corresponding to the set partition ``sp``
1183
is planar; otherwise, it return ``False``.
1184
1185
EXAMPLES::
1186
1187
sage: import sage.combinat.diagram_algebras as da
1188
sage: da.is_planar( da.to_set_partition([[1,-2],[2,-1]]))
1189
False
1190
sage: da.is_planar( da.to_set_partition([[1,-1],[2,-2]]))
1191
True
1192
"""
1193
to_consider = map(list, sp)
1194
1195
#Singletons don't affect planarity
1196
to_consider = filter(lambda x: len(x) > 1, to_consider)
1197
n = len(to_consider)
1198
1199
for i in range(n):
1200
#Get the positive and negative entries of this part
1201
ap = filter(lambda x: x>0, to_consider[i])
1202
an = filter(lambda x: x<0, to_consider[i])
1203
an = map(abs, an)
1204
1205
#Check if a includes numbers in both the top and bottom rows
1206
if len(ap) > 0 and len(an) > 0:
1207
for j in range(n):
1208
if i == j:
1209
continue
1210
#Get the positive and negative entries of this part
1211
bp = filter(lambda x: x>0, to_consider[j])
1212
bn = filter(lambda x: x<0, to_consider[j])
1213
bn = map(abs, bn)
1214
1215
#Skip the ones that don't involve numbers in both
1216
#the bottom and top rows
1217
if len(bn) == 0 or len(bp) == 0:
1218
continue
1219
1220
#Make sure that if min(bp) > max(ap)
1221
#then min(bn) > max(an)
1222
if max(bp) > max(ap):
1223
if min(bn) < min(an):
1224
return False
1225
1226
#Go through the bottom and top rows
1227
for row in [ap, an]:
1228
if len(row) > 1:
1229
row.sort()
1230
for s in range(len(row)-1):
1231
if row[s] + 1 == row[s+1]:
1232
#No gap, continue on
1233
continue
1234
1235
rng = range(row[s] + 1, row[s+1])
1236
1237
#Go through and make sure any parts that
1238
#contain numbers in this range are completely
1239
#contained in this range
1240
for j in range(n):
1241
if i == j:
1242
continue
1243
1244
#Make sure we make the numbers negative again
1245
#if we are in the bottom row
1246
if row is ap:
1247
sr = Set(rng)
1248
else:
1249
sr = Set(map(lambda x: -1*x, rng))
1250
1251
sj = Set(to_consider[j])
1252
intersection = sr.intersection(sj)
1253
if intersection:
1254
if sj != intersection:
1255
return False
1256
1257
return True
1258
1259
1260
def to_graph(sp):
1261
r"""
1262
Return a graph representing the set partition ``sp``.
1263
1264
EXAMPLES::
1265
1266
sage: import sage.combinat.diagram_algebras as da
1267
sage: g = da.to_graph( da.to_set_partition([[1,-2],[2,-1]])); g
1268
Graph on 4 vertices
1269
1270
sage: g.vertices()
1271
[-2, -1, 1, 2]
1272
sage: g.edges()
1273
[(-2, 1, None), (-1, 2, None)]
1274
"""
1275
g = Graph()
1276
for part in sp:
1277
part_list = list(part)
1278
if len(part_list) > 0:
1279
g.add_vertex(part_list[0])
1280
for i in range(1, len(part_list)):
1281
g.add_vertex(part_list[i])
1282
g.add_edge(part_list[i-1], part_list[i])
1283
return g
1284
1285
def pair_to_graph(sp1, sp2):
1286
r"""
1287
Return a graph consisting of the graphs of set partitions ``sp1`` and
1288
``sp2`` along with edges joining the bottom row (negative numbers) of
1289
``sp1`` to the top row (positive numbers) of ``sp2``.
1290
1291
EXAMPLES::
1292
1293
sage: import sage.combinat.diagram_algebras as da
1294
sage: sp1 = da.to_set_partition([[1,-2],[2,-1]])
1295
sage: sp2 = da.to_set_partition([[1,-2],[2,-1]])
1296
sage: g = da.pair_to_graph( sp1, sp2 ); g
1297
Graph on 8 vertices
1298
1299
sage: g.vertices()
1300
[(-2, 1), (-2, 2), (-1, 1), (-1, 2), (1, 1), (1, 2), (2, 1), (2, 2)]
1301
sage: g.edges()
1302
[((-2, 1), (1, 1), None), ((-2, 1), (2, 2), None),
1303
((-2, 2), (1, 2), None), ((-1, 1), (1, 2), None),
1304
((-1, 1), (2, 1), None), ((-1, 2), (2, 2), None)]
1305
"""
1306
g = Graph()
1307
1308
#Add the first set partition to the graph
1309
for part in sp1:
1310
part_list = list(part)
1311
if len(part_list) > 0:
1312
g.add_vertex( (part_list[0],1) )
1313
1314
#Add the edge to the second part of the graph
1315
if part_list[0] < 0 and len(part_list) > 1:
1316
g.add_edge( (part_list[0], 1), (abs(part_list[0]), 2) )
1317
1318
for i in range(1, len(part_list)):
1319
g.add_vertex( (part_list[i],1) )
1320
1321
#Add the edge to the second part of the graph
1322
if part_list[i] < 0:
1323
g.add_edge( (part_list[i], 1), (abs(part_list[i]),2) )
1324
1325
#Add the edge between parts
1326
g.add_edge( (part_list[i-1],1), (part_list[i],1) )
1327
1328
#Add the second set partition to the graph
1329
for part in sp2:
1330
part_list = list(part)
1331
if len(part_list) > 0:
1332
g.add_vertex( (part_list[0],2) )
1333
for i in range(1, len(part_list)):
1334
g.add_vertex( (part_list[i],2) )
1335
g.add_edge( (part_list[i-1],2), (part_list[i],2) )
1336
1337
return g
1338
1339
def propagating_number(sp):
1340
r"""
1341
Return the propagating number of the set partition ``sp``.
1342
1343
The propagating number is the number of blocks with both a positive and
1344
negative number.
1345
1346
EXAMPLES::
1347
1348
sage: import sage.combinat.diagram_algebras as da
1349
sage: sp1 = da.to_set_partition([[1,-2],[2,-1]])
1350
sage: sp2 = da.to_set_partition([[1,2],[-2,-1]])
1351
sage: da.propagating_number(sp1)
1352
2
1353
sage: da.propagating_number(sp2)
1354
0
1355
"""
1356
pn = 0
1357
for part in sp:
1358
if min(part) < 0 and max(part) > 0:
1359
pn += 1
1360
return pn
1361
1362
def to_set_partition(l, k=None):
1363
r"""
1364
Convert a list of a list of numbers to a set partitions. Each list
1365
of numbers in the outer list specifies the numbers contained in one
1366
of the blocks in the set partition.
1367
1368
If `k` is specified, then the set partition will be a set partition
1369
of `\{1, \ldots, k, -1, \ldots, -k\}`. Otherwise, `k` will default to
1370
the minimum number needed to contain all of the specified numbers.
1371
1372
EXAMPLES::
1373
1374
sage: import sage.combinat.diagram_algebras as da
1375
sage: da.to_set_partition([[1,-1],[2,-2]]) == da.identity_set_partition(2)
1376
True
1377
"""
1378
if k == None:
1379
if l == []:
1380
return SetPartition([])
1381
else:
1382
k = max( map( lambda x: max( map(abs, x) ), l) )
1383
1384
to_be_added = Set( range(1, k+1) + map(lambda x: -1*x, range(1, k+1) ) )
1385
1386
sp = []
1387
for part in l:
1388
spart = Set(part)
1389
to_be_added -= spart
1390
sp.append(spart)
1391
1392
for singleton in to_be_added:
1393
sp.append(Set([singleton]))
1394
1395
return SetPartition(sp)
1396
1397
def to_Brauer_partition(l, k=None):
1398
r"""
1399
Same as :func:`to_set_partition` but assumes omitted elements are
1400
connected straight through.
1401
1402
EXAMPLES::
1403
1404
sage: import sage.combinat.diagram_algebras as da
1405
sage: da.to_Brauer_partition([[1,2],[-1,-2]]) == SetPartition([[1,2],[-1,-2]])
1406
True
1407
sage: da.to_Brauer_partition([[1,3],[-1,-3]]) == SetPartition([[1,3],[-3,-1],[2,-2]])
1408
True
1409
sage: da.to_Brauer_partition([[1,2],[-1,-2]], k=4) == SetPartition([[1,2],[-1,-2],[3,-3],[4,-4]])
1410
True
1411
sage: da.to_Brauer_partition([[1,-4],[-3,-1],[3,4]]) == SetPartition([[-3,-1],[2,-2],[1,-4],[3,4]])
1412
True
1413
"""
1414
L = to_set_partition(l, k=k)
1415
L2 = []
1416
paired = []
1417
not_paired = []
1418
for i in L:
1419
L2.append(list(i))
1420
for i in L2:
1421
if len(i) >= 3:
1422
raise ValueError("blocks must have size at most 2, but {0} has {1}".format(i, len(i)))
1423
if (len(i) == 2):
1424
paired.append(i)
1425
if (len(i) == 1):
1426
not_paired.append(i)
1427
if any(i[0] in j or -1*i[0] in j for i in not_paired for j in paired):
1428
raise ValueError("unable to convert {0} to a Brauer partition due to the invalid block {1}".format(l, i))
1429
for i in not_paired:
1430
if [-1*i[0]] in not_paired:
1431
not_paired.remove([-1*i[0]])
1432
paired.append([i[0], -1*i[0]])
1433
return to_set_partition(paired)
1434
1435
def identity_set_partition(k):
1436
"""
1437
Return the identity set partition `\{\{1, -1\}, \ldots, \{k, -k\}\}`
1438
1439
EXAMPLES::
1440
1441
sage: import sage.combinat.diagram_algebras as da
1442
sage: da.identity_set_partition(2)
1443
{{-2, 2}, {-1, 1}}
1444
"""
1445
if k in ZZ:
1446
return SetPartition( [[i,-i] for i in range(1, k + 1)] )
1447
# Else k in 1/2 ZZ
1448
return SetPartition( [[i, -i] for i in range(1, k + ZZ(3)/ZZ(2))] )
1449
1450
def set_partition_composition(sp1, sp2):
1451
r"""
1452
Return a tuple consisting of the composition of the set partitions
1453
``sp1`` and ``sp2`` and the number of components removed from the middle
1454
rows of the graph.
1455
1456
EXAMPLES::
1457
1458
sage: import sage.combinat.diagram_algebras as da
1459
sage: sp1 = da.to_set_partition([[1,-2],[2,-1]])
1460
sage: sp2 = da.to_set_partition([[1,-2],[2,-1]])
1461
sage: da.set_partition_composition(sp1, sp2) == (da.identity_set_partition(2), 0)
1462
True
1463
"""
1464
g = pair_to_graph(sp1, sp2)
1465
connected_components = g.connected_components()
1466
1467
res = []
1468
total_removed = 0
1469
for cc in connected_components:
1470
#Remove the vertices that live in the middle two rows
1471
new_cc = filter(lambda x: not ( (x[0]<0 and x[1] == 1) or (x[0]>0 and x[1]==2)), cc)
1472
1473
if new_cc == []:
1474
if len(cc) > 1:
1475
total_removed += 1
1476
else:
1477
res.append( Set(map(lambda x: x[0], new_cc)) )
1478
1479
return (SetPartition(Set(res)), total_removed)
1480
1481
##########################################################################
1482
# END BORROWED CODE
1483
##########################################################################
1484
1485
1486