def __init__(self, permutation=None, lengths=None, base_ring=None): r""" INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths TEST:: sage: p = iet.IntervalExchangeTransformation(('a','a'), [1]) sage: p == loads(dumps(p)) True """ from labelled import LabelledPermutationIET if permutation is None or lengths is None: self._permutation = LabelledPermutationIET() self._lengths = [] self._base_ring = base_ring else: self._permutation = permutation if base_ring is None: from sage.structure.sequence import Sequence base_ring = Sequence(lengths).universe() self._lengths = [base_ring(x) for x in lengths] self._base_ring = base_ring
def __init__(self, permutation=None, lengths=None): r""" INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths TEST:: sage: p=iet.IntervalExchangeTransformation(('a','a'),[1]) sage: p == loads(dumps(p)) True """ from labelled import LabelledPermutationIET if permutation is None or lengths is None: self._permutation = LabelledPermutationIET() self._lengths = [] else: self._permutation = permutation self._lengths = lengths
def __init__(self,permutation=None,lengths=None): r""" INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths TEST:: sage: p=iet.IntervalExchangeTransformation(('a','a'),[1]) sage: p == loads(dumps(p)) True """ from labelled import LabelledPermutationIET if permutation is None or lengths is None: self._permutation = LabelledPermutationIET() self._lengths = [] else: self._permutation = permutation self._lengths = lengths
def Permutation(*args,**kargs): r""" Returns a permutation of an interval exchange transformation. Those permutations are the combinatoric part of an interval exchange transformation (IET). The combinatorial study of those objects starts with Gerard Rauzy [R79]_ and William Veech [V78]_. The combinatoric part of interval exchange transformation can be taken independently from its dynamical origin. It has an important link with strata of Abelian differential (see :mod:`~sage.combinat.iet.strata`) INPUT: - ``intervals`` - string, two strings, list, tuples that can be converted to two lists - ``reduced`` - boolean (default: False) specifies reduction. False means labelled permutation and True means reduced permutation. - ``flips`` - iterable (default: None) the letters which correspond to flipped intervals. OUTPUT: permutation -- the output type depends of the data. EXAMPLES: Creation of labelled permutations :: sage: iet.Permutation('a b c d','d c b a') a b c d d c b a sage: iet.Permutation([[0,1,2,3],[2,1,3,0]]) 0 1 2 3 2 1 3 0 sage: iet.Permutation([0, 'A', 'B', 1], ['B', 0, 1, 'A']) 0 A B 1 B 0 1 A Creation of reduced permutations:: sage: iet.Permutation('a b c', 'c b a', reduced = True) a b c c b a sage: iet.Permutation([0, 1, 2, 3], [1, 3, 0, 2]) 0 1 2 3 1 3 0 2 Creation of flipped permutations:: sage: iet.Permutation('a b c', 'c b a', flips=['a','b']) -a -b c c -b -a sage: iet.Permutation('a b c', 'c b a', flips=['a'], reduced=True) -a b c c b -a TESTS: :: sage: p = iet.Permutation('a b c','c b a') sage: iet.Permutation(p) == p True sage: iet.Permutation(p, reduced=True) == p.reduced() True :: sage: p = iet.Permutation('a','a',flips='a',reduced=True) sage: iet.Permutation(p) == p True :: sage: p = iet.Permutation('a b c','c b a',flips='a') sage: iet.Permutation(p) == p True sage: iet.Permutation(p, reduced=True) == p.reduced() True :: sage: p = iet.Permutation('a b c','c b a',reduced=True) sage: iet.Permutation(p) == p True """ from labelled import LabelledPermutation from labelled import LabelledPermutationIET from labelled import FlippedLabelledPermutationIET from reduced import ReducedPermutation from reduced import ReducedPermutationIET from reduced import FlippedReducedPermutationIET if 'reduced' not in kargs : reduction = None elif not isinstance(kargs["reduced"], bool) : raise TypeError("reduced must be of type boolean") else : if kargs["reduced"] == True : reduction = True else : reduction = False if 'flips' not in kargs : flips = [] else : flips = list(kargs['flips']) if 'alphabet' not in kargs : alphabet = None else : alphabet = kargs['alphabet'] if len(args) == 1: args = args[0] if isinstance(args, LabelledPermutation): if flips == []: if reduction is None or reduction is False: from copy import copy return copy(args) else: return args.reduced() else: # conversion not yet implemented reduced = reduction in (None, False) return PermutationIET( args.list(), reduced=reduced, flips=flips, alphabet=alphabet) if isinstance(args, ReducedPermutation): if flips == []: if reduction is None or reduction is True: from copy import copy return copy(args) else: # conversion not yet implemented return PermutationIET( args.list(), reduced=True) else: # conversion not yet implemented reduced = reduction in (None, True) return PermutationIET( args.list(), reduced=reduced, flips=flips, alphabet=alphabet) a = _two_lists(args) l = a[0] + a[1] letters = set(l) for letter in flips : if letter not in letters : raise ValueError, "flips contains not valid letters" for letter in letters : if a[0].count(letter) != 1 or a[1].count(letter) != 1: raise ValueError, "letters must appear once in each interval" if reduction == True : if flips == [] : return ReducedPermutationIET(a, alphabet=alphabet) else : return FlippedReducedPermutationIET(a, alphabet=alphabet, flips=flips) else : if flips == [] : return LabelledPermutationIET(a, alphabet=alphabet) else : return FlippedLabelledPermutationIET(a, alphabet=alphabet, flips=flips)
class IntervalExchangeTransformation(SageObject): r""" Interval exchange transformation INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths EXAMPLES: Direct initialization:: sage: p = iet.IET(('a b c','c b a'),{'a':1,'b':1,'c':1}) sage: p.permutation() a b c c b a sage: p.lengths() [1, 1, 1] Initialization from a iet.Permutation:: sage: perm = iet.Permutation('a b c','c b a') sage: l = [0.5,1,1.2] sage: t = iet.IET(perm,l) sage: t.permutation() == perm True sage: t.lengths() == l True Initialization from a Permutation:: sage: p = Permutation([3,2,1]) sage: iet.IET(p, [1,1,1]) Interval exchange transformation of [0, 3[ with permutation 1 2 3 3 2 1 If it is not possible to convert lengths to real values an error is raised:: sage: iet.IntervalExchangeTransformation(('a b','b a'),['e','f']) Traceback (most recent call last): ... TypeError: unable to convert x (='e') into a real number The value for the lengths must be positive:: sage: iet.IET(('a b','b a'),[-1,-1]) Traceback (most recent call last): ... ValueError: lengths must be positive """ def __init__(self,permutation=None,lengths=None): r""" INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths TEST:: sage: p=iet.IntervalExchangeTransformation(('a','a'),[1]) sage: p == loads(dumps(p)) True """ from labelled import LabelledPermutationIET if permutation is None or lengths is None: self._permutation = LabelledPermutationIET() self._lengths = [] else: self._permutation = permutation self._lengths = lengths def permutation(self): r""" Returns the permutation associated to this iet. OUTPUT: permutation -- the permutation associated to this iet EXAMPLES:: sage: perm = iet.Permutation('a b c','c b a') sage: p = iet.IntervalExchangeTransformation(perm,(1,2,1)) sage: p.permutation() == perm True """ return copy(self._permutation) def length(self): r""" Returns the total length of the interval. OUTPUT: real -- the length of the interval EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t.length() 2 """ return sum(self._lengths) def lengths(self): r""" Returns the list of lengths associated to this iet. OUTPUT: list -- the list of lengths of subinterval EXAMPLES:: sage: p = iet.IntervalExchangeTransformation(('a b','b a'),[1,3]) sage: p.lengths() [1, 3] """ return copy(self._lengths) def normalize(self, total=1): r""" Returns a interval exchange transformation of normalized lengths. The normalization consists in multiplying all lengths by a constant in such way that their sum is given by ``total`` (default is 1). INPUT: - ``total`` - (default: 1) The total length of the interval OUTPUT: iet -- the normalized iet EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'), [1,3]) sage: t.length() 4 sage: s = t.normalize(2) sage: s.length() 2 sage: s.lengths() [1/2, 3/2] TESTS:: sage: s = t.normalize('bla') Traceback (most recent call last): ... TypeError: unable to convert total (='bla') into a real number sage: s = t.normalize(-691) Traceback (most recent call last): ... ValueError: the total length must be positive """ try: float(total) except ValueError: raise TypeError("unable to convert total (='%s') into a real number" % (str(total))) if total <= 0: raise ValueError("the total length must be positive") res = copy(self) coeff = total / res.length() res._multiply_lengths(coeff) return res def _multiply_lengths(self, x): r""" Multiplies the lengths of self by x (no verification on x). INPUT: - ``x`` - a positive number TESTS:: sage: t = iet.IET(("a","a"), [1]) sage: t.lengths() [1] sage: t._multiply_lengths(2) sage: t.lengths() [2] """ self._lengths = map(lambda t: t*x, self._lengths) def _repr_(self): r""" A representation string. EXAMPLES:: sage: a = iet.IntervalExchangeTransformation(('a','a'),[1]) sage: a # indirect doctest Interval exchange transformation of [0, 1[ with permutation a a """ interval = "[0, %s["%self.length() s = "Interval exchange transformation of %s "%interval s += "with permutation\n%s"%self._permutation return s def is_identity(self): r""" Returns True if self is the identity. OUTPUT: boolean -- the answer EXAMPLES:: sage: p = iet.Permutation("a b","b a") sage: q = iet.Permutation("c d","d c") sage: s = iet.IET(p, [1,5]) sage: t = iet.IET(q, [5,1]) sage: (s*t).is_identity() True sage: (t*s).is_identity() True """ return self._permutation.is_identity() def inverse(self): r""" Returns the inverse iet. OUTPUT: iet -- the inverse interval exchange transformation EXAMPLES:: sage: p = iet.Permutation("a b","b a") sage: s = iet.IET(p, [1,sqrt(2)-1]) sage: t = s.inverse() sage: t.permutation() b a a b sage: t.lengths() [1, sqrt(2) - 1] sage: t*s Interval exchange transformation of [0, sqrt(2)[ with permutation aa bb aa bb We can verify with the method .is_identity():: sage: p = iet.Permutation("a b c d","d a c b") sage: s = iet.IET(p, [1, sqrt(2), sqrt(3), sqrt(5)]) sage: (s * s.inverse()).is_identity() True sage: (s.inverse() * s).is_identity() True """ res = copy(self) res._permutation._inversed() return res def __mul__(self, other): r""" Composition of iet. The domain (i.e. the length) of the two iets must be the same). The alphabet choosen depends on the permutation. TESTS: :: sage: p = iet.Permutation("a b", "a b") sage: t = iet.IET(p, [1,1]) sage: r = t*t sage: r.permutation() aa bb aa bb sage: r.lengths() [1, 1] :: sage: p = iet.Permutation("a b","b a") sage: t = iet.IET(p, [1,1]) sage: r = t*t sage: r.permutation() ab ba ab ba sage: r.lengths() [1, 1] :: sage: p = iet.Permutation("1 2 3 4 5","5 4 3 2 1") sage: q = iet.Permutation("a b","b a") sage: s = iet.IET(p, [1]*5) sage: t = iet.IET(q, [1/2, 9/2]) sage: r = s*t sage: r.permutation() a5 b1 b2 b3 b4 b5 b5 a5 b4 b3 b2 b1 sage: r.lengths() [1/2, 1, 1, 1, 1, 1/2] sage: r = t*s sage: r.permutation() 1b 2b 3b 4b 5a 5b 5b 4b 3b 2b 1b 5a sage: r.lengths() [1, 1, 1, 1, 1/2, 1/2] sage: t = iet.IET(q, [3/2, 7/2]) sage: r = s*t sage: r.permutation() a4 a5 b1 b2 b3 b4 a5 b4 a4 b3 b2 b1 sage: r.lengths() [1/2, 1, 1, 1, 1, 1/2] sage: t = iet.IET(q, [5/2,5/2]) sage: r = s*t sage: r.permutation() a3 a4 a5 b1 b2 b3 a5 a4 b3 a3 b2 b1 sage: r = t*s sage: r.permutation() 1b 2b 3a 3b 4a 5a 3b 2b 1b 5a 4a 3a :: sage: p = iet.Permutation("a b","b a") sage: s = iet.IET(p, [4,2]) sage: q = iet.Permutation("c d","d c") sage: t = iet.IET(q, [3, 3]) sage: r1 = t * s sage: r1.permutation() ac ad bc ad bc ac sage: r1.lengths() [1, 3, 2] sage: r2 = s * t sage: r2.permutation() ca cb da cb da ca sage: r2.lengths() [1, 2, 3] :: sage: r * s Traceback (most recent call last): ... ValueError: self and other are not IET of the same length """ if not(isinstance(other, IntervalExchangeTransformation) and self.length() == other.length()): raise ValueError("self and other are not IET of the same length") from labelled import LabelledPermutationIET other_sg = other.range_singularities()[1:] self_sg = self.domain_singularities()[1:] n_other = len(other._permutation) n_self = len(self._permutation) interval_other = other._permutation._intervals[1] interval_self = self._permutation._intervals[0] d_other = dict([(i,[]) for i in interval_other]) d_self = dict([(i,[]) for i in interval_self]) i_other = 0 i_self = 0 x = 0 l_lengths = [] while i_other < n_other and i_self < n_self: j_other = interval_other[i_other] j_self = interval_self[i_self] d_other[j_other].append(j_self) d_self[j_self].append(j_other) if other_sg[i_other] < self_sg[i_self]: l = other_sg[i_other] - x x = other_sg[i_other] i_other += 1 elif other_sg[i_other] > self_sg[i_self]: l = self_sg[i_self] - x x = self_sg[i_self] i_self += 1 else: l = self_sg[i_self] - x x = self_sg[i_self] i_other += 1 i_self += 1 l_lengths.append(((j_other,j_self),l)) alphabet_other = other._permutation.alphabet() alphabet_self = self._permutation.alphabet() d_lengths = dict(l_lengths) l_lengths = [] top_interval = [] for i in other._permutation._intervals[0]: for j in d_other[i]: a = alphabet_other.unrank(i) b = alphabet_self.unrank(j) top_interval.append(str(a)+str(b)) l_lengths.append(d_lengths[(i,j)]) bottom_interval = [] for i in self._permutation._intervals[1]: for j in d_self[i]: a = alphabet_other.unrank(j) b = alphabet_self.unrank(i) bottom_interval.append(str(a)+str(b)) p = LabelledPermutationIET((top_interval,bottom_interval)) return IntervalExchangeTransformation(p,l_lengths) def __eq__(self, other): r""" Tests equality TESTS:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t == t True """ return ( type(self) == type(other) and self._permutation == other._permutation and self._lengths == other._lengths) def __ne__(self, other): r""" Tests difference TESTS:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t != t False """ return ( type(self) != type(other) or self._permutation != other._permutation or self._lengths != other._lengths) def in_which_interval(self, x, interval=0): r""" Returns the letter for which x is in this interval. INPUT: - ``x`` - a positive number - ``interval`` - (default: 'top') 'top' or 'bottom' OUTPUT: label -- a label corresponding to an interval TEST: :: sage: t = iet.IntervalExchangeTransformation(('a b c','c b a'),[1,1,1]) sage: t.in_which_interval(0) 'a' sage: t.in_which_interval(0.3) 'a' sage: t.in_which_interval(1) 'b' sage: t.in_which_interval(1.9) 'b' sage: t.in_which_interval(2) 'c' sage: t.in_which_interval(2.1) 'c' sage: t.in_which_interval(3) Traceback (most recent call last): ... ValueError: your value does not lie in [0;l[ .. and for the bottom interval:: sage: t.in_which_interval(0,'bottom') 'c' sage: t.in_which_interval(1.2,'bottom') 'b' sage: t.in_which_interval(2.9,'bottom') 'a' TESTS:: sage: t.in_which_interval(-2.9,'bottom') Traceback (most recent call last): ... ValueError: your value does not lie in [0;l[ """ interval = interval_conversion(interval) if x < 0 or x >= self.length(): raise ValueError("your value does not lie in [0;l[") i = 0 while x >= 0: x -= self._lengths[self._permutation._intervals[interval][i]] i += 1 i -= 1 x += self._lengths[self._permutation._intervals[interval][i]] j = self._permutation._intervals[interval][i] return self._permutation._alphabet.unrank(j) def singularities(self): r""" The list of singularities of `T` and `T^{-1}`. OUTPUT: list -- two lists of positive numbers which corresponds to extremities of subintervals EXAMPLE:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1/2,3/2]) sage: t.singularities() [[0, 1/2, 2], [0, 3/2, 2]] """ return [self.domain_singularities(), self.range_singularities()] def domain_singularities(self): r""" Returns the list of singularities of T OUTPUT: list -- positive reals that corresponds to singularities in the top interval EXAMPLES:: sage: t = iet.IET(("a b","b a"), [1, sqrt(2)]) sage: t.domain_singularities() [0, 1, sqrt(2) + 1] """ l = [0] for j in self._permutation._intervals[0]: l.append(l[-1] + self._lengths[j]) return l def range_singularities(self): r""" Returns the list of singularities of `T^{-1}` OUTPUT: list -- real numbers that are singular for `T^{-1}` EXAMPLES:: sage: t = iet.IET(("a b","b a"), [1, sqrt(2)]) sage: t.range_singularities() [0, sqrt(2), sqrt(2) + 1] """ l = [0] for j in self._permutation._intervals[1]: l.append(l[-1] + self._lengths[j]) return l def __call__(self, value): r""" Return the image of value by this transformation EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1/2,3/2]) sage: t(0) 3/2 sage: t(1/2) 0 sage: t(1) 1/2 sage: t(3/2) 1 TESTS:: sage: t(-3/2) Traceback (most recent call last): ... ValueError: value must positive and smaller than length """ if not(value >= 0 and value < self.length()): raise ValueError("value must positive and smaller than length") dom_sg = self.domain_singularities() im_sg = self.range_singularities() a = self.in_which_interval(value) i0 = self._permutation[0].index(a) i1 = self._permutation[1].index(a) return value - dom_sg[i0] + im_sg[i1] def rauzy_move(self, side='right', iterations=1): r""" Performs a Rauzy move. INPUT: - ``side`` - 'left' (or 'l' or 0) or 'right' (or 'r' or 1) - ``iterations`` - integer (default :1) the number of iteration of Rauzy moves to perform OUTPUT: iet -- the Rauzy move of self EXAMPLES:: sage: phi = QQbar((sqrt(5)-1)/2) sage: t1 = iet.IntervalExchangeTransformation(('a b','b a'),[1,phi]) sage: t2 = t1.rauzy_move().normalize(t1.length()) sage: l2 = t2.lengths() sage: l1 = t1.lengths() sage: l2[0] == l1[1] and l2[1] == l1[0] True """ side = side_conversion(side) res = copy(self) for i in range(iterations): res = res._rauzy_move(side) return res def _rauzy_move(self,side=-1): r""" Performs a Rauzy move INPUT: - ``side`` - must be 0 or -1 (no verification) TEST: sage: t = iet.IntervalExchangeTransformation(('a b c','c b a'),[1,1,3]) sage: t Interval exchange transformation of [0, 5[ with permutation a b c c b a sage: t1 = t.rauzy_move() #indirect doctest sage: t1 Interval exchange transformation of [0, 4[ with permutation a b c c a b sage: t2 = t1.rauzy_move() #indirect doctest sage: t2 Interval exchange transformation of [0, 3[ with permutation a b c c b a sage: t2.rauzy_move() #indirect doctest Traceback (most recent call last): ... ValueError: top and bottom extrem intervals have equal lengths """ top = self._permutation._intervals[0][side] bottom = self._permutation._intervals[1][side] length_top = self._lengths[top] length_bottom = self._lengths[bottom] if length_top > length_bottom: winner = 0 winner_interval = top loser_interval = bottom elif length_top < length_bottom: winner = 1 winner_interval = bottom loser_interval = top else: raise ValueError("top and bottom extrem intervals have equal lengths") res = IntervalExchangeTransformation(([],[]),{}) res._permutation = self._permutation.rauzy_move(winner=winner,side=side) res._lengths = self._lengths[:] res._lengths[winner_interval] -= res._lengths[loser_interval] return res def __copy__(self): r""" Returns a copy of this interval exchange transformation. EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: s = copy(t) sage: s == t True sage: s is t False """ res = self.__class__() res._permutation = copy(self._permutation) res._lengths = copy(self._lengths) return res def plot_function(self,**d): r""" Return a plot of the interval exchange transformation as a function. INPUT: - Any option that is accepted by line2d OUTPUT: 2d plot -- a plot of the iet as a function EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b c d','d a c b'),[1,1,1,1]) sage: t.plot_function(rgbcolor=(0,1,0)) """ from sage.plot.all import Graphics from sage.plot.plot import line2d G = Graphics() l = self.singularities() t = self._permutation._twin for i in range(len(self._permutation)): j = t[0][i] G += line2d([(l[0][i],l[1][j]),(l[0][i+1],l[1][j+1])],**d) return G def plot_two_intervals(self, position=(0,0), vertical_alignment='center', horizontal_alignment='left', interval_height=0.1, labels_height=0.05, fontsize=14, labels=True, colors=None): r""" Returns a picture of the interval exchange transformation. INPUT: - ``position`` - a 2-uple of the position - ``horizontal_alignment`` - left (defaut), center or right - ``labels`` - boolean (defaut: True) - ``fontsize`` - the size of the label OUTPUT: 2d plot -- a plot of the two intervals (domain and range) EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t.plot_two_intervals() """ from sage.plot.all import Graphics from sage.plot.plot import line2d from sage.plot.plot import text from sage.plot.colors import rainbow G = Graphics() lengths = map(float,self._lengths) total_length = sum(lengths) if colors is None: colors = rainbow(len(self._permutation), 'rgbtuple') if horizontal_alignment == 'left': s = position[0] elif horizontal_alignment == 'center': s = position[0] - total_length / 2 elif horizontal_alignment == 'right': s = position[0] - total_length else: raise ValueError("horizontal_alignement must be left, center or right") top_height = position[1] + interval_height for i in self._permutation._intervals[0]: G += line2d([(s,top_height), (s+lengths[i],top_height)], rgbcolor=colors[i]) if labels: G += text(str(self._permutation._alphabet.unrank(i)), (s+float(lengths[i])/2, top_height+labels_height), horizontal_alignment='center', rgbcolor=colors[i], fontsize=fontsize) s += lengths[i] if horizontal_alignment == 'left': s = position[0] elif horizontal_alignment == 'center': s = position[0] - total_length / 2 elif horizontal_alignment == 'right': s = position[0] - total_length else: raise ValueError("horizontal_alignement must be left, center or right") bottom_height = position[1] - interval_height for i in self._permutation._intervals[1]: G += line2d([(s,bottom_height), (s+lengths[i],bottom_height)], rgbcolor=colors[i]) if labels: G += text(str(self._permutation._alphabet.unrank(i)), (s+float(lengths[i])/2, bottom_height-labels_height), horizontal_alignment='center', rgbcolor=colors[i], fontsize=fontsize) s += lengths[i] return G plot = plot_two_intervals def show(self): r""" Shows a picture of the interval exchange transformation EXAMPLES:: sage: phi = QQbar((sqrt(5)-1)/2) sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,phi]) sage: t.show() """ self.plot_two_intervals().show(axes=False)
class IntervalExchangeTransformation(SageObject): r""" Interval exchange transformation INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths EXAMPLES: Direct initialization:: sage: p = iet.IET(('a b c','c b a'),{'a':1,'b':1,'c':1}) sage: p.permutation() a b c c b a sage: p.lengths() [1, 1, 1] Initialization from a iet.Permutation:: sage: perm = iet.Permutation('a b c','c b a') sage: l = [0.5,1,1.2] sage: t = iet.IET(perm,l) sage: t.permutation() == perm True sage: t.lengths() == l True Initialization from a Permutation:: sage: p = Permutation([3,2,1]) sage: iet.IET(p, [1,1,1]) Interval exchange transformation of [0, 3[ with permutation 1 2 3 3 2 1 If it is not possible to convert lengths to real values an error is raised:: sage: iet.IntervalExchangeTransformation(('a b','b a'),['e','f']) Traceback (most recent call last): ... TypeError: unable to convert x (='e') into a real number The value for the lengths must be positive:: sage: iet.IET(('a b','b a'),[-1,-1]) Traceback (most recent call last): ... ValueError: lengths must be positive """ def __init__(self, permutation=None, lengths=None): r""" INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths TEST:: sage: p=iet.IntervalExchangeTransformation(('a','a'),[1]) sage: p == loads(dumps(p)) True """ from labelled import LabelledPermutationIET if permutation is None or lengths is None: self._permutation = LabelledPermutationIET() self._lengths = [] else: self._permutation = permutation self._lengths = lengths def permutation(self): r""" Returns the permutation associated to this iet. OUTPUT: permutation -- the permutation associated to this iet EXAMPLES:: sage: perm = iet.Permutation('a b c','c b a') sage: p = iet.IntervalExchangeTransformation(perm,(1,2,1)) sage: p.permutation() == perm True """ return copy(self._permutation) def length(self): r""" Returns the total length of the interval. OUTPUT: real -- the length of the interval EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t.length() 2 """ return sum(self._lengths) def lengths(self): r""" Returns the list of lengths associated to this iet. OUTPUT: list -- the list of lengths of subinterval EXAMPLES:: sage: p = iet.IntervalExchangeTransformation(('a b','b a'),[1,3]) sage: p.lengths() [1, 3] """ return copy(self._lengths) def normalize(self, total=1): r""" Returns a interval exchange transformation of normalized lengths. The normalization consist in consider a constant homothetic value for each lengths in such way that the sum is given (default is 1). INPUT: - ``total`` - (default: 1) The total length of the interval OUTPUT: iet -- the normalized iet EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'), [1,3]) sage: t.length() 4 sage: s = t.normalize(2) sage: s.length() 2 sage: s.lengths() [1/2, 3/2] """ try: y = float(total) except ValueError: raise TypeError, "unable to convert x (='%s') into a real number" % ( str(x)) if total <= 0: raise ValueError, "the total length must be positive" res = copy(self) coeff = total / res.length() res._multiply_lengths(coeff) return res def _multiply_lengths(self, x): r""" Multiplies the lengths of self by x (no verification on x). INPUT: - ``x`` - a positive number TESTS:: sage: t = iet.IET(("a","a"), [1]) sage: t.lengths() [1] sage: t._multiply_lengths(2) sage: t.lengths() [2] """ self._lengths = map(lambda t: t * x, self._lengths) def _repr_(self): r""" A representation string. EXAMPLES:: sage: a = iet.IntervalExchangeTransformation(('a','a'),[1]) sage: a #indirect doctest Interval exchange transformation of [0, 1[ with permutation a a """ interval = "[0, %s[" % self.length() s = "Interval exchange transformation of %s " % interval s += "with permutation\n%s" % self._permutation return s def is_identity(self): r""" Returns True if self is the identity. OUTPUT: boolean -- the answer EXAMPLES:: sage: p = iet.Permutation("a b","b a") sage: q = iet.Permutation("c d","d c") sage: s = iet.IET(p, [1,5]) sage: t = iet.IET(q, [5,1]) sage: (s*t).is_identity() True sage: (t*s).is_identity() True """ return self._permutation.is_identity() def inverse(self): r""" Returns the inverse iet. OUTPUT: iet -- the inverse interval exchange transformation EXAMPLES:: sage: p = iet.Permutation("a b","b a") sage: s = iet.IET(p, [1,sqrt(2)-1]) sage: t = s.inverse() sage: t.permutation() b a a b sage: t.lengths() [1, sqrt(2) - 1] sage: t*s Interval exchange transformation of [0, sqrt(2)[ with permutation aa bb aa bb We can verify with the method .is_identity():: sage: p = iet.Permutation("a b c d","d a c b") sage: s = iet.IET(p, [1, sqrt(2), sqrt(3), sqrt(5)]) sage: (s * s.inverse()).is_identity() True sage: (s.inverse() * s).is_identity() True """ res = copy(self) res._permutation._inversed() return res def __mul__(self, other): r""" Composition of iet. The domain (i.e. the length) of the two iets must be the same). The alphabet choosen depends on the permutation. TESTS: :: sage: p = iet.Permutation("a b", "a b") sage: t = iet.IET(p, [1,1]) sage: r = t*t sage: r.permutation() aa bb aa bb sage: r.lengths() [1, 1] :: sage: p = iet.Permutation("a b","b a") sage: t = iet.IET(p, [1,1]) sage: r = t*t sage: r.permutation() ab ba ab ba sage: r.lengths() [1, 1] :: sage: p = iet.Permutation("1 2 3 4 5","5 4 3 2 1") sage: q = iet.Permutation("a b","b a") sage: s = iet.IET(p, [1]*5) sage: t = iet.IET(q, [1/2, 9/2]) sage: r = s*t sage: r.permutation() a5 b1 b2 b3 b4 b5 b5 a5 b4 b3 b2 b1 sage: r.lengths() [1/2, 1, 1, 1, 1, 1/2] sage: r = t*s sage: r.permutation() 1b 2b 3b 4b 5a 5b 5b 4b 3b 2b 1b 5a sage: r.lengths() [1, 1, 1, 1, 1/2, 1/2] sage: t = iet.IET(q, [3/2, 7/2]) sage: r = s*t sage: r.permutation() a4 a5 b1 b2 b3 b4 a5 b4 a4 b3 b2 b1 sage: r.lengths() [1/2, 1, 1, 1, 1, 1/2] sage: t = iet.IET(q, [5/2,5/2]) sage: r = s*t sage: r.permutation() a3 a4 a5 b1 b2 b3 a5 a4 b3 a3 b2 b1 sage: r = t*s sage: r.permutation() 1b 2b 3a 3b 4a 5a 3b 2b 1b 5a 4a 3a :: sage: p = iet.Permutation("a b","b a") sage: s = iet.IET(p, [4,2]) sage: q = iet.Permutation("c d","d c") sage: t = iet.IET(q, [3, 3]) sage: r1 = t * s sage: r1.permutation() ac ad bc ad bc ac sage: r1.lengths() [1, 3, 2] sage: r2 = s * t sage: r2.permutation() ca cb da cb da ca sage: r2.lengths() [1, 2, 3] """ assert (isinstance(other, IntervalExchangeTransformation) and self.length() == other.length()) from labelled import LabelledPermutationIET from sage.combinat.words.words import Words other_sg = other.range_singularities()[1:] self_sg = self.domain_singularities()[1:] n_other = len(other._permutation) n_self = len(self._permutation) interval_other = other._permutation._intervals[1] interval_self = self._permutation._intervals[0] d_other = dict([(i, []) for i in interval_other]) d_self = dict([(i, []) for i in interval_self]) i_other = 0 i_self = 0 x = 0 l_lengths = [] while i_other < n_other and i_self < n_self: j_other = interval_other[i_other] j_self = interval_self[i_self] d_other[j_other].append(j_self) d_self[j_self].append(j_other) if other_sg[i_other] < self_sg[i_self]: l = other_sg[i_other] - x x = other_sg[i_other] i_other += 1 elif other_sg[i_other] > self_sg[i_self]: l = self_sg[i_self] - x x = self_sg[i_self] i_self += 1 else: l = self_sg[i_self] - x x = self_sg[i_self] i_other += 1 i_self += 1 l_lengths.append(((j_other, j_self), l)) alphabet_other = other._permutation.alphabet() alphabet_self = self._permutation.alphabet() d_lengths = dict(l_lengths) l_lengths = [] top_interval = [] for i in other._permutation._intervals[0]: for j in d_other[i]: a = alphabet_other.unrank(i) b = alphabet_self.unrank(j) top_interval.append(str(a) + str(b)) l_lengths.append(d_lengths[(i, j)]) bottom_interval = [] for i in self._permutation._intervals[1]: for j in d_self[i]: a = alphabet_other.unrank(j) b = alphabet_self.unrank(i) bottom_interval.append(str(a) + str(b)) p = LabelledPermutationIET((top_interval, bottom_interval)) return IntervalExchangeTransformation(p, l_lengths) def __eq__(self, other): r""" Tests equality TESTS:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t == t True """ return (type(self) == type(other) and self._permutation == other._permutation and self._lengths == other._lengths) def __ne__(self, other): r""" Tests difference TESTS:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t != t False """ return (type(self) != type(other) or self._permutation != other._permutation or self._lengths != other._lengths) def in_which_interval(self, x, interval=0): r""" Returns the letter for which x is in this interval. INPUT: - ``x`` - a positive number - ``interval`` - (default: 'top') 'top' or 'bottom' OUTPUT: label -- a label corresponding to an interval TEST: :: sage: t = iet.IntervalExchangeTransformation(('a b c','c b a'),[1,1,1]) sage: t.in_which_interval(0) 'a' sage: t.in_which_interval(0.3) 'a' sage: t.in_which_interval(1) 'b' sage: t.in_which_interval(1.9) 'b' sage: t.in_which_interval(2) 'c' sage: t.in_which_interval(2.1) 'c' sage: t.in_which_interval(3) Traceback (most recent call last): ... ValueError: your value does not lie in [0;l[ .. and for the bottom interval:: sage: t.in_which_interval(0,'bottom') 'c' sage: t.in_which_interval(1.2,'bottom') 'b' sage: t.in_which_interval(2.9,'bottom') 'a' """ interval = interval_conversion(interval) if x < 0 or x >= self.length(): raise ValueError, "your value does not lie in [0;l[" i = 0 while x >= 0: x -= self._lengths[self._permutation._intervals[interval][i]] i += 1 i -= 1 x += self._lengths[self._permutation._intervals[interval][i]] j = self._permutation._intervals[interval][i] return self._permutation._alphabet.unrank(j) def singularities(self): r""" The list of singularities of 'T' and 'T^{-1}'. OUTPUT: list -- two lists of positive numbers which corresponds to extremities of subintervals EXAMPLE:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1/2,3/2]) sage: t.singularities() [[0, 1/2, 2], [0, 3/2, 2]] """ return [self.domain_singularities(), self.range_singularities()] def domain_singularities(self): r""" Returns the list of singularities of T OUTPUT: list -- positive reals that corresponds to singularities in the top interval EXAMPLES:: sage: t = iet.IET(("a b","b a"), [1, sqrt(2)]) sage: t.domain_singularities() [0, 1, sqrt(2) + 1] """ l = [0] for j in self._permutation._intervals[0]: l.append(l[-1] + self._lengths[j]) return l def range_singularities(self): r""" Returns the list of singularities of `T^{-1}` OUTPUT: list -- real numbers that are singular for `T^{-1}` EXAMPLES:: sage: t = iet.IET(("a b","b a"), [1, sqrt(2)]) sage: t.range_singularities() [0, sqrt(2), sqrt(2) + 1] """ l = [0] for j in self._permutation._intervals[1]: l.append(l[-1] + self._lengths[j]) return l def __call__(self, value): r""" Return the image of value by this transformation EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1/2,3/2]) sage: t(0) 3/2 sage: t(1/2) 0 sage: t(1) 1/2 sage: t(3/2) 1 """ assert (value >= 0 and value < self.length()) dom_sg = self.domain_singularities() im_sg = self.range_singularities() a = self.in_which_interval(value) i0 = self._permutation[0].index(a) i1 = self._permutation[1].index(a) return value - dom_sg[i0] + im_sg[i1] def rauzy_move(self, side='right', iterations=1): r""" Performs a Rauzy move. INPUT: - ``side`` - 'left' (or 'l' or 0) or 'right' (or 'r' or 1) - ``iterations`` - integer (default :1) the number of iteration of Rauzy moves to perform OUTPUT: iet -- the Rauzy move of self EXAMPLES:: sage: phi = QQbar((sqrt(5)-1)/2) sage: t1 = iet.IntervalExchangeTransformation(('a b','b a'),[1,phi]) sage: t2 = t1.rauzy_move().normalize(t1.length()) sage: l2 = t2.lengths() sage: l1 = t1.lengths() sage: l2[0] == l1[1] and l2[1] == l1[0] True """ side = side_conversion(side) res = copy(self) for i in range(iterations): res = res._rauzy_move(side) return res def _rauzy_move(self, side=-1): r""" Performs a Rauzy move INPUT: - ``side`` - must be 0 or -1 (no verification) TEST: sage: t = iet.IntervalExchangeTransformation(('a b c','c b a'),[1,1,3]) sage: t Interval exchange transformation of [0, 5[ with permutation a b c c b a sage: t1 = t.rauzy_move() #indirect doctest sage: t1 Interval exchange transformation of [0, 4[ with permutation a b c c a b sage: t2 = t1.rauzy_move() #indirect doctest sage: t2 Interval exchange transformation of [0, 3[ with permutation a b c c b a sage: t2.rauzy_move() #indirect doctest Traceback (most recent call last): ... ValueError: top and bottom extrem intervals have equal lengths """ top = self._permutation._intervals[0][side] bottom = self._permutation._intervals[1][side] length_top = self._lengths[top] length_bottom = self._lengths[bottom] if length_top > length_bottom: winner = 0 winner_interval = top loser_interval = bottom elif length_top < length_bottom: winner = 1 winner_interval = bottom loser_interval = top else: raise ValueError, "top and bottom extrem intervals have equal lengths" res = IntervalExchangeTransformation(([], []), {}) res._permutation = self._permutation.rauzy_move(winner=winner, side=side) res._lengths = self._lengths[:] res._lengths[winner_interval] -= res._lengths[loser_interval] return res def __copy__(self): r""" Returns a copy of this interval exchange transformation. EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: s = copy(t) sage: s == t True sage: s is t False """ res = self.__class__() res._permutation = copy(self._permutation) res._lengths = copy(self._lengths) return res def plot_function(self, **d): r""" Return a plot of the interval exchange transformation as a function. INPUT: - Any option that is accepted by line2d OUTPUT: 2d plot -- a plot of the iet as a function EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b c d','d a c b'),[1,1,1,1]) sage: t.plot_function(rgbcolor=(0,1,0)) """ from sage.plot.all import Graphics from sage.plot.plot import line2d G = Graphics() l = self.singularities() t = self._permutation._twin for i in range(len(self._permutation)): j = t[0][i] G += line2d([(l[0][i], l[1][j]), (l[0][i + 1], l[1][j + 1])], **d) return G def plot_two_intervals(self, position=(0, 0), vertical_alignment='center', horizontal_alignment='left', interval_height=0.1, labels_height=0.05, fontsize=14, labels=True, colors=None): r""" Returns a picture of the interval exchange transformation. INPUT: - ``position`` - a 2-uple of the position - ``horizontal_alignment`` - left (defaut), center or right - ``labels`` - boolean (defaut: True) - ``fontsize`` - the size of the label OUTPUT: 2d plot -- a plot of the two intervals (domain and range) EXAMPLES:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t.plot_two_intervals() """ from sage.plot.all import Graphics from sage.plot.plot import line2d from sage.plot.plot import text from sage.plot.colors import rainbow G = Graphics() lengths = map(float, self._lengths) total_length = sum(lengths) if colors is None: colors = rainbow(len(self._permutation), 'rgbtuple') if horizontal_alignment == 'left': s = position[0] elif horizontal_alignment == 'center': s = position[0] - total_length / 2 elif horizontal_alignment == 'right': s = position[0] - total_length else: raise ValueError, "horizontal_alignement must be left, center or right" top_height = position[1] + interval_height for i in self._permutation._intervals[0]: G += line2d([(s, top_height), (s + lengths[i], top_height)], rgbcolor=colors[i]) if labels == True: G += text( str(self._permutation._alphabet.unrank(i)), (s + float(lengths[i]) / 2, top_height + labels_height), horizontal_alignment='center', rgbcolor=colors[i], fontsize=fontsize) s += lengths[i] if horizontal_alignment == 'left': s = position[0] elif horizontal_alignment == 'center': s = position[0] - total_length / 2 elif horizontal_alignment == 'right': s = position[0] - total_length else: raise ValueError, "horizontal_alignement must be left, center or right" bottom_height = position[1] - interval_height for i in self._permutation._intervals[1]: G += line2d([(s, bottom_height), (s + lengths[i], bottom_height)], rgbcolor=colors[i]) if labels == True: G += text( str(self._permutation._alphabet.unrank(i)), (s + float(lengths[i]) / 2, bottom_height - labels_height), horizontal_alignment='center', rgbcolor=colors[i], fontsize=fontsize) s += lengths[i] return G plot = plot_two_intervals def show(self): r""" Shows a picture of the interval exchange transformation EXAMPLES:: sage: phi = QQbar((sqrt(5)-1)/2) sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,phi]) sage: t.show() """ self.plot_two_intervals().show(axes=False)
def __mul__(self, other): r""" Composition of iet. The domain (i.e. the length) of the two iets must be the same). The alphabet choosen depends on the permutation. TESTS: :: sage: p = iet.Permutation("a b", "a b") sage: t = iet.IET(p, [1,1]) sage: r = t*t sage: r.permutation() aa bb aa bb sage: r.lengths() [1, 1] :: sage: p = iet.Permutation("a b","b a") sage: t = iet.IET(p, [1,1]) sage: r = t*t sage: r.permutation() ab ba ab ba sage: r.lengths() [1, 1] :: sage: p = iet.Permutation("1 2 3 4 5","5 4 3 2 1") sage: q = iet.Permutation("a b","b a") sage: s = iet.IET(p, [1]*5) sage: t = iet.IET(q, [1/2, 9/2]) sage: r = s*t sage: r.permutation() a5 b1 b2 b3 b4 b5 b5 a5 b4 b3 b2 b1 sage: r.lengths() [1/2, 1, 1, 1, 1, 1/2] sage: r = t*s sage: r.permutation() 1b 2b 3b 4b 5a 5b 5b 4b 3b 2b 1b 5a sage: r.lengths() [1, 1, 1, 1, 1/2, 1/2] sage: t = iet.IET(q, [3/2, 7/2]) sage: r = s*t sage: r.permutation() a4 a5 b1 b2 b3 b4 a5 b4 a4 b3 b2 b1 sage: r.lengths() [1/2, 1, 1, 1, 1, 1/2] sage: t = iet.IET(q, [5/2,5/2]) sage: r = s*t sage: r.permutation() a3 a4 a5 b1 b2 b3 a5 a4 b3 a3 b2 b1 sage: r = t*s sage: r.permutation() 1b 2b 3a 3b 4a 5a 3b 2b 1b 5a 4a 3a :: sage: p = iet.Permutation("a b","b a") sage: s = iet.IET(p, [4,2]) sage: q = iet.Permutation("c d","d c") sage: t = iet.IET(q, [3, 3]) sage: r1 = t * s sage: r1.permutation() ac ad bc ad bc ac sage: r1.lengths() [1, 3, 2] sage: r2 = s * t sage: r2.permutation() ca cb da cb da ca sage: r2.lengths() [1, 2, 3] """ assert (isinstance(other, IntervalExchangeTransformation) and self.length() == other.length()) from labelled import LabelledPermutationIET from sage.combinat.words.words import Words other_sg = other.range_singularities()[1:] self_sg = self.domain_singularities()[1:] n_other = len(other._permutation) n_self = len(self._permutation) interval_other = other._permutation._intervals[1] interval_self = self._permutation._intervals[0] d_other = dict([(i, []) for i in interval_other]) d_self = dict([(i, []) for i in interval_self]) i_other = 0 i_self = 0 x = 0 l_lengths = [] while i_other < n_other and i_self < n_self: j_other = interval_other[i_other] j_self = interval_self[i_self] d_other[j_other].append(j_self) d_self[j_self].append(j_other) if other_sg[i_other] < self_sg[i_self]: l = other_sg[i_other] - x x = other_sg[i_other] i_other += 1 elif other_sg[i_other] > self_sg[i_self]: l = self_sg[i_self] - x x = self_sg[i_self] i_self += 1 else: l = self_sg[i_self] - x x = self_sg[i_self] i_other += 1 i_self += 1 l_lengths.append(((j_other, j_self), l)) alphabet_other = other._permutation.alphabet() alphabet_self = self._permutation.alphabet() d_lengths = dict(l_lengths) l_lengths = [] top_interval = [] for i in other._permutation._intervals[0]: for j in d_other[i]: a = alphabet_other.unrank(i) b = alphabet_self.unrank(j) top_interval.append(str(a) + str(b)) l_lengths.append(d_lengths[(i, j)]) bottom_interval = [] for i in self._permutation._intervals[1]: for j in d_self[i]: a = alphabet_other.unrank(j) b = alphabet_self.unrank(i) bottom_interval.append(str(a) + str(b)) p = LabelledPermutationIET((top_interval, bottom_interval)) return IntervalExchangeTransformation(p, l_lengths)
class IntervalExchangeTransformation(object): r""" Interval exchange transformation INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths EXAMPLES:: sage: from surface_dynamics.all import * Direct initialization:: sage: p = iet.IET(('a b c','c b a'),{'a':1,'b':1,'c':1}) sage: p.permutation() a b c c b a sage: p.lengths() [1, 1, 1] Initialization from a iet.Permutation:: sage: perm = iet.Permutation('a b c','c b a') sage: l = [0.5,1,1.2] sage: t = iet.IET(perm,l) sage: t.permutation() == perm True sage: t.lengths() == l True Initialization from a Permutation:: sage: p = Permutation([3,2,1]) sage: iet.IET(p, [1,1,1]) Interval exchange transformation of [0, 3[ with permutation 1 2 3 3 2 1 If it is not possible to convert lengths to real values an error is raised:: sage: iet.IntervalExchangeTransformation(('a b','b a'),['e','f']) Traceback (most recent call last): ... TypeError: unable to convert x (='e') into a real number The value for the lengths must be positive:: sage: iet.IET(('a b','b a'),[-1,-1]) Traceback (most recent call last): ... ValueError: lengths must be positive """ def __init__(self, permutation=None, lengths=None, base_ring=None): r""" INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths TEST:: sage: p = iet.IntervalExchangeTransformation(('a','a'), [1]) sage: p == loads(dumps(p)) True """ from labelled import LabelledPermutationIET if permutation is None or lengths is None: self._permutation = LabelledPermutationIET() self._lengths = [] self._base_ring = base_ring else: self._permutation = permutation if base_ring is None: from sage.structure.sequence import Sequence base_ring = Sequence(lengths).universe() self._lengths = [base_ring(x) for x in lengths] self._base_ring = base_ring def _zero(self): try: return self._base_ring.zero() except AttributeError: return self._base_ring(0) def _one(self): try: return self._base_ring.one() except AttributeError: return self._base_ring(1) def permutation(self): r""" Returns the permutation associated to this iet. OUTPUT: permutation -- the permutation associated to this iet EXAMPLES:: sage: from surface_dynamics.all import * sage: perm = iet.Permutation('a b c','c b a') sage: p = iet.IntervalExchangeTransformation(perm,(1,2,1)) sage: p.permutation() == perm True """ return copy(self._permutation) def length(self): r""" Returns the total length of the interval. OUTPUT: real -- the length of the interval EXAMPLES:: sage: from surface_dynamics.all import * sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t.length() 2 """ return sum(self._lengths) def lengths(self): r""" Returns the list of lengths associated to this iet. OUTPUT: list -- the list of lengths of subinterval EXAMPLES:: sage: from surface_dynamics.all import * sage: p = iet.IntervalExchangeTransformation(('a b','b a'),[1,3]) sage: p.lengths() [1, 3] """ return self._lengths[:] def translations(self): r""" Return the translations operated on each intervals. EXAMPLES:: sage: from surface_dynamics.all import * sage: p = iet.Permutation('a b c', 'c b a') sage: T = iet.IntervalExchangeTransformation(p, [5,1,3]) sage: T.translations() [4, -2, -6] """ dom_sg = self.domain_singularities() im_sg = self.range_singularities() p = self._permutation._labels top_twin = self._permutation._twin[0] top = p[0] bot = p[1] translations = [None] * (max(top)+1) for i0,j in enumerate(top): i1 = top_twin[i0] translations[j] = im_sg[i1] - dom_sg[i0] return translations def normalize(self, total=1): r""" Returns a interval exchange transformation of normalized lengths. The normalization consist in consider a constant homothetic value for each lengths in such way that the sum is given (default is 1). INPUT: - ``total`` - (default: 1) The total length of the interval OUTPUT: iet -- the normalized iet EXAMPLES:: sage: from surface_dynamics.all import * sage: t = iet.IntervalExchangeTransformation(('a b','b a'), [1,3]) sage: t.length() 4 sage: s = t.normalize(2) sage: s.length() 2 sage: s.lengths() [1/2, 3/2] """ try: y = float(total) except ValueError: raise TypeError("unable to convert x (='%s') into a real number" %(str(x))) if total <= 0: raise ValueError("the total length must be positive") res = copy(self) coeff = total / res.length() res._multiply_lengths(coeff) return res def _multiply_lengths(self, x): r""" Multiplies the lengths of self by x (no verification on x). INPUT: - ``x`` - a positive number TESTS:: sage: t = iet.IET(("a","a"), [1]) sage: t.lengths() [1] sage: t._multiply_lengths(2) sage: t.lengths() [2] """ self._lengths = [x*t for t in self._lengths] def __repr__(self): r""" A representation string. EXAMPLES:: sage: from surface_dynamics.all import * sage: a = iet.IntervalExchangeTransformation(('a','a'),[1]) sage: a #indirect doctest Interval exchange transformation of [0, 1[ with permutation a a """ interval = "[0, %s["%self.length() s = "Interval exchange transformation of %s "%interval s += "with permutation\n%s"%self._permutation return s def is_identity(self): r""" Returns True if self is the identity. OUTPUT: boolean -- the answer EXAMPLES:: sage: from surface_dynamics.all import * sage: p = iet.Permutation("a b","b a") sage: q = iet.Permutation("c d","d c") sage: s = iet.IET(p, [1,5]) sage: t = iet.IET(q, [5,1]) sage: (s*t).is_identity() True sage: (t*s).is_identity() True """ return self._permutation.is_identity() def inverse(self): r""" Returns the inverse iet. OUTPUT: iet -- the inverse interval exchange transformation EXAMPLES:: sage: from surface_dynamics.all import * sage: p = iet.Permutation("a b","b a") sage: s = iet.IET(p, [1,sqrt(2)-1]) sage: t = s.inverse() sage: t.permutation() b a a b sage: t.lengths() [1, sqrt(2) - 1] sage: t*s Interval exchange transformation of [0, sqrt(2)[ with permutation aa bb aa bb We can verify with the method .is_identity():: sage: p = iet.Permutation("a b c d","d a c b") sage: s = iet.IET(p, [1, sqrt(2), sqrt(3), sqrt(5)]) sage: (s * s.inverse()).is_identity() True sage: (s.inverse() * s).is_identity() True """ res = copy(self) res._permutation = self._permutation.top_bottom_inverse() return res def __mul__(self, other): r""" Composition of iet. The domain (i.e. the length) of the two iets must be the same). The alphabet choosen depends on the permutation. TESTS: :: sage: p = iet.Permutation("a b", "a b") sage: t = iet.IET(p, [1,1]) sage: r = t*t sage: r.permutation() aa bb aa bb sage: r.lengths() [1, 1] :: sage: p = iet.Permutation("a b","b a") sage: t = iet.IET(p, [1,1]) sage: r = t*t sage: r.permutation() ab ba ab ba sage: r.lengths() [1, 1] :: sage: p = iet.Permutation("1 2 3 4 5","5 4 3 2 1") sage: q = iet.Permutation("a b","b a") sage: s = iet.IET(p, [1]*5) sage: t = iet.IET(q, [1/2, 9/2]) sage: r = s*t sage: r.permutation() a5 b1 b2 b3 b4 b5 b5 a5 b4 b3 b2 b1 sage: r.lengths() [1/2, 1, 1, 1, 1, 1/2] sage: r = t*s sage: r.permutation() 1b 2b 3b 4b 5a 5b 5b 4b 3b 2b 1b 5a sage: r.lengths() [1, 1, 1, 1, 1/2, 1/2] sage: t = iet.IET(q, [3/2, 7/2]) sage: r = s*t sage: r.permutation() a4 a5 b1 b2 b3 b4 a5 b4 a4 b3 b2 b1 sage: r.lengths() [1/2, 1, 1, 1, 1, 1/2] sage: t = iet.IET(q, [5/2,5/2]) sage: r = s*t sage: r.permutation() a3 a4 a5 b1 b2 b3 a5 a4 b3 a3 b2 b1 sage: r = t*s sage: r.permutation() 1b 2b 3a 3b 4a 5a 3b 2b 1b 5a 4a 3a :: sage: p = iet.Permutation("a b","b a") sage: s = iet.IET(p, [4,2]) sage: q = iet.Permutation("c d","d c") sage: t = iet.IET(q, [3, 3]) sage: r1 = t * s sage: r1.permutation() ac ad bc ad bc ac sage: r1.lengths() [1, 3, 2] sage: r2 = s * t sage: r2.permutation() ca cb da cb da ca sage: r2.lengths() [1, 2, 3] """ assert( isinstance(other, IntervalExchangeTransformation) and self.length() == other.length()) from labelled import LabelledPermutationIET from sage.combinat.words.words import Words other_sg = other.range_singularities()[1:] self_sg = self.domain_singularities()[1:] n_other = len(other._permutation) n_self = len(self._permutation) interval_other = other._permutation._labels[1] interval_self = self._permutation._labels[0] d_other = dict([(i,[]) for i in interval_other]) d_self = dict([(i,[]) for i in interval_self]) i_other = 0 i_self = 0 x = self._zero() l_lengths = [] while i_other < n_other and i_self < n_self: j_other = interval_other[i_other] j_self = interval_self[i_self] d_other[j_other].append(j_self) d_self[j_self].append(j_other) if other_sg[i_other] < self_sg[i_self]: l = other_sg[i_other] - x x = other_sg[i_other] i_other += 1 elif other_sg[i_other] > self_sg[i_self]: l = self_sg[i_self] - x x = self_sg[i_self] i_self += 1 else: l = self_sg[i_self] - x x = self_sg[i_self] i_other += 1 i_self += 1 l_lengths.append(((j_other,j_self),l)) alphabet_other = other._permutation.alphabet() alphabet_self = self._permutation.alphabet() d_lengths = dict(l_lengths) l_lengths = [] top_interval = [] for i in other._permutation._labels[0]: for j in d_other[i]: a = alphabet_other.unrank(i) b = alphabet_self.unrank(j) top_interval.append(str(a)+str(b)) l_lengths.append(d_lengths[(i,j)]) bottom_interval = [] for i in self._permutation._labels[1]: for j in d_self[i]: a = alphabet_other.unrank(j) b = alphabet_self.unrank(i) bottom_interval.append(str(a)+str(b)) p = LabelledPermutationIET((top_interval,bottom_interval)) return IntervalExchangeTransformation(p,l_lengths) def __eq__(self, other): r""" Tests equality TESTS:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t == t True """ return ( isinstance(self, type(other)) and self._permutation == other._permutation and self._lengths == other._lengths) def __ne__(self, other): r""" Tests difference TESTS:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t != t False """ return ( not isinstance(self, type(other)) or self._permutation != other._permutation or self._lengths != other._lengths) def recoding(self, n): r""" Recode this interval exchange transformation on the words of length ``n``. EXAMPLES:: sage: from surface_dynamics.all import * sage: p = iet.Permutation('a d c b', 'b c a d', alphabet='abcd') sage: T = iet.IntervalExchangeTransformation(p, [119,213,82,33]) sage: T.recoding(2) Interval exchange transformation of [0, 447[ with permutation ab db cc cb ba bd bc ba bd bc cc cb ab db sage: T.recoding(3) Interval exchange transformation of [0, 447[ with permutation aba abd abc dbc ccb cba bab bdb bcc bcb cba aba abd abc dbc bcc bcb ccb bab bdb """ length = len(self._permutation) lengths = self._lengths A = self.permutation().alphabet() unrank = A.unrank top = self._permutation._labels[0] bot = self._permutation._labels[1] bot_twin = self._permutation._twin[1] sg_top = self.domain_singularities()[1:-1] # iterates of the top singularities cuts = [[[i], lengths[i]] for i in bot] # the refined bottom interval translations = self.translations() for step in range(n-1): i = 0 y = self._zero() new_sg_top = [] limits = [0] for j,x in enumerate(sg_top): while y < x: cuts[i][0].append(top[j]) y += cuts[i][1] i += 1 limits.append(i) if y != x: cuts.insert(i, [cuts[i-1][0][:-1], cuts[i-1][1]]) cuts[i-1][1] -= y-x cuts[i][0].append(top[j+1]) cuts[i][1] = y-x i += 1 while i < len(cuts): cuts[i][0].append(top[j+1]) i += 1 limits.append(len(cuts)) # now we reorder cuts with respect to T according to the cut at # limits if step != n-2: new_cuts = [] for j in bot_twin: new_cuts.extend(cuts[limits[j]:limits[j+1]]) cuts = new_cuts # build the new interval exchange transformations itop = [None]*(max(top)+1) for i,j in enumerate(top): itop[j] = i key = lambda x: [itop[i] for i in x[0]] top = sorted(cuts, key=key) lengths = [y for x,y in top] top = [''.join(str(unrank(i)) for i in x) for x,y in top] bot = cuts bot = [''.join(str(unrank(i)) for i in x) for x,y in bot] from labelled import LabelledPermutationIET p = LabelledPermutationIET((top,bot)) return IntervalExchangeTransformation(p,lengths) def in_which_interval(self, x, interval=0): r""" Returns the letter for which x is in this interval. INPUT: - ``x`` - a positive number - ``interval`` - (default: 'top') 'top' or 'bottom' OUTPUT: label -- a label corresponding to an interval TEST: :: sage: t = iet.IntervalExchangeTransformation(('a b c','c b a'),[1,1,1]) sage: t.in_which_interval(0) 'a' sage: t.in_which_interval(0.3) 'a' sage: t.in_which_interval(1) 'b' sage: t.in_which_interval(1.9) 'b' sage: t.in_which_interval(2) 'c' sage: t.in_which_interval(2.1) 'c' sage: t.in_which_interval(3) Traceback (most recent call last): ... ValueError: your value does not lie in [0;l[ .. and for the bottom interval:: sage: t.in_which_interval(0,'bottom') 'c' sage: t.in_which_interval(1.2,'bottom') 'b' sage: t.in_which_interval(2.9,'bottom') 'a' """ interval = interval_conversion(interval) if x < 0 or x >= self.length(): raise ValueError("your value does not lie in [0; {}[".format(self.length())) i = 0 while x >= 0: x -= self._lengths[self._permutation._labels[interval][i]] i += 1 i -= 1 x += self._lengths[self._permutation._labels[interval][i]] j = self._permutation._labels[interval][i] return self._permutation._alphabet.unrank(j) def singularities(self): r""" The list of singularities of 'T' and 'T^{-1}'. OUTPUT: list -- two lists of positive numbers which corresponds to extremities of subintervals EXAMPLE:: sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1/2,3/2]) sage: t.singularities() [[0, 1/2, 2], [0, 3/2, 2]] """ return [self.domain_singularities(), self.range_singularities()] def domain_singularities(self): r""" Returns the list of singularities of T OUTPUT: list -- positive reals that corresponds to singularities in the top interval EXAMPLES:: sage: from surface_dynamics.all import * sage: t = iet.IET(("a b","b a"), [1, sqrt(2)]) sage: t.domain_singularities() [0, 1, sqrt(2) + 1] """ l = [self._zero()] for j in self._permutation._labels[0]: l.append(l[-1] + self._lengths[j]) return l def range_singularities(self): r""" Returns the list of singularities of `T^{-1}` OUTPUT: list -- real numbers that are singular for `T^{-1}` EXAMPLES:: sage: from surface_dynamics.all import * sage: t = iet.IET(("a b","b a"), [1, sqrt(2)]) sage: t.range_singularities() [0, sqrt(2), sqrt(2) + 1] """ l = [self._zero()] for j in self._permutation._labels[1]: l.append(l[-1] + self._lengths[j]) return l def __call__(self, value): r""" Return the image of value by this transformation EXAMPLES:: sage: from surface_dynamics.all import * sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1/2,3/2]) sage: t(0) 3/2 sage: t(1/2) 0 sage: t(1) 1/2 sage: t(3/2) 1 """ assert(value >= 0 and value < self.length()) dom_sg = self.domain_singularities() im_sg = self.range_singularities() a = self.in_which_interval(value) i0 = self._permutation[0].index(a) i1 = self._permutation[1].index(a) return value - dom_sg[i0] + im_sg[i1] def rauzy_move(self, side='right', iterations=1, data=False): r""" Performs a Rauzy move. INPUT: - ``side`` - 'left' (or 'l' or 0) or 'right' (or 'r' or 1) - ``iterations`` - integer (default :1) the number of iteration of Rauzy moves to perform - ``data`` - whether to return also the paths and composition of towers OUTPUT: - ``iet`` -- the Rauzy move of self - ``path`` -- (if ``data=True``) a list of 't' and 'b' - ``towers`` -- (if ``data=True``) the towers of the Rauzy induction as a word morphism EXAMPLES:: sage: from surface_dynamics.all import * sage: phi = QQbar((sqrt(5)-1)/2) sage: t1 = iet.IntervalExchangeTransformation(('a b','b a'),[1,phi]) sage: t2 = t1.rauzy_move().normalize(t1.length()) sage: l2 = t2.lengths() sage: l1 = t1.lengths() sage: l2[0] == l1[1] and l2[1] == l1[0] True sage: tt,path,sub = t1.rauzy_move(iterations=3, data=True) sage: tt Interval exchange transformation of [0, 0.3819660112501051?[ with permutation a b b a sage: path ['b', 't', 'b'] sage: sub WordMorphism: a->aab, b->aabab The substitution can also be recovered from the Rauzy diagram:: sage: p = t1.permutation() sage: p.rauzy_diagram().path(p, *path).substitution() == sub True """ if data: towers = {a:[a] for a in self._permutation.letters()} path = [] side = side_conversion(side) res = copy(self) for i in range(iterations): res,winner,(a,b,c) = res._rauzy_move(side) if data: towers[a] = towers[b] + towers[c] path.append(winner) if data: from sage.combinat.words.morphism import WordMorphism return res, path, WordMorphism(towers) else: return res def _rauzy_move(self,side=-1): r""" Performs a Rauzy move INPUT: - ``side`` - must be 0 or -1 (no verification) OUTPUT: - ``T`` - the iet after Rauzy induction - ``winner`` - either ``'t'`` or ``'b'`` - ``(a,b,c)`` - a triple of letter such that the towers are obtained by applying the substitution `a \mapsto bc`. TEST:: sage: from surface_dynamics.all import * sage: t = iet.IntervalExchangeTransformation(('a b c','c b a'),[1,1,3]) sage: t Interval exchange transformation of [0, 5[ with permutation a b c c b a sage: t1 = t.rauzy_move() #indirect doctest sage: t1 Interval exchange transformation of [0, 4[ with permutation a b c c a b sage: t2 = t1.rauzy_move() #indirect doctest sage: t2 Interval exchange transformation of [0, 3[ with permutation a b c c b a sage: t2.rauzy_move() #indirect doctest Interval exchange transformation of [0, 2[ with permutation a b a b Degenerate cases:: sage: p = iet.Permutation('a b', 'b a') sage: T = iet.IntervalExchangeTransformation(p, [1,1]) sage: T.rauzy_move() Interval exchange transformation of [0, 1[ with permutation a a """ top = self._permutation._labels[0][side] bot = self._permutation._labels[1][side] A = self._permutation.alphabet() top_letter = A.unrank(top) bot_letter = A.unrank(bot) length_top = self._lengths[top] length_bot = self._lengths[bot] if length_top > length_bot: winner = 0 winner_interval = top loser_interval = bot abc = (bot_letter, bot_letter, top_letter) winner = 't' elif length_top < length_bot: winner = 1 winner_interval = bot loser_interval = top abc = (top_letter, bot_letter, top_letter) winner = 'b' else: # we do a pseudo-top Rauzy induction and remove the bot interval res = IntervalExchangeTransformation(([],[]),{}) p = self._permutation.__copy__() top = p._labels[0][-1] bot = p._labels[1][-1] p._identify_intervals(len(p)-1, len(p)-1) res._permutation = p res._lengths = self._lengths[:] res._lengths[top] = 0 return res,0,(bot_letter,bot_letter,top_letter) res = IntervalExchangeTransformation(([],[]),{}) res._permutation = self._permutation.rauzy_move(winner=winner,side=side) res._lengths = self._lengths[:] res._lengths[winner_interval] -= res._lengths[loser_interval] return res,winner,abc def __copy__(self): r""" Returns a copy of this interval exchange transformation. EXAMPLES:: sage: from surface_dynamics.all import * sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: s = copy(t) sage: s == t True sage: s is t False """ res = self.__class__() res._base_ring = self._base_ring res._permutation = copy(self._permutation) res._lengths = copy(self._lengths) return res def plot_function(self,**d): r""" Return a plot of the interval exchange transformation as a function. INPUT: - Any option that is accepted by line2d OUTPUT: 2d plot -- a plot of the iet as a function EXAMPLES:: sage: from surface_dynamics.all import * sage: t = iet.IntervalExchangeTransformation(('a b c d','d a c b'),[1,1,1,1]) sage: t.plot_function(rgbcolor=(0,1,0)) Graphics object consisting of 4 graphics primitives """ from sage.plot.plot import Graphics from sage.plot.plot import line2d G = Graphics() l = self.singularities() t = self._permutation._twin for i in range(len(self._permutation)): j = t[0][i] G += line2d([(l[0][i],l[1][j]),(l[0][i+1],l[1][j+1])],**d) return G def plot_two_intervals( self, position=(0,0), vertical_alignment = 'center', horizontal_alignment = 'left', interval_height=0.1, labels_height=0.05, fontsize=14, labels=True, colors=None): r""" Returns a picture of the interval exchange transformation. INPUT: - ``position`` - a 2-uple of the position - ``horizontal_alignment`` - left (defaut), center or right - ``labels`` - boolean (defaut: True) - ``fontsize`` - the size of the label OUTPUT: 2d plot -- a plot of the two intervals (domain and range) EXAMPLES:: sage: from surface_dynamics.all import * sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t.plot_two_intervals() Graphics object consisting of 8 graphics primitives """ from sage.plot.plot import Graphics from sage.plot.plot import line2d from sage.plot.plot import text from sage.plot.colors import rainbow G = Graphics() lengths = map(float,self._lengths) total_length = sum(lengths) if colors is None: colors = rainbow(len(self._permutation), 'rgbtuple') if horizontal_alignment == 'left': s = position[0] elif horizontal_alignment == 'center': s = position[0] - total_length / 2 elif horizontal_alignment == 'right': s = position[0] - total_length else: raise ValueError, "horizontal_alignement must be left, center or right" top_height = position[1] + interval_height for i in self._permutation._labels[0]: G += line2d([(s,top_height),(s+lengths[i],top_height)], rgbcolor=colors[i]) if labels == True: G += text(str(self._permutation._alphabet.unrank(i)), (s+float(lengths[i])/2,top_height+labels_height), horizontal_alignment='center', rgbcolor=colors[i], fontsize=fontsize) s += lengths[i] if horizontal_alignment == 'left': s = position[0] elif horizontal_alignment == 'center': s = position[0] - total_length / 2 elif horizontal_alignment == 'right': s = position[0] - total_length else: raise ValueError, "horizontal_alignement must be left, center or right" bottom_height = position[1] - interval_height for i in self._permutation._labels[1]: G += line2d([(s,bottom_height), (s+lengths[i],bottom_height)], rgbcolor=colors[i]) if labels == True: G += text(str(self._permutation._alphabet.unrank(i)), (s+float(lengths[i])/2,bottom_height-labels_height), horizontal_alignment='center', rgbcolor=colors[i], fontsize=fontsize) s += lengths[i] return G def plot_towers(self, iterations, position=(0,0), colors=None): """ Plot the towers of this interval exchange obtained from Rauzy induction. INPUT: - ``nb_iterations`` -- the number of steps of Rauzy induction - ``colors`` -- (optional) colors for the towers EXAMPLES:: sage: from surface_dynamics.all import * sage: p = iet.Permutation('A B', 'B A') sage: T = iet.IntervalExchangeTransformation(p, [0.41510826, 0.58489174]) sage: T.plot_towers(iterations=5) Graphics object consisting of 65 graphics primitives """ px,py = map(float, position) T,_,towers = self.rauzy_move(iterations=iterations,data=True) pi = T.permutation() A = pi.alphabet() lengths = map(float, T.lengths()) if colors is None: from sage.plot.colors import rainbow colors = {a:z for a,z in zip(A, rainbow(len(A)))} from sage.plot.graphics import Graphics from sage.plot.line import line2d from sage.plot.polygon import polygon2d from sage.plot.text import text G = Graphics() x = px for letter in pi[0]: y = x + lengths[A.rank(letter)] tower = towers.image(letter) h = tower.length() G += line2d([(x,py),(x,py+h)], color='black') G += line2d([(y,py),(y,py+h)], color='black') for i,a in enumerate(tower): G += line2d([(x,py+i),(y,py+i)], color='black') G += polygon2d([(x,py+i),(y,py+i),(y,py+i+1),(x,py+i+1)], color=colors[a], alpha=0.4) G += text(a, ((x+y)/2, py+i+.5), color='darkgray') G += line2d([(x,py+h),(y,py+h)], color='black', linestyle='dashed') G += text(letter, ((x+y)/2, py+h+.5), color='black', fontsize='large') x = y x = px G += line2d([(px,py-.5),(px+sum(lengths),py-.5)], color='black') for letter in pi[1]: y = x + lengths[A.rank(letter)] G += line2d([(x,py-.7),(x,py-.3)], color='black') G += text(letter, ((x+y)/2, py-.5), color='black', fontsize='large') x = y G += line2d([(x,py-.7),(x,py-.3)], color='black') return G plot = plot_two_intervals def show(self): r""" Shows a picture of the interval exchange transformation EXAMPLES:: sage: from surface_dynamics.all import * sage: phi = QQbar((sqrt(5)-1)/2) sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,phi]) sage: t.show() """ self.plot_two_intervals().show(axes=False)
def recoding(self, n): r""" Recode this interval exchange transformation on the words of length ``n``. EXAMPLES:: sage: from surface_dynamics.all import * sage: p = iet.Permutation('a d c b', 'b c a d', alphabet='abcd') sage: T = iet.IntervalExchangeTransformation(p, [119,213,82,33]) sage: T.recoding(2) Interval exchange transformation of [0, 447[ with permutation ab db cc cb ba bd bc ba bd bc cc cb ab db sage: T.recoding(3) Interval exchange transformation of [0, 447[ with permutation aba abd abc dbc ccb cba bab bdb bcc bcb cba aba abd abc dbc bcc bcb ccb bab bdb """ length = len(self._permutation) lengths = self._lengths A = self.permutation().alphabet() unrank = A.unrank top = self._permutation._labels[0] bot = self._permutation._labels[1] bot_twin = self._permutation._twin[1] sg_top = self.domain_singularities()[ 1:-1] # iterates of the top singularities cuts = [[[i], lengths[i]] for i in bot] # the refined bottom interval translations = self.translations() for step in range(n - 1): i = 0 y = self.base_ring().zero() new_sg_top = [] limits = [0] for j, x in enumerate(sg_top): while y < x: cuts[i][0].append(top[j]) y += cuts[i][1] i += 1 limits.append(i) if y != x: cuts.insert(i, [cuts[i - 1][0][:-1], cuts[i - 1][1]]) cuts[i - 1][1] -= y - x cuts[i][0].append(top[j + 1]) cuts[i][1] = y - x i += 1 while i < len(cuts): cuts[i][0].append(top[j + 1]) i += 1 limits.append(len(cuts)) # now we reorder cuts with respect to T according to the cut at # limits if step != n - 2: new_cuts = [] for j in bot_twin: new_cuts.extend(cuts[limits[j]:limits[j + 1]]) cuts = new_cuts # build the new interval exchange transformations itop = [None] * (max(top) + 1) for i, j in enumerate(top): itop[j] = i key = lambda x: [itop[i] for i in x[0]] top = sorted(cuts, key=key) lengths = [y for x, y in top] top = [''.join(str(unrank(i)) for i in x) for x, y in top] bot = cuts bot = [''.join(str(unrank(i)) for i in x) for x, y in bot] from labelled import LabelledPermutationIET p = LabelledPermutationIET((top, bot)) return IntervalExchangeTransformation(p, lengths)