Path: blob/master/src/sage/dynamics/interval_exchanges/iet.py
8815 views
r"""1Interval Exchange Transformations and Linear Involution23An interval exchage transformation is a map defined on an interval (see4help(iet.IntervalExchangeTransformation) for a more complete help.56EXAMPLES:78Initialization of a simple iet with integer lengths::910sage: T = iet.IntervalExchangeTransformation(Permutation([3,2,1]), [3,1,2])11sage: print T12Interval exchange transformation of [0, 6[ with permutation131 2 3143 2 11516Rotation corresponds to iet with two intervals::1718sage: p = iet.Permutation('a b', 'b a')19sage: T = iet.IntervalExchangeTransformation(p, [1, (sqrt(5)-1)/2])20sage: print T.in_which_interval(0)21a22sage: print T.in_which_interval(T(0))23a24sage: print T.in_which_interval(T(T(0)))25b26sage: print T.in_which_interval(T(T(T(0))))27a2829There are two plotting methods for iet::3031sage: p = iet.Permutation('a b c','c b a')32sage: T = iet.IntervalExchangeTransformation(p, [1, 2, 3])3334.. plot the domain and the range of T::3536sage: T.plot_two_intervals()3738.. plot T as a function::3940sage: T.plot_function()41"""42from copy import copy43from sage.structure.sage_object import SageObject4445from template import side_conversion, interval_conversion4647class IntervalExchangeTransformation(SageObject):48r"""49Interval exchange transformation5051INPUT:5253- ``permutation`` - a permutation (LabelledPermutationIET)5455- ``lengths`` - the list of lengths5657EXAMPLES:5859Direct initialization::6061sage: p = iet.IET(('a b c','c b a'),{'a':1,'b':1,'c':1})62sage: p.permutation()63a b c64c b a65sage: p.lengths()66[1, 1, 1]6768Initialization from a iet.Permutation::6970sage: perm = iet.Permutation('a b c','c b a')71sage: l = [0.5,1,1.2]72sage: t = iet.IET(perm,l)73sage: t.permutation() == perm74True75sage: t.lengths() == l76True7778Initialization from a Permutation::7980sage: p = Permutation([3,2,1])81sage: iet.IET(p, [1,1,1])82Interval exchange transformation of [0, 3[ with permutation831 2 3843 2 18586If it is not possible to convert lengths to real values an error is raised::8788sage: iet.IntervalExchangeTransformation(('a b','b a'),['e','f'])89Traceback (most recent call last):90...91TypeError: unable to convert x (='e') into a real number9293The value for the lengths must be positive::9495sage: iet.IET(('a b','b a'),[-1,-1])96Traceback (most recent call last):97...98ValueError: lengths must be positive99"""100def __init__(self,permutation=None,lengths=None):101r"""102INPUT:103104- ``permutation`` - a permutation (LabelledPermutationIET)105106- ``lengths`` - the list of lengths107108TEST::109110sage: p=iet.IntervalExchangeTransformation(('a','a'),[1])111sage: p == loads(dumps(p))112True113"""114from labelled import LabelledPermutationIET115if permutation is None or lengths is None:116self._permutation = LabelledPermutationIET()117self._lengths = []118else:119self._permutation = permutation120self._lengths = lengths121122def permutation(self):123r"""124Returns the permutation associated to this iet.125126OUTPUT:127128permutation -- the permutation associated to this iet129130EXAMPLES::131132sage: perm = iet.Permutation('a b c','c b a')133sage: p = iet.IntervalExchangeTransformation(perm,(1,2,1))134sage: p.permutation() == perm135True136"""137return copy(self._permutation)138139def length(self):140r"""141Returns the total length of the interval.142143OUTPUT:144145real -- the length of the interval146147EXAMPLES::148149sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1])150sage: t.length()1512152"""153return sum(self._lengths)154155def lengths(self):156r"""157Returns the list of lengths associated to this iet.158159OUTPUT:160161list -- the list of lengths of subinterval162163EXAMPLES::164165sage: p = iet.IntervalExchangeTransformation(('a b','b a'),[1,3])166sage: p.lengths()167[1, 3]168"""169return copy(self._lengths)170171def normalize(self, total=1):172r"""173Returns a interval exchange transformation of normalized lengths.174175The normalization consists in multiplying all lengths by a176constant in such way that their sum is given by ``total``177(default is 1).178179INPUT:180181- ``total`` - (default: 1) The total length of the interval182183OUTPUT:184185iet -- the normalized iet186187EXAMPLES::188189sage: t = iet.IntervalExchangeTransformation(('a b','b a'), [1,3])190sage: t.length()1914192sage: s = t.normalize(2)193sage: s.length()1942195sage: s.lengths()196[1/2, 3/2]197198TESTS::199200sage: s = t.normalize('bla')201Traceback (most recent call last):202...203TypeError: unable to convert total (='bla') into a real number204sage: s = t.normalize(-691)205Traceback (most recent call last):206...207ValueError: the total length must be positive208"""209try:210float(total)211except ValueError:212raise TypeError("unable to convert total (='%s') into a real number"213% (str(total)))214215if total <= 0:216raise ValueError("the total length must be positive")217218res = copy(self)219coeff = total / res.length()220res._multiply_lengths(coeff)221return res222223def _multiply_lengths(self, x):224r"""225Multiplies the lengths of self by x (no verification on x).226227INPUT:228229- ``x`` - a positive number230231TESTS::232233sage: t = iet.IET(("a","a"), [1])234sage: t.lengths()235[1]236sage: t._multiply_lengths(2)237sage: t.lengths()238[2]239"""240self._lengths = map(lambda t: t*x, self._lengths)241242def _repr_(self):243r"""244A representation string.245246EXAMPLES::247248sage: a = iet.IntervalExchangeTransformation(('a','a'),[1])249sage: a # indirect doctest250Interval exchange transformation of [0, 1[ with permutation251a252a253"""254interval = "[0, %s["%self.length()255s = "Interval exchange transformation of %s "%interval256s += "with permutation\n%s"%self._permutation257return s258259def is_identity(self):260r"""261Returns True if self is the identity.262263OUTPUT:264265boolean -- the answer266267EXAMPLES::268269sage: p = iet.Permutation("a b","b a")270sage: q = iet.Permutation("c d","d c")271sage: s = iet.IET(p, [1,5])272sage: t = iet.IET(q, [5,1])273sage: (s*t).is_identity()274True275sage: (t*s).is_identity()276True277"""278return self._permutation.is_identity()279280def inverse(self):281r"""282Returns the inverse iet.283284OUTPUT:285286iet -- the inverse interval exchange transformation287288EXAMPLES::289290sage: p = iet.Permutation("a b","b a")291sage: s = iet.IET(p, [1,sqrt(2)-1])292sage: t = s.inverse()293sage: t.permutation()294b a295a b296sage: t.lengths()297[1, sqrt(2) - 1]298sage: t*s299Interval exchange transformation of [0, sqrt(2)[ with permutation300aa bb301aa bb302303We can verify with the method .is_identity()::304305sage: p = iet.Permutation("a b c d","d a c b")306sage: s = iet.IET(p, [1, sqrt(2), sqrt(3), sqrt(5)])307sage: (s * s.inverse()).is_identity()308True309sage: (s.inverse() * s).is_identity()310True311"""312res = copy(self)313res._permutation._inversed()314return res315316def __mul__(self, other):317r"""318Composition of iet.319320The domain (i.e. the length) of the two iets must be the same). The321alphabet choosen depends on the permutation.322323TESTS:324325::326327sage: p = iet.Permutation("a b", "a b")328sage: t = iet.IET(p, [1,1])329sage: r = t*t330sage: r.permutation()331aa bb332aa bb333sage: r.lengths()334[1, 1]335336::337338sage: p = iet.Permutation("a b","b a")339sage: t = iet.IET(p, [1,1])340sage: r = t*t341sage: r.permutation()342ab ba343ab ba344sage: r.lengths()345[1, 1]346347::348349sage: p = iet.Permutation("1 2 3 4 5","5 4 3 2 1")350sage: q = iet.Permutation("a b","b a")351sage: s = iet.IET(p, [1]*5)352sage: t = iet.IET(q, [1/2, 9/2])353sage: r = s*t354sage: r.permutation()355a5 b1 b2 b3 b4 b5356b5 a5 b4 b3 b2 b1357sage: r.lengths()358[1/2, 1, 1, 1, 1, 1/2]359sage: r = t*s360sage: r.permutation()3611b 2b 3b 4b 5a 5b3625b 4b 3b 2b 1b 5a363sage: r.lengths()364[1, 1, 1, 1, 1/2, 1/2]365sage: t = iet.IET(q, [3/2, 7/2])366sage: r = s*t367sage: r.permutation()368a4 a5 b1 b2 b3 b4369a5 b4 a4 b3 b2 b1370sage: r.lengths()371[1/2, 1, 1, 1, 1, 1/2]372sage: t = iet.IET(q, [5/2,5/2])373sage: r = s*t374sage: r.permutation()375a3 a4 a5 b1 b2 b3376a5 a4 b3 a3 b2 b1377sage: r = t*s378sage: r.permutation()3791b 2b 3a 3b 4a 5a3803b 2b 1b 5a 4a 3a381382::383384sage: p = iet.Permutation("a b","b a")385sage: s = iet.IET(p, [4,2])386sage: q = iet.Permutation("c d","d c")387sage: t = iet.IET(q, [3, 3])388sage: r1 = t * s389sage: r1.permutation()390ac ad bc391ad bc ac392sage: r1.lengths()393[1, 3, 2]394sage: r2 = s * t395sage: r2.permutation()396ca cb da397cb da ca398sage: r2.lengths()399[1, 2, 3]400401::402403sage: r * s404Traceback (most recent call last):405...406ValueError: self and other are not IET of the same length407"""408if not(isinstance(other, IntervalExchangeTransformation) and409self.length() == other.length()):410raise ValueError("self and other are not IET of the same length")411412from labelled import LabelledPermutationIET413414other_sg = other.range_singularities()[1:]415self_sg = self.domain_singularities()[1:]416417n_other = len(other._permutation)418n_self = len(self._permutation)419420interval_other = other._permutation._intervals[1]421interval_self = self._permutation._intervals[0]422423d_other = dict([(i,[]) for i in interval_other])424d_self = dict([(i,[]) for i in interval_self])425426i_other = 0427i_self = 0428429x = 0430l_lengths = []431while i_other < n_other and i_self < n_self:432j_other = interval_other[i_other]433j_self = interval_self[i_self]434435d_other[j_other].append(j_self)436d_self[j_self].append(j_other)437438if other_sg[i_other] < self_sg[i_self]:439l = other_sg[i_other] - x440x = other_sg[i_other]441i_other += 1442elif other_sg[i_other] > self_sg[i_self]:443l = self_sg[i_self] - x444x = self_sg[i_self]445i_self += 1446else:447l = self_sg[i_self] - x448x = self_sg[i_self]449i_other += 1450i_self += 1451452l_lengths.append(((j_other,j_self),l))453454alphabet_other = other._permutation.alphabet()455alphabet_self = self._permutation.alphabet()456457d_lengths = dict(l_lengths)458459l_lengths = []460top_interval = []461for i in other._permutation._intervals[0]:462for j in d_other[i]:463a = alphabet_other.unrank(i)464b = alphabet_self.unrank(j)465top_interval.append(str(a)+str(b))466l_lengths.append(d_lengths[(i,j)])467468bottom_interval = []469for i in self._permutation._intervals[1]:470for j in d_self[i]:471a = alphabet_other.unrank(j)472b = alphabet_self.unrank(i)473bottom_interval.append(str(a)+str(b))474475p = LabelledPermutationIET((top_interval,bottom_interval))476return IntervalExchangeTransformation(p,l_lengths)477478def __eq__(self, other):479r"""480Tests equality481482TESTS::483484sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1])485sage: t == t486True487"""488return (489type(self) == type(other) and490self._permutation == other._permutation and491self._lengths == other._lengths)492493def __ne__(self, other):494r"""495Tests difference496497TESTS::498499sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1])500sage: t != t501False502"""503return (504type(self) != type(other) or505self._permutation != other._permutation or506self._lengths != other._lengths)507508def in_which_interval(self, x, interval=0):509r"""510Returns the letter for which x is in this interval.511512INPUT:513514- ``x`` - a positive number515516- ``interval`` - (default: 'top') 'top' or 'bottom'517518519OUTPUT:520521label -- a label corresponding to an interval522523TEST:524525::526527sage: t = iet.IntervalExchangeTransformation(('a b c','c b a'),[1,1,1])528sage: t.in_which_interval(0)529'a'530sage: t.in_which_interval(0.3)531'a'532sage: t.in_which_interval(1)533'b'534sage: t.in_which_interval(1.9)535'b'536sage: t.in_which_interval(2)537'c'538sage: t.in_which_interval(2.1)539'c'540sage: t.in_which_interval(3)541Traceback (most recent call last):542...543ValueError: your value does not lie in [0;l[544545.. and for the bottom interval::546547sage: t.in_which_interval(0,'bottom')548'c'549sage: t.in_which_interval(1.2,'bottom')550'b'551sage: t.in_which_interval(2.9,'bottom')552'a'553554TESTS::555556sage: t.in_which_interval(-2.9,'bottom')557Traceback (most recent call last):558...559ValueError: your value does not lie in [0;l[560"""561interval = interval_conversion(interval)562563if x < 0 or x >= self.length():564raise ValueError("your value does not lie in [0;l[")565566i = 0567568while x >= 0:569x -= self._lengths[self._permutation._intervals[interval][i]]570i += 1571572i -= 1573x += self._lengths[self._permutation._intervals[interval][i]]574575j = self._permutation._intervals[interval][i]576return self._permutation._alphabet.unrank(j)577578def singularities(self):579r"""580The list of singularities of `T` and `T^{-1}`.581582OUTPUT:583584list -- two lists of positive numbers which corresponds to extremities585of subintervals586587EXAMPLE::588589sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1/2,3/2])590sage: t.singularities()591[[0, 1/2, 2], [0, 3/2, 2]]592"""593return [self.domain_singularities(), self.range_singularities()]594595def domain_singularities(self):596r"""597Returns the list of singularities of T598599OUTPUT:600601list -- positive reals that corresponds to singularities in the top602interval603604EXAMPLES::605606sage: t = iet.IET(("a b","b a"), [1, sqrt(2)])607sage: t.domain_singularities()608[0, 1, sqrt(2) + 1]609"""610l = [0]611for j in self._permutation._intervals[0]:612l.append(l[-1] + self._lengths[j])613return l614615def range_singularities(self):616r"""617Returns the list of singularities of `T^{-1}`618619OUTPUT:620621list -- real numbers that are singular for `T^{-1}`622623624EXAMPLES::625626sage: t = iet.IET(("a b","b a"), [1, sqrt(2)])627sage: t.range_singularities()628[0, sqrt(2), sqrt(2) + 1]629"""630l = [0]631for j in self._permutation._intervals[1]:632l.append(l[-1] + self._lengths[j])633return l634635def __call__(self, value):636r"""637Return the image of value by this transformation638639EXAMPLES::640641sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1/2,3/2])642sage: t(0)6433/2644sage: t(1/2)6450646sage: t(1)6471/2648sage: t(3/2)6491650651TESTS::652653sage: t(-3/2)654Traceback (most recent call last):655...656ValueError: value must positive and smaller than length657"""658if not(value >= 0 and value < self.length()):659raise ValueError("value must positive and smaller than length")660661dom_sg = self.domain_singularities()662im_sg = self.range_singularities()663664a = self.in_which_interval(value)665666i0 = self._permutation[0].index(a)667i1 = self._permutation[1].index(a)668669return value - dom_sg[i0] + im_sg[i1]670671def rauzy_move(self, side='right', iterations=1):672r"""673Performs a Rauzy move.674675INPUT:676677- ``side`` - 'left' (or 'l' or 0) or 'right' (or 'r' or 1)678679- ``iterations`` - integer (default :1) the number of iteration of Rauzy680moves to perform681682OUTPUT:683684iet -- the Rauzy move of self685686EXAMPLES::687688sage: phi = QQbar((sqrt(5)-1)/2)689sage: t1 = iet.IntervalExchangeTransformation(('a b','b a'),[1,phi])690sage: t2 = t1.rauzy_move().normalize(t1.length())691sage: l2 = t2.lengths()692sage: l1 = t1.lengths()693sage: l2[0] == l1[1] and l2[1] == l1[0]694True695"""696side = side_conversion(side)697698res = copy(self)699for i in range(iterations):700res = res._rauzy_move(side)701return res702703def _rauzy_move(self,side=-1):704r"""705Performs a Rauzy move706707INPUT:708709- ``side`` - must be 0 or -1 (no verification)710711TEST::712713sage: t = iet.IntervalExchangeTransformation(('a b c','c b a'),[1,1,3])714sage: t715Interval exchange transformation of [0, 5[ with permutation716a b c717c b a718sage: t1 = t.rauzy_move() #indirect doctest719sage: t1720Interval exchange transformation of [0, 4[ with permutation721a b c722c a b723sage: t2 = t1.rauzy_move() #indirect doctest724sage: t2725Interval exchange transformation of [0, 3[ with permutation726a b c727c b a728sage: t2.rauzy_move() #indirect doctest729Traceback (most recent call last):730...731ValueError: top and bottom extrem intervals have equal lengths732"""733top = self._permutation._intervals[0][side]734bottom = self._permutation._intervals[1][side]735736length_top = self._lengths[top]737length_bottom = self._lengths[bottom]738739if length_top > length_bottom:740winner = 0741winner_interval = top742loser_interval = bottom743elif length_top < length_bottom:744winner = 1745winner_interval = bottom746loser_interval = top747else:748raise ValueError("top and bottom extrem intervals have equal lengths")749750res = IntervalExchangeTransformation(([],[]),{})751res._permutation = self._permutation.rauzy_move(winner=winner,side=side)752res._lengths = self._lengths[:]753res._lengths[winner_interval] -= res._lengths[loser_interval]754755return res756757def __copy__(self):758r"""759Returns a copy of this interval exchange transformation.760761EXAMPLES::762763sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1])764sage: s = copy(t)765sage: s == t766True767sage: s is t768False769"""770res = self.__class__()771res._permutation = copy(self._permutation)772res._lengths = copy(self._lengths)773return res774775def plot_function(self,**d):776r"""777Return a plot of the interval exchange transformation as a778function.779780INPUT:781782- Any option that is accepted by line2d783784OUTPUT:7857862d plot -- a plot of the iet as a function787788EXAMPLES::789790sage: t = iet.IntervalExchangeTransformation(('a b c d','d a c b'),[1,1,1,1])791sage: t.plot_function(rgbcolor=(0,1,0))792"""793from sage.plot.all import Graphics794from sage.plot.plot import line2d795796G = Graphics()797l = self.singularities()798t = self._permutation._twin799800for i in range(len(self._permutation)):801j = t[0][i]802G += line2d([(l[0][i],l[1][j]),(l[0][i+1],l[1][j+1])],**d)803804return G805806def plot_two_intervals(self,807position=(0,0),808vertical_alignment='center',809horizontal_alignment='left',810interval_height=0.1,811labels_height=0.05,812fontsize=14,813labels=True,814colors=None):815r"""816Returns a picture of the interval exchange transformation.817818INPUT:819820- ``position`` - a 2-uple of the position821822- ``horizontal_alignment`` - left (defaut), center or right823824- ``labels`` - boolean (defaut: True)825826- ``fontsize`` - the size of the label827828829OUTPUT:8308312d plot -- a plot of the two intervals (domain and range)832833EXAMPLES::834835sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1])836sage: t.plot_two_intervals()837"""838from sage.plot.all import Graphics839from sage.plot.plot import line2d840from sage.plot.plot import text841from sage.plot.colors import rainbow842843G = Graphics()844845lengths = map(float,self._lengths)846total_length = sum(lengths)847848if colors is None:849colors = rainbow(len(self._permutation), 'rgbtuple')850851if horizontal_alignment == 'left':852s = position[0]853elif horizontal_alignment == 'center':854s = position[0] - total_length / 2855elif horizontal_alignment == 'right':856s = position[0] - total_length857else:858raise ValueError("horizontal_alignement must be left, center or right")859860top_height = position[1] + interval_height861for i in self._permutation._intervals[0]:862G += line2d([(s,top_height), (s+lengths[i],top_height)],863rgbcolor=colors[i])864if labels:865G += text(str(self._permutation._alphabet.unrank(i)),866(s+float(lengths[i])/2, top_height+labels_height),867horizontal_alignment='center',868rgbcolor=colors[i],869fontsize=fontsize)870871s += lengths[i]872873if horizontal_alignment == 'left':874s = position[0]875elif horizontal_alignment == 'center':876s = position[0] - total_length / 2877elif horizontal_alignment == 'right':878s = position[0] - total_length879else:880raise ValueError("horizontal_alignement must be left, center or right")881882bottom_height = position[1] - interval_height883for i in self._permutation._intervals[1]:884G += line2d([(s,bottom_height), (s+lengths[i],bottom_height)],885rgbcolor=colors[i])886if labels:887G += text(str(self._permutation._alphabet.unrank(i)),888(s+float(lengths[i])/2, bottom_height-labels_height),889horizontal_alignment='center',890rgbcolor=colors[i],891fontsize=fontsize)892s += lengths[i]893894return G895896plot = plot_two_intervals897898def show(self):899r"""900Shows a picture of the interval exchange transformation901902EXAMPLES::903904sage: phi = QQbar((sqrt(5)-1)/2)905sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,phi])906sage: t.show()907"""908self.plot_two_intervals().show(axes=False)909910#TODO911# class LinearInvolution(SageObject):912# r"""_913# Linear involutions914# """915# pass916917918