Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/partition_algebra.py
4057 views
1
r"""
2
Partition/Diagram Algebras
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2007 Mike Hansen <[email protected]>,
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
#
9
# This code is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
# General Public License for more details.
13
#
14
# The full text of the GPL is available at:
15
#
16
# http://www.gnu.org/licenses/
17
#*****************************************************************************
18
19
from combinat import CombinatorialClass, catalan_number
20
from combinatorial_algebra import CombinatorialAlgebra, CombinatorialAlgebraElement
21
import set_partition
22
from sage.sets.set import Set
23
from sage.graphs.graph import Graph
24
from sage.rings.arith import factorial, binomial
25
from permutation import Permutations
26
from sage.rings.all import Integer, is_RealNumber
27
from subset import Subsets
28
from sage.functions.all import ceil
29
import functools, math
30
31
def create_set_partition_function(letter, k):
32
"""
33
EXAMPLES::
34
35
sage: from sage.combinat.partition_algebra import create_set_partition_function
36
sage: create_set_partition_function('A', 3)
37
Set partitions of {1, ..., 3, -1, ..., -3}
38
"""
39
from sage.functions.all import floor
40
if isinstance(k, (int, Integer)):
41
if k > 0:
42
return globals()['SetPartitions' + letter + 'k_k'](k)
43
elif is_RealNumber(k):
44
if k - math.floor(k) == 0.5:
45
return globals()['SetPartitions' + letter + 'khalf_k'](floor(k))
46
47
raise ValueError, "k must be an integer or an integer + 1/2"
48
49
50
#####
51
#A_k#
52
#####
53
SetPartitionsAk = functools.partial(create_set_partition_function,"A")
54
SetPartitionsAk.__doc__ = """
55
Returns the combinatorial class of set partitions of type A_k.
56
57
EXAMPLES::
58
59
sage: A3 = SetPartitionsAk(3); A3
60
Set partitions of {1, ..., 3, -1, ..., -3}
61
62
sage: A3.first() #random
63
{{1, 2, 3, -1, -3, -2}}
64
sage: A3.last() #random
65
{{-1}, {-2}, {3}, {1}, {-3}, {2}}
66
sage: A3.random_element() #random
67
{{1, 3, -3, -1}, {2, -2}}
68
69
sage: A3.cardinality()
70
203
71
72
sage: A2p5 = SetPartitionsAk(2.5); A2p5
73
Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block
74
sage: A2p5.cardinality()
75
52
76
77
sage: A2p5.first() #random
78
{{1, 2, 3, -1, -3, -2}}
79
sage: A2p5.last() #random
80
{{-1}, {-2}, {2}, {3, -3}, {1}}
81
sage: A2p5.random_element() #random
82
{{-1}, {-2}, {3, -3}, {1, 2}}
83
84
"""
85
86
class SetPartitionsAk_k(set_partition.SetPartitions_set):
87
def __init__(self, k):
88
"""
89
TESTS::
90
91
sage: A3 = SetPartitionsAk(3); A3
92
Set partitions of {1, ..., 3, -1, ..., -3}
93
sage: A3 == loads(dumps(A3))
94
True
95
"""
96
self.k = k
97
set_partition.SetPartitions_set.__init__(self, Set(range(1,k+1) + map(lambda x: -1*x,range(1,k+1))))
98
99
def __repr__(self):
100
"""
101
TESTS::
102
103
sage: repr(SetPartitionsAk(3))
104
'Set partitions of {1, ..., 3, -1, ..., -3}'
105
"""
106
return "Set partitions of {1, ..., %s, -1, ..., -%s}"%(self.k, self.k)
107
108
class SetPartitionsAkhalf_k(CombinatorialClass):
109
def __init__(self, k):
110
"""
111
TESTS::
112
113
sage: A2p5 = SetPartitionsAk(2.5); A2p5
114
Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block
115
sage: A2p5 == loads(dumps(A2p5))
116
True
117
"""
118
self.k = k
119
self._set = range(1,k+2) + map(lambda x: -1*x, range(1,k+1))
120
121
def __repr__(self):
122
"""
123
TESTS::
124
125
sage: repr(SetPartitionsAk(2.5))
126
'Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block'
127
"""
128
s = self.k+1
129
return "Set partitions of {1, ..., %s, -1, ..., -%s} with %s and -%s in the same block"%(s,s,s,s)
130
131
def __contains__(self, x):
132
"""
133
TESTS::
134
135
sage: A2p5 = SetPartitionsAk(2.5)
136
sage: all([ sp in A2p5 for sp in A2p5])
137
True
138
sage: A3 = SetPartitionsAk(3)
139
sage: len(filter(lambda x: x in A2p5, A3))
140
52
141
sage: A2p5.cardinality()
142
52
143
"""
144
if x not in SetPartitionsAk_k(self.k+1):
145
return False
146
147
for part in x:
148
if self.k+1 in part and -self.k-1 not in part:
149
return False
150
151
return True
152
153
def cardinality(self):
154
"""
155
TESTS::
156
157
sage: SetPartitionsAk(1.5).cardinality()
158
5
159
sage: SetPartitionsAk(2.5).cardinality()
160
52
161
sage: SetPartitionsAk(3.5).cardinality()
162
877
163
"""
164
return set_partition.SetPartitions_set(self._set).cardinality()
165
166
def __iter__(self):
167
"""
168
TESTS::
169
170
sage: SetPartitionsAk(1.5).list() #random
171
[{{1, 2, -2, -1}},
172
{{2, -2, -1}, {1}},
173
{{2, -2}, {1, -1}},
174
{{-1}, {1, 2, -2}},
175
{{-1}, {2, -2}, {1}}]
176
177
::
178
179
sage: ks = [ 1.5, 2.5, 3.5 ]
180
sage: aks = map(SetPartitionsAk, ks)
181
sage: all([ak.cardinality() == len(ak.list()) for ak in aks])
182
True
183
"""
184
kp = Set([-self.k-1])
185
for sp in set_partition.SetPartitions_set(self._set):
186
res = []
187
for part in sp:
188
if self.k+1 in part:
189
res.append( part + kp )
190
else:
191
res.append(part)
192
yield Set(res)
193
194
#####
195
#S_k#
196
#####
197
SetPartitionsSk = functools.partial(create_set_partition_function,"S")
198
SetPartitionsSk.__doc__ = """
199
Returns the combinatorial class of set partitions of type S_k. There
200
is a bijection between these set partitions and the permutations
201
of 1, ..., k.
202
203
EXAMPLES::
204
205
sage: S3 = SetPartitionsSk(3); S3
206
Set partitions of {1, ..., 3, -1, ..., -3} with propagating number 3
207
sage: S3.cardinality()
208
6
209
210
sage: S3.list() #random
211
[{{2, -2}, {3, -3}, {1, -1}},
212
{{1, -1}, {2, -3}, {3, -2}},
213
{{2, -1}, {3, -3}, {1, -2}},
214
{{1, -2}, {2, -3}, {3, -1}},
215
{{1, -3}, {2, -1}, {3, -2}},
216
{{1, -3}, {2, -2}, {3, -1}}]
217
sage: S3.first() #random
218
{{2, -2}, {3, -3}, {1, -1}}
219
sage: S3.last() #random
220
{{1, -3}, {2, -2}, {3, -1}}
221
sage: S3.random_element() #random
222
{{1, -3}, {2, -1}, {3, -2}}
223
224
sage: S3p5 = SetPartitionsSk(3.5); S3p5
225
Set partitions of {1, ..., 4, -1, ..., -4} with 4 and -4 in the same block and propagating number 4
226
sage: S3p5.cardinality()
227
6
228
229
sage: S3p5.list() #random
230
[{{2, -2}, {3, -3}, {1, -1}, {4, -4}},
231
{{2, -3}, {1, -1}, {4, -4}, {3, -2}},
232
{{2, -1}, {3, -3}, {1, -2}, {4, -4}},
233
{{2, -3}, {1, -2}, {4, -4}, {3, -1}},
234
{{1, -3}, {2, -1}, {4, -4}, {3, -2}},
235
{{1, -3}, {2, -2}, {4, -4}, {3, -1}}]
236
sage: S3p5.first() #random
237
{{2, -2}, {3, -3}, {1, -1}, {4, -4}}
238
sage: S3p5.last() #random
239
{{1, -3}, {2, -2}, {4, -4}, {3, -1}}
240
sage: S3p5.random_element() #random
241
{{1, -3}, {2, -2}, {4, -4}, {3, -1}}
242
"""
243
class SetPartitionsSk_k(SetPartitionsAk_k):
244
def __repr__(self):
245
"""
246
TESTS::
247
248
sage: repr(SetPartitionsSk(3))
249
'Set partitions of {1, ..., 3, -1, ..., -3} with propagating number 3'
250
"""
251
return SetPartitionsAk_k.__repr__(self) + " with propagating number %s"%self.k
252
253
def __contains__(self, x):
254
"""
255
TESTS::
256
257
sage: A3 = SetPartitionsAk(3)
258
sage: S3 = SetPartitionsSk(3)
259
sage: all([ sp in S3 for sp in S3])
260
True
261
sage: S3.cardinality()
262
6
263
sage: len(filter(lambda x: x in S3, A3))
264
6
265
"""
266
if not SetPartitionsAk_k.__contains__(self, x):
267
return False
268
269
if propagating_number(x) != self.k:
270
return False
271
272
return True
273
274
def cardinality(self):
275
"""
276
Returns k!.
277
278
TESTS::
279
280
sage: SetPartitionsSk(2).cardinality()
281
2
282
sage: SetPartitionsSk(3).cardinality()
283
6
284
sage: SetPartitionsSk(4).cardinality()
285
24
286
sage: SetPartitionsSk(5).cardinality()
287
120
288
"""
289
return factorial(self.k)
290
291
def __iter__(self):
292
"""
293
TESTS::
294
295
sage: SetPartitionsSk(3).list() #random
296
[{{2, -2}, {3, -3}, {1, -1}},
297
{{1, -1}, {2, -3}, {3, -2}},
298
{{2, -1}, {3, -3}, {1, -2}},
299
{{1, -2}, {2, -3}, {3, -1}},
300
{{1, -3}, {2, -1}, {3, -2}},
301
{{1, -3}, {2, -2}, {3, -1}}]
302
sage: ks = range(1, 6)
303
sage: sks = map(SetPartitionsSk, ks)
304
sage: all([ sk.cardinality() == len(sk.list()) for sk in sks])
305
True
306
"""
307
for p in Permutations(self.k):
308
res = []
309
for i in range(self.k):
310
res.append( Set([ i+1, -p[i] ]) )
311
yield Set(res)
312
313
class SetPartitionsSkhalf_k(SetPartitionsAkhalf_k):
314
def __contains__(self, x):
315
"""
316
TESTS::
317
318
sage: S2p5 = SetPartitionsSk(2.5)
319
sage: A3 = SetPartitionsAk(3)
320
sage: all([sp in S2p5 for sp in S2p5])
321
True
322
sage: len(filter(lambda x: x in S2p5, A3))
323
2
324
sage: S2p5.cardinality()
325
2
326
"""
327
if not SetPartitionsAkhalf_k.__contains__(self, x):
328
return False
329
if propagating_number(x) != self.k+1:
330
return False
331
return True
332
333
def __repr__(self):
334
"""
335
TESTS::
336
337
sage: repr(SetPartitionsSk(2.5))
338
'Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and propagating number 3'
339
"""
340
s = self.k+1
341
return SetPartitionsAkhalf_k.__repr__(self) + " and propagating number %s"%s
342
343
def cardinality(self):
344
"""
345
TESTS::
346
347
sage: SetPartitionsSk(2.5).cardinality()
348
2
349
sage: SetPartitionsSk(3.5).cardinality()
350
6
351
sage: SetPartitionsSk(4.5).cardinality()
352
24
353
354
::
355
356
sage: ks = [2.5, 3.5, 4.5, 5.5]
357
sage: sks = [SetPartitionsSk(k) for k in ks]
358
sage: all([ sk.cardinality() == len(sk.list()) for sk in sks])
359
True
360
"""
361
return factorial(self.k)
362
363
def __iter__(self):
364
"""
365
TESTS::
366
367
sage: SetPartitionsSk(3.5).list() #random indirect test
368
[{{2, -2}, {3, -3}, {1, -1}, {4, -4}},
369
{{2, -3}, {1, -1}, {4, -4}, {3, -2}},
370
{{2, -1}, {3, -3}, {1, -2}, {4, -4}},
371
{{2, -3}, {1, -2}, {4, -4}, {3, -1}},
372
{{1, -3}, {2, -1}, {4, -4}, {3, -2}},
373
{{1, -3}, {2, -2}, {4, -4}, {3, -1}}]
374
"""
375
for p in Permutations(self.k):
376
res = []
377
for i in range(self.k):
378
res.append( Set([ i+1, -p[i] ]) )
379
380
res.append(Set([self.k+1, -self.k - 1]))
381
yield Set(res)
382
383
#####
384
#I_k#
385
#####
386
SetPartitionsIk = functools.partial(create_set_partition_function,"I")
387
SetPartitionsIk.__doc__ = """
388
Returns the combinatorial class of set partitions of type I_k. These
389
are set partitions with a propagating number of less than k. Note
390
that the identity set partition {{1, -1}, ..., {k, -k}} is not
391
in I_k.
392
393
EXAMPLES::
394
395
sage: I3 = SetPartitionsIk(3); I3
396
Set partitions of {1, ..., 3, -1, ..., -3} with propagating number < 3
397
sage: I3.cardinality()
398
197
399
400
sage: I3.first() #random
401
{{1, 2, 3, -1, -3, -2}}
402
sage: I3.last() #random
403
{{-1}, {-2}, {3}, {1}, {-3}, {2}}
404
sage: I3.random_element() #random
405
{{-1}, {-3, -2}, {2, 3}, {1}}
406
407
sage: I2p5 = SetPartitionsIk(2.5); I2p5
408
Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and propagating number < 3
409
sage: I2p5.cardinality()
410
50
411
412
sage: I2p5.first() #random
413
{{1, 2, 3, -1, -3, -2}}
414
sage: I2p5.last() #random
415
{{-1}, {-2}, {2}, {3, -3}, {1}}
416
sage: I2p5.random_element() #random
417
{{-1}, {-2}, {1, 3, -3}, {2}}
418
419
"""
420
class SetPartitionsIk_k(SetPartitionsAk_k):
421
def __repr__(self):
422
"""
423
TESTS::
424
425
sage: repr(SetPartitionsIk(3))
426
'Set partitions of {1, ..., 3, -1, ..., -3} with propagating number < 3'
427
"""
428
return SetPartitionsAk_k.__repr__(self) + " with propagating number < %s"%self.k
429
430
def __contains__(self, x):
431
"""
432
TESTS::
433
434
sage: I3 = SetPartitionsIk(3)
435
sage: A3 = SetPartitionsAk(3)
436
sage: all([ sp in I3 for sp in I3])
437
True
438
sage: len(filter(lambda x: x in I3, A3))
439
197
440
sage: I3.cardinality()
441
197
442
"""
443
if not SetPartitionsAk_k.__contains__(self, x):
444
return False
445
if propagating_number(x) >= self.k:
446
return False
447
return True
448
449
def cardinality(self):
450
"""
451
TESTS::
452
453
sage: SetPartitionsIk(2).cardinality()
454
13
455
"""
456
return len(self.list())
457
458
def __iter__(self):
459
"""
460
TESTS::
461
462
sage: SetPartitionsIk(2).list() #random indirect test
463
[{{1, 2, -1, -2}},
464
{{2, -1, -2}, {1}},
465
{{2}, {1, -1, -2}},
466
{{-1}, {1, 2, -2}},
467
{{-2}, {1, 2, -1}},
468
{{1, 2}, {-1, -2}},
469
{{2}, {-1, -2}, {1}},
470
{{-1}, {2, -2}, {1}},
471
{{-2}, {2, -1}, {1}},
472
{{-1}, {2}, {1, -2}},
473
{{-2}, {2}, {1, -1}},
474
{{-1}, {-2}, {1, 2}},
475
{{-1}, {-2}, {2}, {1}}]
476
"""
477
for sp in SetPartitionsAk_k.__iter__(self):
478
if propagating_number(sp) < self.k:
479
yield sp
480
481
class SetPartitionsIkhalf_k(SetPartitionsAkhalf_k):
482
def __contains__(self, x):
483
"""
484
TESTS::
485
486
sage: I2p5 = SetPartitionsIk(2.5)
487
sage: A3 = SetPartitionsAk(3)
488
sage: all([ sp in I2p5 for sp in I2p5])
489
True
490
sage: len(filter(lambda x: x in I2p5, A3))
491
50
492
sage: I2p5.cardinality()
493
50
494
"""
495
if not SetPartitionsAkhalf_k.__contains__(self, x):
496
return False
497
if propagating_number(x) >= self.k+1:
498
return False
499
return True
500
501
def __repr__(self):
502
"""
503
TESTS::
504
505
sage: repr(SetPartitionsIk(2.5))
506
'Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and propagating number < 3'
507
"""
508
return SetPartitionsAkhalf_k.__repr__(self) + " and propagating number < %s"%(self.k+1)
509
510
def cardinality(self):
511
"""
512
TESTS::
513
514
sage: SetPartitionsIk(1.5).cardinality()
515
4
516
sage: SetPartitionsIk(2.5).cardinality()
517
50
518
sage: SetPartitionsIk(3.5).cardinality()
519
871
520
"""
521
return len(self.list())
522
523
def __iter__(self):
524
"""
525
TESTS::
526
527
sage: SetPartitionsIk(1.5).list() #random
528
[{{1, 2, -2, -1}},
529
{{2, -2, -1}, {1}},
530
{{-1}, {1, 2, -2}},
531
{{-1}, {2, -2}, {1}}]
532
"""
533
534
for sp in SetPartitionsAkhalf_k.__iter__(self):
535
if propagating_number(sp) < self.k+1:
536
yield sp
537
#####
538
#B_k#
539
#####
540
SetPartitionsBk = functools.partial(create_set_partition_function,"B")
541
SetPartitionsBk.__doc__ = """
542
Returns the combinatorial class of set partitions of type B_k.
543
These are the set partitions where every block has size 2.
544
545
EXAMPLES::
546
547
sage: B3 = SetPartitionsBk(3); B3
548
Set partitions of {1, ..., 3, -1, ..., -3} with block size 2
549
550
sage: B3.first() #random
551
{{2, -2}, {1, -3}, {3, -1}}
552
sage: B3.last() #random
553
{{1, 2}, {3, -2}, {-3, -1}}
554
sage: B3.random_element() #random
555
{{2, -1}, {1, -3}, {3, -2}}
556
557
sage: B3.cardinality()
558
15
559
560
sage: B2p5 = SetPartitionsBk(2.5); B2p5
561
Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and with block size 2
562
563
sage: B2p5.first() #random
564
{{2, -1}, {3, -3}, {1, -2}}
565
sage: B2p5.last() #random
566
{{1, 2}, {3, -3}, {-1, -2}}
567
sage: B2p5.random_element() #random
568
{{2, -2}, {3, -3}, {1, -1}}
569
570
sage: B2p5.cardinality()
571
3
572
"""
573
574
class SetPartitionsBk_k(SetPartitionsAk_k):
575
def __repr__(self):
576
"""
577
TESTS::
578
579
sage: repr(SetPartitionsBk(2.5))
580
'Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and with block size 2'
581
"""
582
return SetPartitionsAk_k.__repr__(self) + " with block size 2"
583
584
def __contains__(self, x):
585
"""
586
TESTS::
587
588
sage: B3 = SetPartitionsBk(3)
589
sage: A3 = SetPartitionsAk(3)
590
sage: len(filter(lambda x: x in B3, A3))
591
15
592
sage: B3.cardinality()
593
15
594
"""
595
if not SetPartitionsAk_k.__contains__(self, x):
596
return False
597
598
for part in x:
599
if len(part) != 2:
600
return False
601
602
return True
603
604
def cardinality(self):
605
"""
606
Returns the number of set partitions in B_k where k is an integer.
607
This is given by (2k)!! = (2k-1)\*(2k-3)\*...\*5\*3\*1.
608
609
EXAMPLES::
610
611
sage: SetPartitionsBk(3).cardinality()
612
15
613
sage: SetPartitionsBk(2).cardinality()
614
3
615
sage: SetPartitionsBk(1).cardinality()
616
1
617
sage: SetPartitionsBk(4).cardinality()
618
105
619
sage: SetPartitionsBk(5).cardinality()
620
945
621
"""
622
c = 1
623
for i in range(1, 2*self.k,2):
624
c *= i
625
return c
626
627
def __iter__(self):
628
"""
629
TESTS::
630
631
sage: SetPartitionsBk(1).list()
632
[{{1, -1}}]
633
634
::
635
636
sage: SetPartitionsBk(2).list() #random
637
[{{2, -1}, {1, -2}}, {{2, -2}, {1, -1}}, {{1, 2}, {-1, -2}}]
638
sage: SetPartitionsBk(3).list() #random
639
[{{2, -2}, {1, -3}, {3, -1}},
640
{{2, -1}, {1, -3}, {3, -2}},
641
{{1, -3}, {2, 3}, {-1, -2}},
642
{{3, -1}, {1, -2}, {2, -3}},
643
{{3, -2}, {1, -1}, {2, -3}},
644
{{1, 3}, {2, -3}, {-1, -2}},
645
{{2, -1}, {3, -3}, {1, -2}},
646
{{2, -2}, {3, -3}, {1, -1}},
647
{{1, 2}, {3, -3}, {-1, -2}},
648
{{-3, -2}, {2, 3}, {1, -1}},
649
{{1, 3}, {-3, -2}, {2, -1}},
650
{{1, 2}, {3, -1}, {-3, -2}},
651
{{-3, -1}, {2, 3}, {1, -2}},
652
{{1, 3}, {-3, -1}, {2, -2}},
653
{{1, 2}, {3, -2}, {-3, -1}}]
654
655
Check to make sure that the number of elements generated is the
656
same as what is given by cardinality()
657
658
::
659
660
sage: bks = [ SetPartitionsBk(i) for i in range(1, 6) ]
661
sage: all( [ bk.cardinality() == len(bk.list()) for bk in bks] )
662
True
663
"""
664
for sp in set_partition.SetPartitions(self.set, [2]*(len(self.set)/2)):
665
yield sp
666
667
class SetPartitionsBkhalf_k(SetPartitionsAkhalf_k):
668
def __repr__(self):
669
"""
670
TESTS::
671
672
sage: repr(SetPartitionsBk(2.5))
673
'Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and with block size 2'
674
"""
675
return SetPartitionsAkhalf_k.__repr__(self) + " and with block size 2"
676
677
678
def __contains__(self, x):
679
"""
680
TESTS::
681
682
sage: A3 = SetPartitionsAk(3)
683
sage: B2p5 = SetPartitionsBk(2.5)
684
sage: all([ sp in B2p5 for sp in B2p5 ])
685
True
686
sage: len(filter(lambda x: x in B2p5, A3))
687
3
688
sage: B2p5.cardinality()
689
3
690
"""
691
if not SetPartitionsAkhalf_k.__contains__(self, x):
692
return False
693
for part in x:
694
if len(part) != 2:
695
return False
696
return True
697
698
def cardinality(self):
699
"""
700
TESTS::
701
702
sage: B3p5 = SetPartitionsBk(3.5)
703
sage: B3p5.cardinality()
704
15
705
"""
706
return len(self.list())
707
708
def __iter__(self):
709
"""
710
TESTS::
711
712
sage: B3p5 = SetPartitionsBk(3.5)
713
sage: B3p5.cardinality()
714
15
715
716
::
717
718
sage: B3p5.list() #random
719
[{{2, -2}, {1, -3}, {4, -4}, {3, -1}},
720
{{2, -1}, {1, -3}, {4, -4}, {3, -2}},
721
{{1, -3}, {2, 3}, {4, -4}, {-1, -2}},
722
{{2, -3}, {1, -2}, {4, -4}, {3, -1}},
723
{{2, -3}, {1, -1}, {4, -4}, {3, -2}},
724
{{1, 3}, {4, -4}, {2, -3}, {-1, -2}},
725
{{2, -1}, {3, -3}, {1, -2}, {4, -4}},
726
{{2, -2}, {3, -3}, {1, -1}, {4, -4}},
727
{{1, 2}, {3, -3}, {4, -4}, {-1, -2}},
728
{{-3, -2}, {2, 3}, {1, -1}, {4, -4}},
729
{{1, 3}, {-3, -2}, {2, -1}, {4, -4}},
730
{{1, 2}, {-3, -2}, {4, -4}, {3, -1}},
731
{{-3, -1}, {2, 3}, {1, -2}, {4, -4}},
732
{{1, 3}, {-3, -1}, {2, -2}, {4, -4}},
733
{{1, 2}, {-3, -1}, {4, -4}, {3, -2}}]
734
"""
735
set = range(1,self.k+1) + map(lambda x: -1*x, range(1,self.k+1))
736
for sp in set_partition.SetPartitions(set, [2]*(len(set)/2) ):
737
yield sp + Set([Set([self.k+1, -self.k -1])])
738
739
#####
740
#P_k#
741
#####
742
SetPartitionsPk = functools.partial(create_set_partition_function,"P")
743
SetPartitionsPk.__doc__ = """
744
Returns the combinatorial class of set partitions of type P_k.
745
These are the planar set partitions.
746
747
EXAMPLES::
748
749
sage: P3 = SetPartitionsPk(3); P3
750
Set partitions of {1, ..., 3, -1, ..., -3} that are planar
751
sage: P3.cardinality()
752
132
753
754
sage: P3.first() #random
755
{{1, 2, 3, -1, -3, -2}}
756
sage: P3.last() #random
757
{{-1}, {-2}, {3}, {1}, {-3}, {2}}
758
sage: P3.random_element() #random
759
{{1, 2, -1}, {-3}, {3, -2}}
760
761
sage: P2p5 = SetPartitionsPk(2.5); P2p5
762
Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and that are planar
763
sage: P2p5.cardinality()
764
42
765
766
sage: P2p5.first() #random
767
{{1, 2, 3, -1, -3, -2}}
768
sage: P2p5.last() #random
769
{{-1}, {-2}, {2}, {3, -3}, {1}}
770
sage: P2p5.random_element() #random
771
{{1, 2, 3, -3}, {-1, -2}}
772
773
"""
774
class SetPartitionsPk_k(SetPartitionsAk_k):
775
def __repr__(self):
776
"""
777
TESTS::
778
779
sage: repr(SetPartitionsPk(3))
780
'Set partitions of {1, ..., 3, -1, ..., -3} that are planar'
781
"""
782
return SetPartitionsAk_k.__repr__(self) + " that are planar"
783
784
def __contains__(self, x):
785
"""
786
TESTS::
787
788
sage: P3 = SetPartitionsPk(3)
789
sage: A3 = SetPartitionsAk(3)
790
sage: len(filter(lambda x: x in P3, A3))
791
132
792
sage: P3.cardinality()
793
132
794
sage: all([sp in P3 for sp in P3])
795
True
796
"""
797
if not SetPartitionsAk_k.__contains__(self, x):
798
return False
799
800
if not is_planar(x):
801
return False
802
803
return True
804
805
def cardinality(self):
806
"""
807
TESTS::
808
809
sage: SetPartitionsPk(2).cardinality()
810
14
811
sage: SetPartitionsPk(3).cardinality()
812
132
813
sage: SetPartitionsPk(4).cardinality()
814
1430
815
"""
816
return catalan_number(2*self.k)
817
818
def __iter__(self):
819
"""
820
TESTS::
821
822
sage: SetPartitionsPk(2).list() #random indirect test
823
[{{1, 2, -1, -2}},
824
{{2, -1, -2}, {1}},
825
{{2}, {1, -1, -2}},
826
{{-1}, {1, 2, -2}},
827
{{-2}, {1, 2, -1}},
828
{{2, -2}, {1, -1}},
829
{{1, 2}, {-1, -2}},
830
{{2}, {-1, -2}, {1}},
831
{{-1}, {2, -2}, {1}},
832
{{-2}, {2, -1}, {1}},
833
{{-1}, {2}, {1, -2}},
834
{{-2}, {2}, {1, -1}},
835
{{-1}, {-2}, {1, 2}},
836
{{-1}, {-2}, {2}, {1}}]
837
"""
838
for sp in SetPartitionsAk_k.__iter__(self):
839
if is_planar(sp):
840
yield sp
841
842
class SetPartitionsPkhalf_k(SetPartitionsAkhalf_k):
843
def __contains__(self, x):
844
"""
845
TESTS::
846
847
sage: A3 = SetPartitionsAk(3)
848
sage: P2p5 = SetPartitionsPk(2.5)
849
sage: all([ sp in P2p5 for sp in P2p5 ])
850
True
851
sage: len(filter(lambda x: x in P2p5, A3))
852
42
853
sage: P2p5.cardinality()
854
42
855
"""
856
if not SetPartitionsAkhalf_k.__contains__(self, x):
857
return False
858
if not is_planar(x):
859
return False
860
861
return True
862
863
def __repr__(self):
864
"""
865
TESTS::
866
867
sage: repr( SetPartitionsPk(2.5) )
868
'Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and that are planar'
869
"""
870
return SetPartitionsAkhalf_k.__repr__(self) + " and that are planar"
871
872
def cardinality(self):
873
"""
874
TESTS::
875
876
sage: SetPartitionsPk(2.5).cardinality()
877
42
878
sage: SetPartitionsPk(1.5).cardinality()
879
5
880
"""
881
return len(self.list())
882
883
def __iter__(self):
884
"""
885
TESTS::
886
887
sage: SetPartitionsPk(1.5).list() #random
888
[{{1, 2, -2, -1}},
889
{{2, -2, -1}, {1}},
890
{{2, -2}, {1, -1}},
891
{{-1}, {1, 2, -2}},
892
{{-1}, {2, -2}, {1}}]
893
"""
894
for sp in SetPartitionsAkhalf_k.__iter__(self):
895
if is_planar(sp):
896
yield sp
897
898
899
#####
900
#T_k#
901
#####
902
SetPartitionsTk = functools.partial(create_set_partition_function,"T")
903
SetPartitionsTk.__doc__ = """
904
Returns the combinatorial class of set partitions of type T_k.
905
These are planar set partitions where every block is of size 2.
906
907
EXAMPLES::
908
909
sage: T3 = SetPartitionsTk(3); T3
910
Set partitions of {1, ..., 3, -1, ..., -3} with block size 2 and that are planar
911
sage: T3.cardinality()
912
5
913
914
sage: T3.first() #random
915
{{1, -3}, {2, 3}, {-1, -2}}
916
sage: T3.last() #random
917
{{1, 2}, {3, -1}, {-3, -2}}
918
sage: T3.random_element() #random
919
{{1, -3}, {2, 3}, {-1, -2}}
920
921
sage: T2p5 = SetPartitionsTk(2.5); T2p5
922
Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and with block size 2 and that are planar
923
sage: T2p5.cardinality()
924
2
925
926
sage: T2p5.first() #random
927
{{2, -2}, {3, -3}, {1, -1}}
928
sage: T2p5.last() #random
929
{{1, 2}, {3, -3}, {-1, -2}}
930
931
"""
932
class SetPartitionsTk_k(SetPartitionsBk_k):
933
def __repr__(self):
934
"""
935
TESTS::
936
937
sage: repr(SetPartitionsTk(3))
938
'Set partitions of {1, ..., 3, -1, ..., -3} with block size 2 and that are planar'
939
"""
940
return SetPartitionsBk_k.__repr__(self) + " and that are planar"
941
942
def __contains__(self, x):
943
"""
944
TESTS::
945
946
sage: T3 = SetPartitionsTk(3)
947
sage: A3 = SetPartitionsAk(3)
948
sage: all([ sp in T3 for sp in T3])
949
True
950
sage: len(filter(lambda x: x in T3, A3))
951
5
952
sage: T3.cardinality()
953
5
954
"""
955
if not SetPartitionsBk_k.__contains__(self, x):
956
return False
957
958
if not is_planar(x):
959
return False
960
961
return True
962
963
def cardinality(self):
964
"""
965
TESTS::
966
967
sage: SetPartitionsTk(2).cardinality()
968
2
969
sage: SetPartitionsTk(3).cardinality()
970
5
971
sage: SetPartitionsTk(4).cardinality()
972
14
973
sage: SetPartitionsTk(5).cardinality()
974
42
975
"""
976
return catalan_number(self.k)
977
978
def __iter__(self):
979
"""
980
TESTS::
981
982
sage: SetPartitionsTk(3).list() #random
983
[{{1, -3}, {2, 3}, {-1, -2}},
984
{{2, -2}, {3, -3}, {1, -1}},
985
{{1, 2}, {3, -3}, {-1, -2}},
986
{{-3, -2}, {2, 3}, {1, -1}},
987
{{1, 2}, {3, -1}, {-3, -2}}]
988
"""
989
for sp in SetPartitionsBk_k.__iter__(self):
990
if is_planar(sp):
991
yield sp
992
993
class SetPartitionsTkhalf_k(SetPartitionsBkhalf_k):
994
def __contains__(self, x):
995
"""
996
TESTS::
997
998
sage: A3 = SetPartitionsAk(3)
999
sage: T2p5 = SetPartitionsTk(2.5)
1000
sage: all([ sp in T2p5 for sp in T2p5 ])
1001
True
1002
sage: len(filter(lambda x: x in T2p5, A3))
1003
2
1004
sage: T2p5.cardinality()
1005
2
1006
"""
1007
if not SetPartitionsBkhalf_k.__contains__(self, x):
1008
return False
1009
if not is_planar(x):
1010
return False
1011
1012
return True
1013
1014
def __repr__(self):
1015
"""
1016
TESTS::
1017
1018
sage: repr(SetPartitionsTk(2.5))
1019
'Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and with block size 2 and that are planar'
1020
"""
1021
return SetPartitionsBkhalf_k.__repr__(self) + " and that are planar"
1022
1023
def cardinality(self):
1024
"""
1025
TESTS::
1026
1027
sage: SetPartitionsTk(2.5).cardinality()
1028
2
1029
sage: SetPartitionsTk(3.5).cardinality()
1030
5
1031
sage: SetPartitionsTk(4.5).cardinality()
1032
14
1033
"""
1034
return catalan_number(self.k)
1035
1036
def __iter__(self):
1037
"""
1038
TESTS::
1039
1040
sage: SetPartitionsTk(3.5).list() #random
1041
[{{1, -3}, {2, 3}, {4, -4}, {-1, -2}},
1042
{{2, -2}, {3, -3}, {1, -1}, {4, -4}},
1043
{{1, 2}, {3, -3}, {4, -4}, {-1, -2}},
1044
{{-3, -2}, {2, 3}, {1, -1}, {4, -4}},
1045
{{1, 2}, {-3, -2}, {4, -4}, {3, -1}}]
1046
"""
1047
for sp in SetPartitionsBkhalf_k.__iter__(self):
1048
if is_planar(sp):
1049
yield sp
1050
1051
1052
1053
SetPartitionsRk = functools.partial(create_set_partition_function,"R")
1054
SetPartitionsRk.__doc__ = """
1055
"""
1056
class SetPartitionsRk_k(SetPartitionsAk_k):
1057
def __init__(self, k):
1058
"""
1059
TESTS::
1060
1061
sage: R3 = SetPartitionsRk(3); R3
1062
Set partitions of {1, ..., 3, -1, ..., -3} with at most 1 positive and negative entry in each block
1063
sage: R3 == loads(dumps(R3))
1064
True
1065
"""
1066
self.k = k
1067
SetPartitionsAk_k.__init__(self, k)
1068
1069
def __repr__(self):
1070
"""
1071
TESTS::
1072
1073
sage: repr(SetPartitionsRk(3))
1074
'Set partitions of {1, ..., 3, -1, ..., -3} with at most 1 positive and negative entry in each block'
1075
"""
1076
return SetPartitionsAk_k.__repr__(self) + " with at most 1 positive and negative entry in each block"
1077
1078
def __contains__(self, x):
1079
"""
1080
TESTS::
1081
1082
sage: R3 = SetPartitionsRk(3)
1083
sage: A3 = SetPartitionsAk(3)
1084
sage: all([ sp in R3 for sp in R3])
1085
True
1086
sage: len(filter(lambda x: x in R3, A3))
1087
34
1088
sage: R3.cardinality()
1089
34
1090
"""
1091
if not SetPartitionsAk_k.__contains__(self, x):
1092
return False
1093
1094
for block in x:
1095
if len(block) > 2:
1096
return False
1097
1098
negatives = 0
1099
positives = 0
1100
for i in block:
1101
if i < 0:
1102
negatives += 1
1103
else:
1104
positives += 1
1105
1106
if negatives > 1 or positives > 1:
1107
return False
1108
1109
return True
1110
1111
def cardinality(self):
1112
"""
1113
TESTS::
1114
1115
sage: SetPartitionsRk(2).cardinality()
1116
7
1117
sage: SetPartitionsRk(3).cardinality()
1118
34
1119
sage: SetPartitionsRk(4).cardinality()
1120
209
1121
sage: SetPartitionsRk(5).cardinality()
1122
1546
1123
"""
1124
return sum( [ binomial(self.k, l)**2*factorial(l) for l in range(self.k + 1) ] )
1125
1126
def __iter__(self):
1127
"""
1128
TESTS::
1129
1130
sage: len(SetPartitionsRk(3).list() ) == SetPartitionsRk(3).cardinality()
1131
True
1132
"""
1133
#The number of blocks with at most two things
1134
positives = Set(range(1, self.k+1))
1135
negatives = Set( [ -i for i in positives ] )
1136
1137
yield to_set_partition([],self.k)
1138
for n in range(1,self.k+1):
1139
for top in Subsets(positives, n):
1140
t = list(top)
1141
for bottom in Subsets(negatives, n):
1142
b = list(bottom)
1143
for permutation in Permutations(n):
1144
l = [ [t[i], b[ permutation[i] - 1 ] ] for i in range(n) ]
1145
yield to_set_partition(l, k=self.k)
1146
1147
class SetPartitionsRkhalf_k(SetPartitionsAkhalf_k):
1148
def __contains__(self, x):
1149
"""
1150
TESTS::
1151
1152
sage: A3 = SetPartitionsAk(3)
1153
sage: R2p5 = SetPartitionsRk(2.5)
1154
sage: all([ sp in R2p5 for sp in R2p5 ])
1155
True
1156
sage: len(filter(lambda x: x in R2p5, A3))
1157
7
1158
sage: R2p5.cardinality()
1159
7
1160
"""
1161
if not SetPartitionsAkhalf_k.__contains__(self, x):
1162
return False
1163
1164
for block in x:
1165
if len(block) > 2:
1166
return False
1167
1168
negatives = 0
1169
positives = 0
1170
for i in block:
1171
if i < 0:
1172
negatives += 1
1173
else:
1174
positives += 1
1175
1176
if negatives > 1 or positives > 1:
1177
return False
1178
1179
1180
return True
1181
1182
def __repr__(self):
1183
"""
1184
TESTS::
1185
1186
sage: repr(SetPartitionsRk(2.5))
1187
'Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and with at most 1 positive and negative entry in each block'
1188
"""
1189
return SetPartitionsAkhalf_k.__repr__(self) + " and with at most 1 positive and negative entry in each block"
1190
1191
def cardinality(self):
1192
"""
1193
TESTS::
1194
1195
sage: SetPartitionsRk(2.5).cardinality()
1196
7
1197
sage: SetPartitionsRk(3.5).cardinality()
1198
34
1199
sage: SetPartitionsRk(4.5).cardinality()
1200
209
1201
"""
1202
return sum( [ binomial(self.k, l)**2*factorial(l) for l in range(self.k + 1) ] )
1203
1204
def __iter__(self):
1205
"""
1206
TESTS::
1207
1208
sage: R2p5 = SetPartitionsRk(2.5)
1209
sage: list(R2p5) #random due to sets
1210
[{{-2}, {-1}, {3, -3}, {2}, {1}},
1211
{{-2}, {3, -3}, {2}, {1, -1}},
1212
{{-1}, {3, -3}, {2}, {1, -2}},
1213
{{-2}, {2, -1}, {3, -3}, {1}},
1214
{{-1}, {2, -2}, {3, -3}, {1}},
1215
{{2, -2}, {3, -3}, {1, -1}},
1216
{{2, -1}, {3, -3}, {1, -2}}]
1217
sage: len(_)
1218
7
1219
"""
1220
positives = Set(range(1, self.k+1))
1221
negatives = Set( [ -i for i in positives ] )
1222
1223
yield to_set_partition([[self.k+1, -self.k-1]], self.k+1)
1224
for n in range(1,self.k+1):
1225
for top in Subsets(positives, n):
1226
t = list(top)
1227
for bottom in Subsets(negatives, n):
1228
b = list(bottom)
1229
for permutation in Permutations(n):
1230
l = [ [t[i], b[ permutation[i] - 1 ] ] for i in range(n) ] + [ [self.k+1, -self.k-1] ]
1231
yield to_set_partition(l, k=self.k+1)
1232
1233
1234
SetPartitionsPRk = functools.partial(create_set_partition_function,"PR")
1235
SetPartitionsPRk.__doc__ = """
1236
"""
1237
class SetPartitionsPRk_k(SetPartitionsRk_k):
1238
def __init__(self, k):
1239
"""
1240
TESTS::
1241
1242
sage: PR3 = SetPartitionsPRk(3); PR3
1243
Set partitions of {1, ..., 3, -1, ..., -3} with at most 1 positive and negative entry in each block and that are planar
1244
sage: PR3 == loads(dumps(PR3))
1245
True
1246
"""
1247
self.k = k
1248
SetPartitionsRk_k.__init__(self, k)
1249
1250
def __repr__(self):
1251
"""
1252
TESTS::
1253
1254
sage: repr(SetPartitionsPRk(3))
1255
'Set partitions of {1, ..., 3, -1, ..., -3} with at most 1 positive and negative entry in each block and that are planar'
1256
"""
1257
return SetPartitionsRk_k.__repr__(self) + " and that are planar"
1258
1259
def __contains__(self, x):
1260
"""
1261
TESTS::
1262
1263
sage: PR3 = SetPartitionsPRk(3)
1264
sage: A3 = SetPartitionsAk(3)
1265
sage: all([ sp in PR3 for sp in PR3])
1266
True
1267
sage: len(filter(lambda x: x in PR3, A3))
1268
20
1269
sage: PR3.cardinality()
1270
20
1271
"""
1272
if not SetPartitionsRk_k.__contains__(self, x):
1273
return False
1274
1275
if not is_planar(x):
1276
return False
1277
1278
return True
1279
1280
def cardinality(self):
1281
"""
1282
TESTS::
1283
1284
sage: SetPartitionsPRk(2).cardinality()
1285
6
1286
sage: SetPartitionsPRk(3).cardinality()
1287
20
1288
sage: SetPartitionsPRk(4).cardinality()
1289
70
1290
sage: SetPartitionsPRk(5).cardinality()
1291
252
1292
"""
1293
return binomial(2*self.k, self.k)
1294
1295
def __iter__(self):
1296
"""
1297
TESTS::
1298
1299
sage: len(SetPartitionsPRk(3).list() ) == SetPartitionsPRk(3).cardinality()
1300
True
1301
"""
1302
#The number of blocks with at most two things
1303
positives = Set(range(1, self.k+1))
1304
negatives = Set( [ -i for i in positives ] )
1305
1306
yield to_set_partition([], self.k)
1307
for n in range(1,self.k+1):
1308
for top in Subsets(positives, n):
1309
t = list(top)
1310
t.sort()
1311
for bottom in Subsets(negatives, n):
1312
b = list(bottom)
1313
b.sort(reverse=True)
1314
l = [ [t[i], b[ i ] ] for i in range(n) ]
1315
yield to_set_partition(l, k=self.k)
1316
1317
class SetPartitionsPRkhalf_k(SetPartitionsRkhalf_k):
1318
def __contains__(self, x):
1319
"""
1320
TESTS::
1321
1322
sage: A3 = SetPartitionsAk(3)
1323
sage: PR2p5 = SetPartitionsPRk(2.5)
1324
sage: all([ sp in PR2p5 for sp in PR2p5 ])
1325
True
1326
sage: len(filter(lambda x: x in PR2p5, A3))
1327
6
1328
sage: PR2p5.cardinality()
1329
6
1330
"""
1331
if not SetPartitionsRkhalf_k.__contains__(self, x):
1332
return False
1333
1334
if not is_planar(x):
1335
return False
1336
1337
return True
1338
1339
def __repr__(self):
1340
"""
1341
TESTS::
1342
1343
sage: repr(SetPartitionsPRk(2.5))
1344
'Set partitions of {1, ..., 3, -1, ..., -3} with 3 and -3 in the same block and with at most 1 positive and negative entry in each block and that are planar'
1345
"""
1346
return SetPartitionsRkhalf_k.__repr__(self) + " and that are planar"
1347
1348
def cardinality(self):
1349
"""
1350
TESTS::
1351
1352
sage: SetPartitionsPRk(2.5).cardinality()
1353
6
1354
sage: SetPartitionsPRk(3.5).cardinality()
1355
20
1356
sage: SetPartitionsPRk(4.5).cardinality()
1357
70
1358
"""
1359
return binomial(2*self.k, self.k)
1360
1361
def __iter__(self):
1362
"""
1363
TESTS::
1364
1365
sage: list(SetPartitionsPRk(2.5))
1366
[{{-2}, {-1}, {3, -3}, {2}, {1}},
1367
{{-2}, {3, -3}, {2}, {1, -1}},
1368
{{-1}, {3, -3}, {2}, {1, -2}},
1369
{{-2}, {2, -1}, {3, -3}, {1}},
1370
{{-1}, {2, -2}, {3, -3}, {1}},
1371
{{2, -2}, {3, -3}, {1, -1}}]
1372
sage: len(_)
1373
6
1374
"""
1375
positives = Set(range(1, self.k+1))
1376
negatives = Set( [ -i for i in positives ] )
1377
1378
yield to_set_partition([[self.k+1, -self.k-1]],k=self.k+1)
1379
for n in range(1,self.k+1):
1380
for top in Subsets(positives, n):
1381
t = list(top)
1382
t.sort()
1383
for bottom in Subsets(negatives, n):
1384
b = list(bottom)
1385
b.sort(reverse=True)
1386
l = [ [t[i], b[ i ] ] for i in range(n) ] + [ [self.k+1, -self.k-1] ]
1387
yield to_set_partition(l, k=self.k+1)
1388
1389
#########################################################
1390
#Algebras
1391
1392
class PartitionAlgebra_generic(CombinatorialAlgebra):
1393
def __init__(self, R, cclass, n, k, name=None, prefix=None):
1394
"""
1395
EXAMPLES::
1396
1397
sage: from sage.combinat.partition_algebra import *
1398
sage: s = PartitionAlgebra_sk(QQ, 3, 1)
1399
sage: s == loads(dumps(s))
1400
True
1401
"""
1402
self.k = k
1403
self.n = n
1404
self._basis_keys = cclass
1405
self._name = "Generic partition algebra with k = %s and n = %s and basis %s"%( self.k, self.n, cclass) if name is None else name
1406
self._one = identity(ceil(self.k))
1407
self._prefix = "" if prefix is None else prefix
1408
CombinatorialAlgebra.__init__(self, R)
1409
1410
def _multiply_basis(self, left, right):
1411
"""
1412
EXAMPLES::
1413
1414
sage: from sage.combinat.partition_algebra import *
1415
sage: s = PartitionAlgebra_sk(QQ, 3, 1)
1416
sage: t12 = s(Set([Set([1,-2]),Set([2,-1]),Set([3,-3])]))
1417
sage: t12^2 == s(1) #indirect doctest
1418
True
1419
"""
1420
(sp, l) = set_partition_composition(left, right)
1421
return {sp: self.n**l}
1422
class PartitionAlgebraElement_generic(CombinatorialAlgebraElement):
1423
pass
1424
1425
class PartitionAlgebraElement_ak(PartitionAlgebraElement_generic):
1426
pass
1427
class PartitionAlgebra_ak(PartitionAlgebra_generic):
1428
def __init__(self, R, k, n, name=None):
1429
"""
1430
EXAMPLES::
1431
1432
sage: from sage.combinat.partition_algebra import *
1433
sage: p = PartitionAlgebra_ak(QQ, 3, 1)
1434
sage: p == loads(dumps(p))
1435
True
1436
"""
1437
if name is None:
1438
name = "Partition algebra A_%s(%s)"%(k, n)
1439
cclass = SetPartitionsAk(k)
1440
self._element_class = PartitionAlgebraElement_ak
1441
PartitionAlgebra_generic.__init__(self, R, cclass, n, k, name=name, prefix="A")
1442
1443
class PartitionAlgebraElement_bk(PartitionAlgebraElement_generic):
1444
pass
1445
class PartitionAlgebra_bk(PartitionAlgebra_generic):
1446
def __init__(self, R, k, n, name=None):
1447
"""
1448
EXAMPLES::
1449
1450
sage: from sage.combinat.partition_algebra import *
1451
sage: p = PartitionAlgebra_bk(QQ, 3, 1)
1452
sage: p == loads(dumps(p))
1453
True
1454
"""
1455
if name is None:
1456
name = "Partition algebra B_%s(%s)"%(k, n)
1457
cclass = SetPartitionsBk(k)
1458
self._element_class = PartitionAlgebraElement_bk
1459
PartitionAlgebra_generic.__init__(self, R, cclass, n, k, name=name, prefix="B")
1460
1461
class PartitionAlgebraElement_sk(PartitionAlgebraElement_generic):
1462
pass
1463
class PartitionAlgebra_sk(PartitionAlgebra_generic):
1464
def __init__(self, R, k, n, name=None):
1465
"""
1466
EXAMPLES::
1467
1468
sage: from sage.combinat.partition_algebra import *
1469
sage: p = PartitionAlgebra_sk(QQ, 3, 1)
1470
sage: p == loads(dumps(p))
1471
True
1472
"""
1473
if name is None:
1474
name = "Partition algebra S_%s(%s)"%(k, n)
1475
cclass = SetPartitionsSk(k)
1476
self._element_class = PartitionAlgebraElement_sk
1477
PartitionAlgebra_generic.__init__(self, R, cclass, n, k, name=name, prefix="S")
1478
1479
class PartitionAlgebraElement_pk(PartitionAlgebraElement_generic):
1480
pass
1481
class PartitionAlgebra_pk(PartitionAlgebra_generic):
1482
def __init__(self, R, k, n, name=None):
1483
"""
1484
EXAMPLES::
1485
1486
sage: from sage.combinat.partition_algebra import *
1487
sage: p = PartitionAlgebra_pk(QQ, 3, 1)
1488
sage: p == loads(dumps(p))
1489
True
1490
"""
1491
if name is None:
1492
name = "Partition algebra P_%s(%s)"%(k, n)
1493
cclass = SetPartitionsPk(k)
1494
self._element_class = PartitionAlgebraElement_pk
1495
PartitionAlgebra_generic.__init__(self, R, cclass, n, k, name=name, prefix="P")
1496
1497
class PartitionAlgebraElement_tk(PartitionAlgebraElement_generic):
1498
pass
1499
class PartitionAlgebra_tk(PartitionAlgebra_generic):
1500
def __init__(self, R, k, n, name=None):
1501
"""
1502
EXAMPLES::
1503
1504
sage: from sage.combinat.partition_algebra import *
1505
sage: p = PartitionAlgebra_tk(QQ, 3, 1)
1506
sage: p == loads(dumps(p))
1507
True
1508
"""
1509
if name is None:
1510
name = "Partition algebra T_%s(%s)"%(k, n)
1511
cclass = SetPartitionsTk(k)
1512
self._element_class = PartitionAlgebraElement_tk
1513
PartitionAlgebra_generic.__init__(self, R, cclass, n, k, name=name, prefix="T")
1514
1515
class PartitionAlgebraElement_rk(PartitionAlgebraElement_generic):
1516
pass
1517
class PartitionAlgebra_rk(PartitionAlgebra_generic):
1518
def __init__(self, R, k, n, name=None):
1519
"""
1520
EXAMPLES::
1521
1522
sage: from sage.combinat.partition_algebra import *
1523
sage: p = PartitionAlgebra_rk(QQ, 3, 1)
1524
sage: p == loads(dumps(p))
1525
True
1526
"""
1527
if name is None:
1528
name = "Partition algebra R_%s(%s)"%(k, n)
1529
cclass = SetPartitionsRk(k)
1530
self._element_class = PartitionAlgebraElement_rk
1531
PartitionAlgebra_generic.__init__(self, R, cclass, n, k, name=name, prefix="R")
1532
1533
class PartitionAlgebraElement_prk(PartitionAlgebraElement_generic):
1534
pass
1535
class PartitionAlgebra_prk(PartitionAlgebra_generic):
1536
def __init__(self, R, k, n, name=None):
1537
"""
1538
EXAMPLES::
1539
1540
sage: from sage.combinat.partition_algebra import *
1541
sage: p = PartitionAlgebra_prk(QQ, 3, 1)
1542
sage: p == loads(dumps(p))
1543
True
1544
"""
1545
if name is None:
1546
name = "Partition algebra PR_%s(%s)"%(k, n)
1547
cclass = SetPartitionsPRk(k)
1548
self._element_class = PartitionAlgebraElement_prk
1549
PartitionAlgebra_generic.__init__(self, R, cclass, n, k, name=name, prefix="PR")
1550
1551
##########################################################
1552
1553
def is_planar(sp):
1554
"""
1555
Returns True if the diagram corresponding to the set partition is
1556
planar; otherwise, it returns False.
1557
1558
EXAMPLES::
1559
1560
sage: import sage.combinat.partition_algebra as pa
1561
sage: pa.is_planar( pa.to_set_partition([[1,-2],[2,-1]]))
1562
False
1563
sage: pa.is_planar( pa.to_set_partition([[1,-1],[2,-2]]))
1564
True
1565
"""
1566
to_consider = map(list, sp)
1567
1568
#Singletons don't affect planarity
1569
to_consider = filter(lambda x: len(x) > 1, to_consider)
1570
n = len(to_consider)
1571
1572
for i in range(n):
1573
#Get the positive and negative entries of this
1574
#part
1575
ap = filter(lambda x: x>0, to_consider[i])
1576
an = filter(lambda x: x<0, to_consider[i])
1577
an = map(abs, an)
1578
#print a, ap, an
1579
1580
1581
#Check if a includes numbers in both the top and bottom rows
1582
if len(ap) > 0 and len(an) > 0:
1583
1584
for j in range(n):
1585
if i == j:
1586
continue
1587
#Get the positive and negative entries of this part
1588
bp = filter(lambda x: x>0, to_consider[j])
1589
bn = filter(lambda x: x<0, to_consider[j])
1590
bn = map(abs, bn)
1591
1592
#Skip the ones that don't involve numbers in both
1593
#the bottom and top rows
1594
if len(bn) == 0 or len(bp) == 0:
1595
continue
1596
1597
#Make sure that if min(bp) > max(ap)
1598
#then min(bn) > max(an)
1599
if max(bp) > max(ap):
1600
if min(bn) < min(an):
1601
return False
1602
1603
1604
#Go through the bottom and top rows
1605
for row in [ap, an]:
1606
if len(row) > 1:
1607
row.sort()
1608
for s in range(len(row)-1):
1609
if row[s] + 1 == row[s+1]:
1610
#No gap, continue on
1611
continue
1612
else:
1613
rng = range(row[s] + 1, row[s+1])
1614
1615
#Go through and make sure any parts that
1616
#contain numbers in this range are completely
1617
#contained in this range
1618
for j in range(n):
1619
if i == j:
1620
continue
1621
1622
#Make sure we make the numbers negative again
1623
#if we are in the bottom row
1624
if row is ap:
1625
sr = Set(rng)
1626
else:
1627
sr = Set(map(lambda x: -1*x, rng))
1628
1629
1630
sj = Set(to_consider[j])
1631
intersection = sr.intersection(sj)
1632
if intersection:
1633
if sj != intersection:
1634
return False
1635
1636
return True
1637
1638
1639
def to_graph(sp):
1640
"""
1641
Returns a graph representing the set partition sp.
1642
1643
EXAMPLES::
1644
1645
sage: import sage.combinat.partition_algebra as pa
1646
sage: g = pa.to_graph( pa.to_set_partition([[1,-2],[2,-1]])); g
1647
Graph on 4 vertices
1648
1649
::
1650
1651
sage: g.vertices() #random
1652
[1, 2, -2, -1]
1653
sage: g.edges() #random
1654
[(1, -2, None), (2, -1, None)]
1655
"""
1656
g = Graph()
1657
for part in sp:
1658
part_list = list(part)
1659
if len(part_list) > 0:
1660
g.add_vertex(part_list[0])
1661
for i in range(1, len(part_list)):
1662
g.add_vertex(part_list[i])
1663
g.add_edge(part_list[i-1], part_list[i])
1664
return g
1665
1666
def pair_to_graph(sp1, sp2):
1667
"""
1668
Returns a graph consisting of the graphs of set partitions sp1 and
1669
sp2 along with edges joining the bottom row (negative numbers) of
1670
sp1 to the top row (positive numbers) of sp2.
1671
1672
EXAMPLES::
1673
1674
sage: import sage.combinat.partition_algebra as pa
1675
sage: sp1 = pa.to_set_partition([[1,-2],[2,-1]])
1676
sage: sp2 = pa.to_set_partition([[1,-2],[2,-1]])
1677
sage: g = pa.pair_to_graph( sp1, sp2 ); g
1678
Graph on 8 vertices
1679
1680
::
1681
1682
sage: g.vertices() #random
1683
[(1, 2), (-1, 1), (-2, 2), (-1, 2), (-2, 1), (2, 1), (2, 2), (1, 1)]
1684
sage: g.edges() #random
1685
[((1, 2), (-1, 1), None),
1686
((1, 2), (-2, 2), None),
1687
((-1, 1), (2, 1), None),
1688
((-1, 2), (2, 2), None),
1689
((-2, 1), (1, 1), None),
1690
((-2, 1), (2, 2), None)]
1691
"""
1692
g = Graph()
1693
1694
#Add the first set partition to the graph
1695
for part in sp1:
1696
part_list = list(part)
1697
if len(part_list) > 0:
1698
g.add_vertex( (part_list[0],1) )
1699
1700
#Add the edge to the second part of the graph
1701
if part_list[0] < 0 and len(part_list) > 1:
1702
g.add_edge( (part_list[0], 1), (abs(part_list[0]),2) )
1703
1704
for i in range(1, len(part_list)):
1705
g.add_vertex( (part_list[i],1) )
1706
1707
#Add the edge to the second part of the graph
1708
if part_list[i] < 0:
1709
g.add_edge( (part_list[i], 1), (abs(part_list[i]),2) )
1710
1711
#Add the edge between parts
1712
g.add_edge( (part_list[i-1],1), (part_list[i],1) )
1713
1714
#Add the second set partition to the graph
1715
for part in sp2:
1716
part_list = list(part)
1717
if len(part_list) > 0:
1718
g.add_vertex( (part_list[0],2) )
1719
for i in range(1, len(part_list)):
1720
g.add_vertex( (part_list[i],2) )
1721
g.add_edge( (part_list[i-1],2), (part_list[i],2) )
1722
1723
1724
return g
1725
1726
def propagating_number(sp):
1727
"""
1728
Returns the propagating number of the set partition sp. The
1729
propagating number is the number of blocks with both a positive and
1730
negative number.
1731
1732
EXAMPLES::
1733
1734
sage: import sage.combinat.partition_algebra as pa
1735
sage: sp1 = pa.to_set_partition([[1,-2],[2,-1]])
1736
sage: sp2 = pa.to_set_partition([[1,2],[-2,-1]])
1737
sage: pa.propagating_number(sp1)
1738
2
1739
sage: pa.propagating_number(sp2)
1740
0
1741
"""
1742
pn = 0
1743
for part in sp:
1744
if min(part) < 0 and max(part) > 0:
1745
pn += 1
1746
return pn
1747
1748
def to_set_partition(l,k=None):
1749
"""
1750
Coverts a list of a list of numbers to a set partitions. Each list
1751
of numbers in the outer list specifies the numbers contained in one
1752
of the blocks in the set partition.
1753
1754
If k is specified, then the set partition will be a set partition
1755
of 1, ..., k, -1, ..., -k. Otherwise, k will default to the minimum
1756
number needed to contain all of the specified numbers.
1757
1758
EXAMPLES::
1759
1760
sage: import sage.combinat.partition_algebra as pa
1761
sage: pa.to_set_partition([[1,-1],[2,-2]]) == pa.identity(2)
1762
True
1763
"""
1764
if k == None:
1765
if l == []:
1766
return Set([])
1767
else:
1768
k = max( map( lambda x: max( map(abs, x) ), l) )
1769
1770
to_be_added = Set( range(1, k+1) + map(lambda x: -1*x, range(1, k+1) ) )
1771
1772
sp = []
1773
for part in l:
1774
spart = Set(part)
1775
to_be_added -= spart
1776
sp.append(spart)
1777
1778
for singleton in to_be_added:
1779
sp.append(Set([singleton]))
1780
1781
return Set(sp)
1782
1783
def identity(k):
1784
"""
1785
Returns the identity set partition 1, -1, ..., k, -k
1786
1787
EXAMPLES::
1788
1789
sage: import sage.combinat.partition_algebra as pa
1790
sage: pa.identity(2)
1791
{{2, -2}, {1, -1}}
1792
"""
1793
res = []
1794
for i in range(1, k+1):
1795
res.append(Set([i, -i]))
1796
return Set(res)
1797
1798
1799
def set_partition_composition(sp1, sp2):
1800
"""
1801
Returns a tuple consisting of the composition of the set partitions
1802
sp1 and sp2 and the number of components removed from the middle
1803
rows of the graph.
1804
1805
EXAMPLES::
1806
1807
sage: import sage.combinat.partition_algebra as pa
1808
sage: sp1 = pa.to_set_partition([[1,-2],[2,-1]])
1809
sage: sp2 = pa.to_set_partition([[1,-2],[2,-1]])
1810
sage: pa.set_partition_composition(sp1, sp2) == (pa.identity(2), 0)
1811
True
1812
"""
1813
g = pair_to_graph(sp1, sp2)
1814
connected_components = g.connected_components()
1815
1816
res = []
1817
total_removed = 0
1818
for cc in connected_components:
1819
#Remove the vertices that live in the middle two rows
1820
new_cc = filter(lambda x: not ( (x[0]<0 and x[1] == 1) or (x[0]>0 and x[1]==2)), cc)
1821
1822
if new_cc == []:
1823
if len(cc) > 1:
1824
total_removed += 1
1825
else:
1826
res.append( Set(map(lambda x: x[0], new_cc)) )
1827
1828
1829
return ( Set(res), total_removed )
1830
1831