Source code for surface_dynamics.interval_exchanges.iet

r"""
Interval Exchange Transformations and Linear Involution

An interval exchange transformation is a map defined on an interval (see
help(iet.IntervalExchangeTransformation) for a more complete help.

EXAMPLES::

    sage: from surface_dynamics import *

Initialization of a simple iet with integer lengths::

    sage: T = iet.IntervalExchangeTransformation(Permutation([3,2,1]), [3,1,2])
    sage: T
    Interval exchange transformation of [0, 6[ with permutation
    1 2 3
    3 2 1

Rotation corresponds to iet with two intervals::

    sage: p = iet.Permutation('a b', 'b a')
    sage: T = iet.IntervalExchangeTransformation(p, [1, (sqrt(5)-1)/2])  # random output due to deprecation warnings in some versions of SageMath
    sage: T.in_which_interval(0)
    'a'
    sage: T.in_which_interval(T(0))
    'a'
    sage: T.in_which_interval(T(T(0)))
    'b'
    sage: T.in_which_interval(T(T(T(0))))
    'a'

There are two plotting methods for iet::

    sage: p = iet.Permutation('a b c','c b a')
    sage: T = iet.IntervalExchangeTransformation(p, [1, 2, 3])

.. plot the domain and the range of T::

    sage: T.plot_two_intervals()   # not tested (problem with matplotlib font cache)
    Graphics object consisting of 12 graphics primitives

.. plot T as a function::

    sage: T.plot_function()  # not tested (problem with matplotlib font cache)
    Graphics object consisting of 3 graphics primitives
"""
#*****************************************************************************
#       Copyright (C) 2019 Vincent Delecroix <20100.delecroix@gmail.com>
#
#  Distributed under the terms of the GNU General Public License (GPL)
#  as published by the Free Software Foundation; either version 2 of
#  the License, or (at your option) any later version.
#                  https://www.gnu.org/licenses/
#*****************************************************************************

from __future__ import print_function, absolute_import
from six.moves import range, map, filter, zip

from copy import copy
from sage.modules.free_module_element import free_module_element
from sage.rings.all import ZZ, QQ

from .template import side_conversion, interval_conversion
from .labelled import LabelledPermutationIET


