Path: blob/master/src/sage/dynamics/interval_exchanges/template.py
8815 views
r"""1Permutations template23This file define high level operations on permutations (alphabet,4the different rauzy induction, ...) shared by reduced and labeled5permutations.67AUTHORS:89- Vincent Delecroix (2008-12-20): initial version1011.. TODO::1213- construct as options different string representations for a permutation1415- the two intervals: str16- the two intervals on one line: str_one_line17- the separatrix diagram: str_separatrix_diagram18- twin[0] and twin[1] for reduced permutation19- nothing (useful for Rauzy diagram)20"""21#*****************************************************************************22# Copyright (C) 2008 Vincent Delecroix <[email protected]>23#24# Distributed under the terms of the GNU General Public License (GPL)25# http://www.gnu.org/licenses/26#*****************************************************************************2728from sage.structure.sage_object import SageObject2930from copy import copy3132from sage.rings.integer import Integer33from sage.combinat.words.alphabet import Alphabet34from sage.graphs.graph import DiGraph35from sage.matrix.constructor import identity_matrix, matrix36from sage.misc.nested_class import NestedClassMetaclass373839def interval_conversion(interval=None):40r"""41Converts the argument in 0 or 1.4243INPUT:4445- ``winner`` - 'top' (or 't' or 0) or bottom (or 'b' or 1)4647OUTPUT:4849integer -- 0 or 15051TESTS:5253::5455sage: from sage.dynamics.interval_exchanges.template import interval_conversion56sage: interval_conversion('top')57058sage: interval_conversion('t')59060sage: interval_conversion(0)61062sage: interval_conversion('bottom')63164sage: interval_conversion('b')65166sage: interval_conversion(1)6716869.. Non admissible strings raise a ValueError::7071sage: interval_conversion('')72Traceback (most recent call last):73...74ValueError: the interval can not be the empty string75sage: interval_conversion('right')76Traceback (most recent call last):77...78ValueError: 'right' can not be converted to interval79sage: interval_conversion('top_right')80Traceback (most recent call last):81...82ValueError: 'top_right' can not be converted to interval83"""84if isinstance(interval, (int, Integer)):85if interval != 0 and interval != 1:86raise ValueError("interval must be 0 or 1")87return interval8889if isinstance(interval,str):90if interval == '':91raise ValueError("the interval can not be the empty string")92if 'top'.startswith(interval): return 093if 'bottom'.startswith(interval): return 194raise ValueError("'%s' can not be converted to interval" % (interval))9596raise TypeError("'%s' is not an admissible type" % (str(interval)))9798def side_conversion(side=None):99r"""100Converts the argument in 0 or -1.101102INPUT:103104- ``side`` - either 'left' (or 'l' or 0) or 'right' (or 'r' or -1)105106OUTPUT:107108integer -- 0 or -1109110TESTS:111112::113114sage: from sage.dynamics.interval_exchanges.template import side_conversion115sage: side_conversion('left')1160117sage: side_conversion('l')1180119sage: side_conversion(0)1200121sage: side_conversion('right')122-1123sage: side_conversion('r')124-1125sage: side_conversion(1)126-1127sage: side_conversion(-1)128-1129130.. Non admissible strings raise a ValueError::131132sage: side_conversion('')133Traceback (most recent call last):134...135ValueError: no empty string for side136sage: side_conversion('top')137Traceback (most recent call last):138...139ValueError: 'top' can not be converted to a side140"""141if side is None: return -1142143if isinstance(side,str):144if side == '':145raise ValueError("no empty string for side")146if 'left'.startswith(side): return 0147if 'right'.startswith(side): return -1148raise ValueError("'%s' can not be converted to a side" % (side))149150if isinstance(side, (int,Integer)):151if side != 0 and side != 1 and side != -1:152raise ValueError("side must be 0 or 1")153if side == 0: return 0154return -1155156raise TypeError("'%s' is not an admissible type" % (str(side)))157158def twin_list_iet(a=None):159r"""160Returns the twin list of intervals.161162The twin intervals is the correspondance between positions of labels in such163way that a[interval][position] is a[1-interval][twin[interval][position]]164165INPUT:166167- ``a`` - two lists of labels168169OUTPUT:170171list -- a list of two lists of integers172173TESTS::174175sage: from sage.dynamics.interval_exchanges.template import twin_list_iet176sage: twin_list_iet([['a','b','c'],['a','b','c']])177[[0, 1, 2], [0, 1, 2]]178sage: twin_list_iet([['a','b','c'],['a','c','b']])179[[0, 2, 1], [0, 2, 1]]180sage: twin_list_iet([['a','b','c'],['b','a','c']])181[[1, 0, 2], [1, 0, 2]]182sage: twin_list_iet([['a','b','c'],['b','c','a']])183[[2, 0, 1], [1, 2, 0]]184sage: twin_list_iet([['a','b','c'],['c','a','b']])185[[1, 2, 0], [2, 0, 1]]186sage: twin_list_iet([['a','b','c'],['c','b','a']])187[[2, 1, 0], [2, 1, 0]]188"""189if a is None : return [[],[]]190191twin = [[0]*len(a[0]), [0]*len(a[1])]192193for i in range(len(twin[0])) :194c = a[0][i]195j = a[1].index(c)196twin[0][i] = j197twin[1][j] = i198199return twin200201def twin_list_li(a=None):202r"""203Returns the twin list of intervals204205INPUT:206207- ``a`` - two lists of labels208209OUTPUT:210211list -- a list of two lists of couples of integers212213TESTS::214215sage: from sage.dynamics.interval_exchanges.template import twin_list_li216sage: twin_list_li([['a','a','b','b'],[]])217[[(0, 1), (0, 0), (0, 3), (0, 2)], []]218sage: twin_list_li([['a','a','b'],['b']])219[[(0, 1), (0, 0), (1, 0)], [(0, 2)]]220sage: twin_list_li([['a','a'],['b','b']])221[[(0, 1), (0, 0)], [(1, 1), (1, 0)]]222sage: twin_list_li([['a'], ['a','b','b']])223[[(1, 0)], [(0, 0), (1, 2), (1, 1)]]224sage: twin_list_li([[], ['a','a','b','b']])225[[], [(1, 1), (1, 0), (1, 3), (1, 2)]]226"""227if a is None: return [[],[]]228229twin = [230[(0,j) for j in range(len(a[0]))],231[(1,j) for j in range(len(a[1]))]]232233for i in (0,1):234for j in range(len(twin[i])) :235if twin[i][j] == (i,j) :236if a[i][j] in a[i][j+1:] :237# two up or two down238j2 = (a[i][j+1:]).index(a[i][j]) + j + 1239twin[i][j] = (i,j2)240twin[i][j2] = (i,j)241else :242# one up, one down (here i=0)243j2 = a[1].index(a[i][j])244twin[0][j] = (1,j2)245twin[1][j2] = (0,j)246247return twin248249class Permutation(SageObject):250r"""251Template for all permutations.252253.. warning::254255Internal class! Do not use directly!256257This class implement generic algorithm (stratum, connected component, ...)258and unfies all its children.259"""260def _repr_(self):261r"""262Representation method of self.263264Apply the function str to _repr_type(_repr_options) if _repr_type is265callable and _repr_type else.266267TESTS:268269::270271sage: p = iet.Permutation('a b c','c b a')272sage: p._repr_type = 'str'273sage: p._repr_options = ('\n',)274sage: p #indirect doctest275a b c276c b a277sage: p._repr_options = (' / ',)278sage: p #indirect doctest279a b c / c b a280281::282283sage: p._repr_type = 'separatrix_diagram'284sage: p._repr_options = (False,)285sage: p #indirect doctest286[[('c', 'a'), 'b'], ['b', ('c', 'a')]]287sage: p._repr_options = (True,)288sage: p289[[(('c', 'a'), 'L'), ('b', 'R')], [('b', 'L'), (('c', 'a'), 'R')]]290291::292293sage: p._repr_type = '_twin'294sage: p #indirect doctest295[[2, 1, 0], [2, 1, 0]]296"""297if self._repr_type is None:298return ''299300elif self._repr_type == 'reduced':301return ''.join(map(str,self[1]))302303else:304f = getattr(self, self._repr_type)305if callable(f):306return str(f(*self._repr_options))307else:308return str(f)309310def str(self, sep= "\n"):311r"""312A string representation of the generalized permutation.313314INPUT:315316- ``sep`` - (default: '\n') a separator for the two intervals317318OUTPUT:319320string -- the string that represents the permutation321322323EXAMPLES:324325For permutations of iet::326327sage: p = iet.Permutation('a b c','c b a')328sage: p.str()329'a b c\nc b a'330sage: p.str(sep=' | ')331'a b c | c b a'332333..the permutation can be rebuilt from the standard string::334335sage: p == iet.Permutation(p.str())336True337338For permutations of li::339340sage: p = iet.GeneralizedPermutation('a b b','c c a')341sage: p.str()342'a b b\nc c a'343sage: p.str(sep=' | ')344'a b b | c c a'345346..the generalized permutation can be rebuilt from the standard string::347348sage: p == iet.GeneralizedPermutation(p.str())349True350351"""352l = self.list()353s0 = ' '.join(map(str,l[0]))354s1 = ' '.join(map(str,l[1]))355return s0 + sep + s1356357_repr_type = 'str'358_repr_options = ("\n",)359360def _set_alphabet(self, alphabet):361r"""362Sets the alphabet of self.363364TESTS:365366sage: p = iet.GeneralizedPermutation('a a','b b')367sage: p.alphabet([0,1]) #indirect doctest368sage: p.alphabet() == Alphabet([0,1])369True370sage: p3710 03721 1373sage: p.alphabet("cd") #indirect doctest374sage: p.alphabet() == Alphabet(['c','d'])375True376sage: p377c c378d d379380Tests with reduced permutations::381382sage: p = iet.Permutation('a b','b a',reduced=True)383sage: p.alphabet([0,1]) #indirect doctest384sage: p.alphabet() == Alphabet([0,1])385True386sage: p3870 13881 0389sage: p.alphabet("cd") #indirect doctest390sage: p.alphabet() == Alphabet(['c','d'])391True392sage: p393c d394d c395396::397398sage: p = iet.GeneralizedPermutation('a a','b b',reduced=True)399sage: p.alphabet([0,1]) #indirect doctest400sage: p.alphabet() == Alphabet([0,1])401True402sage: p4030 04041 1405sage: p.alphabet("cd") #indirect doctest406sage: p.alphabet() == Alphabet(['c','d'])407True408sage: p409c c410d d411"""412alphabet = Alphabet(alphabet)413if alphabet.cardinality() < len(self):414raise ValueError("Your alphabet has not enough letters")415self._alphabet = alphabet416417def alphabet(self, data=None):418r"""419Manages the alphabet of self.420421If there is no argument, the method returns the alphabet used. If the422argument could be converted to an alphabet, this alphabet will be used.423424INPUT:425426- ``data`` - None or something that could be converted to an alphabet427428429OUTPUT:430431-- either None or the current alphabet432433434EXAMPLES::435436sage: p = iet.Permutation('a b','a b')437sage: p.alphabet([0,1])438sage: p.alphabet() == Alphabet([0,1])439True440sage: p4410 14420 1443sage: p.alphabet("cd")444sage: p.alphabet() == Alphabet(['c','d'])445True446sage: p447c d448c d449"""450if data is None:451return self._alphabet452else:453self._set_alphabet(data)454455def letters(self):456r"""457Returns the list of letters of the alphabet used for representation.458459The letters used are not necessarily the whole alphabet (for example if460the alphabet is infinite).461462OUTPUT:463464-- a list of labels465466467EXAMPLES::468469sage: p = iet.Permutation([1,2],[2,1])470sage: p.alphabet(Alphabet(name="NN"))471sage: p4720 14731 0474sage: p.letters()475[0, 1]476"""477return map(self._alphabet.unrank, range(len(self)))478479def left_right_inverse(self):480r"""481Returns the left-right inverse.482483You can also use the shorter .lr_inverse()484485OUTPUT:486487-- a permutation488489490EXAMPLES::491492sage: p = iet.Permutation('a b c','c a b')493sage: p.left_right_inverse()494c b a495b a c496sage: p = iet.Permutation('a b c d','c d a b')497sage: p.left_right_inverse()498d c b a499b a d c500501::502503sage: p = iet.GeneralizedPermutation('a a','b b c c')504sage: p.left_right_inverse()505a a506c c b b507508::509510sage: p = iet.Permutation('a b c','c b a',reduced=True)511sage: p.left_right_inverse() == p512True513sage: p = iet.Permutation('a b c','c a b',reduced=True)514sage: q = p.left_right_inverse()515sage: q == p516False517sage: q518a b c519b c a520521::522523sage: p = iet.GeneralizedPermutation('a a','b b c c',reduced=True)524sage: p.left_right_inverse() == p525True526sage: p = iet.GeneralizedPermutation('a b b','c c a',reduced=True)527sage: q = p.left_right_inverse()528sage: q == p529False530sage: q531a a b532b c c533534TESTS::535536sage: p = iet.GeneralizedPermutation('a a','b b')537sage: p.left_right_inverse()538a a539b b540sage: p is p.left_right_inverse()541False542sage: p == p.left_right_inverse()543True544"""545result = copy(self)546result._reversed()547return result548549lr_inverse = left_right_inverse550vertical_inverse = left_right_inverse551552def top_bottom_inverse(self):553r"""554Returns the top-bottom inverse.555556You can use also use the shorter .tb_inverse().557558OUTPUT:559560-- a permutation561562563EXAMPLES::564565sage: p = iet.Permutation('a b','b a')566sage: p.top_bottom_inverse()567b a568a b569sage: p = iet.Permutation('a b','b a',reduced=True)570sage: p.top_bottom_inverse() == p571True572573::574575sage: p = iet.Permutation('a b c d','c d a b')576sage: p.top_bottom_inverse()577c d a b578a b c d579580TESTS::581582sage: p = iet.Permutation('a b','a b')583sage: p == p.top_bottom_inverse()584True585sage: p is p.top_bottom_inverse()586False587sage: p = iet.GeneralizedPermutation('a a','b b',reduced=True)588sage: p == p.top_bottom_inverse()589True590sage: p is p.top_bottom_inverse()591False592"""593result = copy(self)594result._inversed()595return result596597tb_inverse = top_bottom_inverse598horizontal_inverse = top_bottom_inverse599600def symmetric(self):601r"""602Returns the symmetric permutation.603604The symmetric permutation is the composition of the top-bottom605inversion and the left-right inversion (which are geometrically606orientation reversing).607608OUTPUT:609610-- a permutation611612613EXAMPLES::614615sage: p = iet.Permutation("a b c","c b a")616sage: p.symmetric()617a b c618c b a619sage: q = iet.Permutation("a b c d","b d a c")620sage: q.symmetric()621c a d b622d c b a623624::625626sage: p = iet.Permutation('a b c d','c a d b')627sage: q = p.symmetric()628sage: q1 = p.tb_inverse().lr_inverse()629sage: q2 = p.lr_inverse().tb_inverse()630sage: q == q1631True632sage: q == q2633True634635636TESTS:637638::639640sage: p = iet.GeneralizedPermutation('a a b','b c c',reduced=True)641sage: q = p.symmetric()642sage: q1 = p.tb_inverse().lr_inverse()643sage: q2 = p.lr_inverse().tb_inverse()644sage: q == q1645True646sage: q == q2647True648649::650651sage: p = iet.GeneralizedPermutation('a a b','b c c',reduced=True,flips='a')652sage: q = p.symmetric()653sage: q1 = p.tb_inverse().lr_inverse()654sage: q2 = p.lr_inverse().tb_inverse()655sage: q == q1656True657sage: q == q2658True659660"""661res = copy(self)662res._inversed()663res._reversed()664return res665666def has_rauzy_move(self, winner='top', side=None):667r"""668Tests the legality of a Rauzy move.669670INPUT:671672- ``winner`` - 'top' or 'bottom' corresponding to the interval673674- ``side`` - 'left' or 'right' (defaut)675676677OUTPUT:678679-- a boolean680681682EXAMPLES::683684sage: p = iet.Permutation('a b','a b')685sage: p.has_rauzy_move('top','right')686False687sage: p.has_rauzy_move('bottom','right')688False689sage: p.has_rauzy_move('top','left')690False691sage: p.has_rauzy_move('bottom','left')692False693694::695696sage: p = iet.Permutation('a b c','b a c')697sage: p.has_rauzy_move('top','right')698False699sage: p.has_rauzy_move('bottom', 'right')700False701sage: p.has_rauzy_move('top','left')702True703sage: p.has_rauzy_move('bottom','left')704True705706::707708sage: p = iet.Permutation('a b','b a')709sage: p.has_rauzy_move('top','right')710True711sage: p.has_rauzy_move('bottom','right')712True713sage: p.has_rauzy_move('top','left')714True715sage: p.has_rauzy_move('bottom','left')716True717"""718winner = interval_conversion(winner)719side = side_conversion(side)720721if side == -1:722return self.has_right_rauzy_move(winner)723elif side == 0:724return self.lr_inverse().has_right_rauzy_move(winner)725726def rauzy_move(self, winner, side='right', iteration=1):727r"""728Returns the permutation after a Rauzy move.729730INPUT:731732- ``winner`` - 'top' or 'bottom' interval733734- ``side`` - 'right' or 'left' (defaut: 'right') corresponding735to the side on which the Rauzy move must be performed.736737- ``iteration`` - a non negative integer738739740OUTPUT:741742- a permutation743744745TESTS::746747sage: p = iet.Permutation('a b','b a')748sage: p.rauzy_move(winner=0, side='right') == p749True750sage: p.rauzy_move(winner=1, side='right') == p751True752sage: p.rauzy_move(winner=0, side='left') == p753True754sage: p.rauzy_move(winner=1, side='left') == p755True756757::758759sage: p = iet.Permutation('a b c','c b a')760sage: p.rauzy_move(winner=0, side='right')761a b c762c a b763sage: p.rauzy_move(winner=1, side='right')764a c b765c b a766sage: p.rauzy_move(winner=0, side='left')767a b c768b c a769sage: p.rauzy_move(winner=1, side='left')770b a c771c b a772"""773winner = interval_conversion(winner)774side = side_conversion(side)775776if side == -1:777tmp = self778for k in range(iteration):779tmp = tmp.right_rauzy_move(winner)780return tmp781782elif side == 0:783tmp = self784for k in range(iteration):785tmp = tmp.left_rauzy_move(winner)786return tmp787788class PermutationIET(Permutation):789"""790Template for permutation from Interval Exchange Transformation.791792.. warning::793794Internal class! Do not use directly!795796AUTHOR:797798- Vincent Delecroix (2008-12-20): initial version799"""800def _init_alphabet(self,a) :801r"""802Initializes the alphabet from intervals.803804TESTS::805806sage: p = iet.Permutation('a b c d','d c a b') #indirect doctest807sage: p.alphabet() == Alphabet(['a', 'b', 'c', 'd'])808True809sage: p = iet.Permutation([0,1,2],[1,0,2],reduced=True) #indirect doctest810sage: p.alphabet() == Alphabet([0,1,2])811True812"""813self._alphabet = Alphabet(a[0])814815def is_irreducible(self, return_decomposition=False) :816r"""817Tests the irreducibility.818819An abelian permutation p = (p0,p1) is reducible if:820set(p0[:i]) = set(p1[:i]) for an i < len(p0)821822OUTPUT:823824- a boolean825826EXAMPLES::827828sage: p = iet.Permutation('a b c', 'c b a')829sage: p.is_irreducible()830True831832sage: p = iet.Permutation('a b c', 'b a c')833sage: p.is_irreducible()834False835"""836s0, s1 = 0, 0837for i in range(len(self)-1) :838s0 += i839s1 += self._twin[0][i]840if s0 == s1 :841if return_decomposition :842return False, (self[0][:i+1], self[0][i+1:], self[1][:i+1], self[1][i+1:])843return False844if return_decomposition:845return True, (self[0],[],self[1],[])846return True847848def decompose(self):849r"""850Returns the decomposition of self.851852OUTPUT:853854-- a list of permutations855856857EXAMPLES::858859sage: p = iet.Permutation('a b c','c b a').decompose()[0]860sage: p861a b c862c b a863864::865866sage: p1,p2,p3 = iet.Permutation('a b c d e','b a c e d').decompose()867sage: p1868a b869b a870sage: p2871c872c873sage: p3874d e875e d876"""877l = []878test, t = self.is_irreducible(return_decomposition=True)879l.append(self.__class__((t[0],t[2])))880881while not test:882q = self.__class__((t[1],t[3]))883test, t = q.is_irreducible(return_decomposition=True)884l.append(self.__class__((t[0],t[2])))885886return l887888def intersection_matrix(self):889r"""890Returns the intersection matrix.891892This `d*d` antisymmetric matrix is given by the rule :893894.. math::895896m_{ij} = \begin{cases}8971 & \text{$i < j$ and $\pi(i) > \pi(j)$} \\898-1 & \text{$i > j$ and $\pi(i) < \pi(j)$} \\8990 & \text{else}900\end{cases}901902OUTPUT:903904- a matrix905906EXAMPLES::907908sage: p = iet.Permutation('a b c d','d c b a')909sage: p.intersection_matrix()910[ 0 1 1 1]911[-1 0 1 1]912[-1 -1 0 1]913[-1 -1 -1 0]914915::916917sage: p = iet.Permutation('1 2 3 4 5','5 3 2 4 1')918sage: p.intersection_matrix()919[ 0 1 1 1 1]920[-1 0 1 0 1]921[-1 -1 0 0 1]922[-1 0 0 0 1]923[-1 -1 -1 -1 0]924"""925n = self.length_top()926m = matrix(n)927for i in range(n):928for j in range(i,n):929if self._twin[0][i] > self._twin[0][j]:930m[i,j] = 1931m[j,i] = -1932return m933934def attached_out_degree(self):935r"""936Returns the degree of the singularity at the left of the interval.937938OUTPUT:939940-- a positive integer941942943EXAMPLES::944945sage: p1 = iet.Permutation('a b c d e f g','d c g f e b a')946sage: p2 = iet.Permutation('a b c d e f g','e d c g f b a')947sage: p1.attached_out_degree()9483949sage: p2.attached_out_degree()9501951"""952left_corner = ((self[1][0], self[0][0]), 'L')953for s in self.separatrix_diagram(side=True):954if left_corner in s:955return len(s)/2 - 1956957def attached_in_degree(self):958r"""959Returns the degree of the singularity at the right of the interval.960961OUTPUT:962963-- a positive integer964965966EXAMPLES::967968sage: p1 = iet.Permutation('a b c d e f g','d c g f e b a')969sage: p2 = iet.Permutation('a b c d e f g','e d c g f b a')970sage: p1.attached_in_degree()9711972sage: p2.attached_in_degree()9733974"""975right_corner = ((self[0][-1], self[1][-1]), 'R')976977for s in self.separatrix_diagram(side=True):978if right_corner in s:979return len(s)/2 - 1980981def attached_type(self):982r"""983Return the singularity degree attached on the left and the right.984985OUTPUT:986987``([degre], angle_parity)`` -- if the same singularity is attached on the left and right988989``([left_degree, right_degree], 0)`` -- the degrees at the left and the right which are different singularitites990991EXAMPLES:992993With two intervals::994995sage: p = iet.Permutation('a b','b a')996sage: p.attached_type()997([0], 1)998999With three intervals::10001001sage: p = iet.Permutation('a b c','b c a')1002sage: p.attached_type()1003([0], 1)10041005sage: p = iet.Permutation('a b c','c a b')1006sage: p.attached_type()1007([0], 1)10081009sage: p = iet.Permutation('a b c','c b a')1010sage: p.attached_type()1011([0, 0], 0)10121013With four intervals::10141015sage: p = iet.Permutation('1 2 3 4','4 3 2 1')1016sage: p.attached_type()1017([2], 0)1018"""1019left_corner = ((self[1][0], self[0][0]), 'L')1020right_corner = ((self[0][-1], self[1][-1]), 'R')10211022l = self.separatrix_diagram(side=True)10231024for s in l:1025if left_corner in s and right_corner in s:1026i1 = s.index(left_corner)1027i2 = s.index(right_corner)1028return ([len(s)/2-1], ((i2-i1+1)/2) % 2)1029elif left_corner in s:1030left_degree = len(s)/2-11031elif right_corner in s:1032right_degree = len(s)/2-110331034return ([left_degree,right_degree], 0)10351036def separatrix_diagram(self,side=False):1037r"""1038Returns the separatrix diagram of the permutation.10391040INPUT:10411042- ``side`` - boolean104310441045OUTPUT:10461047-- a list of lists104810491050EXAMPLES::10511052sage: iet.Permutation([0, 1], [1, 0]).separatrix_diagram()1053[[(1, 0), (1, 0)]]10541055::10561057sage: iet.Permutation('a b c d','d c b a').separatrix_diagram()1058[[('d', 'a'), 'b', 'c', ('d', 'a'), 'b', 'c']]1059"""1060separatrices = range(len(self)) # bottom intervals1061labels = self[1] # their labels10621063singularities = []106410651066twin = self._twin1067n = len(self)-110681069while separatrices != []:1070start = separatrices.pop(0)1071separatrix = start1072if side:1073singularity = [(labels[start],'L')]1074else:1075singularity = [labels[start]]10761077while True:1078if separatrix == 0:1079separatrix = twin[0][0]1080if side:1081a = singularity.pop()[0]1082else:1083a = singularity.pop()1084if side:1085singularity.append(((a,labels[separatrix]), 'L'))1086else:1087singularity.append((a,labels[separatrix]))10881089if separatrix == start:1090singularities.append(singularity)1091break10921093del separatrices[separatrices.index(separatrix)]10941095else:1096separatrix -= 11097if side:1098singularity.append((labels[separatrix],'R'))1099else:1100singularity.append(labels[separatrix])11011102if separatrix == twin[0][n] :1103separatrix = n1104if side:1105a = singularity.pop()[0]1106else:1107a = singularity.pop()1108if side:1109singularity.append(((a,labels[separatrix]),'R'))1110else:1111singularity.append((a,labels[separatrix]))11121113separatrix = twin[0][twin[1][separatrix]+1]11141115if separatrix == start:1116singularities.append(singularity)1117break11181119elif separatrix != twin[0][0]:1120del separatrices[separatrices.index(separatrix)]1121if side:1122singularity.append((labels[separatrix],'L'))1123else:1124singularity.append(labels[separatrix])11251126return singularities11271128def stratum(self, marked_separatrix='no'):1129r"""1130Returns the strata in which any suspension of this permutation lives.11311132OUTPUT:11331134- a stratum of Abelian differentials11351136EXAMPLES::11371138sage: p = iet.Permutation('a b c', 'c b a')1139sage: print p.stratum()1140H(0, 0)11411142sage: p = iet.Permutation('a b c d', 'd a b c')1143sage: print p.stratum()1144H(0, 0, 0)11451146sage: p = iet.Permutation(range(9), [8,5,2,7,4,1,6,3,0])1147sage: print p.stratum()1148H(1, 1, 1, 1)11491150You can specify that you want to attach the singularity on the left (or1151on the right) with the option marked_separatrix::11521153sage: a = 'a b c d e f g h i j'1154sage: b3 = 'd c g f e j i h b a'1155sage: b2 = 'd c e g f j i h b a'1156sage: b1 = 'e d c g f h j i b a'1157sage: p3 = iet.Permutation(a, b3)1158sage: p3.stratum()1159H(3, 2, 1)1160sage: p3.stratum(marked_separatrix='out')1161H^out(3, 2, 1)1162sage: p2 = iet.Permutation(a, b2)1163sage: p2.stratum()1164H(3, 2, 1)1165sage: p2.stratum(marked_separatrix='out')1166H^out(2, 3, 1)1167sage: p1 = iet.Permutation(a, b1)1168sage: p1.stratum()1169H(3, 2, 1)1170sage: p1.stratum(marked_separatrix='out')1171H^out(1, 3, 2)11721173AUTHORS:1174- Vincent Delecroix (2008-12-20)1175"""1176from sage.dynamics.flat_surfaces.strata import AbelianStratum11771178if not self.is_irreducible():1179return map(lambda x: x.stratum(marked_separatrix), self.decompose())11801181if len(self) == 1:1182return AbelianStratum([])11831184singularities = [len(x)/2 - 1 for x in self.separatrix_diagram()]11851186return AbelianStratum(singularities,marked_separatrix=marked_separatrix)11871188def genus(self) :1189r"""1190Returns the genus corresponding to any suspension of the permutation.11911192OUTPUT:11931194-- a positive integer11951196EXAMPLES::11971198sage: p = iet.Permutation('a b c', 'c b a')1199sage: p.genus()1200112011202::12031204sage: p = iet.Permutation('a b c d','d c b a')1205sage: p.genus()1206212071208REFERENCES:1209Veech1210"""1211return self.stratum().genus()12121213def arf_invariant(self):1214r"""1215Returns the Arf invariant of the suspension of self.12161217OUTPUT:12181219integer -- 0 or 112201221EXAMPLES:12221223Permutations from the odd and even component of H(2,2,2)::12241225sage: a = range(10)1226sage: b1 = [3,2,4,6,5,7,9,8,1,0]1227sage: b0 = [6,5,4,3,2,7,9,8,1,0]1228sage: p1 = iet.Permutation(a,b1)1229sage: print p1.arf_invariant()123011231sage: p0 = iet.Permutation(a,b0)1232sage: print p0.arf_invariant()1233012341235Permutations from the odd and even component of H(4,4)::12361237sage: a = range(11)1238sage: b1 = [3,2,5,4,6,8,7,10,9,1,0]1239sage: b0 = [5,4,3,2,6,8,7,10,9,1,0]1240sage: p1 = iet.Permutation(a,b1)1241sage: print p1.arf_invariant()124211243sage: p0 = iet.Permutation(a,b0)1244sage: print p0.arf_invariant()1245012461247REFERENCES:12481249[Jo80] D. Johnson, "Spin structures and quadratic forms on surfaces", J.1250London Math. Soc (2), 22, 1980, 365-37312511252[KoZo03] M. Kontsevich, A. Zorich "Connected components of the moduli1253spaces of Abelian differentials with prescribed singularities",1254Inventiones Mathematicae, 153, 2003, 631-6781255"""1256M = self.intersection_matrix()1257F, C = M.symplectic_form()12581259g = F.rank()/21260n = F.ncols()12611262s = 01263for i in range(g):1264a = C.row(i)12651266a_indices = []1267for k in xrange(n):1268if a[k] != 0: a_indices.append(k)12691270t_a = len(a_indices) % 21271for j1 in xrange(len(a_indices)):1272for j2 in xrange(j1+1,len(a_indices)):1273t_a = (t_a + M[a_indices[j1], a_indices[j2]]) % 212741275b = C.row(g+i)12761277b_indices = []1278for k in xrange(n):1279if b[k] != 0: b_indices.append(k)12801281t_b = len(b_indices) % 21282for j1 in xrange(len(b_indices)):1283for j2 in xrange(j1+1,len(b_indices)):1284t_b = (t_b + M[b_indices[j1],b_indices[j2]]) % 212851286s = (s + t_a * t_b) % 212871288return s12891290def connected_component(self,marked_separatrix='no'):1291r"""1292Returns a connected components of a stratum.12931294EXAMPLES:12951296Permutations from the stratum H(6)::12971298sage: a = range(8)1299sage: b_hyp = [7,6,5,4,3,2,1,0]1300sage: b_odd = [3,2,5,4,7,6,1,0]1301sage: b_even = [5,4,3,2,7,6,1,0]1302sage: p_hyp = iet.Permutation(a, b_hyp)1303sage: p_odd = iet.Permutation(a, b_odd)1304sage: p_even = iet.Permutation(a, b_even)1305sage: print p_hyp.connected_component()1306H_hyp(6)1307sage: print p_odd.connected_component()1308H_odd(6)1309sage: print p_even.connected_component()1310H_even(6)13111312Permutations from the stratum H(4,4)::13131314sage: a = range(11)1315sage: b_hyp = [10,9,8,7,6,5,4,3,2,1,0]1316sage: b_odd = [3,2,5,4,6,8,7,10,9,1,0]1317sage: b_even = [5,4,3,2,6,8,7,10,9,1,0]1318sage: p_hyp = iet.Permutation(a,b_hyp)1319sage: p_odd = iet.Permutation(a,b_odd)1320sage: p_even = iet.Permutation(a,b_even)1321sage: p_hyp.stratum() == AbelianStratum(4,4)1322True1323sage: print p_hyp.connected_component()1324H_hyp(4, 4)1325sage: p_odd.stratum() == AbelianStratum(4,4)1326True1327sage: print p_odd.connected_component()1328H_odd(4, 4)1329sage: p_even.stratum() == AbelianStratum(4,4)1330True1331sage: print p_even.connected_component()1332H_even(4, 4)13331334As for stratum you can specify that you want to attach the singularity1335on the left of the interval using the option marked_separatrix::13361337sage: a = [1,2,3,4,5,6,7,8,9]1338sage: b4_odd = [4,3,6,5,7,9,8,2,1]1339sage: b4_even = [6,5,4,3,7,9,8,2,1]1340sage: b2_odd = [4,3,5,7,6,9,8,2,1]1341sage: b2_even = [7,6,5,4,3,9,8,2,1]1342sage: p4_odd = iet.Permutation(a,b4_odd)1343sage: p4_even = iet.Permutation(a,b4_even)1344sage: p2_odd = iet.Permutation(a,b2_odd)1345sage: p2_even = iet.Permutation(a,b2_even)1346sage: p4_odd.connected_component(marked_separatrix='out')1347H_odd^out(4, 2)1348sage: p4_even.connected_component(marked_separatrix='out')1349H_even^out(4, 2)1350sage: p2_odd.connected_component(marked_separatrix='out')1351H_odd^out(2, 4)1352sage: p2_even.connected_component(marked_separatrix='out')1353H_even^out(2, 4)1354sage: p2_odd.connected_component() == p4_odd.connected_component()1355True1356sage: p2_odd.connected_component('out') == p4_odd.connected_component('out')1357False1358"""1359from sage.dynamics.flat_surfaces.strata import (HypCCA,1360OddCCA, EvenCCA)13611362if not self.is_irreducible():1363return map(lambda x: x.connected_component(marked_separatrix),1364self.decompose())13651366stratum = self.stratum(marked_separatrix=marked_separatrix)1367cc = stratum._cc13681369if len(cc) == 1:1370return stratum.connected_components()[0]13711372if HypCCA in cc:1373if self.is_hyperelliptic():1374return HypCCA(stratum)1375else:1376cc = cc[1:]13771378if len(cc) == 1:1379return cc[0](stratum)13801381else:1382spin = self.arf_invariant()1383if spin == 0:1384return EvenCCA(stratum)1385else:1386return OddCCA(stratum)13871388def order_of_rauzy_action(self, winner, side=None):1389r"""1390Returns the order of the action of a Rauzy move.13911392INPUT:13931394- ``winner`` - string ``'top'`` or ``'bottom'``13951396- ``side`` - string ``'left'`` or ``'right'``13971398OUTPUT:13991400An integer corresponding to the order of the Rauzy action.14011402EXAMPLES::14031404sage: p = iet.Permutation('a b c d','d a c b')1405sage: p.order_of_rauzy_action('top', 'right')140631407sage: p.order_of_rauzy_action('bottom', 'right')140821409sage: p.order_of_rauzy_action('top', 'left')141011411sage: p.order_of_rauzy_action('bottom', 'left')141231413"""1414winner = interval_conversion(winner)1415side = side_conversion(side)14161417if side == -1:1418return self.length(winner) - self._twin[winner][-1] - 11419elif side == 0:1420return self._twin[winner][0]14211422def erase_marked_points(self):1423r"""1424Returns a permutation equivalent to self but without marked points.14251426EXAMPLES::14271428sage: a = iet.Permutation('a b1 b2 c d', 'd c b1 b2 a')1429sage: a.erase_marked_points()1430a b1 c d1431d c b1 a1432"""1433res = copy(self)14341435l = res.list()1436left_corner = ((l[1][0], l[0][0]), 'L')1437right_corner = ((l[0][-1], l[1][-1]), 'R')14381439s = res.separatrix_diagram(side=True)1440lengths = map(len, s)14411442while 2 in lengths:1443if lengths == [2]:1444return res14451446i = lengths.index(2)1447t = s[i]1448if t[0] == left_corner or t[0] == right_corner:1449letter = t[1][0]1450else:1451letter = t[0][0]14521453res = res.erase_letter(letter)14541455l = res.list()14561457s = res.separatrix_diagram(side=True)1458lengths = map(len, s)14591460return res14611462def is_hyperelliptic(self):1463r"""1464Returns True if the permutation is in the class of the symmetric1465permutations (with eventual marked points).14661467This is equivalent to say that the suspension lives in an hyperelliptic1468stratum of Abelian differentials H_hyp(2g-2) or H_hyp(g-1, g-1) with1469some marked points.14701471EXAMPLES::14721473sage: iet.Permutation('a b c d','d c b a').is_hyperelliptic()1474True1475sage: iet.Permutation('0 1 2 3 4 5','5 2 1 4 3 0').is_hyperelliptic()1476False14771478REFERENCES:14791480Gerard Rauzy, "Echanges d'intervalles et transformations induites",1481Acta Arith. 34, no. 3, 203-212, 198014821483M. Kontsevich, A. Zorich "Connected components of the moduli space1484of Abelian differentials with prescripebd singularities" Invent. math.1485153, 631-678 (2003)1486"""1487test = self.erase_marked_points()14881489n = test.length_top()1490cylindric = test.cylindric()1491return cylindric._twin[0] == range(n-1,-1,-1)14921493def cylindric(self):1494r"""1495Returns a permutation in the Rauzy class such that14961497twin[0][-1] == 01498twin[1][-1] == 014991500TESTS::15011502sage: p = iet.Permutation('a b c','c b a')1503sage: p.cylindric() == p1504True1505sage: p = iet.Permutation('a b c d','b d a c')1506sage: q = p.cylindric()1507sage: q[0][0] == q[1][-1]1508True1509sage: q[1][0] == q[1][0]1510True1511"""1512tmp = copy(self)1513n = self.length(0)15141515a0 = tmp._twin[0][-1]1516a1 = tmp._twin[1][-1]1517p_min = min(a0,a1)15181519while p_min > 0:1520if p_min == a0:1521k_min = min(tmp._twin[1][a0+1:])1522k = n - tmp._twin[1].index(k_min) - 115231524tmp = tmp.rauzy_move(0, iteration=k)15251526else:1527k_min = min(tmp._twin[0][a1+1:])1528k = n - tmp._twin[0].index(k_min) - 115291530tmp = tmp.rauzy_move(1, iteration=k)15311532a0 = tmp._twin[0][-1]1533a1 = tmp._twin[1][-1]1534p_min = min(a0,a1)15351536if a0 == 0:1537k = n - tmp._twin[1].index(0) - 11538tmp = tmp.rauzy_move(0, iteration = k)15391540else:1541k = n - tmp._twin[0].index(0) - 11542tmp = tmp.rauzy_move(1, iteration=k)15431544return tmp15451546def is_cylindric(self):1547r"""1548Returns True if the permutation is Rauzy_1n.15491550A permutation is cylindric if 1 and n are exchanged.15511552EXAMPLES::15531554sage: iet.Permutation('1 2 3','3 2 1').is_cylindric()1555True1556sage: iet.Permutation('1 2 3','2 1 3').is_cylindric()1557False1558"""1559return self._twin[0][-1] == 0 and self._twin[1][-1] == 015601561def to_permutation(self):1562r"""1563Returns the permutation as an element of the symetric group.15641565EXAMPLES::15661567sage: p = iet.Permutation('a b c','c b a')1568sage: p.to_permutation()1569[3, 2, 1]15701571::15721573sage: p = Permutation([2,4,1,3])1574sage: q = iet.Permutation(p)1575sage: q.to_permutation() == p1576True1577"""1578from sage.combinat.permutation import Permutation1579return Permutation(map(lambda x: x+1,self._twin[1]))15801581class PermutationLI(Permutation):1582r"""1583Template for quadratic permutation.15841585.. warning::15861587Internal class! Do not use directly!15881589AUTHOR:15901591- Vincent Delecroix (2008-12-20): initial version1592"""1593def _init_twin(self,a):1594r"""1595Initialization of the twin data.15961597TEST::15981599sage: p = iet.GeneralizedPermutation('a a','b b',reduced=True) #indirect doctest1600sage: p._twin1601[[(0, 1), (0, 0)], [(1, 1), (1, 0)]]1602"""1603# creation of the twin1604self._twin = [[],[]]1605l = [[(0,j) for j in range(len(a[0]))],[(1,j) for j in range(len(a[1]))]]1606for i in range(2) :1607for j in range(len(l[i])) :1608if l[i][j] == (i,j) :1609if a[i][j] in a[i][j+1:] :1610# two up or two down1611j2 = (a[i][j+1:]).index(a[i][j]) + j + 11612l[i][j] = (i,j2)1613l[i][j2] = (i,j)1614else :1615# one up, one down (here i=0)1616j2 = a[1].index(a[i][j])1617l[0][j] = (1,j2)1618l[1][j2] = (0,j)16191620self._twin[0] = l[0]1621self._twin[1] = l[1]16221623def _init_alphabet(self, intervals) :1624r"""1625Intialization procedure of the alphabet of self from intervals list16261627TEST::16281629sage: p = iet.GeneralizedPermutation('a a','b b') #indirect doctest1630sage: p.alphabet()1631{'a', 'b'}1632"""1633tmp_alphabet = []1634for letter in intervals[0] + intervals[1] :1635if letter not in tmp_alphabet :1636tmp_alphabet.append(letter)16371638self._alphabet = Alphabet(tmp_alphabet)16391640def is_irreducible(self, return_decomposition=False):1641r"""1642Test of reducibility16431644A quadratic (or generalized) permutation is *reducible* if there exists1645a decomposition16461647.. math::16481649A1 u B1 | ... | B1 u A216501651A1 u B2 | ... | B2 u A216521653where no corners is empty, or exactly one corner is empty1654and it is on the left, or two and they are both on the1655right or on the left. The definition is due to [BL08]_ where they prove1656that the property of being irreducible is stable under Rauzy induction.16571658INPUT:16591660- ``return_decomposition`` - boolean (default: False) - if True, and1661the permutation is reducible, returns also the blocs A1 u B1, B1 u1662A2, A1 u B2 and B2 u A2 of a decomposition as above.16631664OUTPUT:16651666If return_decomposition is True, returns a 2-uple1667(test,decomposition) where test is the preceding test and1668decomposition is a 4-uple (A11,A12,A21,A22) where:16691670A11 = A1 u B11671A12 = B1 u A21672A21 = A1 u B21673A22 = B2 u A216741675EXAMPLES::16761677sage: GP = iet.GeneralizedPermutation16781679sage: GP('a a','b b').is_irreducible()1680False1681sage: GP('a a b','b c c').is_irreducible()1682True1683sage: GP('1 2 3 4 5 1','5 6 6 4 3 2').is_irreducible()1684True16851686TESTS:16871688Test reducible permutations with no empty corner::16891690sage: GP('1 4 1 3','4 2 3 2').is_irreducible(True)1691(False, (['1', '4'], ['1', '3'], ['4', '2'], ['3', '2']))16921693Test reducible permutations with one left corner empty::16941695sage: GP('1 2 2 3 1','4 4 3').is_irreducible(True)1696(False, (['1'], ['3', '1'], [], ['3']))1697sage: GP('4 4 3','1 2 2 3 1').is_irreducible(True)1698(False, ([], ['3'], ['1'], ['3', '1']))16991700Test reducible permutations with two left corners empty::17011702sage: GP('1 1 2 3','4 2 4 3').is_irreducible(True)1703(False, ([], ['3'], [], ['3']))17041705Test reducible permutations with two right corners empty::17061707sage: GP('1 2 2 3 3','1 4 4').is_irreducible(True)1708(False, (['1'], [], ['1'], []))1709sage: GP('1 2 2','1 3 3').is_irreducible(True)1710(False, (['1'], [], ['1'], []))1711sage: GP('1 2 3 3','2 1 4 4 5 5').is_irreducible(True)1712(False, (['1', '2'], [], ['2', '1'], []))17131714AUTHORS:17151716- Vincent Delecroix (2008-12-20)1717"""1718l0 = self.length_top()1719l1 = self.length_bottom()1720s0,s1 = self.list()17211722# testing two corners empty on the right (i12 = i22 = 0)1723A11, A21, A12, A22 = [],[],[],[]17241725for i11 in range(1, l0):1726if s0[i11-1] in A11:1727break1728A11 = s0[:i11]17291730for i21 in range(1, l1):1731if s1[i21-1] in A21:1732break1733A21 = s1[:i21]17341735if sorted(A11) == sorted(A21):1736if return_decomposition:1737return False,(A11,A12,A21,A22)1738return False1739A21 = []17401741# testing no corner empty but one or two on the left1742A11, A12, A21, A22 = [], [], [], []1743for i11 in range(0, l0):1744if i11 > 0 and s0[i11-1] in A11:1745break1746A11 = s0[:i11]17471748for i21 in xrange(0, l1) :1749if i21 > 0 and s1[i21-1] in A21:1750break1751A21 = s1[:i21]17521753for i12 in xrange(l0 - 1, i11 - 1, -1) :1754if s0[i12] in A12 or s0[i12] in A21:1755break1756A12 = s0[i12:]17571758for i22 in xrange(l1 - 1, i21 - 1, -1) :1759if s1[i22] in A22 or s1[i22] in A11:1760break1761A22 = s1[i22:]17621763if sorted(A11 + A22) == sorted(A12 + A21) :1764if return_decomposition :1765return False, (A11,A12,A21,A22)1766return False1767A22 = []1768A12 = []1769A21 = []177017711772if return_decomposition:1773return True, ()1774return True17751776def has_right_rauzy_move(self, winner):1777r"""1778Test of Rauzy movability (with an eventual specified choice of winner)17791780A quadratic (or generalized) permutation is rauzy_movable type1781depending on the possible length of the last interval. It's1782dependent of the length equation.17831784INPUT:17851786- ``winner`` - the integer 'top' or 'bottom'17871788EXAMPLES::17891790sage: p = iet.GeneralizedPermutation('a a','b b')1791sage: p.has_right_rauzy_move('top')1792False1793sage: p.has_right_rauzy_move('bottom')1794False17951796::17971798sage: p = iet.GeneralizedPermutation('a a b','b c c')1799sage: p.has_right_rauzy_move('top')1800True1801sage: p.has_right_rauzy_move('bottom')1802True18031804::18051806sage: p = iet.GeneralizedPermutation('a a','b b c c')1807sage: p.has_right_rauzy_move('top')1808True1809sage: p.has_right_rauzy_move('bottom')1810False18111812::18131814sage: p = iet.GeneralizedPermutation('a a b b','c c')1815sage: p.has_right_rauzy_move('top')1816False1817sage: p.has_right_rauzy_move('bottom')1818True1819"""1820winner = interval_conversion(winner)1821loser = 1 - winner18221823# the same letter at the right-end (False)1824if (self._twin[0][-1][0] == 1 and1825self._twin[0][-1][1] == self.length_bottom() - 1):1826return False18271828# the winner (or loser) letter is repeated on the other interval (True)1829if self._twin[winner][-1][0] == loser:1830return True1831if self._twin[loser][-1][0] == winner:1832return True18331834# the loser letters is the only letter repeated in1835# the loser interval (False)1836if [i for i,_ in self._twin[loser]].count(loser) == 2:1837return False18381839return True18401841def labelize_flip(couple):1842r"""1843Returns a string from a 2-uple couple of the form (name, flip).18441845TESTS::18461847sage: from sage.dynamics.interval_exchanges.template import labelize_flip1848sage: labelize_flip((0,1))1849' 0'1850sage: labelize_flip((0,-1))1851'-0'1852"""1853if couple[1] == -1: return '-' + str(couple[0])1854return ' ' + str(couple[0])18551856class FlippedPermutation(Permutation):1857r"""1858Template for flipped generalized permutations.18591860.. warning::18611862Internal class! Do not use directly!18631864AUTHORS:18651866- Vincent Delecroix (2008-12-20): initial version1867"""1868def _init_flips(self,intervals,flips):1869r"""1870Initialize the flip list18711872TESTS:18731874sage: iet.Permutation('a b','b a',flips='a').flips() #indirect doctest1875['a']1876sage: iet.Permutation('a b','b a',flips='b').flips() #indirect doctest1877['b']1878sage: iet.Permutation('a b','b a',flips='ab').flips() #indirect doctest1879['a', 'b']18801881::18821883sage: iet.GeneralizedPermutation('a a','b b',flips='a').flips()1884['a']1885sage: iet.GeneralizedPermutation('a a','b b',flips='b').flips()1886['b']1887sage: iet.GeneralizedPermutation('a a','b b',flips='ab').flips()1888['a', 'b']1889"""1890self._flips = [[1]*self.length_top(), [1]*self.length_bottom()]1891for interval in (0,1):1892for i,letter in enumerate(intervals[interval]):1893if letter in flips:1894self._flips[interval][i] = -118951896def str(self, sep="\n"):1897r"""1898String representation.18991900TESTS::19011902sage: p = iet.GeneralizedPermutation('a a','b b',flips='a')1903sage: print p.str()1904-a -a1905b b1906sage: print p.str('/')1907-a -a/ b b1908"""1909l = self.list(flips=True)1910return (' '.join(map(labelize_flip, l[0]))1911+ sep1912+ ' '.join(map(labelize_flip, l[1])))191319141915class FlippedPermutationIET(FlippedPermutation, PermutationIET):1916r"""1917Template for flipped Abelian permutations.19181919.. warning::19201921Internal class! Do not use directly!19221923AUTHORS:19241925- Vincent Delecroix (2008-12-20): initial version1926"""1927def flips(self):1928r"""1929Returns the list of flips.19301931EXAMPLES::19321933sage: p = iet.Permutation('a b c','c b a',flips='ac')1934sage: p.flips()1935['a', 'c']1936"""1937result = []1938l = self.list(flips=False)1939for i,f in enumerate(self._flips[0]):1940if f == -1:1941result.append(l[0][i])1942return result19431944class FlippedPermutationLI(FlippedPermutation, PermutationLI):1945r"""1946Template for flipped quadratic permutations.19471948.. warning::19491950Internal class! Do not use directly!19511952AUTHORS:19531954- Vincent Delecroix (2008-12-20): initial version1955"""1956def flips(self):1957r"""1958Returns the list of flipped intervals.19591960EXAMPLES::19611962sage: p = iet.GeneralizedPermutation('a a','b b',flips='a')1963sage: p.flips()1964['a']1965sage: p = iet.GeneralizedPermutation('a a','b b',flips='b',reduced=True)1966sage: p.flips()1967['b']1968"""1969res = []1970l = self.list(flips=False)1971for i,f in enumerate(self._flips[0]):1972if f == -1:1973res.append(l[0][i])1974for i,f in enumerate(self._flips[1]):1975if f == -1:1976res.append(l[1][i])1977return list(set(res))197819791980class RauzyDiagram(SageObject):1981r"""1982Template for Rauzy diagrams.19831984.. warning:19851986Internal class! Do not use directly!19871988AUTHORS:19891990- Vincent Delecroix (2008-12-20): initial version1991"""1992# TODO: pickle problem of Path (it does not understand what is its parent)1993__metaclass__ = NestedClassMetaclass19941995class Path(SageObject):1996r"""1997Path in Rauzy diagram.19981999A path in a Rauzy diagram corresponds to a subsimplex of the simplex of2000lengths. This correspondance is obtained via the Rauzy induction. To a2001idoc IET we can associate a unique path in a Rauzy diagram. This2002establishes a correspondance between infinite full path in Rauzy diagram2003and equivalence topologic class of IET.2004"""2005def __init__(self, parent, *data):2006r"""2007Constructor of the path.20082009TEST::20102011sage: p = iet.Permutation('a b c', 'c b a')2012sage: r = p.rauzy_diagram()2013sage: g = r.path(p, 0, 1, 0); g2014Path of length 3 in a Rauzy diagram20152016Check for :trac:`8388`::20172018sage: loads(dumps(g)) == g2019True2020"""2021self._parent = parent20222023if len(data) == 0:2024raise ValueError("No empty data")20252026start = data[0]2027if start not in self._parent:2028raise ValueError("Starting point not in this Rauzy diagram")20292030self._start = self._parent._permutation_to_vertex(start)20312032cur_vertex = self._start2033self._edge_types = []20342035n = len(self._parent._edge_types)2036for i in data[1:]:2037if not isinstance(i, (int,Integer)): # try parent method2038i = self._parent.edge_types_index(i)20392040if i < 0 or i > n:2041raise ValueError("indices must be integer between 0 and %d" % (n))2042neighbours = self._parent._succ[cur_vertex]2043if neighbours[i] is None:2044raise ValueError("Invalid path")20452046cur_vertex = neighbours[i]2047self._edge_types.append(i)20482049self._end = cur_vertex20502051def _repr_(self):2052r"""2053Returns a representation of the path.20542055TEST::20562057sage: p = iet.Permutation('a b','b a')2058sage: r = p.rauzy_diagram()2059sage: r.path(p) #indirect doctest2060Path of length 0 in a Rauzy diagram2061sage: r.path(p,'top') #indirect doctest2062Path of length 1 in a Rauzy diagram2063sage: r.path(p,'bottom') #indirect doctest2064Path of length 1 in a Rauzy diagram2065"""2066return "Path of length %d in a Rauzy diagram" % (len(self))20672068def start(self):2069r"""2070Returns the first vertex of the path.20712072EXAMPLES::20732074sage: p = iet.Permutation('a b c','c b a')2075sage: r = p.rauzy_diagram()2076sage: g = r.path(p, 't', 'b')2077sage: g.start() == p2078True2079"""2080return self._parent._vertex_to_permutation(self._start)20812082def end(self):2083r"""2084Returns the last vertex of the path.20852086EXAMPLES::20872088sage: p = iet.Permutation('a b c','c b a')2089sage: r = p.rauzy_diagram()2090sage: g1 = r.path(p, 't', 'b', 't')2091sage: g1.end() == p2092True2093sage: g2 = r.path(p, 'b', 't', 'b')2094sage: g2.end() == p2095True2096"""2097return self._parent._vertex_to_permutation(self._end)20982099def edge_types(self):2100r"""2101Returns the edge types of the path.21022103EXAMPLES::21042105sage: p = iet.Permutation('a b c','c b a')2106sage: r = p.rauzy_diagram()2107sage: g = r.path(p, 0, 1)2108sage: g.edge_types()2109[0, 1]2110"""2111return copy(self._edge_types)21122113def __eq__(self, other):2114r"""2115Tests equality21162117TEST::21182119sage: p1 = iet.Permutation('a b','b a')2120sage: r1 = p1.rauzy_diagram()2121sage: p2 = p1.reduced()2122sage: r2 = p2.rauzy_diagram()2123sage: r1.path(p1,0,1) == r2.path(p2,0,1)2124False2125sage: r1.path(p1,0) == r1.path(p1,0)2126True2127sage: r1.path(p1,1) == r1.path(p1,0)2128False2129"""2130return (2131type(self) == type(other) and2132self._parent == other._parent and2133self._start == other._start and2134self._edge_types == other._edge_types)21352136def __ne__(self,other):2137r"""2138Tests inequality21392140TEST::21412142sage: p1 = iet.Permutation('a b','b a')2143sage: r1 = p1.rauzy_diagram()2144sage: p2 = p1.reduced()2145sage: r2 = p2.rauzy_diagram()2146sage: r1.path(p1,0,1) != r2.path(p2,0,1)2147True2148sage: r1.path(p1,0) != r1.path(p1,0)2149False2150sage: r1.path(p1,1) != r1.path(p1,0)2151True2152"""2153return (2154type(self) != type(other) or2155self._parent != other._parent or2156self._start != other._start or2157self._edge_types != other._edge_types)21582159def __copy__(self):2160r"""2161Returns a copy of the path.21622163TESTS::21642165sage: p = iet.Permutation('a b c','c b a')2166sage: r = p.rauzy_diagram()2167sage: g1 = r.path(p,0,1,0,0)2168sage: g2 = copy(g1)2169sage: g1 is g22170False2171"""2172res = self.__class__(self._parent, self.start())2173res._edge_types = copy(self._edge_types)2174res._end = copy(self._end)2175return res21762177def pop(self):2178r"""2179Pops the queue of the path21802181OUTPUT:21822183a path corresponding to the last edge21842185EXAMPLES::21862187sage: p = iet.Permutation('a b','b a')2188sage: r = p.rauzy_diagram()2189sage: g = r.path(p,0,1,0)2190sage: g0,g1,g2,g3 = g[0], g[1], g[2], g[3]2191sage: g.pop() == r.path(g2,0)2192True2193sage: g == r.path(g0,0,1)2194True2195sage: g.pop() == r.path(g1,1)2196True2197sage: g == r.path(g0,0)2198True2199sage: g.pop() == r.path(g0,0)2200True2201sage: g == r.path(g0)2202True2203sage: g.pop() == r.path(g0)2204True2205"""2206if len(self) == 0:2207return self._parent.path(self.start())22082209else:2210e = self._edge_types.pop()2211self._end = self._parent._pred[self._end][e]2212return self._parent.path(self.end(),e)22132214def append(self, edge_type):2215r"""2216Append an edge to the path.22172218EXAMPLES::22192220sage: p = iet.Permutation('a b c','c b a')2221sage: r = p.rauzy_diagram()2222sage: g = r.path(p)2223sage: g.append('top')2224sage: g2225Path of length 1 in a Rauzy diagram2226sage: g.append('bottom')2227sage: g2228Path of length 2 in a Rauzy diagram2229"""2230if not isinstance(edge_type, (int,Integer)):2231edge_type = self._parent.edge_types_index(edge_type)22322233elif edge_type < 0 or edge_type >= len(self._parent._edge_types):2234raise ValueError("Edge type not valid")22352236if self._parent._succ[self._end][edge_type] is None:2237raise ValueError("%d is not a valid edge" % (edge_type))22382239self._edge_types.append(edge_type)2240self._end = self._parent._succ[self._end][edge_type]22412242def _fast_append(self, edge_type):2243r"""2244Append an edge to the path without verification.22452246EXAMPLES::22472248sage: p = iet.GeneralizedPermutation('a a','b b c c')2249sage: r = p.rauzy_diagram()22502251.. try to add 1 with append::22522253sage: g = r.path(p)2254sage: r[p][1] is None2255True2256sage: g.append(1)2257Traceback (most recent call last):2258...2259ValueError: 1 is not a valid edge22602261.. the same with fast append::22622263sage: g = r.path(p)2264sage: r[p][1] is None2265True2266sage: g._fast_append(1)2267"""2268self._edge_types.append(edge_type)2269self._end = self._parent._succ[self._end][edge_type]22702271def extend(self, path):2272r"""2273Extends self with another path.22742275EXAMPLES::22762277sage: p = iet.Permutation('a b c d','d c b a')2278sage: r = p.rauzy_diagram()2279sage: g1 = r.path(p,'t','t')2280sage: g2 = r.path(p.rauzy_move('t',iteration=2),'b','b')2281sage: g = r.path(p,'t','t','b','b')2282sage: g == g1 + g22283True2284sage: g = copy(g1)2285sage: g.extend(g2)2286sage: g == g1 + g22287True2288"""2289if self._parent != path._parent:2290raise ValueError("Not on the same Rauzy diagram")22912292if self._end != path._start:2293raise ValueError("The end of the first path must the start of the second")22942295self._edge_types.extend(path._edge_types)2296self._end = path._end22972298def _fast_extend(self, path):2299r"""2300Extension with no verification.23012302EXAMPLES::23032304sage: p = iet.Permutation('a b c','c b a')2305sage: r = p.rauzy_diagram()2306sage: p0, p1 = r[p]2307sage: g = r.path(p)2308sage: g._fast_extend(r.path(p0))2309sage: g2310Path of length 0 in a Rauzy diagram2311sage: g._fast_extend(r.path(p1))2312sage: g2313Path of length 0 in a Rauzy diagram2314"""2315self._edge_types.extend(path._edge_types)2316self._end = path._end23172318def __len__(self):2319r"""2320Returns the length of the path.23212322TEST::232323242325sage: p = iet.Permutation('a b c','c b a')2326sage: r = p.rauzy_diagram()2327sage: len(r.path(p))232802329sage: len(r.path(p,0))233012331sage: len(r.path(p,1))233212333"""2334return len(self._edge_types)23352336def __getitem__(self, i):2337r"""2338TESTS::23392340sage: p = iet.Permutation('a b c','c b a')2341sage: r = p.rauzy_diagram()2342sage: g = r.path(p,'t','b')2343sage: g[0] == p2344True2345sage: g[1] == p.rauzy_move('t')2346True2347sage: g[2] == p.rauzy_move('t').rauzy_move('b')2348True2349sage: g[-1] == g[2]2350True2351sage: g[-2] == g[1]2352True2353sage: g[-3] == g[0]2354True2355"""2356if i > len(self) or i < -len(self)-1:2357raise IndexError("path index out of range")23582359if i == 0: return self.start()2360if i < 0: i = i + len(self) + 123612362v = self._start2363for k in range(i):2364v = self._parent._succ[v][self._edge_types[k]]2365return self._parent._vertex_to_permutation(v)23662367def __add__(self, other):2368r"""2369Concatenation of paths.23702371EXAMPLES::23722373sage: p = iet.Permutation('a b','b a')2374sage: r = p.rauzy_diagram()2375sage: r.path(p) + r.path(p,'b') == r.path(p,'b')2376True2377sage: r.path(p,'b') + r.path(p) == r.path(p,'b')2378True2379sage: r.path(p,'t') + r.path(p,'b') == r.path(p,'t','b')2380True2381"""2382if self._end != other._start:2383raise ValueError("The end of the first path is not the start of the second")23842385res = copy(self)2386res._fast_extend(other)2387return res23882389def __mul__(self, n):2390r"""2391Multiple of a loop.23922393EXAMPLES::23942395sage: p = iet.Permutation('a b','b a')2396sage: r = p.rauzy_diagram()2397sage: l = r.path(p,'b')2398sage: l * 2 == r.path(p,'b','b')2399True2400sage: l * 3 == r.path(p,'b','b','b')2401True2402"""2403if not self.is_loop():2404raise ValueError("Must be a loop to have multiple")24052406if not isinstance(n, (Integer,int)):2407raise TypeError("The multiplier must be an integer")24082409if n < 0:2410raise ValueError("The multiplier must be non negative")24112412res = copy(self)2413for i in range(n-1):2414res += self2415return res24162417def is_loop(self):2418r"""2419Tests whether the path is a loop (start point = end point).24202421EXAMPLES::24222423sage: p = iet.Permutation('a b','b a')2424sage: r = p.rauzy_diagram()2425sage: r.path(p).is_loop()2426True2427sage: r.path(p,0,1,0,0).is_loop()2428True2429"""2430return self._start == self._end24312432def winners(self):2433r"""2434Returns the winner list associated to the edge of the path.24352436EXAMPLES::24372438sage: p = iet.Permutation('a b','b a')2439sage: r = p.rauzy_diagram()2440sage: r.path(p).winners()2441[]2442sage: r.path(p,0).winners()2443['b']2444sage: r.path(p,1).winners()2445['a']2446"""2447return self.composition(2448self._parent.edge_to_winner,2449list.__add__)24502451def losers(self):2452r"""2453Returns a list of the loosers on the path.24542455EXAMPLES::24562457sage: p = iet.Permutation('a b c','c b a')2458sage: r = p.rauzy_diagram()2459sage: g0 = r.path(p,'t','b','t')2460sage: g0.losers()2461['a', 'c', 'b']2462sage: g1 = r.path(p,'b','t','b')2463sage: g1.losers()2464['c', 'a', 'b']2465"""2466return self.composition(2467self._parent.edge_to_loser,2468list.__add__)24692470def __iter__(self):2471r"""2472Iterator over the permutations of the path.24732474EXAMPLES::24752476sage: p = iet.Permutation('a b c','c b a')2477sage: r = p.rauzy_diagram()2478sage: g = r.path(p)2479sage: for q in g:2480....: print p2481a b c2482c b a2483sage: g = r.path(p, 't', 't')2484sage: for q in g:2485....: print q, "\n*****"2486a b c2487c b a2488*****2489a b c2490c a b2491*****2492a b c2493c b a2494*****2495sage: g = r.path(p,'b','t')2496sage: for q in g:2497....: print q, "\n*****"2498a b c2499c b a2500*****2501a c b2502c b a2503*****2504a c b2505c b a2506*****2507"""2508i = self._start25092510for edge_type in self._edge_types:2511yield self._parent._vertex_to_permutation(i)2512i = self._parent._succ[i][edge_type]25132514yield self.end()25152516def composition(self, function, composition = None):2517r"""2518Compose an edges function on a path25192520INPUT:25212522- ``path`` - either a Path or a tuple describing a path25232524- ``function`` - function must be of the form25252526- ``composition`` - the composition function25272528AUTHOR:25292530- Vincent Delecroix (2009-09-29)25312532EXAMPLES::25332534sage: p = iet.Permutation('a b','b a')2535sage: r = p.rauzy_diagram()2536sage: def f(i,t):2537....: if t is None: return []2538....: return [t]2539sage: g = r.path(p)2540sage: g.composition(f,list.__add__)2541[]2542sage: g = r.path(p,0,1)2543sage: g.composition(f, list.__add__)2544[0, 1]2545"""2546result = function(None,None)2547cur_vertex = self._start2548p = self._parent._element25492550if composition is None: composition = result.__class__.__mul__25512552for i in self._edge_types:2553self._parent._set_element(cur_vertex)2554result = composition(result, function(p,i))2555cur_vertex = self._parent._succ[cur_vertex][i]25562557return result25582559def right_composition(self, function, composition = None) :2560r"""2561Compose an edges function on a path25622563INPUT:25642565- ``function`` - function must be of the form (indice,type) -> element. Moreover function(None,None) must be an identity element for initialization.25662567- ``composition`` - the composition function for the function. * if None (defaut None)25682569TEST::25702571sage: p = iet.Permutation('a b','b a')2572sage: r = p.rauzy_diagram()2573sage: def f(i,t):2574....: if t is None: return []2575....: return [t]2576sage: g = r.path(p)2577sage: g.right_composition(f,list.__add__)2578[]2579sage: g = r.path(p,0,1)2580sage: g.right_composition(f, list.__add__)2581[1, 0]2582"""2583result = function(None,None)2584p = self._parent._element2585cur_vertex = self._start25862587if composition is None: composition = result.__class__.__mul__25882589for i in self._edge_types:2590self._parent._set_element(cur_vertex)2591result = composition(function(p,i),result)2592cur_vertex = self._parent._succ[cur_vertex][i]25932594return result25952596def __init__(self, p,2597right_induction=True,2598left_induction=False,2599left_right_inversion=False,2600top_bottom_inversion=False,2601symmetric=False):2602r"""2603self._succ contains successors2604self._pred contains predecessors26052606self._element_class is the class of elements of self2607self._element is an instance of this class (hence contains the alphabet,2608the representation mode, ...). It is used to store data about property2609of permutations and also as a fast iterator.26102611INPUT:26122613- ``right_induction`` - boolean or 'top' or 'bottom': consider the2614right induction26152616- ``left_induction`` - boolean or 'top' or 'bottom': consider the2617left induction26182619- ``left_right_inversion`` - consider the left right inversion26202621- ``top_bottom_inversion`` - consider the top bottom inversion26222623- ``symmetric`` - consider the symmetric26242625TESTS::26262627sage: r1 = iet.RauzyDiagram('a b','b a')2628sage: r2 = loads(dumps(r1))2629"""2630self._edge_types = []2631self._index = {}26322633if right_induction is True:2634self._index['rt_rauzy'] = len(self._edge_types)2635self._edge_types.append(('rauzy_move',(0,-1)))2636self._index['rb_rauzy'] = len(self._edge_types)2637self._edge_types.append(('rauzy_move',(1,-1)))26382639elif isinstance(right_induction, str):2640if right_induction == '':2641raise ValueError("right_induction can not be empty string")26422643elif 'top'.startswith(right_induction):2644self._index['rt_rauzy'] = len(self._edge_types)2645self._edge_types.append(('rauzy_move',(0,-1)))26462647elif 'bottom'.startswith(right_induction):2648self._index['rb_rauzy'] = len(self._edge_types)2649self._edge_types.append(('rauzy_move',(1,-1)))26502651else:2652raise ValueError("%s is not valid for right_induction" % (right_induction))26532654if left_induction is True:2655self._index['lt_rauzy'] = len(self._edge_types)2656self._edge_types.append(('rauzy_move',(0,0)))2657self._index['lb_rauzy'] = len(self._edge_types)2658self._edge_types.append(('rauzy_move',(1,0)))26592660elif isinstance(left_induction,str):2661if left_induction == '':2662raise ValueError("left_induction can not be empty string")26632664elif 'top'.startswith(left_induction):2665self._index['lt_rauzy'] = len(self._edge_types)2666self._edge_types.append(('rauzy_move', (0,0)))26672668elif 'bottom'.startswith(left_induction):2669self._index['lb_rauzy'] = len(self._edge_types)2670self._edge_types.append(('rauzy_move', (1,0)))26712672else:2673raise ValueError("%s is not valid for left_induction" % (right_induction))26742675if left_right_inversion is True:2676self._index['lr_inverse'] = len(self._edge_types)2677self._edge_types.append(('left_right_inverse', ()))26782679if top_bottom_inversion is True:2680self._index['tb_inverse'] = len(self._edge_types)2681self._edge_types.append(('top_bottom_inverse', ()))26822683if symmetric is True:2684self._index['symmetric'] = len(self._edge_types)2685self._edge_types.append(('symmetric', ()))26862687self._n = len(p)2688self._element_class = p.__class__2689self._element = copy(p)2690self._alphabet = self._element._alphabet26912692self._pred = {}2693self._succ = {}26942695self.complete(p)26962697def __eq__(self, other):2698r"""2699Tests equality.27002701TESTS:27022703::27042705sage: iet.RauzyDiagram('a b','b a') == iet.RauzyDiagram('a b c','c b a')2706False2707sage: r = iet.RauzyDiagram('a b c','c b a')2708sage: r1 = iet.RauzyDiagram('a c b','c b a', alphabet='abc')2709sage: r2 = iet.RauzyDiagram('a b c','c a b', alphabet='abc')2710sage: r == r12711True2712sage: r == r22713True2714sage: r1 == r22715True27162717::27182719sage: r = iet.RauzyDiagram('a b c d','d c b a')2720sage: for p in r:2721....: p.rauzy_diagram() == r2722True2723True2724True2725True2726True2727True2728True2729"""2730return (2731type(self) == type(other) and2732self._edge_types == other._edge_types and2733self._succ.keys()[0] in other._succ)27342735def __ne__(self, other):2736r"""2737Tests difference.273827392740TEST::27412742sage: iet.RauzyDiagram('a b','b a') != iet.RauzyDiagram('a b c','c b a')2743True2744sage: r = iet.RauzyDiagram('a b c','c b a')2745sage: r1 = iet.RauzyDiagram('a c b','c b a', alphabet='abc')2746sage: r2 = iet.RauzyDiagram('a b c','c a b', alphabet='abc')2747sage: r != r12748False2749sage: r != r22750False2751sage: r1 != r22752False2753"""2754return (2755type(self) != type(other) or2756self._edge_types != other._edge_types or2757self._succ.keys()[0] not in other._succ)27582759def vertices(self):2760r"""2761Returns a list of the vertices.27622763EXAMPLES::27642765sage: r = iet.RauzyDiagram('a b','b a')2766sage: for p in r.vertices(): print p2767a b2768b a2769"""2770return map(2771lambda x: self._vertex_to_permutation(x),2772self._succ.keys())27732774def vertex_iterator(self):2775r"""2776Returns an iterator over the vertices27772778EXAMPLES::27792780sage: r = iet.RauzyDiagram('a b','b a')2781sage: for p in r.vertex_iterator(): print p2782a b2783b a27842785::27862787sage: r = iet.RauzyDiagram('a b c d','d c b a')2788sage: from itertools import ifilter2789sage: r_1n = ifilter(lambda x: x.is_cylindric(), r)2790sage: for p in r_1n: print p2791a b c d2792d c b a2793"""2794from itertools import imap2795return imap(2796lambda x: self._vertex_to_permutation(x),2797self._succ.keys())27982799def edges(self,labels=True):2800r"""2801Returns a list of the edges.28022803EXAMPLES::28042805sage: r = iet.RauzyDiagram('a b','b a')2806sage: len(r.edges())280722808"""2809return list(self.edge_iterator())28102811def edge_iterator(self):2812r"""2813Returns an iterator over the edges of the graph.28142815EXAMPLES::28162817sage: p = iet.Permutation('a b','b a')2818sage: r = p.rauzy_diagram()2819sage: for e in r.edge_iterator():2820....: print e[0].str(sep='/'), '-->', e[1].str(sep='/')2821a b/b a --> a b/b a2822a b/b a --> a b/b a2823"""2824for x in self._succ.keys():2825for i,y in enumerate(self._succ[x]):2826if y is not None:2827yield(2828(self._vertex_to_permutation(x),2829self._vertex_to_permutation(y),2830i))28312832def edge_types_index(self, data):2833r"""2834Try to convert the data as an edge type.28352836INPUT:28372838- ``data`` - a string28392840OUTPUT:28412842integer28432844EXAMPLES:28452846For a standard Rauzy diagram (only right induction) the 0 index2847corresponds to the 'top' induction and the index 1 corresponds to the2848'bottom' one::28492850sage: p = iet.Permutation('a b c','c b a')2851sage: r = p.rauzy_diagram()2852sage: r.edge_types_index('top')285302854sage: r[p][0] == p.rauzy_move('top')2855True2856sage: r.edge_types_index('bottom')285712858sage: r[p][1] == p.rauzy_move('bottom')2859True28602861The special operations (inversion and symmetry) always appears after the2862different Rauzy inductions::28632864sage: p = iet.Permutation('a b c','c b a')2865sage: r = p.rauzy_diagram(symmetric=True)2866sage: r.edge_types_index('symmetric')286722868sage: r[p][2] == p.symmetric()2869True28702871This function always try to resolve conflictuous name. If it's2872impossible a ValueError is raised::28732874sage: p = iet.Permutation('a b c','c b a')2875sage: r = p.rauzy_diagram(left_induction=True)2876sage: r.edge_types_index('top')2877Traceback (most recent call last):2878...2879ValueError: left and right inductions must be differentiated2880sage: r.edge_types_index('top_right')288102882sage: r[p][0] == p.rauzy_move(0)2883True2884sage: r.edge_types_index('bottom_left')288532886sage: r[p][3] == p.rauzy_move('bottom', 'left')2887True28882889::28902891sage: p = iet.Permutation('a b c','c b a')2892sage: r = p.rauzy_diagram(left_right_inversion=True,top_bottom_inversion=True)2893sage: r.edge_types_index('inversion')2894Traceback (most recent call last):2895...2896ValueError: left-right and top-bottom inversions must be differentiated2897sage: r.edge_types_index('lr_inverse')289822899sage: p.lr_inverse() == r[p][2]2900True2901sage: r.edge_types_index('tb_inverse')290232903sage: p.tb_inverse() == r[p][3]2904True29052906Short names are accepted::29072908sage: p = iet.Permutation('a b c','c b a')2909sage: r = p.rauzy_diagram(right_induction='top',top_bottom_inversion=True)2910sage: r.edge_types_index('top_rauzy_move')291102912sage: r.edge_types_index('t')291302914sage: r.edge_types_index('tb')291512916sage: r.edge_types_index('inversion')291712918sage: r.edge_types_index('inverse')291912920sage: r.edge_types_index('i')292112922"""2923if not isinstance(data,str):2924raise ValueError("the edge type must be a string")29252926if 'top_rauzy_move'.startswith(data) or 't_rauzy_move'.startswith(data):2927if 'lt_rauzy' in self._index:2928if 'rt_rauzy' in self._index:2929raise ValueError("left and right inductions must "2930"be differentiated")2931return self._index['lt_rauzy']29322933if 'rt_rauzy' in self._index:2934return self._index['rt_rauzy']29352936raise ValueError("no top induction in this Rauzy diagram")29372938if ('bottom_rauzy_move'.startswith(data) or2939'b_rauzy_move'.startswith(data)):2940if 'lb_rauzy' in self._index:2941if 'rb_rauzy' in self._index:2942raise ValueError("left and right inductions must "2943"be differentiated")2944return self._index['lb_rauzy']29452946if 'rb_rauzy' in self._index:2947return self._index['rb_rauzy']29482949raise ValueError("no bottom Rauzy induction in this diagram")29502951if ('left_rauzy_move'.startswith(data) or2952'l_rauzy_move'.startswith(data)):2953if 'lt_rauzy' in self._index:2954if 'lb_rauzy' in self._index:2955raise ValueError("top and bottom inductions must be differentiated")2956return self._index['lt_rauzy']29572958if 'lb_rauzy' in self._index:2959return self._index('lb_rauzy')29602961raise ValueError("no left Rauzy induction in this diagram")29622963if ('lt_rauzy_move'.startswith(data) or2964'tl_rauzy_move'.startswith(data) or2965'left_top_rauzy_move'.startswith(data) or2966'top_left_rauzy_move'.startswith(data)):2967if not 'lt_rauzy' in self._index:2968raise ValueError("no top-left Rauzy induction in this diagram")2969else:2970return self._index['lt_rauzy']29712972if ('lb_rauzy_move'.startswith(data) or2973'bl_rauzy_move'.startswith(data) or2974'left_bottom_rauzy_move'.startswith(data) or2975'bottom_left_rauzy_move'.startswith(data)):2976if not 'lb_rauzy' in self._index:2977raise ValueError("no bottom-left Rauzy induction in this diagram")2978else:2979return self._index['lb_rauzy']29802981if 'right'.startswith(data):2982raise ValueError("ambiguity with your edge name: %s" % (data))29832984if ('rt_rauzy_move'.startswith(data) or2985'tr_rauzy_move'.startswith(data) or2986'right_top_rauzy_move'.startswith(data) or2987'top_right_rauzy_move'.startswith(data)):2988if not 'rt_rauzy' in self._index:2989raise ValueError("no top-right Rauzy induction in this diagram")2990else:2991return self._index['rt_rauzy']29922993if ('rb_rauzy_move'.startswith(data) or2994'br_rauzy_move'.startswith(data) or2995'right_bottom_rauzy_move'.startswith(data) or2996'bottom_right_rauzy_move'.startswith(data)):2997if not 'rb_rauzy' in self._index:2998raise ValueError("no bottom-right Rauzy induction in this diagram")2999else:3000return self._index['rb_rauzy']30013002if 'symmetric'.startswith(data):3003if not 'symmetric' in self._index:3004raise ValueError("no symmetric in this diagram")3005else:3006return self._index['symmetric']30073008if 'inversion'.startswith(data) or data == 'inverse':3009if 'lr_inverse' in self._index:3010if 'tb_inverse' in self._index:3011raise ValueError("left-right and top-bottom inversions must be differentiated")3012return self._index['lr_inverse']30133014if 'tb_inverse' in self._index:3015return self._index['tb_inverse']30163017raise ValueError("no inversion in this diagram")30183019if ('lr_inversion'.startswith(data) or3020data == 'lr_inverse' or3021'left_right_inversion'.startswith(data) or3022data == 'left_right_inverse'):3023if not 'lr_inverse' in self._index:3024raise ValueError("no left-right inversion in this diagram")3025else:3026return self._index['lr_inverse']30273028if ('tb_inversion'.startswith(data) or3029data == 'tb_inverse' or3030'top_bottom_inversion'.startswith(data)3031or data == 'top_bottom_inverse'):3032if not 'tb_inverse' in self._index:3033raise ValueError("no top-bottom inversion in this diagram")3034else:3035return self._index['tb_inverse']30363037raise ValueError("this edge type does not exist: %s" % (data))30383039def edge_types(self):3040r"""3041Print information about edges.30423043EXAMPLES::30443045sage: r = iet.RauzyDiagram('a b', 'b a')3046sage: r.edge_types()30470: rauzy_move(0, -1)30481: rauzy_move(1, -1)30493050::30513052sage: r = iet.RauzyDiagram('a b', 'b a', left_induction=True)3053sage: r.edge_types()30540: rauzy_move(0, -1)30551: rauzy_move(1, -1)30562: rauzy_move(0, 0)30573: rauzy_move(1, 0)30583059::30603061sage: r = iet.RauzyDiagram('a b',' b a',symmetric=True)3062sage: r.edge_types()30630: rauzy_move(0, -1)30641: rauzy_move(1, -1)30652: symmetric()3066"""3067for i,(edge_type,t) in enumerate(self._edge_types):3068print str(i) + ": " + edge_type + str(t)30693070def alphabet(self, data=None):3071r"""3072TESTS::30733074sage: r = iet.RauzyDiagram('a b','b a')3075sage: r.alphabet() == Alphabet(['a','b'])3076True3077sage: r = iet.RauzyDiagram([0,1],[1,0])3078sage: r.alphabet() == Alphabet([0,1])3079True3080"""3081if data is None:3082return self._element._alphabet3083else:3084self._element._set_alphabet(data)30853086def letters(self):3087r"""3088Returns the letters used by the RauzyDiagram.30893090EXAMPLES::30913092sage: r = iet.RauzyDiagram('a b','b a')3093sage: r.alphabet()3094{'a', 'b'}3095sage: r.letters()3096['a', 'b']3097sage: r.alphabet('ABCDEF')3098sage: r.alphabet()3099{'A', 'B', 'C', 'D', 'E', 'F'}3100sage: r.letters()3101['A', 'B']3102"""3103return self._element.letters()31043105def _vertex_to_permutation(self,data=None):3106r"""3107Converts the (vertex) data to a permutation.31083109TESTS:31103111sage: r = iet.RauzyDiagram('a b','b a') #indirect doctest3112"""3113if data is not None:3114self._set_element(data)3115return copy(self._element)31163117def edge_to_matrix(self, p=None, edge_type=None):3118r"""3119Return the corresponding matrix31203121INPUT:31223123- ``p`` - a permutation31243125- ``edge_type`` - 0 or 1 corresponding to the type of the edge31263127OUTPUT:31283129A matrix31303131EXAMPLES::31323133sage: p = iet.Permutation('a b c','c b a')3134sage: d = p.rauzy_diagram()3135sage: print d.edge_to_matrix(p,1)3136[1 0 1]3137[0 1 0]3138[0 0 1]3139"""3140if p is None and edge_type is None:3141return identity_matrix(self._n)31423143function_name = self._edge_types[edge_type][0] + '_matrix'31443145if not hasattr(self._element_class,function_name):3146return identity_matrix(self._n)31473148arguments = self._edge_types[edge_type][1]31493150return getattr(p,function_name)(*arguments)31513152def edge_to_winner(self, p=None, edge_type=None):3153r"""3154Return the corresponding winner31553156TEST::31573158sage: r = iet.RauzyDiagram('a b','b a')3159sage: r.edge_to_winner(None,None)3160[]3161"""3162if p is None and edge_type is None:3163return []31643165function_name = self._edge_types[edge_type][0] + '_winner'31663167if not hasattr(self._element_class, function_name):3168return [None]31693170arguments = self._edge_types[edge_type][1]31713172return [getattr(p,function_name)(*arguments)]31733174def edge_to_loser(self, p=None, edge_type=None):3175r"""3176Return the corresponding loser31773178TEST::31793180sage: r = iet.RauzyDiagram('a b','b a')3181sage: r.edge_to_loser(None,None)3182[]3183"""3184if p is None and edge_type is None:3185return []31863187function_name = self._edge_types[edge_type][0] + '_loser'31883189if not hasattr(self._element_class, function_name):3190return [None]31913192arguments = self._edge_types[edge_type][1]31933194return [getattr(p,function_name)(*arguments)]31953196def _all_npath_extension(self, g, length=0):3197r"""3198Returns an iterator over all extension of fixed length of p.31993200INPUT:32013202- ``p`` - a path32033204- ``length`` - a non-negative integer32053206TESTS:32073208::32093210sage: p = iet.Permutation('a b','b a')3211sage: r = p.rauzy_diagram()3212sage: g0 = r.path(p)3213sage: for g in r._all_npath_extension(g0,0):3214....: print g3215Path of length 0 in a Rauzy diagram3216sage: for g in r._all_npath_extension(g0,1):3217....: print g3218Path of length 1 in a Rauzy diagram3219Path of length 1 in a Rauzy diagram3220sage: for g in r._all_npath_extension(g0,2):3221....: print g3222Path of length 2 in a Rauzy diagram3223Path of length 2 in a Rauzy diagram3224Path of length 2 in a Rauzy diagram3225Path of length 2 in a Rauzy diagram32263227::32283229sage: p = iet.GeneralizedPermutation('a a','b b c c',reduced=True)3230sage: r = p.rauzy_diagram()3231sage: g0 = r.path(p)3232sage: len(list(r._all_npath_extension(g0,0)))323313234sage: len(list(r._all_npath_extension(g0,1)))323513236sage: len(list(r._all_npath_extension(g0,2)))323723238sage: len(list(r._all_npath_extension(g0,3)))323933240sage: len(list(r._all_npath_extension(g0,4)))324153242"""3243if length == 0:3244yield g32453246else:3247for i in range(len(self._edge_types)):3248if self._succ[g._end][i] is not None:3249g._fast_append(i)3250for h in self._all_npath_extension(g,length-1): yield h3251g.pop()32523253def _all_path_extension(self, g, length=0):3254r"""3255Returns an iterator over all path extension of p.32563257TESTS:32583259::32603261sage: p = iet.Permutation('a b','b a')3262sage: r = p.rauzy_diagram()3263sage: g0 = r.path(p)3264sage: for g in r._all_path_extension(g0,0):3265....: print g3266Path of length 0 in a Rauzy diagram3267sage: for g in r._all_path_extension(g0, 1):3268....: print g3269Path of length 0 in a Rauzy diagram3270Path of length 1 in a Rauzy diagram3271Path of length 1 in a Rauzy diagram32723273::32743275sage: p = iet.GeneralizedPermutation('a a','b b c c')3276sage: r = p.rauzy_diagram()3277sage: g0 = r.path(p)3278sage: len(list(r._all_path_extension(g0,0)))327913280sage: len(list(r._all_path_extension(g0,1)))328123282sage: len(list(r._all_path_extension(g0,2)))328343284sage: len(list(r._all_path_extension(g0,3)))328573286"""3287yield g32883289if length > 0:3290for i in range(len(self._edge_types)):3291if self._succ[g._end][i] is not None:3292g._fast_append(i)3293for h in self._all_path_extension(g,length-1): yield h3294g.pop()32953296def __iter__(self):3297r"""3298Iterator over the permutations of the Rauzy diagram.32993300EXAMPLES::33013302sage: r = iet.RauzyDiagram('a b','b a')3303sage: for p in r: print p3304a b3305b a3306sage: r = iet.RauzyDiagram('a b c','c b a')3307sage: for p in r: print p.stratum()3308H(0, 0)3309H(0, 0)3310H(0, 0)3311"""3312for data in self._succ.iterkeys():3313yield self._vertex_to_permutation(data)33143315def __contains__(self, element):3316r"""3317Containance test.33183319TESTS::33203321sage: p = iet.Permutation('a b c d','d c b a',reduced=True)3322sage: q = iet.Permutation('a b c d','d b c a',reduced=True)3323sage: r = p.rauzy_diagram()3324sage: s = q.rauzy_diagram()3325sage: p in r3326True3327sage: p in s3328False3329sage: q in r3330False3331sage: q in s3332True3333"""3334for p in self._succ.iterkeys():3335if self._vertex_to_permutation(p) == element:3336return True33373338return False33393340def _repr_(self):3341r"""3342Returns a representation of self33433344TEST::33453346sage: iet.RauzyDiagram('a b','b a') #indirect doctest3347Rauzy diagram with 1 permutation3348sage: iet.RauzyDiagram('a b c','c b a') #indirect doctest3349Rauzy diagram with 3 permutations3350"""3351if len(self._succ) == 0:3352return "Empty Rauzy diagram"3353elif len(self._succ) == 1:3354return "Rauzy diagram with 1 permutation"3355else:3356return "Rauzy diagram with %d permutations" % (len(self._succ))33573358def __getitem__(self,p):3359r"""3360Returns the neighbors of p.33613362Just use the function vertex_to_permutation that must be defined3363in each child.33643365INPUT:33663367- ``p`` - a permutation in the Rauzy diagram33683369TESTS::337033713372sage: p = iet.Permutation('a b c','c b a')3373sage: p0 = iet.Permutation('a b c','c a b',alphabet="abc")3374sage: p1 = iet.Permutation('a c b','c b a',alphabet="abc")3375sage: r = p.rauzy_diagram()3376sage: r[p] == [p0, p1]3377True3378sage: r[p1] == [p1, p]3379True3380sage: r[p0] == [p, p0]3381True3382"""3383if not isinstance(p, self._element_class):3384raise ValueError("Your element does not have the good type")33853386perm = self._permutation_to_vertex(p)3387return map(lambda x: self._vertex_to_permutation(x),3388self._succ[perm])33893390def __len__(self):3391r"""3392Deprecated use cardinality.33933394EXAMPLES::33953396sage: r = iet.RauzyDiagram('a b','b a')3397sage: r.cardinality()339813399"""3400return self.cardinality()34013402def cardinality(self):3403r"""3404Returns the number of permutations in this Rauzy diagram.34053406OUTPUT:34073408- `integer` - the number of vertices in the diagram34093410EXAMPLES::34113412sage: r = iet.RauzyDiagram('a b','b a')3413sage: r.cardinality()341413415sage: r = iet.RauzyDiagram('a b c','c b a')3416sage: r.cardinality()341733418sage: r = iet.RauzyDiagram('a b c d','d c b a')3419sage: r.cardinality()342073421"""3422return len(self._succ)34233424def complete(self, p):3425r"""3426Completion of the Rauzy diagram.34273428Add to the Rauzy diagram all permutations that are obtained by3429successive operations defined by edge_types(). The permutation must be3430of the same type and the same length as the one used for the creation.34313432INPUT:34333434- ``p`` - a permutation of Interval exchange transformation34353436Rauzy diagram is the reunion of all permutations that could be3437obtained with successive rauzy moves. This function just use the3438functions __getitem__ and has_rauzy_move and rauzy_move which must3439be defined for child and their corresponding permutation types.34403441TEST::34423443sage: r = iet.RauzyDiagram('a b c','c b a') #indirect doctest3444sage: r = iet.RauzyDiagram('a b c','c b a',left_induction=True) #indirect doctest3445sage: r = iet.RauzyDiagram('a b c','c b a',symmetric=True) #indirect doctest3446sage: r = iet.RauzyDiagram('a b c','c b a',lr_inversion=True) #indirect doctest3447sage: r = iet.RauzyDiagram('a b c','c b a',tb_inversion=True) #indirect doctest3448"""3449if p.__class__ is not self._element_class:3450raise ValueError("your permutation is not of good type")34513452if len(p) != self._n:3453raise ValueError("your permutation has not the good length")34543455pred = self._pred3456succ = self._succ3457p = self._permutation_to_vertex(p)3458perm = self._element3459l = []34603461if not p in succ:3462succ[p] = [None] * len(self._edge_types)3463pred[p] = [None] * len(self._edge_types)3464l.append(p)34653466while(l != []):3467p = l.pop()3468self._set_element(p)34693470for t, edge in enumerate(self._edge_types):3471if (not hasattr(perm, 'has_'+edge[0]) or3472getattr(perm, 'has_'+edge[0])(*(edge[1]))):3473q = getattr(perm,edge[0])(*(edge[1]))3474q = self._permutation_to_vertex(q)3475if not q in succ:3476succ[q] = [None] * len(self._edge_types)3477pred[q] = [None] * len(self._edge_types)3478l.append(q)3479succ[p][t] = q3480pred[q][t] = p34813482def path(self, *data):3483r"""3484Returns a path over this Rauzy diagram.34853486INPUT:34873488- ``initial_vertex`` - the initial vertex (starting point of the path)34893490- ``data`` - a sequence of edges34913492EXAMPLES::34933494sage: p = iet.Permutation('a b c','c b a')3495sage: r = p.rauzy_diagram()3496sage: g = r.path(p, 'top', 'bottom')3497"""3498if len(data) == 0:3499raise TypeError("Must be non empty")3500elif len(data) == 1 and isinstance(data[0], self.Path):3501return copy(data[0])3502return self.Path(self,*data)35033504def graph(self):3505r"""3506Returns the Rauzy diagram as a Graph object35073508The graph returned is more precisely a DiGraph (directed graph) with3509loops and multiedges allowed.35103511EXAMPLES::35123513sage: r = iet.RauzyDiagram('a b c','c b a')3514sage: r3515Rauzy diagram with 3 permutations3516sage: r.graph()3517Looped multi-digraph on 3 vertices35183519"""3520G = DiGraph(loops=True,multiedges=True)35213522for p,neighbours in self._succ.iteritems():3523p = self._vertex_to_permutation(p)3524for i,n in enumerate(neighbours):3525if n is not None:3526q = self._vertex_to_permutation(n)3527G.add_edge(p,q,i)35283529return G35303531class FlippedRauzyDiagram(RauzyDiagram):3532r"""3533Template for flipped Rauzy diagrams.35343535.. warning:35363537Internal class! Do not use directly!35383539AUTHORS:35403541- Vincent Delecroix (2009-09-29): initial version3542"""3543def complete(self, p, reducible=False):3544r"""3545Completion of the Rauzy diagram35463547Add all successors of p for defined operations in edge_types. Could be3548used for generating non (strongly) connected Rauzy diagrams. Sometimes,3549for flipped permutations, the maximal connected graph in all3550permutations is not strongly connected. Finding such components needs to3551call most than once the .complete() method.35523553INPUT:35543555- ``p`` - a permutation35563557- ``reducible`` - put or not reducible permutations35583559EXAMPLES::35603561sage: p = iet.Permutation('a b c','c b a',flips='a')3562sage: d = p.rauzy_diagram()3563sage: d3564Rauzy diagram with 3 permutations3565sage: p = iet.Permutation('a b c','c b a',flips='b')3566sage: d.complete(p)3567sage: d3568Rauzy diagram with 8 permutations3569sage: p = iet.Permutation('a b c','c b a',flips='a')3570sage: d.complete(p)3571sage: d3572Rauzy diagram with 8 permutations3573"""3574if p.__class__ is not self._element_class:3575raise ValueError("your permutation is not of good type")35763577if len(p) != self._n:3578raise ValueError("your permutation has not the good length")35793580pred = self._pred3581succ = self._succ3582p = self._permutation_to_vertex(p)3583l = []35843585if not p in succ:3586succ[p] = [None] * len(self._edge_types)3587pred[p] = [None] * len(self._edge_types)3588l.append(p)35893590while(l != []):3591p = l.pop()35923593for t, edge_type in enumerate(self._edge_types):3594perm = self._vertex_to_permutation(p)35953596if (not hasattr(perm,'has_' + edge_type[0]) or3597getattr(perm, 'has_' + edge_type[0])(*(edge_type[1]))):3598q = perm.rauzy_move(t)3599q = self._permutation_to_vertex(q)3600if reducible or perm.is_irreducible():3601if not q in succ:3602succ[q] = [None] * len(self._edge_types)3603pred[q] = [None] * len(self._edge_types)3604l.append(q)36053606succ[p][t] = q3607pred[q][t] = p360836093610