Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/iet/template.py
4072 views
1
r"""
2
Permutations template
3
4
This file define high level operations on permutations (alphabet,
5
the different rauzy induction, ...) shared by reduced and labeled
6
permutations.
7
8
AUTHORS:
9
10
- Vincent Delecroix (2008-12-20): initial version
11
12
TODO:
13
14
- construct as options different string representations for a permutation
15
16
- the two intervals: str
17
- the two intervals on one line: str_one_line
18
- the separatrix diagram: str_separatrix_diagram
19
- twin[0] and twin[1] for reduced permutation
20
- nothing (useful for Rauzy diagram)
21
"""
22
#*****************************************************************************
23
# Copyright (C) 2008 Vincent Delecroix <[email protected]>
24
#
25
# Distributed under the terms of the GNU General Public License (GPL)
26
# http://www.gnu.org/licenses/
27
#*****************************************************************************
28
29
from sage.structure.sage_object import SageObject
30
31
from copy import copy
32
33
from sage.rings.integer import Integer
34
from sage.combinat.words.alphabet import Alphabet
35
from sage.graphs.graph import DiGraph
36
from sage.matrix.constructor import identity_matrix, matrix
37
38
def interval_conversion(interval=None):
39
r"""
40
Converts the argument in 0 or 1.
41
42
INPUT:
43
44
- ``winner`` - 'top' (or 't' or 0) or bottom (or 'b' or 1)
45
46
OUTPUT:
47
48
integer -- 0 or 1
49
50
TESTS:
51
52
::
53
54
sage: from sage.combinat.iet.template import interval_conversion
55
sage: interval_conversion('top')
56
0
57
sage: interval_conversion('t')
58
0
59
sage: interval_conversion(0)
60
0
61
sage: interval_conversion('bottom')
62
1
63
sage: interval_conversion('b')
64
1
65
sage: interval_conversion(1)
66
1
67
68
.. Non admissible strings raise a ValueError::
69
70
sage: interval_conversion('')
71
Traceback (most recent call last):
72
...
73
ValueError: the interval can not be the empty string
74
sage: interval_conversion('right')
75
Traceback (most recent call last):
76
...
77
ValueError: 'right' can not be converted to interval
78
sage: interval_conversion('top_right')
79
Traceback (most recent call last):
80
...
81
ValueError: 'top_right' can not be converted to interval
82
"""
83
if isinstance(interval, (int,Integer)):
84
if interval != 0 and interval != 1:
85
raise ValueError, "interval must be 0 or 1"
86
return interval
87
88
if isinstance(interval,str):
89
if interval == '':
90
raise ValueError, "the interval can not be the empty string"
91
if 'top'.startswith(interval): return 0
92
if 'bottom'.startswith(interval): return 1
93
raise ValueError, "'%s' can not be converted to interval" %(interval)
94
95
raise TypeError, "'%s' is not an admissible type" %(str(interval))
96
97
def side_conversion(side=None):
98
r"""
99
Converts the argument in 0 or -1.
100
101
INPUT:
102
103
- ``side`` - either 'left' (or 'l' or 0) or 'right' (or 'r' or -1)
104
105
OUTPUT:
106
107
integer -- 0 or -1
108
109
TESTS:
110
111
::
112
113
sage: from sage.combinat.iet.template import side_conversion
114
sage: side_conversion('left')
115
0
116
sage: side_conversion('l')
117
0
118
sage: side_conversion(0)
119
0
120
sage: side_conversion('right')
121
-1
122
sage: side_conversion('r')
123
-1
124
sage: side_conversion(1)
125
-1
126
sage: side_conversion(-1)
127
-1
128
129
.. Non admissible strings raise a ValueError::
130
131
sage: side_conversion('')
132
Traceback (most recent call last):
133
...
134
ValueError: no empty string for side
135
sage: side_conversion('top')
136
Traceback (most recent call last):
137
...
138
ValueError: 'top' can not be converted to a side
139
"""
140
if side is None: return -1
141
142
if isinstance(side,str):
143
if side == '':
144
raise ValueError, "no empty string for side"
145
if 'left'.startswith(side): return 0
146
if 'right'.startswith(side): return -1
147
raise ValueError, "'%s' can not be converted to a side" %(side)
148
149
if isinstance(side, (int,Integer)):
150
if side != 0 and side != 1 and side != -1:
151
raise ValueError, "side must be 0 or 1"
152
if side == 0: return 0
153
return -1
154
155
raise TypeError, "'%s' is not an admissible type" %(str(side))
156
157
def twin_list_iet(a=None):
158
r"""
159
Returns the twin list of intervals.
160
161
The twin intervals is the correspondance between positions of labels in such
162
way that a[interval][position] is a[1-interval][twin[interval][position]]
163
164
INPUT:
165
166
- ``a`` - two lists of labels
167
168
OUTPUT:
169
170
list -- a list of two lists of integers
171
172
TESTS::
173
174
sage: from sage.combinat.iet.template import twin_list_iet
175
sage: twin_list_iet([['a','b','c'],['a','b','c']])
176
[[0, 1, 2], [0, 1, 2]]
177
sage: twin_list_iet([['a','b','c'],['a','c','b']])
178
[[0, 2, 1], [0, 2, 1]]
179
sage: twin_list_iet([['a','b','c'],['b','a','c']])
180
[[1, 0, 2], [1, 0, 2]]
181
sage: twin_list_iet([['a','b','c'],['b','c','a']])
182
[[2, 0, 1], [1, 2, 0]]
183
sage: twin_list_iet([['a','b','c'],['c','a','b']])
184
[[1, 2, 0], [2, 0, 1]]
185
sage: twin_list_iet([['a','b','c'],['c','b','a']])
186
[[2, 1, 0], [2, 1, 0]]
187
"""
188
if a is None : return [[],[]]
189
190
twin = [[0]*len(a[0]), [0]*len(a[1])]
191
192
for i in range(len(twin[0])) :
193
c = a[0][i]
194
j = a[1].index(c)
195
twin[0][i] = j
196
twin[1][j] = i
197
198
return twin
199
200
def twin_list_li(a=None):
201
r"""
202
Returns the twin list of intervals
203
204
INPUT:
205
206
- ``a`` - two lists of labels
207
208
OUTPUT:
209
210
list -- a list of two lists of couples of integers
211
212
TESTS::
213
214
sage: from sage.combinat.iet.template import twin_list_li
215
sage: twin_list_li([['a','a','b','b'],[]])
216
[[(0, 1), (0, 0), (0, 3), (0, 2)], []]
217
sage: twin_list_li([['a','a','b'],['b']])
218
[[(0, 1), (0, 0), (1, 0)], [(0, 2)]]
219
sage: twin_list_li([['a','a'],['b','b']])
220
[[(0, 1), (0, 0)], [(1, 1), (1, 0)]]
221
sage: twin_list_li([['a'], ['a','b','b']])
222
[[(1, 0)], [(0, 0), (1, 2), (1, 1)]]
223
sage: twin_list_li([[], ['a','a','b','b']])
224
[[], [(1, 1), (1, 0), (1, 3), (1, 2)]]
225
"""
226
if a is None: return [[],[]]
227
228
twin = [
229
[(0,j) for j in range(len(a[0]))],
230
[(1,j) for j in range(len(a[1]))]]
231
232
for i in (0,1):
233
for j in range(len(twin[i])) :
234
if twin[i][j] == (i,j) :
235
if a[i][j] in a[i][j+1:] :
236
# two up or two down
237
j2 = (a[i][j+1:]).index(a[i][j]) + j + 1
238
twin[i][j] = (i,j2)
239
twin[i][j2] = (i,j)
240
else :
241
# one up, one down (here i=0)
242
j2 = a[1].index(a[i][j])
243
twin[0][j] = (1,j2)
244
twin[1][j2] = (0,j)
245
246
return twin
247
248
class Permutation(SageObject):
249
r"""
250
Template for all permutations.
251
252
.. warning::
253
254
Internal class! Do not use directly!
255
256
This class implement generic algorithm (stratum, connected component, ...)
257
and unfies all its children.
258
"""
259
def _repr_(self):
260
r"""
261
Representation method of self.
262
263
Apply the function str to _repr_type(_repr_options) if _repr_type is
264
callable and _repr_type else.
265
266
TESTS:
267
268
::
269
270
sage: p = iet.Permutation('a b c','c b a')
271
sage: p._repr_type = 'str'
272
sage: p._repr_options = ('\n',)
273
sage: p #indirect doctest
274
a b c
275
c b a
276
sage: p._repr_options = (' / ',)
277
sage: p #indirect doctest
278
a b c / c b a
279
280
::
281
282
sage: p._repr_type = 'separatrix_diagram'
283
sage: p._repr_options = (False,)
284
sage: p #indirect doctest
285
[[('c', 'a'), 'b'], ['b', ('c', 'a')]]
286
sage: p._repr_options = (True,)
287
sage: p
288
[[(('c', 'a'), 'L'), ('b', 'R')], [('b', 'L'), (('c', 'a'), 'R')]]
289
290
::
291
292
sage: p._repr_type = '_twin'
293
sage: p #indirect doctest
294
[[2, 1, 0], [2, 1, 0]]
295
"""
296
if self._repr_type is None:
297
return ''
298
299
elif self._repr_type == 'reduced':
300
return ''.join(map(str,self[1]))
301
302
else:
303
f = getattr(self, self._repr_type)
304
if callable(f):
305
return str(f(*self._repr_options))
306
else:
307
return str(f)
308
309
def str(self, sep= "\n"):
310
r"""
311
A string representation of the generalized permutation.
312
313
INPUT:
314
315
- ``sep`` - (default: '\n') a separator for the two intervals
316
317
OUTPUT:
318
319
string -- the string that represents the permutation
320
321
322
EXAMPLES:
323
324
For permutations of iet::
325
326
sage: p = iet.Permutation('a b c','c b a')
327
sage: p.str()
328
'a b c\nc b a'
329
sage: p.str(sep=' | ')
330
'a b c | c b a'
331
332
..the permutation can be rebuilt from the standard string::
333
334
sage: p == iet.Permutation(p.str())
335
True
336
337
For permutations of li::
338
339
sage: p = iet.GeneralizedPermutation('a b b','c c a')
340
sage: p.str()
341
'a b b\nc c a'
342
sage: p.str(sep=' | ')
343
'a b b | c c a'
344
345
..the generalized permutation can be rebuilt from the standard string::
346
347
sage: p == iet.GeneralizedPermutation(p.str())
348
True
349
350
"""
351
l = self.list()
352
s0 = ' '.join(map(str,l[0]))
353
s1 = ' '.join(map(str,l[1]))
354
return s0 + sep + s1
355
356
_repr_type = 'str'
357
_repr_options = ("\n",)
358
359
def _set_alphabet(self, alphabet):
360
r"""
361
Sets the alphabet of self.
362
363
TESTS:
364
365
sage: p = iet.GeneralizedPermutation('a a','b b')
366
sage: p.alphabet([0,1]) #indirect doctest
367
sage: p.alphabet() == Alphabet([0,1])
368
True
369
sage: p
370
0 0
371
1 1
372
sage: p.alphabet("cd") #indirect doctest
373
sage: p.alphabet() == Alphabet(['c','d'])
374
True
375
sage: p
376
c c
377
d d
378
379
Tests with reduced permutations::
380
381
sage: p = iet.Permutation('a b','b a',reduced=True)
382
sage: p.alphabet([0,1]) #indirect doctest
383
sage: p.alphabet() == Alphabet([0,1])
384
True
385
sage: p
386
0 1
387
1 0
388
sage: p.alphabet("cd") #indirect doctest
389
sage: p.alphabet() == Alphabet(['c','d'])
390
True
391
sage: p
392
c d
393
d c
394
395
::
396
397
sage: p = iet.GeneralizedPermutation('a a','b b',reduced=True)
398
sage: p.alphabet([0,1]) #indirect doctest
399
sage: p.alphabet() == Alphabet([0,1])
400
True
401
sage: p
402
0 0
403
1 1
404
sage: p.alphabet("cd") #indirect doctest
405
sage: p.alphabet() == Alphabet(['c','d'])
406
True
407
sage: p
408
c c
409
d d
410
"""
411
alphabet = Alphabet(alphabet)
412
if alphabet.cardinality() <len(self):
413
raise ValueError, "Your alphabet has not enough letters"
414
self._alphabet = alphabet
415
416
def alphabet(self, data=None):
417
r"""
418
Manages the alphabet of self.
419
420
If there is no argument, the method returns the alphabet used. If the
421
argument could be converted to an alphabet, this alphabet will be used.
422
423
INPUT:
424
425
- ``data`` - None or something that could be converted to an alphabet
426
427
428
OUTPUT:
429
430
-- either None or the current alphabet
431
432
433
EXAMPLES::
434
435
sage: p = iet.Permutation('a b','a b')
436
sage: p.alphabet([0,1])
437
sage: p.alphabet() == Alphabet([0,1])
438
True
439
sage: p
440
0 1
441
0 1
442
sage: p.alphabet("cd")
443
sage: p.alphabet() == Alphabet(['c','d'])
444
True
445
sage: p
446
c d
447
c d
448
"""
449
if data is None:
450
return self._alphabet
451
else:
452
self._set_alphabet(data)
453
454
def letters(self):
455
r"""
456
Returns the list of letters of the alphabet used for representation.
457
458
The letters used are not necessarily the whole alphabet (for example if
459
the alphabet is infinite).
460
461
OUTPUT:
462
463
-- a list of labels
464
465
466
EXAMPLES::
467
468
sage: p = iet.Permutation([1,2],[2,1])
469
sage: p.alphabet(Alphabet(name="NN"))
470
sage: p
471
0 1
472
1 0
473
sage: p.letters()
474
[0, 1]
475
"""
476
return map(self._alphabet.unrank, range(len(self)))
477
478
def left_right_inverse(self):
479
r"""
480
Returns the left-right inverse.
481
482
You can also use the shorter .lr_inverse()
483
484
OUTPUT:
485
486
-- a permutation
487
488
489
EXAMPLES::
490
491
sage: p = iet.Permutation('a b c','c a b')
492
sage: p.left_right_inverse()
493
c b a
494
b a c
495
sage: p = iet.Permutation('a b c d','c d a b')
496
sage: p.left_right_inverse()
497
d c b a
498
b a d c
499
500
::
501
502
sage: p = iet.GeneralizedPermutation('a a','b b c c')
503
sage: p.left_right_inverse()
504
a a
505
c c b b
506
507
::
508
509
sage: p = iet.Permutation('a b c','c b a',reduced=True)
510
sage: p.left_right_inverse() == p
511
True
512
sage: p = iet.Permutation('a b c','c a b',reduced=True)
513
sage: q = p.left_right_inverse()
514
sage: q == p
515
False
516
sage: q
517
a b c
518
b c a
519
520
::
521
522
sage: p = iet.GeneralizedPermutation('a a','b b c c',reduced=True)
523
sage: p.left_right_inverse() == p
524
True
525
sage: p = iet.GeneralizedPermutation('a b b','c c a',reduced=True)
526
sage: q = p.left_right_inverse()
527
sage: q == p
528
False
529
sage: q
530
a a b
531
b c c
532
533
TESTS::
534
535
sage: p = iet.GeneralizedPermutation('a a','b b')
536
sage: p.left_right_inverse()
537
a a
538
b b
539
sage: p is p.left_right_inverse()
540
False
541
sage: p == p.left_right_inverse()
542
True
543
"""
544
result = copy(self)
545
result._reversed()
546
return result
547
548
lr_inverse = left_right_inverse
549
vertical_inverse = left_right_inverse
550
551
def top_bottom_inverse(self):
552
r"""
553
Returns the top-bottom inverse.
554
555
You can use also use the shorter .tb_inverse().
556
557
OUTPUT:
558
559
-- a permutation
560
561
562
EXAMPLES::
563
564
sage: p = iet.Permutation('a b','b a')
565
sage: p.top_bottom_inverse()
566
b a
567
a b
568
sage: p = iet.Permutation('a b','b a',reduced=True)
569
sage: p.top_bottom_inverse() == p
570
True
571
572
::
573
574
sage: p = iet.Permutation('a b c d','c d a b')
575
sage: p.top_bottom_inverse()
576
c d a b
577
a b c d
578
579
TESTS::
580
581
sage: p = iet.Permutation('a b','a b')
582
sage: p == p.top_bottom_inverse()
583
True
584
sage: p is p.top_bottom_inverse()
585
False
586
sage: p = iet.GeneralizedPermutation('a a','b b',reduced=True)
587
sage: p == p.top_bottom_inverse()
588
True
589
sage: p is p.top_bottom_inverse()
590
False
591
"""
592
result = copy(self)
593
result._inversed()
594
return result
595
596
tb_inverse = top_bottom_inverse
597
horizontal_inverse = top_bottom_inverse
598
599
def symmetric(self):
600
r"""
601
Returns the symmetric permutation.
602
603
The symmetric permutation is the composition of the top-bottom
604
inversion and the left-right inversion (which are geometrically
605
orientation reversing).
606
607
OUTPUT:
608
609
-- a permutation
610
611
612
EXAMPLES::
613
614
sage: p = iet.Permutation("a b c","c b a")
615
sage: p.symmetric()
616
a b c
617
c b a
618
sage: q = iet.Permutation("a b c d","b d a c")
619
sage: q.symmetric()
620
c a d b
621
d c b a
622
623
::
624
625
sage: p = iet.Permutation('a b c d','c a d b')
626
sage: q = p.symmetric()
627
sage: q1 = p.tb_inverse().lr_inverse()
628
sage: q2 = p.lr_inverse().tb_inverse()
629
sage: q == q1
630
True
631
sage: q == q2
632
True
633
634
635
TESTS:
636
637
::
638
639
sage: p = iet.GeneralizedPermutation('a a b','b c c',reduced=True)
640
sage: q = p.symmetric()
641
sage: q1 = p.tb_inverse().lr_inverse()
642
sage: q2 = p.lr_inverse().tb_inverse()
643
sage: q == q1
644
True
645
sage: q == q2
646
True
647
648
::
649
650
sage: p = iet.GeneralizedPermutation('a a b','b c c',reduced=True,flips='a')
651
sage: q = p.symmetric()
652
sage: q1 = p.tb_inverse().lr_inverse()
653
sage: q2 = p.lr_inverse().tb_inverse()
654
sage: q == q1
655
True
656
sage: q == q2
657
True
658
659
"""
660
res = copy(self)
661
res._inversed()
662
res._reversed()
663
return res
664
665
def has_rauzy_move(self, winner='top', side=None):
666
r"""
667
Tests the legality of a Rauzy move.
668
669
INPUT:
670
671
- ``winner`` - 'top' or 'bottom' corresponding to the interval
672
673
- ``side`` - 'left' or 'right' (defaut)
674
675
676
OUTPUT:
677
678
-- a boolean
679
680
681
EXAMPLES::
682
683
sage: p = iet.Permutation('a b','a b')
684
sage: p.has_rauzy_move('top','right')
685
False
686
sage: p.has_rauzy_move('bottom','right')
687
False
688
sage: p.has_rauzy_move('top','left')
689
False
690
sage: p.has_rauzy_move('bottom','left')
691
False
692
693
::
694
695
sage: p = iet.Permutation('a b c','b a c')
696
sage: p.has_rauzy_move('top','right')
697
False
698
sage: p.has_rauzy_move('bottom', 'right')
699
False
700
sage: p.has_rauzy_move('top','left')
701
True
702
sage: p.has_rauzy_move('bottom','left')
703
True
704
705
::
706
707
sage: p = iet.Permutation('a b','b a')
708
sage: p.has_rauzy_move('top','right')
709
True
710
sage: p.has_rauzy_move('bottom','right')
711
True
712
sage: p.has_rauzy_move('top','left')
713
True
714
sage: p.has_rauzy_move('bottom','left')
715
True
716
"""
717
winner = interval_conversion(winner)
718
side = side_conversion(side)
719
720
if side == -1:
721
return self.has_right_rauzy_move(winner)
722
elif side == 0:
723
return self.lr_inverse().has_right_rauzy_move(winner)
724
725
def rauzy_move(self, winner, side='right', iteration=1):
726
r"""
727
Returns the permutation after a Rauzy move.
728
729
INPUT:
730
731
- ``winner`` - 'top' or 'bottom' interval
732
733
- ``side`` - 'right' or 'left' (defaut: 'right') corresponding
734
to the side on which the Rauzy move must be performed.
735
736
- ``iteration`` - a non negative integer
737
738
739
OUTPUT:
740
741
- a permutation
742
743
744
TESTS::
745
746
sage: p = iet.Permutation('a b','b a')
747
sage: p.rauzy_move(winner=0, side='right') == p
748
True
749
sage: p.rauzy_move(winner=1, side='right') == p
750
True
751
sage: p.rauzy_move(winner=0, side='left') == p
752
True
753
sage: p.rauzy_move(winner=1, side='left') == p
754
True
755
756
::
757
758
sage: p = iet.Permutation('a b c','c b a')
759
sage: p.rauzy_move(winner=0, side='right')
760
a b c
761
c a b
762
sage: p.rauzy_move(winner=1, side='right')
763
a c b
764
c b a
765
sage: p.rauzy_move(winner=0, side='left')
766
a b c
767
b c a
768
sage: p.rauzy_move(winner=1, side='left')
769
b a c
770
c b a
771
"""
772
winner = interval_conversion(winner)
773
side = side_conversion(side)
774
775
if side == -1:
776
tmp = self
777
for k in range(iteration):
778
tmp = tmp.right_rauzy_move(winner)
779
return tmp
780
781
elif side == 0:
782
tmp = self
783
for k in range(iteration):
784
tmp = tmp.left_rauzy_move(winner)
785
return tmp
786
787
class PermutationIET(Permutation):
788
"""
789
Template for permutation from Interval Exchange Transformation.
790
791
.. warning::
792
793
Internal class! Do not use directly!
794
795
AUTHOR:
796
797
- Vincent Delecroix (2008-12-20): initial version
798
"""
799
def _init_alphabet(self,a) :
800
r"""
801
Initializes the alphabet from intervals.
802
803
TESTS::
804
805
sage: p = iet.Permutation('a b c d','d c a b') #indirect doctest
806
sage: p.alphabet() == Alphabet(['a', 'b', 'c', 'd'])
807
True
808
sage: p = iet.Permutation([0,1,2],[1,0,2],reduced=True) #indirect doctest
809
sage: p.alphabet() == Alphabet([0,1,2])
810
True
811
"""
812
self._alphabet = Alphabet(a[0])
813
814
def is_irreducible(self, return_decomposition=False) :
815
r"""
816
Tests the irreducibility.
817
818
An abelian permutation p = (p0,p1) is reducible if:
819
set(p0[:i]) = set(p1[:i]) for an i < len(p0)
820
821
OUTPUT:
822
823
- a boolean
824
825
EXAMPLES::
826
827
sage: p = iet.Permutation('a b c', 'c b a')
828
sage: p.is_irreducible()
829
True
830
831
sage: p = iet.Permutation('a b c', 'b a c')
832
sage: p.is_irreducible()
833
False
834
"""
835
s0, s1 = 0, 0
836
for i in range(len(self)-1) :
837
s0 += i
838
s1 += self._twin[0][i]
839
if s0 == s1 :
840
if return_decomposition :
841
return False, (self[0][:i+1], self[0][i+1:], self[1][:i+1], self[1][i+1:])
842
return False
843
if return_decomposition:
844
return True, (self[0],[],self[1],[])
845
return True
846
847
def decompose(self):
848
r"""
849
Returns the decomposition of self.
850
851
OUTPUT:
852
853
-- a list of permutations
854
855
856
EXAMPLES::
857
858
sage: p = iet.Permutation('a b c','c b a').decompose()[0]
859
sage: p
860
a b c
861
c b a
862
863
::
864
865
sage: p1,p2,p3 = iet.Permutation('a b c d e','b a c e d').decompose()
866
sage: p1
867
a b
868
b a
869
sage: p2
870
c
871
c
872
sage: p3
873
d e
874
e d
875
"""
876
l = []
877
test, t = self.is_irreducible(return_decomposition=True)
878
l.append(self.__class__((t[0],t[2])))
879
880
while not test:
881
q = self.__class__((t[1],t[3]))
882
test, t = q.is_irreducible(return_decomposition=True)
883
l.append(self.__class__((t[0],t[2])))
884
885
return l
886
887
def intersection_matrix(self):
888
r"""
889
Returns the intersection matrix.
890
891
This `d*d` antisymmetric matrix is given by the rule :
892
893
.. math::
894
895
m_{ij} = \begin{cases}
896
1 & \text{$i < j$ and $\pi(i) > \pi(j)$} \\
897
-1 & \text{$i > j$ and $\pi(i) < \pi(j)$} \\
898
0 & \text{else}
899
\end{cases}
900
901
OUTPUT:
902
903
- a matrix
904
905
EXAMPLES::
906
907
sage: p = iet.Permutation('a b c d','d c b a')
908
sage: p.intersection_matrix()
909
[ 0 1 1 1]
910
[-1 0 1 1]
911
[-1 -1 0 1]
912
[-1 -1 -1 0]
913
914
::
915
916
sage: p = iet.Permutation('1 2 3 4 5','5 3 2 4 1')
917
sage: p.intersection_matrix()
918
[ 0 1 1 1 1]
919
[-1 0 1 0 1]
920
[-1 -1 0 0 1]
921
[-1 0 0 0 1]
922
[-1 -1 -1 -1 0]
923
"""
924
n = self.length_top()
925
m = matrix(n)
926
for i in range(n):
927
for j in range(i,n):
928
if self._twin[0][i] > self._twin[0][j]:
929
m[i,j] = 1
930
m[j,i] = -1
931
return m
932
933
def attached_out_degree(self):
934
r"""
935
Returns the degree of the singularity at the left of the interval.
936
937
OUTPUT:
938
939
-- a positive integer
940
941
942
EXAMPLES::
943
944
sage: p1 = iet.Permutation('a b c d e f g','d c g f e b a')
945
sage: p2 = iet.Permutation('a b c d e f g','e d c g f b a')
946
sage: p1.attached_out_degree()
947
3
948
sage: p2.attached_out_degree()
949
1
950
"""
951
left_corner = ((self[1][0], self[0][0]), 'L')
952
for s in self.separatrix_diagram(side=True):
953
if left_corner in s:
954
return len(s)/2 - 1
955
956
def attached_in_degree(self):
957
r"""
958
Returns the degree of the singularity at the right of the interval.
959
960
OUTPUT:
961
962
-- a positive integer
963
964
965
EXAMPLES::
966
967
sage: p1 = iet.Permutation('a b c d e f g','d c g f e b a')
968
sage: p2 = iet.Permutation('a b c d e f g','e d c g f b a')
969
sage: p1.attached_in_degree()
970
1
971
sage: p2.attached_in_degree()
972
3
973
"""
974
right_corner = ((self[0][-1], self[1][-1]), 'R')
975
976
for s in self.separatrix_diagram(side=True):
977
if right_corner in s:
978
return len(s)/2 - 1
979
980
def attached_type(self):
981
r"""
982
Return the singularity degree attached on the left and the right.
983
984
OUTPUT:
985
986
``([degre], angle_parity)`` -- if the same singularity is attached on the left and right
987
988
``([left_degree, right_degree], 0)`` -- the degrees at the left and the right which are different singularitites
989
990
EXAMPLES:
991
992
With two intervals::
993
994
sage: p = iet.Permutation('a b','b a')
995
sage: p.attached_type()
996
([0], 1)
997
998
With three intervals::
999
1000
sage: p = iet.Permutation('a b c','b c a')
1001
sage: p.attached_type()
1002
([0], 1)
1003
1004
sage: p = iet.Permutation('a b c','c a b')
1005
sage: p.attached_type()
1006
([0], 1)
1007
1008
sage: p = iet.Permutation('a b c','c b a')
1009
sage: p.attached_type()
1010
([0, 0], 0)
1011
1012
With four intervals::
1013
1014
sage: p = iet.Permutation('1 2 3 4','4 3 2 1')
1015
sage: p.attached_type()
1016
([2], 0)
1017
"""
1018
left_corner = ((self[1][0], self[0][0]), 'L')
1019
right_corner = ((self[0][-1], self[1][-1]), 'R')
1020
1021
l = self.separatrix_diagram(side=True)
1022
1023
for s in l:
1024
if left_corner in s and right_corner in s:
1025
i1 = s.index(left_corner)
1026
i2 = s.index(right_corner)
1027
return ([len(s)/2-1], ((i2-i1+1)/2) %2)
1028
elif left_corner in s:
1029
left_degree = len(s)/2-1
1030
elif right_corner in s:
1031
right_degree = len(s)/2-1
1032
1033
return ([left_degree,right_degree], 0)
1034
1035
def separatrix_diagram(self,side=False):
1036
r"""
1037
Returns the separatrix diagram of the permutation.
1038
1039
INPUT:
1040
1041
- ``side`` - boolean
1042
1043
1044
OUTPUT:
1045
1046
-- a list of lists
1047
1048
1049
EXAMPLES::
1050
1051
sage: iet.Permutation([0, 1], [1, 0]).separatrix_diagram()
1052
[[(1, 0), (1, 0)]]
1053
1054
::
1055
1056
sage: iet.Permutation('a b c d','d c b a').separatrix_diagram()
1057
[[('d', 'a'), 'b', 'c', ('d', 'a'), 'b', 'c']]
1058
"""
1059
separatrices = range(len(self)) # bottom intervals
1060
labels = self[1] # their labels
1061
1062
singularities = []
1063
1064
1065
twin = self._twin
1066
n = len(self)-1
1067
1068
while separatrices != []:
1069
start = separatrices.pop(0)
1070
separatrix = start
1071
if side:
1072
singularity = [(labels[start],'L')]
1073
else:
1074
singularity = [labels[start]]
1075
1076
while True:
1077
if separatrix == 0:
1078
separatrix = twin[0][0]
1079
if side:
1080
a = singularity.pop()[0]
1081
else:
1082
a = singularity.pop()
1083
if side:
1084
singularity.append(((a,labels[separatrix]), 'L'))
1085
else:
1086
singularity.append((a,labels[separatrix]))
1087
1088
if separatrix == start:
1089
singularities.append(singularity)
1090
break
1091
1092
del separatrices[separatrices.index(separatrix)]
1093
1094
else:
1095
separatrix -= 1
1096
if side:
1097
singularity.append((labels[separatrix],'R'))
1098
else:
1099
singularity.append(labels[separatrix])
1100
1101
if separatrix == twin[0][n] :
1102
separatrix = n
1103
if side:
1104
a = singularity.pop()[0]
1105
else:
1106
a = singularity.pop()
1107
if side:
1108
singularity.append(((a,labels[separatrix]),'R'))
1109
else:
1110
singularity.append((a,labels[separatrix]))
1111
1112
separatrix = twin[0][twin[1][separatrix]+1]
1113
1114
if separatrix == start:
1115
singularities.append(singularity)
1116
break
1117
1118
elif separatrix != twin[0][0]:
1119
del separatrices[separatrices.index(separatrix)]
1120
if side:
1121
singularity.append((labels[separatrix],'L'))
1122
else:
1123
singularity.append(labels[separatrix])
1124
1125
return singularities
1126
1127
def stratum(self, marked_separatrix='no'):
1128
r"""
1129
Returns the strata in which any suspension of this permutation lives.
1130
1131
OUTPUT:
1132
1133
- a stratum of Abelian differentials
1134
1135
EXAMPLES::
1136
1137
sage: p = iet.Permutation('a b c', 'c b a')
1138
sage: print p.stratum()
1139
H(0, 0)
1140
1141
sage: p = iet.Permutation('a b c d', 'd a b c')
1142
sage: print p.stratum()
1143
H(0, 0, 0)
1144
1145
sage: p = iet.Permutation(range(9), [8,5,2,7,4,1,6,3,0])
1146
sage: print p.stratum()
1147
H(1, 1, 1, 1)
1148
1149
You can specify that you want to attach the singularity on the left (or
1150
on the right) with the option marked_separatrix::
1151
1152
sage: a = 'a b c d e f g h i j'
1153
sage: b3 = 'd c g f e j i h b a'
1154
sage: b2 = 'd c e g f j i h b a'
1155
sage: b1 = 'e d c g f h j i b a'
1156
sage: p3 = iet.Permutation(a, b3)
1157
sage: p3.stratum()
1158
H(3, 2, 1)
1159
sage: p3.stratum(marked_separatrix='out')
1160
H^out(3, 2, 1)
1161
sage: p2 = iet.Permutation(a, b2)
1162
sage: p2.stratum()
1163
H(3, 2, 1)
1164
sage: p2.stratum(marked_separatrix='out')
1165
H^out(2, 3, 1)
1166
sage: p1 = iet.Permutation(a, b1)
1167
sage: p1.stratum()
1168
H(3, 2, 1)
1169
sage: p1.stratum(marked_separatrix='out')
1170
H^out(1, 3, 2)
1171
1172
AUTHORS:
1173
- Vincent Delecroix (2008-12-20)
1174
"""
1175
from sage.combinat.iet.strata import AbelianStratum
1176
1177
if not self.is_irreducible():
1178
return map(lambda x: x.stratum(marked_separatrix), self.decompose())
1179
1180
if len(self) == 1:
1181
return AbelianStratum([])
1182
1183
singularities = [len(x)/2 - 1 for x in self.separatrix_diagram()]
1184
1185
return AbelianStratum(singularities,marked_separatrix=marked_separatrix)
1186
1187
def genus(self) :
1188
r"""
1189
Returns the genus corresponding to any suspension of the permutation.
1190
1191
OUTPUT:
1192
1193
-- a positive integer
1194
1195
EXAMPLES::
1196
1197
sage: p = iet.Permutation('a b c', 'c b a')
1198
sage: p.genus()
1199
1
1200
1201
::
1202
1203
sage: p = iet.Permutation('a b c d','d c b a')
1204
sage: p.genus()
1205
2
1206
1207
REFERENCES:
1208
Veech
1209
"""
1210
return self.stratum().genus()
1211
1212
def arf_invariant(self):
1213
r"""
1214
Returns the Arf invariant of the suspension of self.
1215
1216
OUTPUT:
1217
1218
integer -- 0 or 1
1219
1220
EXAMPLES:
1221
1222
Permutations from the odd and even component of H(2,2,2)::
1223
1224
sage: a = range(10)
1225
sage: b1 = [3,2,4,6,5,7,9,8,1,0]
1226
sage: b0 = [6,5,4,3,2,7,9,8,1,0]
1227
sage: p1 = iet.Permutation(a,b1)
1228
sage: print p1.arf_invariant()
1229
1
1230
sage: p0 = iet.Permutation(a,b0)
1231
sage: print p0.arf_invariant()
1232
0
1233
1234
Permutations from the odd and even component of H(4,4)::
1235
1236
sage: a = range(11)
1237
sage: b1 = [3,2,5,4,6,8,7,10,9,1,0]
1238
sage: b0 = [5,4,3,2,6,8,7,10,9,1,0]
1239
sage: p1 = iet.Permutation(a,b1)
1240
sage: print p1.arf_invariant()
1241
1
1242
sage: p0 = iet.Permutation(a,b0)
1243
sage: print p0.arf_invariant()
1244
0
1245
1246
REFERENCES:
1247
1248
[Jo80] D. Johnson, "Spin structures and quadratic forms on surfaces", J.
1249
London Math. Soc (2), 22, 1980, 365-373
1250
1251
[KoZo03] M. Kontsevich, A. Zorich "Connected components of the moduli
1252
spaces of Abelian differentials with prescribed singularities",
1253
Inventiones Mathematicae, 153, 2003, 631-678
1254
"""
1255
M = self.intersection_matrix()
1256
F, C = M.symplectic_form()
1257
1258
g = F.rank()/2
1259
n = F.ncols()
1260
1261
s = 0
1262
for i in range(g):
1263
a = C.row(i)
1264
1265
a_indices = []
1266
for k in xrange(n):
1267
if a[k] != 0: a_indices.append(k)
1268
1269
t_a = len(a_indices) % 2
1270
for j1 in xrange(len(a_indices)):
1271
for j2 in xrange(j1+1,len(a_indices)):
1272
t_a = (t_a + M[a_indices[j1], a_indices[j2]]) % 2
1273
1274
b = C.row(g+i)
1275
1276
b_indices = []
1277
for k in xrange(n):
1278
if b[k] != 0: b_indices.append(k)
1279
1280
t_b = len(b_indices) % 2
1281
for j1 in xrange(len(b_indices)):
1282
for j2 in xrange(j1+1,len(b_indices)):
1283
t_b = (t_b + M[b_indices[j1],b_indices[j2]]) % 2
1284
1285
s = (s + t_a * t_b) % 2
1286
1287
return s
1288
1289
def connected_component(self,marked_separatrix='no'):
1290
r"""
1291
Returns a connected components of a stratum.
1292
1293
EXAMPLES:
1294
1295
Permutations from the stratum H(6)::
1296
1297
sage: a = range(8)
1298
sage: b_hyp = [7,6,5,4,3,2,1,0]
1299
sage: b_odd = [3,2,5,4,7,6,1,0]
1300
sage: b_even = [5,4,3,2,7,6,1,0]
1301
sage: p_hyp = iet.Permutation(a, b_hyp)
1302
sage: p_odd = iet.Permutation(a, b_odd)
1303
sage: p_even = iet.Permutation(a, b_even)
1304
sage: print p_hyp.connected_component()
1305
H_hyp(6)
1306
sage: print p_odd.connected_component()
1307
H_odd(6)
1308
sage: print p_even.connected_component()
1309
H_even(6)
1310
1311
Permutations from the stratum H(4,4)::
1312
1313
sage: a = range(11)
1314
sage: b_hyp = [10,9,8,7,6,5,4,3,2,1,0]
1315
sage: b_odd = [3,2,5,4,6,8,7,10,9,1,0]
1316
sage: b_even = [5,4,3,2,6,8,7,10,9,1,0]
1317
sage: p_hyp = iet.Permutation(a,b_hyp)
1318
sage: p_odd = iet.Permutation(a,b_odd)
1319
sage: p_even = iet.Permutation(a,b_even)
1320
sage: p_hyp.stratum() == AbelianStratum(4,4)
1321
True
1322
sage: print p_hyp.connected_component()
1323
H_hyp(4, 4)
1324
sage: p_odd.stratum() == AbelianStratum(4,4)
1325
True
1326
sage: print p_odd.connected_component()
1327
H_odd(4, 4)
1328
sage: p_even.stratum() == AbelianStratum(4,4)
1329
True
1330
sage: print p_even.connected_component()
1331
H_even(4, 4)
1332
1333
As for stratum you can specify that you want to attach the singularity
1334
on the left of the interval using the option marked_separatrix::
1335
1336
sage: a = [1,2,3,4,5,6,7,8,9]
1337
sage: b4_odd = [4,3,6,5,7,9,8,2,1]
1338
sage: b4_even = [6,5,4,3,7,9,8,2,1]
1339
sage: b2_odd = [4,3,5,7,6,9,8,2,1]
1340
sage: b2_even = [7,6,5,4,3,9,8,2,1]
1341
sage: p4_odd = iet.Permutation(a,b4_odd)
1342
sage: p4_even = iet.Permutation(a,b4_even)
1343
sage: p2_odd = iet.Permutation(a,b2_odd)
1344
sage: p2_even = iet.Permutation(a,b2_even)
1345
sage: p4_odd.connected_component(marked_separatrix='out')
1346
H_odd^out(4, 2)
1347
sage: p4_even.connected_component(marked_separatrix='out')
1348
H_even^out(4, 2)
1349
sage: p2_odd.connected_component(marked_separatrix='out')
1350
H_odd^out(2, 4)
1351
sage: p2_even.connected_component(marked_separatrix='out')
1352
H_even^out(2, 4)
1353
sage: p2_odd.connected_component() == p4_odd.connected_component()
1354
True
1355
sage: p2_odd.connected_component('out') == p4_odd.connected_component('out')
1356
False
1357
"""
1358
from sage.combinat.iet.strata import (CCA, HypCCA,
1359
NonHypCCA, OddCCA, EvenCCA)
1360
1361
if not self.is_irreducible():
1362
return map(lambda x: x.connected_component(marked_separatrix),
1363
self.decompose())
1364
1365
stratum = self.stratum(marked_separatrix=marked_separatrix)
1366
cc = stratum._cc
1367
1368
if len(cc) == 1:
1369
return stratum.connected_components()[0]
1370
1371
if HypCCA in cc:
1372
if self.is_hyperelliptic():
1373
return HypCCA(stratum)
1374
else:
1375
cc = cc[1:]
1376
1377
if len(cc) == 1:
1378
return cc[0](stratum)
1379
1380
else:
1381
spin = self.arf_invariant()
1382
if spin == 0:
1383
return EvenCCA(stratum)
1384
else:
1385
return OddCCA(stratum)
1386
1387
def order_of_rauzy_action(self, winner, side=None):
1388
r"""
1389
Returns the order of the action of a Rauzy move.
1390
1391
INPUT:
1392
1393
- ``winner`` - string ``'top'`` or ``'bottom'``
1394
1395
- ``side`` - string ``'left'`` or ``'right'``
1396
1397
OUTPUT:
1398
1399
An integer corresponding to the order of the Rauzy action.
1400
1401
EXAMPLES::
1402
1403
sage: p = iet.Permutation('a b c d','d a c b')
1404
sage: p.order_of_rauzy_action('top', 'right')
1405
3
1406
sage: p.order_of_rauzy_action('bottom', 'right')
1407
2
1408
sage: p.order_of_rauzy_action('top', 'left')
1409
1
1410
sage: p.order_of_rauzy_action('bottom', 'left')
1411
3
1412
"""
1413
winner = interval_conversion(winner)
1414
side = side_conversion(side)
1415
1416
if side == -1:
1417
return self.length(winner) - self._twin[winner][-1] - 1
1418
elif side == 0:
1419
return self._twin[winner][0]
1420
1421
def erase_marked_points(self):
1422
r"""
1423
Returns a permutation equivalent to self but without marked points.
1424
1425
EXAMPLES::
1426
1427
sage: a = iet.Permutation('a b1 b2 c d', 'd c b1 b2 a')
1428
sage: a.erase_marked_points()
1429
a b1 c d
1430
d c b1 a
1431
"""
1432
res = copy(self)
1433
1434
l = res.list()
1435
left_corner = ((l[1][0], l[0][0]), 'L')
1436
right_corner = ((l[0][-1], l[1][-1]), 'R')
1437
1438
s = res.separatrix_diagram(side=True)
1439
lengths = map(len, s)
1440
1441
while 2 in lengths:
1442
if lengths == [2]:
1443
return res
1444
1445
i = lengths.index(2)
1446
t = s[i]
1447
if t[0] == left_corner or t[0] == right_corner:
1448
letter = t[1][0]
1449
else:
1450
letter = t[0][0]
1451
1452
res = res.erase_letter(letter)
1453
1454
l = res.list()
1455
1456
s = res.separatrix_diagram(side=True)
1457
lengths = map(len, s)
1458
1459
return res
1460
1461
def is_hyperelliptic(self):
1462
r"""
1463
Returns True if the permutation is in the class of the symmetric
1464
permutations (with eventual marked points).
1465
1466
This is equivalent to say that the suspension lives in an hyperelliptic
1467
stratum of Abelian differentials H_hyp(2g-2) or H_hyp(g-1, g-1) with
1468
some marked points.
1469
1470
EXAMPLES::
1471
1472
sage: iet.Permutation('a b c d','d c b a').is_hyperelliptic()
1473
True
1474
sage: iet.Permutation('0 1 2 3 4 5','5 2 1 4 3 0').is_hyperelliptic()
1475
False
1476
1477
REFERENCES:
1478
1479
Gerard Rauzy, "Echanges d'intervalles et transformations induites",
1480
Acta Arith. 34, no. 3, 203-212, 1980
1481
1482
M. Kontsevich, A. Zorich "Connected components of the moduli space
1483
of Abelian differentials with prescripebd singularities" Invent. math.
1484
153, 631-678 (2003)
1485
"""
1486
test = self.erase_marked_points()
1487
1488
n = test.length_top()
1489
cylindric = test.cylindric()
1490
return cylindric._twin[0] == range(n-1,-1,-1)
1491
1492
def cylindric(self):
1493
r"""
1494
Returns a permutation in the Rauzy class such that
1495
1496
twin[0][-1] == 0
1497
twin[1][-1] == 0
1498
1499
TESTS::
1500
1501
sage: p = iet.Permutation('a b c','c b a')
1502
sage: p.cylindric() == p
1503
True
1504
sage: p = iet.Permutation('a b c d','b d a c')
1505
sage: q = p.cylindric()
1506
sage: q[0][0] == q[1][-1]
1507
True
1508
sage: q[1][0] == q[1][0]
1509
True
1510
"""
1511
tmp = copy(self)
1512
n = self.length(0)
1513
1514
a0 = tmp._twin[0][-1]
1515
a1 = tmp._twin[1][-1]
1516
p_min = min(a0,a1)
1517
1518
while p_min > 0:
1519
if p_min == a0:
1520
k_min = min(tmp._twin[1][a0+1:])
1521
k = n - tmp._twin[1].index(k_min) - 1
1522
1523
tmp = tmp.rauzy_move(0, iteration=k)
1524
1525
else:
1526
k_min = min(tmp._twin[0][a1+1:])
1527
k = n - tmp._twin[0].index(k_min) - 1
1528
1529
tmp = tmp.rauzy_move(1, iteration=k)
1530
1531
a0 = tmp._twin[0][-1]
1532
a1 = tmp._twin[1][-1]
1533
p_min = min(a0,a1)
1534
1535
if a0 == 0:
1536
k = n - tmp._twin[1].index(0) - 1
1537
tmp = tmp.rauzy_move(0, iteration = k)
1538
1539
else:
1540
k = n - tmp._twin[0].index(0) - 1
1541
tmp = tmp.rauzy_move(1, iteration=k)
1542
1543
return tmp
1544
1545
def is_cylindric(self):
1546
r"""
1547
Returns True if the permutation is Rauzy_1n.
1548
1549
A permutation is cylindric if 1 and n are exchanged.
1550
1551
EXAMPLES::
1552
1553
sage: iet.Permutation('1 2 3','3 2 1').is_cylindric()
1554
True
1555
sage: iet.Permutation('1 2 3','2 1 3').is_cylindric()
1556
False
1557
"""
1558
return self._twin[0][-1] == 0 and self._twin[1][-1] == 0
1559
1560
def to_permutation(self):
1561
r"""
1562
Returns the permutation as an element of the symetric group.
1563
1564
EXAMPLES::
1565
1566
sage: p = iet.Permutation('a b c','c b a')
1567
sage: p.to_permutation()
1568
[3, 2, 1]
1569
1570
::
1571
1572
sage: p = Permutation([2,4,1,3])
1573
sage: q = iet.Permutation(p)
1574
sage: q.to_permutation() == p
1575
True
1576
"""
1577
from sage.combinat.permutation import Permutation
1578
return Permutation(map(lambda x: x+1,self._twin[1]))
1579
1580
class PermutationLI(Permutation):
1581
r"""
1582
Template for quadratic permutation.
1583
1584
.. warning::
1585
1586
Internal class! Do not use directly!
1587
1588
AUTHOR:
1589
1590
- Vincent Delecroix (2008-12-20): initial version
1591
"""
1592
def _init_twin(self,a):
1593
r"""
1594
Initialization of the twin data.
1595
1596
TEST::
1597
1598
sage: p = iet.GeneralizedPermutation('a a','b b',reduced=True) #indirect doctest
1599
sage: p._twin
1600
[[(0, 1), (0, 0)], [(1, 1), (1, 0)]]
1601
"""
1602
# creation of the twin
1603
self._twin = [[],[]]
1604
l = [[(0,j) for j in range(len(a[0]))],[(1,j) for j in range(len(a[1]))]]
1605
for i in range(2) :
1606
for j in range(len(l[i])) :
1607
if l[i][j] == (i,j) :
1608
if a[i][j] in a[i][j+1:] :
1609
# two up or two down
1610
j2 = (a[i][j+1:]).index(a[i][j]) + j + 1
1611
l[i][j] = (i,j2)
1612
l[i][j2] = (i,j)
1613
else :
1614
# one up, one down (here i=0)
1615
j2 = a[1].index(a[i][j])
1616
l[0][j] = (1,j2)
1617
l[1][j2] = (0,j)
1618
1619
self._twin[0] = l[0]
1620
self._twin[1] = l[1]
1621
1622
def _init_alphabet(self, intervals) :
1623
r"""
1624
Intialization procedure of the alphabet of self from intervals list
1625
1626
TEST::
1627
1628
sage: p = iet.GeneralizedPermutation('a a','b b') #indirect doctest
1629
sage: p.alphabet()
1630
Ordered Alphabet ['a', 'b']
1631
"""
1632
tmp_alphabet = []
1633
for letter in intervals[0] + intervals[1] :
1634
if letter not in tmp_alphabet :
1635
tmp_alphabet.append(letter)
1636
1637
self._alphabet = Alphabet(tmp_alphabet)
1638
1639
def is_irreducible(self, return_decomposition=False) :
1640
r"""
1641
Test of reducibility
1642
1643
A quadratic (or generalized) permutation is reducible if there exist a
1644
decomposition
1645
1646
.. math::
1647
1648
A1 u B1 | ... | B1 u A2
1649
1650
A1 u B2 | ... | B2 u A2
1651
1652
where no corners is empty, or exactly one corner is empty
1653
and it is on the left, or two and they are both on the
1654
right or on the left.
1655
1656
INPUT:
1657
1658
- you can eventually set return_decomposition to True
1659
1660
OUTPUT:
1661
1662
An integer, or an integer and a tuple.
1663
1664
if return_decomposition is True, returns a 2-uple
1665
(test,decomposition) where test is the preceding test and
1666
decomposition is a 4-uple (A11,A12,A21,A22) where:
1667
1668
A11 = A1 u BA
1669
A12 = B1 u A2
1670
A21 = A1 u B2
1671
A22 = B2 u A2
1672
1673
1674
REFERENCES:
1675
1676
Boissy-Lanneau
1677
1678
EXAMPLES::
1679
1680
sage: iet.GeneralizedPermutation('a a','b b').is_irreducible()
1681
False
1682
sage: iet.GeneralizedPermutation('a a b','b c c').is_irreducible()
1683
True
1684
1685
AUTHORS:
1686
- Vincent Delecroix (2008-12-20)
1687
"""
1688
l0 = self.length_top()
1689
l1 = self.length_bottom()
1690
s = self.list()
1691
1692
# testing no corner empty eventually one or two on the left
1693
A11, A12, A21, A22 = [], [], [], []
1694
for i1 in range(0, l0) :
1695
if (i1 > 0) and (s[0][i1-1] in A11) :
1696
A11 = []
1697
break
1698
A11 = s[0][:i1]
1699
1700
for i2 in range(l0 - 1, i1 - 1, -1) :
1701
if s[0][i2] in A12 :
1702
A12 = []
1703
break
1704
A12 = s[0][i2:]
1705
1706
1707
for i3 in range(0, l1) :
1708
if (i3 > 0) and (s[1][i3-1] in A21) :
1709
A21 = []
1710
break
1711
A21 = s[1][:i3]
1712
1713
1714
for i4 in range(l1 - 1, i3 - 1, -1) :
1715
if s[1][i4] in A22 :
1716
A22 = []
1717
break
1718
A22 = s[1][i4:]
1719
1720
1721
if sorted(A11 + A22) == sorted(A12 + A21) :
1722
if return_decomposition :
1723
return False, (A11,A12,A21,A22)
1724
return False
1725
1726
else : A22 = []
1727
else : A21 = []
1728
else : A12 = []
1729
else : A11 = []
1730
1731
1732
# testing two corners empty on the right (i2 = i4 = 0)
1733
A11, A21 = s[0][:1], s[1][:1]
1734
1735
for i1 in range(1, l0) :
1736
if s[0][i1-1] in A11 :
1737
A11 = s[0][:1]
1738
break
1739
A11 = s[0][:i1]
1740
1741
1742
for i3 in range(1, l1) :
1743
if s[1][i3-1] in A21 :
1744
A21 = s[1][:1]
1745
break
1746
A21 = s[1][:i3]
1747
1748
if sorted(A11) == sorted(A21) :
1749
if return_decomposition :
1750
return False,(A11,A12,A21,A22)
1751
return False
1752
else : A11 = s[0][:1]
1753
else : A21 = s[1][:1]
1754
1755
if return_decomposition :
1756
return True, ()
1757
return True
1758
1759
def has_right_rauzy_move(self, winner):
1760
r"""
1761
Test of Rauzy movability (with an eventual specified choice of winner)
1762
1763
A quadratic (or generalized) permutation is rauzy_movable type
1764
depending on the possible length of the last interval. It's
1765
dependent of the length equation.
1766
1767
INPUT:
1768
1769
- ``winner`` - the integer 'top' or 'bottom'
1770
1771
EXAMPLES::
1772
1773
sage: p = iet.GeneralizedPermutation('a a','b b')
1774
sage: p.has_right_rauzy_move('top')
1775
False
1776
sage: p.has_right_rauzy_move('bottom')
1777
False
1778
1779
::
1780
1781
sage: p = iet.GeneralizedPermutation('a a b','b c c')
1782
sage: p.has_right_rauzy_move('top')
1783
True
1784
sage: p.has_right_rauzy_move('bottom')
1785
True
1786
1787
::
1788
1789
sage: p = iet.GeneralizedPermutation('a a','b b c c')
1790
sage: p.has_right_rauzy_move('top')
1791
True
1792
sage: p.has_right_rauzy_move('bottom')
1793
False
1794
1795
::
1796
1797
sage: p = iet.GeneralizedPermutation('a a b b','c c')
1798
sage: p.has_right_rauzy_move('top')
1799
False
1800
sage: p.has_right_rauzy_move('bottom')
1801
True
1802
"""
1803
winner = interval_conversion(winner)
1804
loser = 1 - winner
1805
1806
# the same letter at the right-end (False)
1807
if (self._twin[0][-1][0] == 1 and
1808
self._twin[0][-1][1] == self.length_bottom() - 1) :
1809
return False
1810
1811
# the winner (or loser) letter is repeated on the other interval (True)
1812
if self._twin[winner][-1][0] == loser: return True
1813
if self._twin[loser][-1][0] == winner: return True
1814
1815
# the loser letters is the only letter repeated in
1816
# the loser interval (False)
1817
if [i for i,_ in self._twin[loser]].count(loser) == 2:
1818
return False
1819
1820
return True
1821
1822
def labelize_flip(couple):
1823
r"""
1824
Returns a string from a 2-uple couple of the form (name, flip).
1825
1826
TESTS::
1827
1828
sage: from sage.combinat.iet.template import labelize_flip
1829
sage: labelize_flip((0,1))
1830
' 0'
1831
sage: labelize_flip((0,-1))
1832
'-0'
1833
"""
1834
if couple[1] == -1: return '-' + str(couple[0])
1835
return ' ' + str(couple[0])
1836
1837
class FlippedPermutation(Permutation):
1838
r"""
1839
Template for flipped generalized permutations.
1840
1841
.. warning::
1842
1843
Internal class! Do not use directly!
1844
1845
AUTHORS:
1846
1847
- Vincent Delecroix (2008-12-20): initial version
1848
"""
1849
def _init_flips(self,intervals,flips):
1850
r"""
1851
Initialize the flip list
1852
1853
TESTS:
1854
1855
sage: iet.Permutation('a b','b a',flips='a').flips() #indirect doctest
1856
['a']
1857
sage: iet.Permutation('a b','b a',flips='b').flips() #indirect doctest
1858
['b']
1859
sage: iet.Permutation('a b','b a',flips='ab').flips() #indirect doctest
1860
['a', 'b']
1861
1862
::
1863
1864
sage: iet.GeneralizedPermutation('a a','b b',flips='a').flips()
1865
['a']
1866
sage: iet.GeneralizedPermutation('a a','b b',flips='b').flips()
1867
['b']
1868
sage: iet.GeneralizedPermutation('a a','b b',flips='ab').flips()
1869
['a', 'b']
1870
"""
1871
self._flips = [[1]*self.length_top(), [1]*self.length_bottom()]
1872
for interval in (0,1):
1873
for i,letter in enumerate(intervals[interval]):
1874
if letter in flips:
1875
self._flips[interval][i] = -1
1876
1877
def str(self, sep="\n"):
1878
r"""
1879
String representation.
1880
1881
TESTS::
1882
1883
sage: p = iet.GeneralizedPermutation('a a','b b',flips='a')
1884
sage: print p.str()
1885
-a -a
1886
b b
1887
sage: print p.str('/')
1888
-a -a/ b b
1889
"""
1890
l = self.list(flips=True)
1891
return (' '.join(map(labelize_flip, l[0]))
1892
+ sep
1893
+ ' '.join(map(labelize_flip, l[1])))
1894
1895
return s
1896
1897
class FlippedPermutationIET(FlippedPermutation, PermutationIET):
1898
r"""
1899
Template for flipped Abelian permutations.
1900
1901
.. warning::
1902
1903
Internal class! Do not use directly!
1904
1905
AUTHORS:
1906
1907
- Vincent Delecroix (2008-12-20): initial version
1908
"""
1909
def flips(self):
1910
r"""
1911
Returns the list of flips.
1912
1913
EXAMPLES::
1914
1915
sage: p = iet.Permutation('a b c','c b a',flips='ac')
1916
sage: p.flips()
1917
['a', 'c']
1918
"""
1919
result = []
1920
l = self.list(flips=False)
1921
for i,f in enumerate(self._flips[0]):
1922
if f == -1:
1923
result.append(l[0][i])
1924
return result
1925
1926
class FlippedPermutationLI(FlippedPermutation, PermutationLI):
1927
r"""
1928
Template for flipped quadratic permutations.
1929
1930
.. warning::
1931
1932
Internal class! Do not use directly!
1933
1934
AUTHORS:
1935
1936
- Vincent Delecroix (2008-12-20): initial version
1937
"""
1938
def flips(self):
1939
r"""
1940
Returns the list of flipped intervals.
1941
1942
EXAMPLES::
1943
1944
sage: p = iet.GeneralizedPermutation('a a','b b',flips='a')
1945
sage: p.flips()
1946
['a']
1947
sage: p = iet.GeneralizedPermutation('a a','b b',flips='b',reduced=True)
1948
sage: p.flips()
1949
['b']
1950
"""
1951
res = []
1952
l = self.list(flips=False)
1953
for i,f in enumerate(self._flips[0]):
1954
if f == -1:
1955
res.append(l[0][i])
1956
for i,f in enumerate(self._flips[1]):
1957
if f == -1:
1958
res.append(l[1][i])
1959
return list(set(res))
1960
1961
class RauzyDiagram(SageObject):
1962
r"""
1963
Template for Rauzy diagrams.
1964
1965
.. warning:
1966
1967
Internal class! Do not use directly!
1968
1969
AUTHORS:
1970
1971
- Vincent Delecroix (2008-12-20): initial version
1972
"""
1973
#TODO: pickle problem of Path (it does not understand what is its parent)
1974
class Path(SageObject):
1975
r"""
1976
Path in Rauzy diagram.
1977
1978
A path in a Rauzy diagram corresponds to a subsimplex of the simplex of
1979
lengths. This correspondance is obtained via the Rauzy induction. To a
1980
idoc IET we can associate a unique path in a Rauzy diagram. This
1981
establishes a correspondance between infinite full path in Rauzy diagram
1982
and equivalence topologic class of IET.
1983
"""
1984
def __init__(self, parent, *data):
1985
r"""
1986
Constructor of the path.
1987
1988
TEST::
1989
1990
sage: p = iet.Permutation('a b c','c b a')
1991
sage: r = p.rauzy_diagram()
1992
sage: g = r.path(p,0,1,0)
1993
"""
1994
self._parent = parent
1995
1996
if len(data) == 0:
1997
raise ValueError("No empty data")
1998
1999
start = data[0]
2000
if start not in self._parent:
2001
raise ValueError, "Starting point not in this Rauzy diagram"
2002
2003
self._start = self._parent._permutation_to_vertex(start)
2004
2005
cur_vertex = self._start
2006
self._edge_types = []
2007
2008
n = len(self._parent._edge_types)
2009
for i in data[1:]:
2010
if not isinstance(i, (int,Integer)): # try parent method
2011
i = self._parent.edge_types_index(i)
2012
2013
if i < 0 or i > n:
2014
raise ValueError, "indices must be integer between 0 and %d" %(n)
2015
neighbours = self._parent._succ[cur_vertex]
2016
if neighbours[i] is None:
2017
raise ValueError, "Invalid path"
2018
2019
cur_vertex = neighbours[i]
2020
self._edge_types.append(i)
2021
2022
self._end = cur_vertex
2023
2024
def _repr_(self):
2025
r"""
2026
Returns a representation of the path.
2027
2028
TEST::
2029
2030
sage: p = iet.Permutation('a b','b a')
2031
sage: r = p.rauzy_diagram()
2032
sage: r.path(p) #indirect doctest
2033
Path of length 0 in a Rauzy diagram
2034
sage: r.path(p,'top') #indirect doctest
2035
Path of length 1 in a Rauzy diagram
2036
sage: r.path(p,'bottom') #indirect doctest
2037
Path of length 1 in a Rauzy diagram
2038
"""
2039
return "Path of length %d in a Rauzy diagram" %(len(self))
2040
2041
def start(self):
2042
r"""
2043
Returns the first vertex of the path.
2044
2045
EXAMPLES::
2046
2047
sage: p = iet.Permutation('a b c','c b a')
2048
sage: r = p.rauzy_diagram()
2049
sage: g = r.path(p, 't', 'b')
2050
sage: g.start() == p
2051
True
2052
"""
2053
return self._parent._vertex_to_permutation(self._start)
2054
2055
def end(self):
2056
r"""
2057
Returns the last vertex of the path.
2058
2059
EXAMPLES::
2060
2061
sage: p = iet.Permutation('a b c','c b a')
2062
sage: r = p.rauzy_diagram()
2063
sage: g1 = r.path(p, 't', 'b', 't')
2064
sage: g1.end() == p
2065
True
2066
sage: g2 = r.path(p, 'b', 't', 'b')
2067
sage: g2.end() == p
2068
True
2069
"""
2070
return self._parent._vertex_to_permutation(self._end)
2071
2072
def edge_types(self):
2073
r"""
2074
Returns the edge types of the path.
2075
2076
EXAMPLES::
2077
2078
sage: p = iet.Permutation('a b c','c b a')
2079
sage: r = p.rauzy_diagram()
2080
sage: g = r.path(p, 0, 1)
2081
sage: g.edge_types()
2082
[0, 1]
2083
"""
2084
return copy(self._edge_types)
2085
2086
def __eq__(self, other):
2087
r"""
2088
Tests equality
2089
2090
TEST::
2091
2092
sage: p1 = iet.Permutation('a b','b a')
2093
sage: r1 = p1.rauzy_diagram()
2094
sage: p2 = p1.reduced()
2095
sage: r2 = p2.rauzy_diagram()
2096
sage: r1.path(p1,0,1) == r2.path(p2,0,1)
2097
False
2098
sage: r1.path(p1,0) == r1.path(p1,0)
2099
True
2100
sage: r1.path(p1,1) == r1.path(p1,0)
2101
False
2102
"""
2103
return (
2104
type(self) == type(other) and
2105
self._parent == other._parent and
2106
self._start == other._start and
2107
self._edge_types == other._edge_types)
2108
2109
def __ne__(self,other):
2110
r"""
2111
Tests inequality
2112
2113
TEST::
2114
2115
sage: p1 = iet.Permutation('a b','b a')
2116
sage: r1 = p1.rauzy_diagram()
2117
sage: p2 = p1.reduced()
2118
sage: r2 = p2.rauzy_diagram()
2119
sage: r1.path(p1,0,1) != r2.path(p2,0,1)
2120
True
2121
sage: r1.path(p1,0) != r1.path(p1,0)
2122
False
2123
sage: r1.path(p1,1) != r1.path(p1,0)
2124
True
2125
"""
2126
return (
2127
type(self) != type(other) or
2128
self._parent != other._parent or
2129
self._start != other._start or
2130
self._edge_types != other._edge_types)
2131
2132
def __copy__(self):
2133
r"""
2134
Returns a copy of the path.
2135
2136
TESTS::
2137
2138
sage: p = iet.Permutation('a b c','c b a')
2139
sage: r = p.rauzy_diagram()
2140
sage: g1 = r.path(p,0,1,0,0)
2141
sage: g2 = copy(g1)
2142
sage: g1 is g2
2143
False
2144
"""
2145
res = self.__class__(self._parent, self.start())
2146
res._edge_types = copy(self._edge_types)
2147
res._end = copy(self._end)
2148
return res
2149
2150
def pop(self):
2151
r"""
2152
Pops the queue of the path
2153
2154
OUTPUT:
2155
2156
a path corresponding to the last edge
2157
2158
EXAMPLES::
2159
2160
sage: p = iet.Permutation('a b','b a')
2161
sage: r = p.rauzy_diagram()
2162
sage: g = r.path(p,0,1,0)
2163
sage: g0,g1,g2,g3 = g[0], g[1], g[2], g[3]
2164
sage: g.pop() == r.path(g2,0)
2165
True
2166
sage: g == r.path(g0,0,1)
2167
True
2168
sage: g.pop() == r.path(g1,1)
2169
True
2170
sage: g == r.path(g0,0)
2171
True
2172
sage: g.pop() == r.path(g0,0)
2173
True
2174
sage: g == r.path(g0)
2175
True
2176
sage: g.pop() == r.path(g0)
2177
True
2178
"""
2179
if len(self) == 0:
2180
return self._parent.path(self.start())
2181
2182
else:
2183
e = self._edge_types.pop()
2184
self._end = self._parent._pred[self._end][e]
2185
return self._parent.path(self.end(),e)
2186
2187
def append(self, edge_type):
2188
r"""
2189
Append an edge to the path.
2190
2191
EXAMPLES::
2192
2193
sage: p = iet.Permutation('a b c','c b a')
2194
sage: r = p.rauzy_diagram()
2195
sage: g = r.path(p)
2196
sage: g.append('top')
2197
sage: g
2198
Path of length 1 in a Rauzy diagram
2199
sage: g.append('bottom')
2200
sage: g
2201
Path of length 2 in a Rauzy diagram
2202
"""
2203
if not isinstance(edge_type, (int,Integer)):
2204
edge_type = self._parent.edge_types_index(edge_type)
2205
2206
elif edge_type < 0 or edge_type >= len(self._parent._edge_types):
2207
raise ValueError, "Edge type not valid"
2208
2209
if self._parent._succ[self._end][edge_type] is None:
2210
raise ValueError, "%d is not a valid edge" %(edge_type)
2211
2212
self._edge_types.append(edge_type)
2213
self._end = self._parent._succ[self._end][edge_type]
2214
2215
def _fast_append(self, edge_type):
2216
r"""
2217
Append an edge to the path without verification.
2218
2219
EXAMPLES::
2220
2221
sage: p = iet.GeneralizedPermutation('a a','b b c c')
2222
sage: r = p.rauzy_diagram()
2223
2224
.. try to add 1 with append::
2225
2226
sage: g = r.path(p)
2227
sage: r[p][1] is None
2228
True
2229
sage: g.append(1)
2230
Traceback (most recent call last):
2231
...
2232
ValueError: 1 is not a valid edge
2233
2234
.. the same with fast append::
2235
2236
sage: g = r.path(p)
2237
sage: r[p][1] is None
2238
True
2239
sage: g._fast_append(1)
2240
"""
2241
self._edge_types.append(edge_type)
2242
self._end = self._parent._succ[self._end][edge_type]
2243
2244
def extend(self, path):
2245
r"""
2246
Extends self with another path.
2247
2248
EXAMPLES::
2249
2250
sage: p = iet.Permutation('a b c d','d c b a')
2251
sage: r = p.rauzy_diagram()
2252
sage: g1 = r.path(p,'t','t')
2253
sage: g2 = r.path(p.rauzy_move('t',iteration=2),'b','b')
2254
sage: g = r.path(p,'t','t','b','b')
2255
sage: g == g1 + g2
2256
True
2257
sage: g = copy(g1)
2258
sage: g.extend(g2)
2259
sage: g == g1 + g2
2260
True
2261
"""
2262
if self._parent != path._parent:
2263
raise ValueError, "Not on the same Rauzy diagram"
2264
2265
if self._end != path._start:
2266
raise ValueError, "The end of the first path must the start of the second"
2267
2268
self._edge_types.extend(path._edge_types)
2269
self._end = path._end
2270
2271
def _fast_extend(self, path):
2272
r"""
2273
Extension with no verification.
2274
2275
EXAMPLES::
2276
2277
sage: p = iet.Permutation('a b c','c b a')
2278
sage: r = p.rauzy_diagram()
2279
sage: p0, p1 = r[p]
2280
sage: g = r.path(p)
2281
sage: g._fast_extend(r.path(p0))
2282
sage: g
2283
Path of length 0 in a Rauzy diagram
2284
sage: g._fast_extend(r.path(p1))
2285
sage: g
2286
Path of length 0 in a Rauzy diagram
2287
"""
2288
self._edge_types.extend(path._edge_types)
2289
self._end = path._end
2290
2291
def __len__(self):
2292
r"""
2293
Returns the length of the path.
2294
2295
TEST::
2296
2297
2298
sage: p = iet.Permutation('a b c','c b a')
2299
sage: r = p.rauzy_diagram()
2300
sage: len(r.path(p))
2301
0
2302
sage: len(r.path(p,0))
2303
1
2304
sage: len(r.path(p,1))
2305
1
2306
"""
2307
return len(self._edge_types)
2308
2309
def __getitem__(self, i):
2310
r"""
2311
TESTS::
2312
2313
sage: p = iet.Permutation('a b c','c b a')
2314
sage: r = p.rauzy_diagram()
2315
sage: g = r.path(p,'t','b')
2316
sage: g[0] == p
2317
True
2318
sage: g[1] == p.rauzy_move('t')
2319
True
2320
sage: g[2] == p.rauzy_move('t').rauzy_move('b')
2321
True
2322
sage: g[-1] == g[2]
2323
True
2324
sage: g[-2] == g[1]
2325
True
2326
sage: g[-3] == g[0]
2327
True
2328
"""
2329
if i > len(self) or i < -len(self)-1:
2330
raise IndexError, "path index out of range"
2331
2332
if i == 0: return self.start()
2333
if i < 0: i = i + len(self) + 1
2334
2335
v = self._start
2336
for k in range(i):
2337
v = self._parent._succ[v][self._edge_types[k]]
2338
return self._parent._vertex_to_permutation(v)
2339
2340
def __add__(self, other):
2341
r"""
2342
Concatenation of paths.
2343
2344
EXAMPLES::
2345
2346
sage: p = iet.Permutation('a b','b a')
2347
sage: r = p.rauzy_diagram()
2348
sage: r.path(p) + r.path(p,'b') == r.path(p,'b')
2349
True
2350
sage: r.path(p,'b') + r.path(p) == r.path(p,'b')
2351
True
2352
sage: r.path(p,'t') + r.path(p,'b') == r.path(p,'t','b')
2353
True
2354
"""
2355
if self._end != other._start:
2356
raise ValueError, "The end of the first path is not the start of the second"
2357
2358
res = copy(self)
2359
res._fast_extend(other)
2360
return res
2361
2362
def __mul__(self, n):
2363
r"""
2364
Multiple of a loop.
2365
2366
EXAMPLES::
2367
2368
sage: p = iet.Permutation('a b','b a')
2369
sage: r = p.rauzy_diagram()
2370
sage: l = r.path(p,'b')
2371
sage: l * 2 == r.path(p,'b','b')
2372
True
2373
sage: l * 3 == r.path(p,'b','b','b')
2374
True
2375
"""
2376
if not self.is_loop():
2377
raise ValueError("Must be a loop to have multiple")
2378
2379
if not isinstance(n, (Integer,int)):
2380
raise TypeError("The multiplier must be an integer")
2381
2382
if n < 0:
2383
raise ValueError("The multiplier must be non negative")
2384
2385
res = copy(self)
2386
for i in range(n-1):
2387
res += self
2388
return res
2389
2390
def is_loop(self):
2391
r"""
2392
Tests whether the path is a loop (start point = end point).
2393
2394
EXAMPLES::
2395
2396
sage: p = iet.Permutation('a b','b a')
2397
sage: r = p.rauzy_diagram()
2398
sage: r.path(p).is_loop()
2399
True
2400
sage: r.path(p,0,1,0,0).is_loop()
2401
True
2402
"""
2403
return self._start == self._end
2404
2405
def winners(self):
2406
r"""
2407
Returns the winner list associated to the edge of the path.
2408
2409
EXAMPLES::
2410
2411
sage: p = iet.Permutation('a b','b a')
2412
sage: r = p.rauzy_diagram()
2413
sage: r.path(p).winners()
2414
[]
2415
sage: r.path(p,0).winners()
2416
['b']
2417
sage: r.path(p,1).winners()
2418
['a']
2419
"""
2420
return self.composition(
2421
self._parent.edge_to_winner,
2422
list.__add__)
2423
2424
def losers(self):
2425
r"""
2426
Returns a list of the loosers on the path.
2427
2428
EXAMPLES::
2429
2430
sage: p = iet.Permutation('a b c','c b a')
2431
sage: r = p.rauzy_diagram()
2432
sage: g0 = r.path(p,'t','b','t')
2433
sage: g0.losers()
2434
['a', 'c', 'b']
2435
sage: g1 = r.path(p,'b','t','b')
2436
sage: g1.losers()
2437
['c', 'a', 'b']
2438
"""
2439
return self.composition(
2440
self._parent.edge_to_loser,
2441
list.__add__)
2442
2443
def __iter__(self):
2444
r"""
2445
Iterator over the permutations of the path.
2446
2447
EXAMPLES::
2448
2449
sage: p = iet.Permutation('a b c','c b a')
2450
sage: r = p.rauzy_diagram()
2451
sage: g = r.path(p)
2452
sage: for q in g:
2453
... print p
2454
a b c
2455
c b a
2456
sage: g = r.path(p, 't', 't')
2457
sage: for q in g:
2458
... print q, "\n*****"
2459
a b c
2460
c b a
2461
*****
2462
a b c
2463
c a b
2464
*****
2465
a b c
2466
c b a
2467
*****
2468
sage: g = r.path(p,'b','t')
2469
sage: for q in g:
2470
... print q, "\n*****"
2471
a b c
2472
c b a
2473
*****
2474
a c b
2475
c b a
2476
*****
2477
a c b
2478
c b a
2479
*****
2480
"""
2481
i = self._start
2482
2483
for edge_type in self._edge_types:
2484
yield self._parent._vertex_to_permutation(i)
2485
i = self._parent._succ[i][edge_type]
2486
2487
yield self.end()
2488
2489
def composition(self, function, composition = None):
2490
r"""
2491
Compose an edges function on a path
2492
2493
INPUT:
2494
2495
- ``path`` - either a Path or a tuple describing a path
2496
2497
- ``function`` - function must be of the form
2498
2499
- ``composition`` - the composition function
2500
2501
AUTHOR:
2502
2503
- Vincent Delecroix (2009-09-29)
2504
2505
EXAMPLES::
2506
2507
sage: p = iet.Permutation('a b','b a')
2508
sage: r = p.rauzy_diagram()
2509
sage: def f(i,t):
2510
... if t is None: return []
2511
... return [t]
2512
sage: g = r.path(p)
2513
sage: g.composition(f,list.__add__)
2514
[]
2515
sage: g = r.path(p,0,1)
2516
sage: g.composition(f, list.__add__)
2517
[0, 1]
2518
"""
2519
result = function(None,None)
2520
cur_vertex = self._start
2521
p = self._parent._element
2522
2523
if composition is None: composition = result.__class__.__mul__
2524
2525
for i in self._edge_types:
2526
self._parent._set_element(cur_vertex)
2527
result = composition(result, function(p,i))
2528
cur_vertex = self._parent._succ[cur_vertex][i]
2529
2530
return result
2531
2532
def right_composition(self, function, composition = None) :
2533
r"""
2534
Compose an edges function on a path
2535
2536
INPUT:
2537
2538
- ``function`` - function must be of the form (indice,type) -> element. Moreover function(None,None) must be an identity element for initialization.
2539
2540
- ``composition`` - the composition function for the function. * if None (defaut None)
2541
2542
TEST::
2543
2544
sage: p = iet.Permutation('a b','b a')
2545
sage: r = p.rauzy_diagram()
2546
sage: def f(i,t):
2547
... if t is None: return []
2548
... return [t]
2549
sage: g = r.path(p)
2550
sage: g.right_composition(f,list.__add__)
2551
[]
2552
sage: g = r.path(p,0,1)
2553
sage: g.right_composition(f, list.__add__)
2554
[1, 0]
2555
"""
2556
result = function(None,None)
2557
p = self._parent._element
2558
cur_vertex = self._start
2559
2560
if composition is None: composition = result.__class__.__mul__
2561
2562
for i in self._edge_types:
2563
self._parent._set_element(cur_vertex)
2564
result = composition(function(p,i),result)
2565
cur_vertex = self._parent._succ[cur_vertex][i]
2566
2567
return result
2568
2569
def __init__(self, p,
2570
right_induction=True,
2571
left_induction=False,
2572
left_right_inversion=False,
2573
top_bottom_inversion=False,
2574
symmetric=False):
2575
r"""
2576
self._succ contains successors
2577
self._pred contains predecessors
2578
2579
self._element_class is the class of elements of self
2580
self._element is an instance of this class (hence contains the alphabet,
2581
the representation mode, ...). It is used to store data about property
2582
of permutations and also as a fast iterator.
2583
2584
INPUT:
2585
2586
- ``right_induction`` - boolean or 'top' or 'bottom': consider the
2587
right induction
2588
2589
- ``left_induction`` - boolean or 'top' or 'bottom': consider the
2590
left induction
2591
2592
- ``left_right_inversion`` - consider the left right inversion
2593
2594
- ``top_bottom_inversion`` - consider the top bottom inversion
2595
2596
- ``symmetric`` - consider the symmetric
2597
2598
TESTS::
2599
2600
sage: r1 = iet.RauzyDiagram('a b','b a')
2601
sage: r2 = loads(dumps(r1))
2602
"""
2603
self._edge_types = []
2604
self._index = {}
2605
2606
if right_induction is True:
2607
self._index['rt_rauzy'] = len(self._edge_types)
2608
self._edge_types.append(('rauzy_move',(0,-1)))
2609
self._index['rb_rauzy'] = len(self._edge_types)
2610
self._edge_types.append(('rauzy_move',(1,-1)))
2611
2612
elif isinstance(right_induction,str):
2613
if right_induction == '':
2614
raise ValueError, "right_induction can not be empty string"
2615
2616
elif 'top'.startswith(right_induction):
2617
self._index['rt_rauzy'] = len(self._edge_types)
2618
self._edge_types.append(('rauzy_move',(0,-1)))
2619
2620
elif 'bottom'.startswith(right_induction):
2621
self._index['rb_rauzy'] = len(self._edge_types)
2622
self._edge_types.append(('rauzy_move',(1,-1)))
2623
2624
else:
2625
raise ValueError, "%s is not valid for right_induction" %(right_induction)
2626
2627
if left_induction is True:
2628
self._index['lt_rauzy'] = len(self._edge_types)
2629
self._edge_types.append(('rauzy_move',(0,0)))
2630
self._index['lb_rauzy'] = len(self._edge_types)
2631
self._edge_types.append(('rauzy_move',(1,0)))
2632
2633
elif isinstance(left_induction,str):
2634
if left_induction == '':
2635
raise ValueError, "left_induction can not be empty string"
2636
2637
elif 'top'.startswith(left_induction):
2638
self._index['lt_rauzy'] = len(self._edge_types)
2639
self._edge_types.append(('rauzy_move',(0,0)))
2640
2641
elif 'bottom'.startswith(left_induction):
2642
self._index['lb_rauzy'] = len(self._edge_types)
2643
self._edge_types.append(('rauzy_move',(1,0)))
2644
2645
else:
2646
raise ValueError, "%s is not valid for left_induction" %(right_induction)
2647
2648
if left_right_inversion is True:
2649
self._index['lr_inverse'] = len(self._edge_types)
2650
self._edge_types.append(('left_right_inverse',()))
2651
2652
if top_bottom_inversion is True:
2653
self._index['tb_inverse'] = len(self._edge_types)
2654
self._edge_types.append(('top_bottom_inverse',()))
2655
2656
if symmetric is True:
2657
self._index['symmetric'] = len(self._edge_types)
2658
self._edge_types.append(('symmetric',()))
2659
2660
self._n = len(p)
2661
self._element_class = p.__class__
2662
self._element = copy(p)
2663
self._alphabet = self._element._alphabet
2664
2665
self._pred = {}
2666
self._succ = {}
2667
2668
self.complete(p)
2669
2670
def __eq__(self, other):
2671
r"""
2672
Tests equality.
2673
2674
TESTS:
2675
2676
::
2677
2678
sage: iet.RauzyDiagram('a b','b a') == iet.RauzyDiagram('a b c','c b a')
2679
False
2680
sage: r = iet.RauzyDiagram('a b c','c b a')
2681
sage: r1 = iet.RauzyDiagram('a c b','c b a', alphabet='abc')
2682
sage: r2 = iet.RauzyDiagram('a b c','c a b', alphabet='abc')
2683
sage: r == r1
2684
True
2685
sage: r == r2
2686
True
2687
sage: r1 == r2
2688
True
2689
2690
::
2691
2692
sage: r = iet.RauzyDiagram('a b c d','d c b a')
2693
sage: for p in r:
2694
... p.rauzy_diagram() == r
2695
True
2696
True
2697
True
2698
True
2699
True
2700
True
2701
True
2702
"""
2703
return (
2704
type(self) == type(other) and
2705
self._edge_types == other._edge_types and
2706
self._succ.keys()[0] in other._succ)
2707
2708
def __ne__(self, other):
2709
r"""
2710
Tests difference.
2711
2712
2713
TEST::
2714
2715
sage: iet.RauzyDiagram('a b','b a') != iet.RauzyDiagram('a b c','c b a')
2716
True
2717
sage: r = iet.RauzyDiagram('a b c','c b a')
2718
sage: r1 = iet.RauzyDiagram('a c b','c b a', alphabet='abc')
2719
sage: r2 = iet.RauzyDiagram('a b c','c a b', alphabet='abc')
2720
sage: r != r1
2721
False
2722
sage: r != r2
2723
False
2724
sage: r1 != r2
2725
False
2726
"""
2727
return (
2728
type(self) != type(other) or
2729
self._edge_types != other._edge_types or
2730
self._succ.keys()[0] not in other._succ)
2731
2732
def vertices(self):
2733
r"""
2734
Returns a list of the vertices.
2735
2736
EXAMPLES::
2737
2738
sage: r = iet.RauzyDiagram('a b','b a')
2739
sage: for p in r.vertices(): print p
2740
a b
2741
b a
2742
"""
2743
return map(
2744
lambda x: self._vertex_to_permutation(x),
2745
self._succ.keys())
2746
2747
def vertex_iterator(self):
2748
r"""
2749
Returns an iterator over the vertices
2750
2751
EXAMPLES::
2752
2753
sage: r = iet.RauzyDiagram('a b','b a')
2754
sage: for p in r.vertex_iterator(): print p
2755
a b
2756
b a
2757
2758
::
2759
2760
sage: r = iet.RauzyDiagram('a b c d','d c b a')
2761
sage: from itertools import ifilter
2762
sage: r_1n = ifilter(lambda x: x.is_cylindric(), r)
2763
sage: for p in r_1n: print p
2764
a b c d
2765
d c b a
2766
"""
2767
from itertools import imap
2768
return imap(
2769
lambda x: self._vertex_to_permutation(x),
2770
self._succ.keys())
2771
2772
def edges(self,labels=True):
2773
r"""
2774
Returns a list of the edges.
2775
2776
EXAMPLES::
2777
2778
sage: r = iet.RauzyDiagram('a b','b a')
2779
sage: len(r.edges())
2780
2
2781
"""
2782
return list(self.edge_iterator())
2783
2784
def edge_iterator(self):
2785
r"""
2786
Returns an iterator over the edges of the graph.
2787
2788
EXAMPLES::
2789
2790
sage: p = iet.Permutation('a b','b a')
2791
sage: r = p.rauzy_diagram()
2792
sage: for e in r.edge_iterator():
2793
... print e[0].str(sep='/'), '-->', e[1].str(sep='/')
2794
a b/b a --> a b/b a
2795
a b/b a --> a b/b a
2796
"""
2797
for x in self._succ.keys():
2798
for i,y in enumerate(self._succ[x]):
2799
if y is not None:
2800
yield(
2801
(self._vertex_to_permutation(x),
2802
self._vertex_to_permutation(y),
2803
i))
2804
2805
def edge_types_index(self, data):
2806
r"""
2807
Try to convert the data as an edge type.
2808
2809
INPUT:
2810
2811
- ``data`` - a string
2812
2813
OUTPUT:
2814
2815
integer
2816
2817
EXAMPLES:
2818
2819
For a standard Rauzy diagram (only right induction) the 0 index
2820
corresponds to the 'top' induction and the index 1 corresponds to the
2821
'bottom' one::
2822
2823
sage: p = iet.Permutation('a b c','c b a')
2824
sage: r = p.rauzy_diagram()
2825
sage: r.edge_types_index('top')
2826
0
2827
sage: r[p][0] == p.rauzy_move('top')
2828
True
2829
sage: r.edge_types_index('bottom')
2830
1
2831
sage: r[p][1] == p.rauzy_move('bottom')
2832
True
2833
2834
The special operations (inversion and symmetry) always appears after the
2835
different Rauzy inductions::
2836
2837
sage: p = iet.Permutation('a b c','c b a')
2838
sage: r = p.rauzy_diagram(symmetric=True)
2839
sage: r.edge_types_index('symmetric')
2840
2
2841
sage: r[p][2] == p.symmetric()
2842
True
2843
2844
This function always try to resolve conflictuous name. If it's
2845
impossible a ValueError is raised::
2846
2847
sage: p = iet.Permutation('a b c','c b a')
2848
sage: r = p.rauzy_diagram(left_induction=True)
2849
sage: r.edge_types_index('top')
2850
Traceback (most recent call last):
2851
...
2852
ValueError: left and right inductions must be differentiated
2853
sage: r.edge_types_index('top_right')
2854
0
2855
sage: r[p][0] == p.rauzy_move(0)
2856
True
2857
sage: r.edge_types_index('bottom_left')
2858
3
2859
sage: r[p][3] == p.rauzy_move('bottom', 'left')
2860
True
2861
2862
::
2863
2864
sage: p = iet.Permutation('a b c','c b a')
2865
sage: r = p.rauzy_diagram(left_right_inversion=True,top_bottom_inversion=True)
2866
sage: r.edge_types_index('inversion')
2867
Traceback (most recent call last):
2868
...
2869
ValueError: left-right and top-bottom inversions must be differentiated
2870
sage: r.edge_types_index('lr_inverse')
2871
2
2872
sage: p.lr_inverse() == r[p][2]
2873
True
2874
sage: r.edge_types_index('tb_inverse')
2875
3
2876
sage: p.tb_inverse() == r[p][3]
2877
True
2878
2879
Short names are accepted::
2880
2881
sage: p = iet.Permutation('a b c','c b a')
2882
sage: r = p.rauzy_diagram(right_induction='top',top_bottom_inversion=True)
2883
sage: r.edge_types_index('top_rauzy_move')
2884
0
2885
sage: r.edge_types_index('t')
2886
0
2887
sage: r.edge_types_index('tb')
2888
1
2889
sage: r.edge_types_index('inversion')
2890
1
2891
sage: r.edge_types_index('inverse')
2892
1
2893
sage: r.edge_types_index('i')
2894
1
2895
"""
2896
if not isinstance(data,str):
2897
raise ValueError, "the edge type must be a string"
2898
2899
if ('top_rauzy_move'.startswith(data) or
2900
't_rauzy_move'.startswith(data)):
2901
if self._index.has_key('lt_rauzy'):
2902
if self._index.has_key('rt_rauzy'):
2903
raise ValueError, "left and right inductions must be differentiated"
2904
return self._index['lt_rauzy']
2905
2906
if self._index.has_key('rt_rauzy'):
2907
return self._index['rt_rauzy']
2908
2909
raise ValueError, "no top induction in this Rauzy diagram"
2910
2911
if ('bottom_rauzy_move'.startswith(data) or
2912
'b_rauzy_move'.startswith(data)):
2913
if self._index.has_key('lb_rauzy'):
2914
if self._index.has_key('rb_rauzy'):
2915
raise ValueError, "left and right inductions must be differentiated"
2916
return self._index['lb_rauzy']
2917
2918
if self._index.has_key('rb_rauzy'):
2919
return self._index['rb_rauzy']
2920
2921
raise ValueError, "no bottom Rauzy induction in this diagram"
2922
2923
if ('left_rauzy_move'.startswith(data) or
2924
'l_rauzy_move'.startswith(data)):
2925
if self._index.has_key('lt_rauzy'):
2926
if self._index.has_key('lb_rauzy'):
2927
raise ValueError, "top and bottom inductions must be differentiated"
2928
return self._index['lt_rauzy']
2929
2930
if self._index.has_key('lb_rauzy'):
2931
return self._index('lb_rauzy')
2932
2933
raise ValueError, "no left Rauzy induction in this diagram"
2934
2935
if ('lt_rauzy_move'.startswith(data) or
2936
'tl_rauzy_move'.startswith(data) or
2937
'left_top_rauzy_move'.startswith(data) or
2938
'top_left_rauzy_move'.startswith(data)):
2939
if not self._index.has_key('lt_rauzy'):
2940
raise ValueError, "no top-left Rauzy induction in this diagram"
2941
else:
2942
return self._index['lt_rauzy']
2943
2944
if ('lb_rauzy_move'.startswith(data) or
2945
'bl_rauzy_move'.startswith(data) or
2946
'left_bottom_rauzy_move'.startswith(data) or
2947
'bottom_left_rauzy_move'.startswith(data)):
2948
if not self._index.has_key('lb_rauzy'):
2949
raise ValueError, "no bottom-left Rauzy induction in this diagram"
2950
else:
2951
return self._index['lb_rauzy']
2952
2953
if 'right'.startswith(data):
2954
raise ValueError, "ambiguity with your edge name: %s" %(data)
2955
2956
if ('rt_rauzy_move'.startswith(data) or
2957
'tr_rauzy_move'.startswith(data) or
2958
'right_top_rauzy_move'.startswith(data) or
2959
'top_right_rauzy_move'.startswith(data)):
2960
if not self._index.has_key('rt_rauzy'):
2961
raise ValueError, "no top-right Rauzy induction in this diagram"
2962
else:
2963
return self._index['rt_rauzy']
2964
2965
if ('rb_rauzy_move'.startswith(data) or
2966
'br_rauzy_move'.startswith(data) or
2967
'right_bottom_rauzy_move'.startswith(data) or
2968
'bottom_right_rauzy_move'.startswith(data)):
2969
if not self._index.has_key('rb_rauzy'):
2970
raise ValueError, "no bottom-right Rauzy induction in this diagram"
2971
else:
2972
return self._index['rb_rauzy']
2973
2974
if 'symmetric'.startswith(data):
2975
if not self._index.has_key('symmetric'):
2976
raise ValueError, "no symmetric in this diagram"
2977
else:
2978
return self._index['symmetric']
2979
2980
if 'inversion'.startswith(data) or data == 'inverse':
2981
if self._index.has_key('lr_inverse'):
2982
if self._index.has_key('tb_inverse'):
2983
raise ValueError, "left-right and top-bottom inversions must be differentiated"
2984
return self._index['lr_inverse']
2985
2986
if self._index.has_key('tb_inverse'):
2987
return self._index['tb_inverse']
2988
2989
raise ValueError, "no inversion in this diagram"
2990
2991
if ('lr_inversion'.startswith(data) or
2992
data == 'lr_inverse' or
2993
'left_right_inversion'.startswith(data) or
2994
data == 'left_right_inverse'):
2995
if not self._index.has_key('lr_inverse'):
2996
raise ValueError, "no left-right inversion in this diagram"
2997
else:
2998
return self._index['lr_inverse']
2999
3000
if ('tb_inversion'.startswith(data) or
3001
data == 'tb_inverse' or
3002
'top_bottom_inversion'.startswith(data)
3003
or data == 'top_bottom_inverse'):
3004
if not self._index.has_key('tb_inverse'):
3005
raise ValueError, "no top-bottom inversion in this diagram"
3006
else:
3007
return self._index['tb_inverse']
3008
3009
raise ValueError, "this edge type does not exist: %s" %(data)
3010
3011
def edge_types(self):
3012
r"""
3013
Print information about edges.
3014
3015
EXAMPLES::
3016
3017
sage: r = iet.RauzyDiagram('a b', 'b a')
3018
sage: r.edge_types()
3019
0: rauzy_move(0, -1)
3020
1: rauzy_move(1, -1)
3021
3022
::
3023
3024
sage: r = iet.RauzyDiagram('a b', 'b a', left_induction=True)
3025
sage: r.edge_types()
3026
0: rauzy_move(0, -1)
3027
1: rauzy_move(1, -1)
3028
2: rauzy_move(0, 0)
3029
3: rauzy_move(1, 0)
3030
3031
::
3032
3033
sage: r = iet.RauzyDiagram('a b',' b a',symmetric=True)
3034
sage: r.edge_types()
3035
0: rauzy_move(0, -1)
3036
1: rauzy_move(1, -1)
3037
2: symmetric()
3038
"""
3039
for i,(edge_type,t) in enumerate(self._edge_types):
3040
print str(i) + ": " + edge_type + str(t)
3041
3042
def alphabet(self, data=None):
3043
r"""
3044
TESTS::
3045
3046
sage: r = iet.RauzyDiagram('a b','b a')
3047
sage: r.alphabet() == Alphabet(['a','b'])
3048
True
3049
sage: r = iet.RauzyDiagram([0,1],[1,0])
3050
sage: r.alphabet() == Alphabet([0,1])
3051
True
3052
"""
3053
if data is None:
3054
return self._element._alphabet
3055
else:
3056
self._element._set_alphabet(data)
3057
3058
def letters(self):
3059
r"""
3060
Returns the letters used by the RauzyDiagram.
3061
3062
EXAMPLES::
3063
3064
sage: r = iet.RauzyDiagram('a b','b a')
3065
sage: r.alphabet()
3066
Ordered Alphabet ['a', 'b']
3067
sage: r.letters()
3068
['a', 'b']
3069
sage: r.alphabet('ABCDEF')
3070
sage: r.alphabet()
3071
Ordered Alphabet ['A', 'B', 'C', 'D', 'E', 'F']
3072
sage: r.letters()
3073
['A', 'B']
3074
"""
3075
return self._element.letters()
3076
3077
def _vertex_to_permutation(self,data=None):
3078
r"""
3079
Converts the (vertex) data to a permutation.
3080
3081
TESTS:
3082
3083
sage: r = iet.RauzyDiagram('a b','b a') #indirect doctest
3084
"""
3085
if data is not None:
3086
self._set_element(data)
3087
return copy(self._element)
3088
3089
def edge_to_matrix(self, p=None, edge_type=None):
3090
r"""
3091
Return the corresponding matrix
3092
3093
INPUT:
3094
3095
- ``p`` - a permutation
3096
3097
- ``edge_type`` - 0 or 1 corresponding to the type of the edge
3098
3099
OUTPUT:
3100
3101
A matrix
3102
3103
EXAMPLES::
3104
3105
sage: p = iet.Permutation('a b c','c b a')
3106
sage: d = p.rauzy_diagram()
3107
sage: print d.edge_to_matrix(p,1)
3108
[1 0 1]
3109
[0 1 0]
3110
[0 0 1]
3111
"""
3112
if p is None and edge_type is None:
3113
return identity_matrix(self._n)
3114
3115
function_name = self._edge_types[edge_type][0] + '_matrix'
3116
3117
if not hasattr(self._element_class,function_name):
3118
return identity_matrix(self._n)
3119
3120
arguments = self._edge_types[edge_type][1]
3121
3122
return getattr(p,function_name)(*arguments)
3123
3124
def edge_to_winner(self, p=None, edge_type=None):
3125
r"""
3126
Return the corresponding winner
3127
3128
TEST::
3129
3130
sage: r = iet.RauzyDiagram('a b','b a')
3131
sage: r.edge_to_winner(None,None)
3132
[]
3133
"""
3134
if p is None and edge_type is None:
3135
return []
3136
3137
function_name = self._edge_types[edge_type][0] + '_winner'
3138
3139
if not hasattr(self._element_class, function_name):
3140
return [None]
3141
3142
arguments = self._edge_types[edge_type][1]
3143
3144
return [getattr(p,function_name)(*arguments)]
3145
3146
def edge_to_loser(self, p=None, edge_type=None):
3147
r"""
3148
Return the corresponding loser
3149
3150
TEST::
3151
3152
sage: r = iet.RauzyDiagram('a b','b a')
3153
sage: r.edge_to_loser(None,None)
3154
[]
3155
"""
3156
if p is None and edge_type is None:
3157
return []
3158
3159
function_name = self._edge_types[edge_type][0] + '_loser'
3160
3161
if not hasattr(self._element_class, function_name):
3162
return [None]
3163
3164
arguments = self._edge_types[edge_type][1]
3165
3166
return [getattr(p,function_name)(*arguments)]
3167
3168
def _all_npath_extension(self, g, length=0):
3169
r"""
3170
Returns an iterator over all extension of fixed length of p.
3171
3172
INPUT:
3173
3174
- ``p`` - a path
3175
3176
- ``length`` - a non-negative integer
3177
3178
TESTS:
3179
3180
::
3181
3182
sage: p = iet.Permutation('a b','b a')
3183
sage: r = p.rauzy_diagram()
3184
sage: g0 = r.path(p)
3185
sage: for g in r._all_npath_extension(g0,0):
3186
... print g
3187
Path of length 0 in a Rauzy diagram
3188
sage: for g in r._all_npath_extension(g0,1):
3189
... print g
3190
Path of length 1 in a Rauzy diagram
3191
Path of length 1 in a Rauzy diagram
3192
sage: for g in r._all_npath_extension(g0,2):
3193
... print g
3194
Path of length 2 in a Rauzy diagram
3195
Path of length 2 in a Rauzy diagram
3196
Path of length 2 in a Rauzy diagram
3197
Path of length 2 in a Rauzy diagram
3198
3199
::
3200
3201
sage: p = iet.GeneralizedPermutation('a a','b b c c',reduced=True)
3202
sage: r = p.rauzy_diagram()
3203
sage: g0 = r.path(p)
3204
sage: len(list(r._all_npath_extension(g0,0)))
3205
1
3206
sage: len(list(r._all_npath_extension(g0,1)))
3207
1
3208
sage: len(list(r._all_npath_extension(g0,2)))
3209
2
3210
sage: len(list(r._all_npath_extension(g0,3)))
3211
3
3212
sage: len(list(r._all_npath_extension(g0,4)))
3213
5
3214
"""
3215
if length == 0:
3216
yield g
3217
3218
else:
3219
for i in range(len(self._edge_types)):
3220
if self._succ[g._end][i] is not None:
3221
g._fast_append(i)
3222
for h in self._all_npath_extension(g,length-1): yield h
3223
g.pop()
3224
3225
def _all_path_extension(self, g, length=0):
3226
r"""
3227
Returns an iterator over all path extension of p.
3228
3229
TESTS:
3230
3231
::
3232
3233
sage: p = iet.Permutation('a b','b a')
3234
sage: r = p.rauzy_diagram()
3235
sage: g0 = r.path(p)
3236
sage: for g in r._all_path_extension(g0,0):
3237
... print g
3238
Path of length 0 in a Rauzy diagram
3239
sage: for g in r._all_path_extension(g0, 1):
3240
... print g
3241
Path of length 0 in a Rauzy diagram
3242
Path of length 1 in a Rauzy diagram
3243
Path of length 1 in a Rauzy diagram
3244
3245
::
3246
3247
sage: p = iet.GeneralizedPermutation('a a','b b c c')
3248
sage: r = p.rauzy_diagram()
3249
sage: g0 = r.path(p)
3250
sage: len(list(r._all_path_extension(g0,0)))
3251
1
3252
sage: len(list(r._all_path_extension(g0,1)))
3253
2
3254
sage: len(list(r._all_path_extension(g0,2)))
3255
4
3256
sage: len(list(r._all_path_extension(g0,3)))
3257
7
3258
"""
3259
yield g
3260
3261
if length > 0:
3262
for i in range(len(self._edge_types)):
3263
if self._succ[g._end][i] is not None:
3264
g._fast_append(i)
3265
for h in self._all_path_extension(g,length-1): yield h
3266
g.pop()
3267
3268
def __iter__(self):
3269
r"""
3270
Iterator over the permutations of the Rauzy diagram.
3271
3272
EXAMPLES::
3273
3274
sage: r = iet.RauzyDiagram('a b','b a')
3275
sage: for p in r: print p
3276
a b
3277
b a
3278
sage: r = iet.RauzyDiagram('a b c','c b a')
3279
sage: for p in r: print p.stratum()
3280
H(0, 0)
3281
H(0, 0)
3282
H(0, 0)
3283
"""
3284
for data in self._succ.iterkeys():
3285
yield self._vertex_to_permutation(data)
3286
3287
def __contains__(self, element):
3288
r"""
3289
Containance test.
3290
3291
TESTS::
3292
3293
sage: p = iet.Permutation('a b c d','d c b a',reduced=True)
3294
sage: q = iet.Permutation('a b c d','d b c a',reduced=True)
3295
sage: r = p.rauzy_diagram()
3296
sage: s = q.rauzy_diagram()
3297
sage: p in r
3298
True
3299
sage: p in s
3300
False
3301
sage: q in r
3302
False
3303
sage: q in s
3304
True
3305
"""
3306
for p in self._succ.iterkeys():
3307
if self._vertex_to_permutation(p) == element:
3308
return True
3309
3310
return False
3311
3312
def _repr_(self):
3313
r"""
3314
Returns a representation of self
3315
3316
TEST::
3317
3318
sage: iet.RauzyDiagram('a b','b a') #indirect doctest
3319
Rauzy diagram with 1 permutation
3320
sage: iet.RauzyDiagram('a b c','c b a') #indirect doctest
3321
Rauzy diagram with 3 permutations
3322
"""
3323
if len(self._succ) == 0:
3324
return "Empty Rauzy diagram"
3325
elif len(self._succ) == 1:
3326
return "Rauzy diagram with 1 permutation"
3327
else:
3328
return "Rauzy diagram with %d permutations" %(len(self._succ))
3329
3330
def __getitem__(self,p):
3331
r"""
3332
Returns the neighbors of p.
3333
3334
Just use the function vertex_to_permutation that must be defined
3335
in each child.
3336
3337
INPUT:
3338
3339
- ``p`` - a permutation in the Rauzy diagram
3340
3341
TESTS::
3342
3343
3344
sage: p = iet.Permutation('a b c','c b a')
3345
sage: p0 = iet.Permutation('a b c','c a b',alphabet="abc")
3346
sage: p1 = iet.Permutation('a c b','c b a',alphabet="abc")
3347
sage: r = p.rauzy_diagram()
3348
sage: r[p] == [p0, p1]
3349
True
3350
sage: r[p1] == [p1, p]
3351
True
3352
sage: r[p0] == [p, p0]
3353
True
3354
"""
3355
if not isinstance(p, self._element_class):
3356
raise ValueError, "Your element does not have the good type"
3357
3358
perm = self._permutation_to_vertex(p)
3359
return map(lambda x: self._vertex_to_permutation(x),
3360
self._succ[perm])
3361
3362
def __len__(self):
3363
r"""
3364
Deprecated use cardinality.
3365
3366
EXAMPLES::
3367
3368
sage: r = iet.RauzyDiagram('a b','b a')
3369
sage: r.cardinality()
3370
1
3371
"""
3372
return self.cardinality()
3373
3374
def cardinality(self):
3375
r"""
3376
Returns the number of permutations in this Rauzy diagram.
3377
3378
OUTPUT:
3379
3380
- `integer` - the number of vertices in the diagram
3381
3382
EXAMPLES::
3383
3384
sage: r = iet.RauzyDiagram('a b','b a')
3385
sage: r.cardinality()
3386
1
3387
sage: r = iet.RauzyDiagram('a b c','c b a')
3388
sage: r.cardinality()
3389
3
3390
sage: r = iet.RauzyDiagram('a b c d','d c b a')
3391
sage: r.cardinality()
3392
7
3393
"""
3394
return len(self._succ)
3395
3396
def complete(self, p):
3397
r"""
3398
Completion of the Rauzy diagram.
3399
3400
Add to the Rauzy diagram all permutations that are obtained by
3401
successive operations defined by edge_types(). The permutation must be
3402
of the same type and the same length as the one used for the creation.
3403
3404
INPUT:
3405
3406
- ``p`` - a permutation of Interval exchange transformation
3407
3408
Rauzy diagram is the reunion of all permutations that could be
3409
obtained with successive rauzy moves. This function just use the
3410
functions __getitem__ and has_rauzy_move and rauzy_move which must
3411
be defined for child and their corresponding permutation types.
3412
3413
TEST::
3414
3415
sage: r = iet.RauzyDiagram('a b c','c b a') #indirect doctest
3416
sage: r = iet.RauzyDiagram('a b c','c b a',left_induction=True) #indirect doctest
3417
sage: r = iet.RauzyDiagram('a b c','c b a',symmetric=True) #indirect doctest
3418
sage: r = iet.RauzyDiagram('a b c','c b a',lr_inversion=True) #indirect doctest
3419
sage: r = iet.RauzyDiagram('a b c','c b a',tb_inversion=True) #indirect doctest
3420
"""
3421
if p.__class__ is not self._element_class:
3422
raise ValueError, "your permutation is not of good type"
3423
3424
if len(p) != self._n:
3425
raise ValueError, "your permutation has not the good length"
3426
3427
pred = self._pred
3428
succ = self._succ
3429
p = self._permutation_to_vertex(p)
3430
perm = self._element
3431
l = []
3432
3433
if not succ.has_key(p):
3434
succ[p] = [None] * len(self._edge_types)
3435
pred[p] = [None] * len(self._edge_types)
3436
l.append(p)
3437
3438
while(l != []):
3439
p = l.pop()
3440
self._set_element(p)
3441
3442
for t,edge in enumerate(self._edge_types):
3443
if (not hasattr(perm, 'has_'+edge[0]) or
3444
getattr(perm, 'has_'+edge[0])(*(edge[1]))):
3445
q = getattr(perm,edge[0])(*(edge[1]))
3446
q = self._permutation_to_vertex(q)
3447
if not succ.has_key(q):
3448
succ[q] = [None] * len(self._edge_types)
3449
pred[q] = [None] * len(self._edge_types)
3450
l.append(q)
3451
succ[p][t] = q
3452
pred[q][t] = p
3453
3454
def path(self, *data):
3455
r"""
3456
Returns a path over this Rauzy diagram.
3457
3458
INPUT:
3459
3460
- ``initial_vertex`` - the initial vertex (starting point of the path)
3461
3462
- ``data`` - a sequence of edges
3463
3464
EXAMPLES::
3465
3466
sage: p = iet.Permutation('a b c','c b a')
3467
sage: r = p.rauzy_diagram()
3468
sage: g = r.path(p, 'top', 'bottom')
3469
"""
3470
if len(data) == 0:
3471
raise TypeError("Must be non empty")
3472
elif len(data) == 1 and isinstance(data[0], self.Path):
3473
return copy(data[0])
3474
return self.Path(self,*data)
3475
3476
def graph(self):
3477
r"""
3478
Returns the Rauzy diagram as a Graph object
3479
3480
The graph returned is more precisely a DiGraph (directed graph) with
3481
loops and multiedges allowed.
3482
3483
EXAMPLES::
3484
3485
sage: r = iet.RauzyDiagram('a b c','c b a')
3486
sage: r
3487
Rauzy diagram with 3 permutations
3488
sage: r.graph()
3489
Looped multi-digraph on 3 vertices
3490
3491
"""
3492
G = DiGraph(loops=True,multiedges=True)
3493
3494
for p,neighbours in self._succ.iteritems():
3495
p = self._vertex_to_permutation(p)
3496
for i,n in enumerate(neighbours):
3497
if n is not None:
3498
q = self._vertex_to_permutation(n)
3499
G.add_edge(p,q,i)
3500
3501
return G
3502
3503
class FlippedRauzyDiagram(RauzyDiagram):
3504
r"""
3505
Template for flipped Rauzy diagrams.
3506
3507
.. warning:
3508
3509
Internal class! Do not use directly!
3510
3511
AUTHORS:
3512
3513
- Vincent Delecroix (2009-09-29): initial version
3514
"""
3515
def complete(self, p, reducible=False):
3516
r"""
3517
Completion of the Rauzy diagram
3518
3519
Add all successors of p for defined operations in edge_types. Could be
3520
used for generating non (strongly) connected Rauzy diagrams. Sometimes,
3521
for flipped permutations, the maximal connected graph in all
3522
permutations is not strongly connected. Finding such components needs to
3523
call most than once the .complete() method.
3524
3525
INPUT:
3526
3527
- ``p`` - a permutation
3528
3529
- ``reducible`` - put or not reducible permutations
3530
3531
EXAMPLES::
3532
3533
sage: p = iet.Permutation('a b c','c b a',flips='a')
3534
sage: d = p.rauzy_diagram()
3535
sage: d
3536
Rauzy diagram with 3 permutations
3537
sage: p = iet.Permutation('a b c','c b a',flips='b')
3538
sage: d.complete(p)
3539
sage: d
3540
Rauzy diagram with 8 permutations
3541
sage: p = iet.Permutation('a b c','c b a',flips='a')
3542
sage: d.complete(p)
3543
sage: d
3544
Rauzy diagram with 8 permutations
3545
"""
3546
if p.__class__ is not self._element_class:
3547
raise ValueError, "your permutation is not of good type"
3548
3549
if len(p) != self._n:
3550
raise ValueError, "your permutation has not the good length"
3551
3552
pred = self._pred
3553
succ = self._succ
3554
p = self._permutation_to_vertex(p)
3555
l = []
3556
3557
if not succ.has_key(p):
3558
succ[p] = [None] * len(self._edge_types)
3559
pred[p] = [None] * len(self._edge_types)
3560
l.append(p)
3561
3562
while(l != []):
3563
p = l.pop()
3564
3565
for t,edge_type in enumerate(self._edge_types):
3566
perm = self._vertex_to_permutation(p)
3567
3568
if (not hasattr(perm,'has_' + edge_type[0]) or
3569
getattr(perm, 'has_' + edge_type[0])(*(edge_type[1]))):
3570
q = perm.rauzy_move(t)
3571
q = self._permutation_to_vertex(q)
3572
if reducible == True or perm.is_irreducible():
3573
if not succ.has_key(q):
3574
succ[q] = [None] * len(self._edge_types)
3575
pred[q] = [None] * len(self._edge_types)
3576
l.append(q)
3577
3578
succ[p][t] = q
3579
pred[q][t] = p
3580
3581
3582