[docs] def wedge(u, v): r""" Return the wedge of the vectors ``u`` and ``v`` """ d = len(u) R = u.base_ring() assert len(u) == len(v) and v.base_ring() == R return free_module_element(R, d*(d-1)//2, [(u[i]*v[j] - u[j]*v[i]) for i in range(d-1) for j in range(i+1,d)])
[docs] class IntervalExchangeTransformation: r""" Interval exchange transformation INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths EXAMPLES:: sage: from surface_dynamics 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 = vector([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 non-negative, got -1.0 """ def __init__(self, permutation=None, lengths=None, base_ring=None): r""" INPUT: - ``permutation`` - a permutation (LabelledPermutationIET) - ``lengths`` - the list of lengths TESTS:: sage: from surface_dynamics import * sage: p = iet.IntervalExchangeTransformation(('a','a'), [1]) sage: p == loads(dumps(p)) True """ from sage.modules.free_module_element import free_module_element as vector if permutation is None or lengths is None: self._permutation = None self._lengths = [] self._base_ring = None self._vector_space = None else: self._permutation = copy(permutation) if isinstance(lengths, dict): l = [0] * len(permutation) rank = permutation._alphabet.rank for letter, length in lengths.items(): l[rank(letter)] = length lengths = l self._lengths = vector(lengths) if self._lengths is lengths: # NOTE: vector(.) never performs copy self._lengths = self._lengths.__copy__() V = self._lengths.parent() self._base_ring = self._lengths.base_ring() self._vector_space = V.ambient_module()
[docs] def vector_space(self): return self._vector_space
[docs] def base_ring(self): r""" Return the base ring over which the lengths are defined EXAMPLES:: sage: from surface_dynamics import * sage: p = iet.Permutation('a b', 'b a') sage: T = iet.IntervalExchangeTransformation(p, [3, 12]) sage: T.base_ring() Integer Ring sage: T = iet.IntervalExchangeTransformation(p, [3, 12/5]) sage: T.base_ring() Rational Field sage: T = iet.IntervalExchangeTransformation(p, [3, AA(12).sqrt()]) sage: T.base_ring() Algebraic Real Field sage: sqrt2 = QuadraticField(2).gen() sage: T = iet.IntervalExchangeTransformation(p, [1, sqrt2]) sage: T.base_ring() Number Field in a ... """ return self._base_ring
[docs] def permutation(self): r""" Returns the permutation associated to this iet. OUTPUT: permutation -- the permutation associated to this iet EXAMPLES:: sage: from surface_dynamics 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)
[docs] def length(self, label=None): r""" Return the length of a subinterval, or if ``label`` is None the total length. EXAMPLES:: sage: from surface_dynamics import * sage: t = iet.IntervalExchangeTransformation(('a b','b a'), [1,3]) sage: t.length() 4 sage: t.length('a') 1 sage: t.length('b') 3 """ if label is None: return sum(self._lengths) else: i = self._permutation._alphabet.rank(label) return self._lengths[i]
[docs] def lengths(self): r""" Return the list of lengths associated to this iet. OUTPUT: vector -- the list of lengths of subinterval (the order of the entries correspond to the alphabet) EXAMPLES:: sage: from surface_dynamics import * sage: p = iet.IntervalExchangeTransformation(('a b','b a'),[1,3]) sage: p.lengths() (1, 3) """ return self._lengths.__copy__()
[docs] def translations(self): r""" Return the vector of translations operated on each intervals. EXAMPLES:: sage: from surface_dynamics 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) The order of the entries correspond to the alphabet:: sage: p = iet.Permutation('a c d b', 'b d c a', alphabet='abcd') sage: T = iet.IntervalExchangeTransformation(p, [1, 1, 1, 1]) sage: T.translations() (3, -3, 1, -1) This vector is covariant with respect to the Rauzy matrices:: sage: p = iet.Permutation('a b c d', 'd c b a') sage: R = p.rauzy_diagram() sage: g = R.path(p, *'ttbtbtbtbb') sage: T = g.self_similar_iet() sage: for i in range(12): ....: S, code = T.zorich_move(iterations=i, data=True) ....: gg = R.path(p, *code) ....: m = gg.matrix() ....: assert m * S.lengths() == T.lengths() ....: assert m.transpose() * T.translations() == S.translations() """ 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 = self.vector_space()() for i0,j in enumerate(top): i1 = top_twin[i0] translations[j] = im_sg[i1] - dom_sg[i0] return translations
[docs] def sah_arnoux_fathi_invariant(self): r""" Return the Sah-Arnoux-Fathi invariant The interval exchange needs to be defined over a number field. The output is then a vector with rational entries of dimension `d (d-1) / 2` where `d` is the degree of the field. EXAMPLES: The golden rotation:: sage: from surface_dynamics import * sage: p = iet.Permutation('a b','b a') sage: R = p.rauzy_diagram() sage: g = R.path(p, 't', 'b') sage: T = g.self_similar_iet() sage: T.sah_arnoux_fathi_invariant() (2) The Sah-Arnoux-Fathi invariant is not changed under Rauzy (or Zorich) induction:: sage: S = T.zorich_move(iterations=100) sage: S.sah_arnoux_fathi_invariant() (2) sage: (T.length().n(), S.length().n()) (2.61803398874989, 0.000000000000000) An other rotation:: sage: g = R.path(p, 't', 'b', 'b') sage: T = g.self_similar_iet() sage: T.sah_arnoux_fathi_invariant() (1) sage: T.rauzy_move().sah_arnoux_fathi_invariant() (1) Arnoux-Yoccoz in genus 3:: sage: x = polygen(ZZ) sage: poly = x^3 - x^2 - x - 1 sage: l = max(poly.roots(AA, False)) sage: K.<a> = NumberField(poly, embedding=l) sage: top = 'A1l A1r A2 B1 B2 C1 C2' sage: bot = 'A1r B2 B1 C2 C1 A2 A1l' sage: p = iet.Permutation(top, bot) sage: lengths = vector((a+1, a**2-a-1, a**2, a, a, 1, 1)) sage: T = iet.IntervalExchangeTransformation(p, lengths) sage: T.sah_arnoux_fathi_invariant() (0, 0, 0) Arnoux-Yoccoz examples in genus 4 (an auto-similar iet):: sage: x = polygen(ZZ) sage: poly = x^4 - x^3 - x^2 - x - 1 sage: l = max(poly.roots(AA, False)) sage: K.<a> = NumberField(poly, embedding=l) sage: top = 'A1l A1r A2 B1 B2 C1 C2 D1 D2' sage: bot = 'A1r B2 B1 C2 C1 D2 D1 A2 A1l' sage: p = iet.Permutation(top, bot) sage: lengths = vector((a**4-a**3, 2*a**3-a**4, a**3, a**2, a**2, a, a, 1, 1)) sage: T = iet.IntervalExchangeTransformation(p, lengths) sage: T.sah_arnoux_fathi_invariant() (0, 0, 0, 0, 0, 0) sage: T.zorich_move(iterations=10).sah_arnoux_fathi_invariant() (0, 0, 0, 0, 0, 0) A cubic example in genus 4 (H_4(6)^hyp):: sage: p = iet.Permutation([0, 1, 2, 3, 4, 5, 6, 7], [7, 6, 5, 4, 3, 2, 1, 0]) sage: x = polygen(QQ) sage: poly = x^3 - 6*x^2 + 9*x - 3 sage: emb = AA.polynomial_root(poly, RIF(1.64, 1.66)) sage: K.<A> = NumberField(poly, embedding=emb) sage: lengths= (-4*A^2 + 4*A + 18, -2*A^2 + 19*A - 25, 168*A^2 - 355*A + 128, ....: 586*A^2 - 1317*A + 576, -725*A^2 + 1626*A - 707, ....: -11*A^2 - 6*A + 41, -32*A^2 + 100*A - 77, 20*A^2 - 71*A + 63) sage: S = iet.IntervalExchangeTransformation(p, lengths) sage: S.permutation().stratum_component() H_4(6)^hyp sage: S.sah_arnoux_fathi_invariant() (0, 0, 0) sage: S.zorich_move('left', 120).normalize(17) == S True A quartic example in genus 4 (H_4(6)^even):: sage: p = iet.Permutation([0, 1, 2, 3, 4, 5, 6, 7], [7, 1, 0, 5, 4, 3, 2, 6]) sage: poly = x^4 - 7*x^3 + 14*x^2 - 8*x + 1 sage: emb = AA.polynomial_root(poly, RIF(0.17, 0.18)) sage: K.<A> = NumberField(poly, embedding=emb) sage: lengths = (2*A^3 - 18*A^2 + 44*A - 4, -10*A^3 + 55*A^2 - 60*A + 10, ....: 15*A^3 - 90*A^2 + 120*A - 15, -16*A^3 + 94*A^2 - 132*A + 22, ....: 9*A^3 - 56*A^2 + 88*A - 13, 4*A^3 - 16*A^2 + 3*A + 2, ....: -8*A^3 + 57*A^2 - 101*A + 16, 4*A^3 - 26*A^2 + 38*A - 3) sage: S = iet.IntervalExchangeTransformation(p, lengths) sage: S.permutation().stratum_component() H_4(6)^even sage: S.sah_arnoux_fathi_invariant() (0, 0, 0, 0, 0, 0) sage: S.zorich_move('left', 38).normalize(15) == S True """ if self.base_ring() is ZZ: return free_module_element(ZZ, []) elif self.base_ring() is QQ: return free_module_element(QQ, []) try: K, from_V, to_V = self.base_ring().vector_space() except (AttributeError, ValueError): raise ValueError("the interval exchange needs to be define over a number field") return sum(wedge(to_V(u), to_V(v)) for u,v in zip(self.lengths(), self.translations()))
[docs] def normalize(self, total=1, inplace=False): 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 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, got {}".format(total)) if inplace: res = self else: res = copy(self) res._lengths *= total / res.length() if not inplace: return res
def __repr__(self): r""" A representation string. EXAMPLES:: sage: from surface_dynamics 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
[docs] def is_identity(self): r""" Returns True if self is the identity. OUTPUT: boolean -- the answer EXAMPLES:: sage: from surface_dynamics 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()
[docs] def inverse(self): r""" Returns the inverse iet. OUTPUT: iet -- the inverse interval exchange transformation EXAMPLES:: sage: from surface_dynamics 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
[docs] def erase_marked_points(self): """ Remove the marked points. EXAMPLES:: sage: from surface_dynamics import * sage: p = iet.Permutation([0,1,2,3,4], [4,2,3,0,1]) sage: T1 = iet.IntervalExchangeTransformation(p, [13,2,4,5,7]) sage: T1.erase_marked_points() Interval exchange transformation of [0, 40[ with permutation 0 4 4 0 TESTS: sage: K.<a> = NumberField(x^5 - 2, embedding=AA(2)**(1/5)) Left side fake zero:: sage: p = iet.Permutation([1,5,3,4,6,2], [3,2,5,1,4,6]) sage: T1 = iet.IntervalExchangeTransformation(p, [1, a, a**2, a**3, a**4, 1 + a]) sage: T2 = T1.erase_marked_points() sage: T2 Interval exchange transformation of [0, a^4 + a^3 + a^2 + 3*a + 2[ with permutation 1 3 4 2 3 2 1 4 sage: T2.permutation().stratum() H_2(2) sage: assert T2.length(1) == T1.length(1) + T1.length(5) sage: assert T2.length(2) == T1.length(2) sage: assert T2.length(3) == T1.length(3) + T1.length(5) sage: assert T2.length(4) == T1.length(4) + T1.length(6) sage: assert T1.sah_arnoux_fathi_invariant() == T2.sah_arnoux_fathi_invariant() Right side fake zero:: sage: p = iet.Permutation([1,3,4,5,2], [3,2,5,1,4]) sage: T1 = iet.IntervalExchangeTransformation(p, [1,a,a**2,a**3,a**4]) sage: T2 = T1.erase_marked_points() sage: T2 Interval exchange transformation of [0, a^4 + 2*a^3 + a^2 + a + 1[ with permutation 1 3 4 2 3 2 1 4 sage: T2.permutation().stratum() H_2(2) sage: assert T2.length(1) == T1.length(1) sage: assert T2.length(2) == T1.length(2) + T1.length(5) sage: assert T2.length(3) == T1.length(3) sage: assert T2.length(4) == T1.length(4) + T1.length(5) sage: assert T1.sah_arnoux_fathi_invariant() == T2.sah_arnoux_fathi_invariant() Left and right sides fake zeros:: sage: p = iet.Permutation([1,5,3,4,6,2], [3,2,6,5,1,4]) sage: T1 = iet.IntervalExchangeTransformation(p, [1,a+1,a**2+1,a**3,a**4-a,a**3+a+1]) sage: T2 = T1.erase_marked_points() sage: T2 Interval exchange transformation of [0, 2*a^4 + 2*a^3 + a^2 + a + 5[ with permutation 1 3 4 2 3 2 1 4 sage: T2.permutation().stratum() H_2(2) sage: assert T2.length(1) == T1.length(1) + T1.length(5) sage: assert T2.length(2) == T1.length(2) + T1.length(6) sage: assert T2.length(3) == T1.length(3) + T1.length(5) sage: assert T2.length(4) == T1.length(4) + T1.length(6) sage: assert T1.sah_arnoux_fathi_invariant() == T2.sah_arnoux_fathi_invariant() Left-right fake zero:: sage: p = iet.Permutation([1,3,4,2,5], [2,3,5,1,4]) sage: T1 = iet.IntervalExchangeTransformation(p, [1, a+1, a**2, a**3, a**4-a]) sage: T2 = T1.erase_marked_points() sage: T2 Interval exchange transformation of [0, 2*a^4 + 2*a^3 + a^2 - a + 2[ with permutation 1 3 4 2 2 3 1 4 sage: T2.permutation().stratum() H_2(2) sage: assert T2.length(1) == T1.length(1) + T1.length(5) sage: assert T2.length(2) == T1.length(2) + T1.length(5) sage: assert T2.length(3) == T1.length(3) sage: assert T2.length(4) == T1.length(4) + T1.length(2) sage: assert T1.sah_arnoux_fathi_invariant() == T2.sah_arnoux_fathi_invariant() A small example that end up wrong:: sage: p = iet.Permutation([0,2,1,3,4], [4,0,1,2,3]) sage: K.<a> = NumberField(x^2 - 2, embedding=AA(2)**(1/2)) sage: lengths = [1, a + 1, a, a + 1, 2*a - 2] sage: T = iet.IntervalExchangeTransformation(p, lengths) sage: U = T.erase_marked_points() sage: U.permutation().stratum() H_2(2) sage: assert T.sah_arnoux_fathi_invariant() == U.sah_arnoux_fathi_invariant() sage: top = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] sage: bot = [15, 11, 12, 9, 10, 7, 8, 5, 6, 2, 3, 4, 16, 14, 0, 1, 13] sage: p = iet.Permutation(top, bot) sage: x = polygen(QQ) sage: poly = x^6 - 6*x^4 + 9*x^2 - 3 sage: emb = AA.polynomial_root(poly, RIF(1.25, 1.30)) sage: K.<a> = NumberField(poly, embedding=emb) sage: lengths = (-2*a^4 + 7*a^2 - 6, 10/3*a^4 - 17*a^2 + 19, -a^4 + 6*a^2 - 7, ....: -1/3*a^4 + 3*a^2 - 4, -2/3*a^4 + 3*a^2 - 3, 2/3*a^4 + 11*a^2 - 20, ....: 2/3*a^4 + 11*a^2 - 20, 8*a^4 - 35*a^2 + 36, 8*a^4 - 35*a^2 + 36, ....: -29/3*a^4 + 42*a^2 - 43, -29/3*a^4 + 42*a^2 - 43, 8/3*a^4 - 28*a^2 + 39, ....: 8/3*a^4 - 28*a^2 + 39, -4*a^4 + 20*a^2 - 22, 4/3*a^4 - 4*a^2 + 3, ....: -8/3*a^4 + 16*a^2 - 19, 8/3*a^4 - 14*a^2 + 16) sage: T = iet.IntervalExchangeTransformation(p, lengths) sage: T.permutation().stratum_component() H_4(6, 0^9)^hyp sage: T.sah_arnoux_fathi_invariant() (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) sage: U = T.erase_marked_points() sage: U.permutation().stratum_component() H_4(6)^hyp sage: U.sah_arnoux_fathi_invariant() (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) """ p = self._permutation.__copy__() n = len(p) lengths = self._lengths tt, tb = self._permutation._twin lt, lb = self._permutation._labels newlengths = list(self._lengths) assert max(lt) < len(newlengths) assert max(lb) < len(newlengths) remove = set() newtop = [] i = 0 # singularities strictly in the middle while i < n: k = 1 while i + k < n and tt[i] + k == tt[i + k]: k += 1 newlengths[lt[i]] = sum(lengths[j] for j in range(i, i+k)) for j in range(i+1, i+k): remove.add(lt[j]) newtop.append(lt[i]) i += k if remove: newbot = [j for j in lb if j not in remove] assert set(newtop) == set(newbot) p._labels = [newtop, newbot] p._init_twin(p._labels) tt, tb = p._twin lt, lb = newtop, newbot n = len(lt) i0 = tb[0] - 1 i1 = tt[0] - 1 if len(newtop) > 2 and i0 > 0 and i1 > 0 and tt[i0] == i1: # left end fake zero # the permutation looks like # A ... C B ... # B ... C A ... # backward top-left # A ... C B ... l(A) <- l(A) + l(C) # C B ... A ... # fusion C in B # A ... B ... # B ... A ... assert lt[i0] == lb[i1] A = lt[0] B = lb[0] C = lt[i0] newlengths[A] += newlengths[C] newlengths[B] += newlengths[C] newlengths[C] = 0 newtop.pop(newtop.index(C)) newbot = [i for i in lb if i != C] p._labels = [newtop, newbot] assert set(newtop) == set(newbot) p._init_twin(p._labels) tt, tb = p._twin lt, lb = newtop, newbot n = len(lt) i0 = tb[-1] + 1 i1 = tt[-1] + 1 if len(newtop) > 2 and i0 < n and i1 < n and tt[i0] == i1: # right end fake zero # the permutation looks like # ... B C ... A # ... A C ... B # backward top-right # ... B C ... A # ... A ... B C # fusion C in B # ... B ... A # ... A ... B assert lt[i0] == lb[i1] A = lt[-1] B = lb[-1] C = lt[i0] assert newtop[-1] == A newlengths[A] += newlengths[C] newlengths[B] += newlengths[C] newlengths[C] = 0 newtop.pop(newtop.index(C)) newbot = [i for i in lb if i != C] assert set(newtop) == set(newbot) p._labels = [newtop, newbot] p._init_twin(p._labels) tt, tb = p._twin lt, lb = newtop, newbot n = len(lt) i0 = tb[0] - 1 # D i1 = tt[0] - 1 # C if len(newtop) > 2 and tb[i1] == tt[i0] == n-1: # left-right end fake zero # the permutation looks like # A ... D B ... C or A ... D B or A B ... C # B ... C A ... D B A ... D B ... C A # (B=C case) (A=D case) assert lb[-1] == lt[i0] and lt[-1] == lb[i1] A = lt[0] B = lb[0] C = lb[i1] D = lt[i0] assert newtop[0] == A and newtop[-1] == C and B != D assert A in newtop and B in newtop and C in newtop and D in newtop if B == C: # A ... D B # B A ... D # backward bot-left # D A ... B l(B) <- l(B) + l(D) # B A ... D # backward top-right # D A ... B l(B) <- l(B) + l(A) # B ... D A # fusion A in D # D ... B # B ... D assert newtop[0] == A and newtop[-1] == B and newtop[-2] == D newlengths[B] += newlengths[A] + newlengths[D] newlengths[D] += newlengths[A] newlengths[A] = 0 newtop.pop(0) newtop.pop(-2) newtop.insert(0, D) assert newtop[0] == D and newtop[-1] == B newbot = [i for i in lb if i != A] elif A == D: # A B ... C # B ... C A # backward bot-right # A ... C B l(A) <- l(A) + l(B) # B ... C A # backward top-left # A ... C B l(A) <- l(A) + l(C) # C B ... A # fusion C in B # A ... B # B ... A newlengths[A] += newlengths[B] + newlengths[C] newlengths[B] += newlengths[C] newlengths[C] = 0 assert newtop[-1] == C assert newtop[1] == B newtop.pop(-1) newtop.pop(1) newtop.append(B) assert newtop[0] == A and newtop[-1] == B, (newtop, A, B) assert C not in newtop newbot = [i for i in lb if i != C] else: # do backward Rauzy top-left / bottom-right # A ... D ... C B l(A) <- l(A) + l(C) # C B ... A ... D l(D) <- l(D) + l(B) # fusion C in B # A ... D ... B # B ... A ... D newlengths[A] += newlengths[C] # backward rauzy newlengths[D] += newlengths[B] # backward rauzy newlengths[B] += newlengths[C] # absorption newlengths[C] = 0 newtop.pop(newtop.index(C)) newtop.pop(newtop.index(B)) newtop.append(B) newbot = [i for i in lb if i != C] assert set(newtop) == set(newbot) p._labels = [newtop, newbot] p._init_twin(p._labels) tt, tb = p._twin lt, lb = newtop, newbot unrank = self._permutation.alphabet().unrank newtop = [unrank(lab) for lab in lt] newbot = [unrank(lab) for lab in lb] newlengths = [newlengths[j] for j in lt] p = LabelledPermutationIET([newtop, newbot]) return IntervalExchangeTransformation(p, newlengths)
def __mul__(self, other): r""" Composition of iet. The domain (i.e. the length) of the two iets must be the same). The alphabet chosen depends on the permutation. TESTS: sage: from surface_dynamics import * :: 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 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.base_ring().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: from surface_dynamics import * 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: from surface_dynamics import * 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)
[docs] def recoding(self, n): r""" Recode this interval exchange transformation on the words of length ``n``. EXAMPLES:: sage: from surface_dynamics 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 def key(x): return [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)
[docs] 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 TESTS:: sage: from surface_dynamics import * 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; 3[ .. 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)
[docs] 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 EXAMPLES:: sage: from surface_dynamics import * 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()]
[docs] 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 import * sage: t = iet.IET(("a b","b a"), [1, sqrt(2)]) sage: t.domain_singularities() [0, 1, sqrt(2) + 1] """ l = [self.base_ring().zero()] for j in self._permutation._labels[0]: l.append(l[-1] + self._lengths[j]) return l
[docs] 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 import * sage: t = iet.IET(("a b","b a"), [1, sqrt(2)]) sage: t.range_singularities() [0, sqrt(2), sqrt(2) + 1] """ l = [self.base_ring().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 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]
[docs] def rauzy_move(self, side='right', iterations=1, data=False, error_on_saddles=True): 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 - ``error_on_saddles`` - (default: ``True``) whether to stop when a saddle is encountered 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 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 An other examples involving 3 intervals:: 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() sage: t1 Interval exchange transformation of [0, 4[ with permutation a b c c a b sage: t2 = t1.rauzy_move() sage: t2 Interval exchange transformation of [0, 3[ with permutation a b c c b a sage: t2.rauzy_move() Traceback (most recent call last): ... ValueError: saddle connection found sage: t2.rauzy_move(error_on_saddles=False) 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(error_on_saddles=False) Interval exchange transformation of [0, 1[ with permutation a a """ if data: towers = {a:[a] for a in self._permutation.letters()} path = [] side = side_conversion(side) res = copy(self) for i in range(iterations): winner,(a,b,c) = res._rauzy_move(side, error_on_saddles=error_on_saddles) if data: if winner is None: raise ValueError("does not handle degenerate situations") 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
[docs] def backward_rauzy_move(self, winner, side='right'): r""" Return a new interval exchange transformation obtained by performing a backward Rauzy move. EXAMPLES:: sage: from surface_dynamics import iet sage: perm = iet.Permutation('a b c d e f', 'f c b e d a') sage: x = polygen(QQ) sage: poly = x^3 - x^2 - x - 1 sage: root = AA.polynomial_root(poly, RIF(1.8, 1.9)) sage: K.<a> = NumberField(poly, embedding=root) sage: T = iet.IntervalExchangeTransformation(perm, [a**2, a-1, a + 2, 3, 2*a - 3, 1]) sage: T Interval exchange transformation of [0, a^2 + 4*a + 2[ with permutation a b c d e f f c b e d a sage: S = T.backward_rauzy_move(0).backward_rauzy_move(1).backward_rauzy_move(1) sage: S Interval exchange transformation of [0, a^2 + 7*a + 4[ with permutation a b c f d e f b e d a c sage: S.rauzy_move(iterations=3) == T True """ winner = interval_conversion(winner) side = side_conversion(side) res = copy(self) res._backward_rauzy_move(winner, side) return res
[docs] def zorich_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 path OUTPUT: - ``iet`` -- the Rauzy move of self - ``path`` -- (if ``data=True``) a list of 't' and 'b' EXAMPLES:: sage: from surface_dynamics import * sage: p = iet.Permutation('a b c', 'c b a') sage: T = iet.IntervalExchangeTransformation(p, [12, 35, 67]) sage: T.zorich_move() Interval exchange transformation of [0, 55[ with permutation a b c c a b sage: assert T.permutation() == p and T.lengths() == vector((12,35,67)) A self similar example in genus 2:: sage: p = iet.Permutation('a b c d', 'd a c b') sage: R = p.rauzy_diagram() sage: code = 'b'*4 + 't'*1 + 'b'*3 + 't'*1 + 'b'*3 + 't'*1 + 'b'*1 + 't'*1 + 'b'*4 + 't'*1 + 'b'*2 + 't'*7 sage: g = R.path(p, *code) sage: m = g.matrix() sage: poly = m.charpoly() sage: l = max(poly.roots(AA, False)) sage: K.<a> = NumberField(poly, embedding=l) sage: lengths = (m - a).right_kernel().basis()[0] sage: T = iet.IntervalExchangeTransformation(p, lengths) sage: T.normalize(a, inplace=True) sage: T Interval exchange transformation of [0, a[ with permutation a b c d d a c b sage: T2, path = T.zorich_move(iterations=12, data=True) sage: a*T2.lengths() == T.lengths() True sage: path == code True Saddle connection detection:: sage: p = iet.Permutation('a b c', 'c b a') sage: T = iet.IntervalExchangeTransformation(p, [41, 22, 135]) sage: T.zorich_move(iterations=100) Traceback (most recent call last): ... ValueError: saddle connection found sage: p = iet.Permutation('a b c d e f', 'f c b e d a') sage: T = iet.IntervalExchangeTransformation(p, [41, 132, 22, 135, 55, 333]) sage: T.zorich_move(iterations=100) Traceback (most recent call last): ... ValueError: saddle connection found """ if data: path = '' side = side_conversion(side) res = copy(self) for i in range(iterations): winner, m = res._zorich_move(side) if data: path += winner * m if data: return res, path else: return res
def _rauzy_move(self, side=-1, error_on_saddles=True): r""" Perform a Rauzy move inplace INPUT: - ``side`` - must be 0 or -1 (no verification) - ``error_on_saddles`` - (default ``True``) whether an error is raised when a saddle connections is encountered OUTPUT: - ``T`` - the iet after Rauzy induction - ``winner`` - either ``'t'`` or ``'b'`` (and possibly ``None`` if ``error_on_saddles`` is ``False``) - ``(a,b,c)`` - a triple of letter such that the towers are obtained by applying the substitution `a \mapsto bc`. TESTS:: sage: from surface_dynamics import * sage: p = iet.Permutation('a b c d', 'd c b a') sage: K.<sqrt2> = QuadraticField(2) sage: T = iet.IntervalExchangeTransformation(p, [1,1,1,sqrt2]) sage: T._rauzy_move() ('t', ('a', 'a', 'd')) sage: T._rauzy_move() ('b', ('d', 'b', 'd')) sage: T._rauzy_move() ('t', ('b', 'b', 'c')) sage: T._rauzy_move() ('b', ('c', 'b', 'c')) sage: T._rauzy_move() ('t', ('b', 'b', 'd')) sage: T._rauzy_move() ('b', ('d', 'c', 'd')) sage: T._rauzy_move() ('t', ('c', 'c', 'd')) sage: T._rauzy_move() ('b', ('d', 'a', 'd')) sage: T Interval exchange transformation of [0, -4*sqrt2 + 7[ with permutation a d b c d c b a Saddle connections:: sage: p = iet.Permutation('a b c', 'c b a') sage: T = iet.IntervalExchangeTransformation(p, [1,2,1]) sage: T._rauzy_move() Traceback (most recent call last): ... ValueError: saddle connection found sage: T._rauzy_move(error_on_saddles=False) (None, ('a', 'a', 'c')) sage: T.lengths() (1, 2, 0) sage: T.permutation() a b a b sage: p = iet.Permutation([0, 1, 2, 3], [3, 1, 2, 0]) sage: T = iet.IntervalExchangeTransformation(p, [13, 2, 22, 13]) sage: T._rauzy_move(0, error_on_saddles=False) (None, (3, 3, 0)) sage: T Interval exchange transformation of [0, 37[ with permutation 1 2 3 1 2 3 sage: print(T._lengths) (0, 2, 22, 13) sage: print(T._permutation._labels) [[1, 2, 3], [1, 2, 3]] """ top = self._permutation._labels[0][side] bot = self._permutation._labels[1][side] unrank = self._permutation.alphabet().unrank top_letter = unrank(top) bot_letter = unrank(bot) length_top = self._lengths[top] length_bot = self._lengths[bot] if length_top > length_bot: winner = 0 # TODO: this value is ignored winner_interval = top loser_interval = bot abc = (bot_letter, bot_letter, top_letter) winner = 't' elif length_top < length_bot: winner = 1 # TODO: this value is ignored winner_interval = bot loser_interval = top abc = (top_letter, bot_letter, top_letter) winner = 'b' elif error_on_saddles: raise ValueError("saddle connection found") else: # we do a pseudo-top Rauzy induction and remove the bot interval # (the iet get one interval less) p = self._permutation top = p._labels[0][side] bot = p._labels[1][side] p._identify_intervals(side) self._lengths[top] = 0 return None, (bot_letter,bot_letter,top_letter) self._permutation = self._permutation.rauzy_move(winner=winner, side=side, inplace=True) self._lengths[winner_interval] -= self._lengths[loser_interval] return winner, abc def _backward_rauzy_move(self, winner, side=-1): r""" Inplace backward Rauzy move. EXAMPLES:: sage: from surface_dynamics import iet sage: x = polygen(QQ) sage: K.<cbrt2> = NumberField(x^3 - 2, embedding=AA(2)**(1/3)) sage: p = iet.Permutation("a b c d", "d c b a") sage: T0 = iet.IntervalExchangeTransformation(p, [1, cbrt2, cbrt2**2, cbrt2 - 1]) sage: T1 = copy(T0) sage: T1.lengths() (1, cbrt2, cbrt2^2, cbrt2 - 1) sage: T1._backward_rauzy_move(0) sage: T1.lengths() (1, cbrt2, cbrt2^2, cbrt2^2 + cbrt2 - 1) sage: _ = T1._rauzy_move() sage: T1 == T0 True sage: T1._backward_rauzy_move(0) sage: T1._backward_rauzy_move(1) sage: T1._backward_rauzy_move(1) sage: T1._backward_rauzy_move(0) sage: _ = T1._rauzy_move() sage: _ = T1._rauzy_move() sage: _ = T1._rauzy_move() sage: _ = T1._rauzy_move() sage: T1 == T0 True """ self._permutation = self._permutation.backward_rauzy_move(winner=winner, side=side, inplace=True) winner_interval = self._permutation._labels[winner][side] loser_interval = self._permutation._labels[1-winner][side] self._lengths[winner_interval] += self._lengths[loser_interval] def _zorich_move(self, side=-1): r""" Performs a Zorich move (acceleration of Rauzy) inplace INPUT: - ``side`` - must be 0 or -1 (no verification) OUTPUT: - ``winner_letter`` - whether top or bot was done - ``m`` -- number of Rauzy made on the same side TESTS:: sage: from surface_dynamics import * sage: p = iet.Permutation('a b', 'b a') sage: K.<sqrt3> = QuadraticField(3) sage: t = iet.IntervalExchangeTransformation(p, [1, sqrt3 - 1]) sage: t._zorich_move() ('b', 1) sage: t._zorich_move() ('t', 2) sage: t._zorich_move() ('b', 1) sage: t._zorich_move() ('t', 2) sage: t._zorich_move() ('b', 1) sage: continued_fraction(sqrt3 - 1) [0; (1, 2)*] sage: x = polygen(ZZ) sage: K.<a> = NumberField(x^3 - 2, embedding=AA(2)**(1/3)) sage: t = iet.IntervalExchangeTransformation(p, [1, a]) sage: [t._zorich_move()[1] for _ in range(10)] [1, 3, 1, 5, 1, 1, 4, 1, 1, 8] sage: continued_fraction(a) [1; 3, 1, 5, 1, 1, 4, 1, 1, 8, 1, 14, 1, 10, 2, 1, 4, 12, 2, 3, ...] A self-similar example in genus 2:: sage: x = polygen(ZZ) sage: poly = x^4 - 11*x^3 + 21*x^2 - 11*x + 1 sage: l = max(poly.roots(AA, False)) sage: K.<a> = NumberField(poly, embedding=l) sage: lengths = vector([-3*a^3 + 33*a^2 - 63*a + 33, -4*a^3 + 42*a^2 - 63*a + 14, ....: 7*a^3 - 74*a^2 + 116*a - 34, -a^2 + 10*a - 10]) sage: p = iet.Permutation('a b c d', 'd c b a') sage: t = iet.IntervalExchangeTransformation(p, lengths) sage: [t._zorich_move() for _ in range(6)] [('t', 1), ('b', 2), ('t', 2), ('b', 1), ('t', 2), ('b', 2)] sage: lengths == a * t.lengths() True sage: [t._zorich_move(side=0) for _ in range(6)] [('b', 1), ('t', 2), ('b', 2), ('t', 1), ('b', 2), ('t', 2)] sage: lengths == a^2 * t.lengths() True Saddle connection detection:: sage: p = iet.Permutation('a b c', 'c b a') sage: T = iet.IntervalExchangeTransformation(p, [41, 1, 5]) sage: T._zorich_move() Traceback (most recent call last): ... ValueError: saddle connection found """ top = self._permutation._labels[0][side] bot = self._permutation._labels[1][side] length_top = self._lengths[top] length_bot = self._lengths[bot] if length_top > length_bot: winner = 0 winner_letter = 't' winner_interval = top loser_interval = bot elif length_top < length_bot: winner = 1 winner_letter = 'b' winner_interval = bot loser_interval = top else: raise ValueError("saddle connection found") # number of full loops lwin = self._lengths[winner_interval] loser = 1 - winner loser_row = self._permutation._labels[loser] llos = 0 k = 0 if side == -1: # right induction j = -1 while loser_row[j] != winner_interval: llos += self._lengths[loser_row[j]] j -= 1 k += 1 else: # left induction j = 0 while loser_row[j] != winner_interval: llos += self._lengths[loser_row[j]] j += 1 k += 1 # remove the full loops m = (lwin / llos).floor() self._lengths[winner_interval] -= m*llos # remaining steps r = 0 while self._lengths[winner_interval] >= self._lengths[loser_interval]: self._lengths[winner_interval] -= self._lengths[loser_interval] self._permutation.rauzy_move(winner=winner, side=side, inplace=True) loser_interval = self._permutation._labels[loser][side] r += 1 if self._lengths[winner_interval].is_zero(): raise ValueError("saddle connection found") return winner_letter, k*m + r def __copy__(self): r""" Returns a copy of this interval exchange transformation. EXAMPLES:: sage: from surface_dynamics 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._vector_space = self._vector_space res._permutation = copy(self._permutation) res._lengths = copy(self._lengths) return res
[docs] 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 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)) # not tested (problem with matplotlib font cache) Graphics object consisting of 4 graphics primitives """ from sage.plot.plot import Graphics from sage.plot.line 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
[docs] 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 (default), center or right - ``labels`` - boolean (default: True) - ``fontsize`` - the size of the label OUTPUT: 2d plot -- a plot of the two intervals (domain and range) EXAMPLES:: sage: from surface_dynamics import * sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,1]) sage: t.plot_two_intervals() # random output due to matplotlib deprecation warnings with SageMath <9.1 Graphics object consisting of 8 graphics primitives """ from sage.plot.plot import Graphics from sage.plot.line import line2d try: from sage.plot.plot import text except ImportError: from sage.plot.text import text from sage.plot.colors import rainbow G = Graphics() lengths = list(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 is 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 is 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
[docs] 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 import * sage: p = iet.Permutation('A B', 'B A') sage: T = iet.IntervalExchangeTransformation(p, [0.41510826, 0.58489174]) sage: T.plot_towers(iterations=5) # not tested (problem with matplotlib font cache) 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 = [float(length) for length in 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
[docs] def show(self): r""" Shows a picture of the interval exchange transformation EXAMPLES:: sage: from surface_dynamics import * sage: phi = QQbar((sqrt(5)-1)/2) sage: t = iet.IntervalExchangeTransformation(('a b','b a'),[1,phi]) sage: t.show() # not tested (problem with matplotlib font cache) """ self.plot_two_intervals().show(axes=False)