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