def to_word_morphism(self, forget_inverse=False): r""" Return a word morphism. .. NOTE:: This method should not be there but on the other hand, f.periodic_points() fails for FreeGroupMorphism and FreeGroupAutomorphism EXAMPLES:: sage: f = FreeGroupAutomorphism('a->AD,b->Adac,c->bd,d->c') sage: f.to_word_morphism().periodic_points() [[word: AdacccADDBdacADbdbdbddaCCCADacADbddaCAda..., word: dacADbdbdbddaCCCADacADbddaCAdaccAdaccAda..., word: cADbddaCAdaccAdaccAdacccADDBDBDBdaCADbdd..., word: bddaCAdacccADDBdacADbdbddacADbdbddacADbd...], [word: CCADaCCADacADDBdaCCCADaCCADacADDBdaCAdac..., word: DBDBdaCADDBDBdaCADbddaCCCADacADDBDBDBdaC...]] """ if forget_inverse: A = self.domain().alphabet() f = {} for a in A.positive_letters(): f[a] = map(A.to_positive_letter, self.image(a)) return WordMorphism(f) return WordMorphism( dict((a, list(self.image(a))) for a in self.domain().alphabet()))
def identity_morphism(self): r""" Returns the identity morphism from self to itself. EXAMPLES:: sage: W = Words('ab') sage: W.identity_morphism() WordMorphism: a->a, b->b :: sage: W = Words(range(3)) sage: W.identity_morphism() WordMorphism: 0->0, 1->1, 2->2 There is no support yet for infinite alphabet:: sage: W = Words(alphabet=Alphabet(name='NN')) sage: W Words over Non negative integers sage: W.identity_morphism() Traceback (most recent call last): ... NotImplementedError: size of alphabet must be finite """ if self.size_of_alphabet() not in ZZ: raise NotImplementedError, 'size of alphabet must be finite' from sage.combinat.words.morphism import WordMorphism return WordMorphism(dict((a, a) for a in self.alphabet()))
def blow_up_vertices(self, germ_components): """Blow-up ``self`` according to classes of germs given in ``germ_components``. INPUT: - ``germ_components`` a list of classes of germs outgoing from a vertex. OUTPUT: A dictionary that maps an old edge to the path in the new graph. EXAMPLES:: sage: from train_track import * sage: from train_track.inverse_graph import GraphWithInverses sage: from train_track.marked_graph import MarkedGraph sage: G = MarkedGraph.rose_marked_graph(AlphabetWithInverses(2)) sage: G.blow_up_vertices([['a','A'],['b'],['B']]) {'A': word: cAC, 'B': word: eBD, 'a': word: caC, 'b': word: dbE} sage: print(G) Marked graph: a: 1->1, b: 2->3, c: 0->1, d: 0->2, e: 0->3 Marking: a->caC, b->dbE """ blow_up_map = GraphWithInverses.blow_up_vertices(self, germ_components) blow_up_morph = WordMorphism(blow_up_map) self._marking.set_edge_map(blow_up_morph * self.marking().edge_map()) return blow_up_map
def contract_forest(self, forest): """ Contract the forest. Each tree of the forest is contracted to the initial vertex of its first edge. INPUT: - ``forest`` is a list of disjoint subtrees each given as lists of edges. OUTPUT: A dictionary that maps old edges to new edges. EXAMPLES:: sage: from train_track import * sage: from train_track.inverse_graph import GraphWithInverses sage: from train_track.marked_graph import MarkedGraph sage: G = MarkedGraph.rose_marked_graph(AlphabetWithInverses(2)) sage: G.contract_forest([['b']]) {'A': word: A, 'B': word: , 'a': word: a, 'b': word: } .. SEEALSO:: :meth:`train_track.inverse_graph.GraphWithInverses.contract_forest()`` """ contract_map = GraphWithInverses.contract_forest(self, forest) contract_morph = WordMorphism(contract_map) self._marking.set_edge_map(contract_morph * self._marking._edge_map) return contract_map
def _int_list_to_substitutions(self, words): A = self._start._alphabet W = FiniteWords(A) s = {} for i, w in enumerate(words): s[A.unrank(i)] = [A.unrank(j) for j in w] return WordMorphism(s, domain=W, codomain=W)
def stallings_folding(self): """ Implement Stallings' folding to get an immersion from ``self``. The domain of ``self`` is fold until we get an immersion. ALGORITHM: We first subdivide edges of the domain according to length of their image. Then fold one gate at one vertex and update the edge map and illegal turns list. Repeat the process till no illegal turns remain. REFERENCES: [Stallings] J. Stallings, Topology of Finite Graphs, """ A = self.domain().alphabet() for a in A: if len(self.image(a)) > 1: self.subdivide_domain(a) Turns = self._domain.turns( ) #list of all turns in domain after subdivision Il_turns = self.illegal_turns( Turns) # list of illegal turns in domain after subdivision counter = 0 while len(Il_turns) > 0: counter = counter + 1 # find edge_list (list of edges in the gate correspoding to e1) to fold at exactly one vertex e1 = Il_turns[0][0] edge_list = [e1] for a in A: if self._domain.initial_vertex( a) == self._domain.initial_vertex(e1) and e1 != a: if (e1, a) in Il_turns or (a, e1) in Il_turns: edge_list.append(a) edge_list = list(set(edge_list)) # remove duplicates # fold at initial_vertex of e1 ( this function updates the domain and edge_map) self._domain.fold(edge_list, []) #update edge_map again d = {} d[edge_list[0]] = self.image(edge_list[0]) for a in A: if a not in edge_list: d[a] = self.image(a) wm = WordMorphism(d) self.set_edge_map(wm) Turns = self._domain.turns() #update list of all turns in domain Il_turns = self.illegal_turns( Turns) # update list of illegal turns in domain return self
def iter_conjugate_classP(words, n): r""" EXAMPLES:: sage: from slabbe.word_morphisms import iter_conjugate_classP sage: F = FiniteWords('ab') sage: list(iter_conjugate_classP(F, 2)) [WordMorphism: a->a, b->a, WordMorphism: a->a, b->b, WordMorphism: a->b, b->a, WordMorphism: a->b, b->b] sage: list(iter_conjugate_classP(F, 3)) [WordMorphism: a->aa, b->a, WordMorphism: a->aa, b->b, WordMorphism: a->bb, b->a, WordMorphism: a->bb, b->b, WordMorphism: a->a, b->aa, WordMorphism: a->a, b->bb, WordMorphism: a->b, b->aa, WordMorphism: a->b, b->bb, WordMorphism: a->ba, b->b, WordMorphism: a->ab, b->a, WordMorphism: a->b, b->ba, WordMorphism: a->a, b->ab] """ alphabet = words.alphabet() length = alphabet.cardinality() # images are palindromes for sizes in IntegerListsLex(n=n, length=length, min_part=1): L = [iter_palindromes(words, size) for size in sizes] for pals in itertools.product(*L): d = dict(itertools.izip(alphabet, pals)) yield WordMorphism(d, codomain=words) # images are one common letter + palindrome for sizes in IntegerListsLex(n=n - length, length=length, min_part=0): L = [iter_palindromes(words, size) for size in sizes] for pals in itertools.product(*L): for b in alphabet: d = { a: words([b]) * p for a, p in itertools.izip(alphabet, pals) } if all(w.is_palindrome() for w in d.values()): # already yielded above continue yield WordMorphism(d, codomain=words)
def __init__(self, sigma, k, presuf='prefix', dual=False): self._sigma_dict = sigma self._sigma = WordMorphism(sigma) self._k = k if not presuf in ['prefix', 'suffix']: raise ValueError('Input presuf(={}) should be "prefix" or' ' "suffix"'.format(presuf)) self._presuf = presuf self._dual = dual
def pullback(self, f2, G3, A): """ INPUT : Two Graph maps f1:G1->G, f2:G2->G, an empty GraphWithInverses G3 and an empty AlphabetWithInverses A OPERATION : Find the pullback G3 and a graph map f:G3->G OUTPUT : Graphmap f The pullback method can be used to find intersection of two subgroups of a Free Group. Example : G1 = GraphWithInverses.rose_graph(AlphabetWithInverses(2,type='x0')) G2 = GraphWithInverses.rose_graph(AlphabetWithInverses(2,type='a0')) G = GraphWithInverses.rose_graph(AlphabetWithInverses(2)) n1 = WordMorphism({'x0':['a','a'],'x1':['b','a']}) n2 = WordMorphism({'a0':['b','a'],'a1':['b','b','b','a','B','a']}) f1 = GraphMap(G1,G,n1) f2 = GraphMap(G2,G,n2) G3 = GraphWithInverses() A = AlphabetWithInverses(0,type='a0') f1.pullback(f2,G3,A) """ import itertools #First convert self and f2 into immersions self.stallings_folding() f2.stallings_folding() # G3 = GraphWithInverses() #A = AlphabetWithInverses(0,type='a0') d = {} #get set of vertices V = [] for i in itertools.product(self.domain().vertices(), f2.domain().vertices()): V.append(i) #add edges for v in V: for w in V: for e1 in self.domain().alphabet().positive_letters(): if self.domain().initial_vertex(e1) == v[ 0] and self.domain().terminal_vertex(e1) == w[0]: for e2 in f2.domain().alphabet().positive_letters(): if f2.domain().initial_vertex( e2) == v[1] and f2.domain( ).terminal_vertex(e2) == w[1]: if self.image(e1) == f2.image(e2): e = A.add_new_letter() G3.add_edge(v, w, e) #update dictionary to define map on G3 d[e[0]] = self.image(e1) G3._alphabet = A n3 = WordMorphism(d) G = self.codomain() #same as f2.codomain() return GraphMap(G3, G, n3)
def set_edge_map(self, edge_map): """ Sets the edge map of ``self``. ``edge_map`` is anything that is accepted by ``Wordmorphism(edge_map)``, the image of the inverse letters will be calculated: they need not be explicit in ``edge_map``, only one of the images of each pair [letter,inverse(letter)] need to be given by ``edge_map``. Images of ``edge_map`` need not be reduced. """ A = self.domain().alphabet() tmp_map = WordMorphism(edge_map) m = {} for a in tmp_map._domain.alphabet(): m[a] = self._codomain.reduce_path(tmp_map.image(a)) m[A.inverse_letter(a)] = self._codomain.reverse_path(m[a]) self._edge_map = WordMorphism(m) self._vertex_map = None
def fold(self, edges_full, edges_partial): """ Folds the list of edges. Some edges are fully folded and some are only partially folded. All edges are assumed to start form the same vertex. Edges are given by their label. In the terminology of Stallings folds the partially fold edges are subdivided and then fold. The first element of ``edges_full`` is allowed to be a tuple ``(path,'path')`` and not an ``edge_label``. Then the other edges will be folded to the whole ``path``. In Stallings terminology, this is a sequence of folds of the successive edges of ``path``. INPUT: - ``edges_full``, are list of edges - ``edges_partial`` are list of edges (each possibly empty, but the union must have at least two edges). OUTPUT: A dictionary that maps old edges to new graph paths. EXAMPLES:: sage: from train_track.inverse_graph import GraphWithInverses sage: from train_track.marked_graph import MarkedGraph sage: G = GraphWithInverses([[0,0,'a'],[0,1,'b'],[1,1,'c']]) sage: G = MarkedGraph(G) sage: G.fold(['b'],['a']) {'A': word: AB, 'B': word: B, 'C': word: C, 'a': word: ba, 'b': word: b, 'c': word: c} sage: print(G) Marked graph: a: 1->0, b: 0->1, c: 1->1 Marking: a->ba, b->bcB .. SEEALSO:: :meth:`train_track.inverse_graph.GraphWithInverses.fold()`` """ fold_map = GraphWithInverses.fold(self, edges_full, edges_partial) fold_morph = WordMorphism(fold_map) self._marking.set_edge_map(fold_morph * self._marking._edge_map) return fold_map
def set_edge_map(self, edge_map): """ Sets the edge map of ``self``. ``edge_map`` is anything that is accepted by ``WordMorphism(edge_map)``, the image of the inverse letters will be calculated: they need not be explicit in ``edge_map``, only one of the images of each pair [letter,inverse(letter)] need to be given by ``edge_map``. Images of ``edge_map`` need not be reduced. INPUT: - ``edge_map`` -- anything which is accepted by ``WordMorphism(edge_map)`` EXAMPLES:: sage: from train_track import * sage: from train_track.inverse_graph import GraphWithInverses sage: from train_track.graph_map import GraphMap sage: G = GraphWithInverses([[0,0,'a'],[0,1,'b'],[1,1,'c']]) sage: A = AlphabetWithInverses(2) sage: H = GraphWithInverses.rose_graph(A) sage: f = GraphMap(G,H,"a->ab,b->b,c->B") sage: f.set_edge_map('a->b,b->,c->b') sage: print(f) Graph map: a: 0->0, b: 0->1, c: 1->1 a: 0->0, b: 0->0 edge map: a->b, b->, c->b """ A = self.domain().alphabet() tmp_map = WordMorphism(edge_map) m = {} for a in tmp_map._domain.alphabet(): m[a] = self._codomain.reduce_path(tmp_map.image(a)) m[A.inverse_letter(a)] = self._codomain.reverse_path(m[a]) self._edge_map = WordMorphism(m) self._vertex_map = None
def subdivide(self, edge_list): """ Subdivides each edge in the edge_list into two edges. INPUT: - ``edge_list`` -- edge list OUTPUT: Subdivide map from subdivide GraphWithInverses. .. WARNING: Each edge in ``edge_list`` must appear only once. EXAMPLES:: sage: from train_track.inverse_graph import GraphWithInverses sage: from train_track.marked_graph import MarkedGraph sage: G = GraphWithInverses([[0,0,'a'],[0,1,'b'],[1,1,'c']]) sage: G = MarkedGraph(G) sage: G.subdivide(['a','c']) {'A': word: DA, 'B': word: B, 'C': word: EC, 'a': word: ad, 'b': word: b, 'c': word: ce} sage: print(G) Marked graph: a: 0->2, b: 0->1, c: 1->3, d: 2->0, e: 3->1 Marking: a->ad, b->bceB .. SEEALSO:: :meth:`train_track.inverse_graph.GraphWithInverses.subdivide()` """ subdivide_map = GraphWithInverses.subdivide(self, edge_list) subdivide_morph = WordMorphism(subdivide_map) self._marking.set_edge_map(subdivide_morph * self._marking._edge_map) return subdivide_map
def rauzy_move_relabel(self, winner, side='right'): r""" Returns the relabelization obtained from this move. EXAMPLE:: sage: from surface_dynamics import * sage: p = iet.Permutation('a b c d','d c b a') sage: q = p.reduced() sage: p_t = p.rauzy_move('t') sage: q_t = q.rauzy_move('t') sage: s_t = q.rauzy_move_relabel('t') sage: print(s_t) a->a, b->b, c->c, d->d sage: list(map(s_t, p_t[0])) == list(map(Word, q_t[0])) True sage: list(map(s_t, p_t[1])) == list(map(Word, q_t[1])) True sage: p_b = p.rauzy_move('b') sage: q_b = q.rauzy_move('b') sage: s_b = q.rauzy_move_relabel('b') sage: print(s_b) a->a, b->d, c->b, d->c sage: list(map(s_b, q_b[0])) == list(map(Word, p_b[0])) True sage: list(map(s_b, q_b[1])) == list(map(Word, p_b[1])) True """ from surface_dynamics.interval_exchanges.labelled import LabelledPermutationIET from sage.combinat.words.morphism import WordMorphism winner = interval_conversion(winner) side = side_conversion(side) p = LabelledPermutationIET(self.list()) l0_q = p.rauzy_move(winner, side).list()[0] d = dict([(self._alphabet[i],l0_q[i]) for i in range(len(self))]) return WordMorphism(d)
def compute_xsi(self, u): r""" EXAMPLES:: sage: from slabbe.word_morphisms import compute_xsi sage: s = WordMorphism({0:[0,1],1:[1,0]}) sage: compute_xsi(s, Word([0])) sigma_u= 0->012, 1->02, 2->1 theta_u= 0->011, 1->01, 2->0 psi= 0->(0, 0),(0, 1),(0, 2), 1->(1, 0),(1, 1), 2->(2, 0) psi*sigma_u= 0->(0, 0),(0, 1),(0, 2),(1, 0),(1, 1),(2, 0), 1->(0, 0),(0, 1),(0, 2),(2, 0), 2->(1, 0),(1, 1) Finite words over {(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (2, 0)} [1 0 0] [1 0 0] [1 0 0] [0 1 0] [0 1 0] [0 0 1] We want zeta such that: zeta((0, 0),(0, 1),(0, 2)) = (0, 0),(0, 1),(0, 2),(1, 0),(1, 1),(2, 0) zeta((1, 0),(1, 1)) = (0, 0),(0, 1),(0, 2),(2, 0) zeta((2, 0)) = (1, 0),(1, 1) """ sigma_u, theta_u = return_substitution(self, u, coding=True) assert theta_u * sigma_u == self * theta_u, "identity is not verified" print "sigma_u=", sigma_u print "theta_u=", theta_u d = { k: [(k, i) for i in range(len(v))] for k, v in theta_u._morph.iteritems() } psi = WordMorphism(d) print "psi=", psi print "psi*sigma_u=", psi * sigma_u print psi.codomain() print psi.incidence_matrix() print "We want zeta such that:" for k, v in psi._morph.iteritems(): print "zeta({}) = {}".format(v, psi(sigma_u(k)))
def return_substitution(self, u, coding=False, length=1000): r""" Return the return substitution of self according to factor u. INPUT: - ``self`` -- word morphism - ``u`` -- word such that u is a prefix of self(u) - ``coding`` -- boolean (default: ``False``), whether to include the return word coding morphism - ``length`` -- integer (default: ``1000``), compute the first 1000 letters of the derived sequence to make sure every return word are seen EXAMPLES:: sage: from slabbe.word_morphisms import return_substitution sage: s = WordMorphism({0:[0,1],1:[1,0]}) sage: return_substitution(s, Word([0])) WordMorphism: 0->012, 1->02, 2->1 sage: return_substitution(s, Word([0,1])) WordMorphism: 0->01, 1->23, 2->013, 3->2 sage: return_substitution(s, Word([0,1,1])) WordMorphism: 0->01, 1->23, 2->013, 3->2 :: sage: return_substitution(s, Word([0]), True) (WordMorphism: 0->012, 1->02, 2->1, WordMorphism: 0->011, 1->01, 2->0) sage: return_substitution(s, Word([0,1]), True) (WordMorphism: 0->01, 1->23, 2->013, 3->2, WordMorphism: 0->011, 1->010, 2->0110, 3->01) :: sage: s = WordMorphism({0:[0,0,1],1:[0,1]}) sage: return_substitution(s, Word([0])) WordMorphism: 0->01, 1->011 TESTS:: sage: s = WordMorphism({0:[0,1],1:[1,0]}) sage: sigma_u, theta_u = return_substitution(s, Word([0]), coding=True) sage: sigma_u WordMorphism: 0->012, 1->02, 2->1 sage: theta_u WordMorphism: 0->011, 1->01, 2->0 sage: theta_u*sigma_u == s*theta_u True sage: theta_u*sigma_u WordMorphism: 0->011010, 1->0110, 2->01 """ from slabbe.infinite_word import derived_sequence a = u[0] x = self.fixed_point(a) s, D = derived_sequence(x, u, coding=True) _ = s[length] # make sure that D is complete (exact value # is known by J. Leroy and F. Durand) code_to_return_word = WordMorphism({v: k for k, v in D.iteritems()}) rep = {} for key, value in D.iteritems(): self_key = self(key) L = desubstitute(code_to_return_word, self_key) if len(L) == 0: raise ValueError("desubstitution of {} by {} " "is impossible ".format(self_key, code_to_return_word)) elif len(L) > 1: s = "=".join(["m({})".format(u) for u in L]) msg = ("non unique desubstitution, " "{}={}".format(s, self_key)) raise ValueError(msg) #print key,value,self_key,L[0] preimage = L[0] rep[value] = preimage m = WordMorphism(rep) if coding: return m, code_to_return_word else: return m
def pullback(self, other): r""" Pullback of the graph maps ``self`` and ``other``. The codomain of ``self`` and ``other`` must be the same graph. The pullback is a graph map `f : G_3 \to G` that makes the diagram commute:: G3 -----> G1 | \ | | \ | self | \f | | \ | V _\| V G2 -----> G ....other The pullback method can be used to find intersection of two subgroups of a Free Group. INPUT: - ``other`` -- a graph map `G_2 \to G` with ``self`` (a graph map `G_1 \to G`) OUTPUT: A ``GraphMap``. EXAMPLES:: sage: from train_track import * sage: from train_track.inverse_graph import GraphWithInverses sage: from train_track.graph_map import GraphMap sage: G1 = GraphWithInverses.rose_graph(AlphabetWithInverses(2,type='x0')) sage: G2 = GraphWithInverses.rose_graph(AlphabetWithInverses(2,type='a0')) sage: G = GraphWithInverses.rose_graph(AlphabetWithInverses(2)) sage: n1 = WordMorphism({'x0':['a','a'],'x1':['b','a']}) sage: n2 = WordMorphism({'a0':['b','a'],'a1':['b','b','b','a','B','a']}) sage: f1 = GraphMap(G1,G,n1) sage: f2 = GraphMap(G2,G,n2) sage: print(f1.pullback(f2)) Graph map: a0: (0, 0)->(1, 2), a1: (0, 2)->(1, 0), a2: (0, 2)->(1, 3), a3: (0, 3)->(1, 4), a4: (0, 4)->(1, 3), a5: (1, 2)->(0, 0), a6: (1, 4)->(0, 3) a: 0->0, b: 0->0 edge map: a0->b, a1->a, a2->b, a3->b, a4->a, a5->a, a6->a AUTHORS: - Radhika GUPTA """ import itertools # First convert self and f2 into immersions self.stallings_folding() other.stallings_folding() G3 = GraphWithInverses() A = AlphabetWithInverses(0, type='a0') d = {} # get set of vertices V = [] for i in itertools.product(self.domain().vertices(), other.domain().vertices()): V.append(i) # add edges for v in V: for w in V: for e1 in self.domain().alphabet().positive_letters(): if self.domain().initial_vertex(e1) == v[0] \ and self.domain().terminal_vertex(e1) == w[0]: for e2 in other.domain().alphabet().positive_letters(): if other.domain().initial_vertex(e2) == v[1] \ and other.domain().terminal_vertex(e2) == w[1]: if self.image(e1) == other.image(e2): e = A.add_new_letter() G3.add_edge(v, w, e) # update dictionary to define map on G3 d[e[0]] = self.image(e1) G3._alphabet = A n3 = WordMorphism(d) G = self.codomain() # same as other.codomain() return GraphMap(G3, G, n3)
def stallings_folding(self): """ Implement Stallings' folding to get an immersion from ``self``. The domain of ``self`` is fold until we get an immersion. Intended to be used to compute the pullback of two graph maps and the intersection of subgroupes of a free group. ALGORITHM: We first subdivide edges of the domain according to length of their image. Then fold one gate at one vertex and update the edge map and illegal turns list. Repeat the process till no illegal turns remain. EXAMPLES:: sage: from train_track import * sage: from train_track.inverse_graph import GraphWithInverses sage: from train_track.graph_map import GraphMap sage: A = AlphabetWithInverses(2) sage: G = GraphWithInverses.rose_graph(A) sage: H = GraphWithInverses.rose_graph(A) sage: f = GraphMap(G,H,"a->aba,b->ab") sage: f.stallings_folding() sage: print(f) Graph map: a: 1->1, c: 1->1 a: 0->0, b: 0->0 edge map: a->a, c->b REFERENCES: .. [Stallings] J. Stallings, Topology of Finite Graphs, AUTHOR: - Radhika GUPTA """ A = self.domain().alphabet() for a in A: if len(self.image(a)) > 1: self.subdivide_domain(a) Turns = self._domain.turns() # list of all turns in domain after subdivision Il_turns = self.illegal_turns(Turns) # list of illegal turns in domain after subdivision counter = 0 while len(Il_turns) > 0: counter = counter + 1 # find edge_list (list of edges in the gate correspoding to e1) # to fold at exactly one vertex e1 = Il_turns[0][0] edge_list = [e1] for a in A: if self._domain.initial_vertex(a) == \ self._domain.initial_vertex(e1) and e1 != a: if (e1, a) in Il_turns or (a, e1) in Il_turns: edge_list.append(a) edge_list = list(set(edge_list)) # remove duplicates # fold at initial_vertex of e1 ( this function updates the # domain and edge_map) self._domain.fold(edge_list, []) # update edge_map again d = {} d[edge_list[0]] = self.image(edge_list[0]) for a in A: if a not in edge_list: d[a] = self.image(a) wm = WordMorphism(d) self.set_edge_map(wm) Turns = self._domain.turns() # update list of all turns in domain Il_turns = self.illegal_turns(Turns)
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.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 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
def iter_morphisms(self, arg=None, codomain=None, min_length=1): r""" Iterate over all morphisms with domain ``self`` and the given codomain. INPUT: - ``arg`` - (optional, default: None) It can be one of the following : - ``None`` - then the method iterates through all morphisms. - tuple `(a, b)` of two integers - It specifies the range ``range(a, b)`` of values to consider for the sum of the length of the image of each letter in the alphabet. - list of nonnegative integers - The length of the list must be equal to the size of the alphabet, and the i-th integer of ``arg`` determines the length of the word mapped to by the i-th letter of the (ordered) alphabet. - ``codomain`` - (default: None) a combinatorial class of words. By default, ``codomain`` is ``self``. - ``min_length`` - (default: 1) nonnegative integer. If ``arg`` is not specified, then iterate through all the morphisms where the length of the images of each letter in the alphabet is at least ``min_length``. This is ignored if ``arg`` is a list. OUTPUT: iterator EXAMPLES: Iterator over all non-erasing morphisms:: sage: W = Words('ab') sage: it = W.iter_morphisms() sage: for _ in range(7): it.next() WordMorphism: a->a, b->a WordMorphism: a->a, b->b WordMorphism: a->b, b->a WordMorphism: a->b, b->b WordMorphism: a->aa, b->a WordMorphism: a->aa, b->b WordMorphism: a->ab, b->a Iterator over all morphisms including erasing morphisms:: sage: W = Words('ab') sage: it = W.iter_morphisms(min_length=0) sage: for _ in range(7): it.next() WordMorphism: a->, b-> WordMorphism: a->a, b-> WordMorphism: a->b, b-> WordMorphism: a->, b->a WordMorphism: a->, b->b WordMorphism: a->aa, b-> WordMorphism: a->ab, b-> Iterator over morphisms where the sum of the lengths of the images of the letters is in a specific range:: sage: for m in W.iter_morphisms((0, 3), min_length=0): m WordMorphism: a->, b-> WordMorphism: a->a, b-> WordMorphism: a->b, b-> WordMorphism: a->, b->a WordMorphism: a->, b->b WordMorphism: a->aa, b-> WordMorphism: a->ab, b-> WordMorphism: a->ba, b-> WordMorphism: a->bb, b-> WordMorphism: a->a, b->a WordMorphism: a->a, b->b WordMorphism: a->b, b->a WordMorphism: a->b, b->b WordMorphism: a->, b->aa WordMorphism: a->, b->ab WordMorphism: a->, b->ba WordMorphism: a->, b->bb :: sage: for m in W.iter_morphisms( (2, 4) ): m WordMorphism: a->a, b->a WordMorphism: a->a, b->b WordMorphism: a->b, b->a WordMorphism: a->b, b->b WordMorphism: a->aa, b->a WordMorphism: a->aa, b->b WordMorphism: a->ab, b->a WordMorphism: a->ab, b->b WordMorphism: a->ba, b->a WordMorphism: a->ba, b->b WordMorphism: a->bb, b->a WordMorphism: a->bb, b->b WordMorphism: a->a, b->aa WordMorphism: a->a, b->ab WordMorphism: a->a, b->ba WordMorphism: a->a, b->bb WordMorphism: a->b, b->aa WordMorphism: a->b, b->ab WordMorphism: a->b, b->ba WordMorphism: a->b, b->bb Iterator over morphisms with specific image lengths:: sage: for m in W.iter_morphisms([0, 0]): m WordMorphism: a->, b-> sage: for m in W.iter_morphisms([0, 1]): m WordMorphism: a->, b->a WordMorphism: a->, b->b sage: for m in W.iter_morphisms([2, 1]): m WordMorphism: a->aa, b->a WordMorphism: a->aa, b->b WordMorphism: a->ab, b->a WordMorphism: a->ab, b->b WordMorphism: a->ba, b->a WordMorphism: a->ba, b->b WordMorphism: a->bb, b->a WordMorphism: a->bb, b->b sage: for m in W.iter_morphisms([2, 2]): m WordMorphism: a->aa, b->aa WordMorphism: a->aa, b->ab WordMorphism: a->aa, b->ba WordMorphism: a->aa, b->bb WordMorphism: a->ab, b->aa WordMorphism: a->ab, b->ab WordMorphism: a->ab, b->ba WordMorphism: a->ab, b->bb WordMorphism: a->ba, b->aa WordMorphism: a->ba, b->ab WordMorphism: a->ba, b->ba WordMorphism: a->ba, b->bb WordMorphism: a->bb, b->aa WordMorphism: a->bb, b->ab WordMorphism: a->bb, b->ba WordMorphism: a->bb, b->bb The codomain may be specified as well:: sage: Y = Words('xyz') sage: for m in W.iter_morphisms([0, 2], codomain=Y): m WordMorphism: a->, b->xx WordMorphism: a->, b->xy WordMorphism: a->, b->xz WordMorphism: a->, b->yx WordMorphism: a->, b->yy WordMorphism: a->, b->yz WordMorphism: a->, b->zx WordMorphism: a->, b->zy WordMorphism: a->, b->zz sage: for m in Y.iter_morphisms([0,2,1], codomain=W): m WordMorphism: x->, y->aa, z->a WordMorphism: x->, y->aa, z->b WordMorphism: x->, y->ab, z->a WordMorphism: x->, y->ab, z->b WordMorphism: x->, y->ba, z->a WordMorphism: x->, y->ba, z->b WordMorphism: x->, y->bb, z->a WordMorphism: x->, y->bb, z->b sage: it = W.iter_morphisms(codomain=Y) sage: for _ in range(10): it.next() WordMorphism: a->x, b->x WordMorphism: a->x, b->y WordMorphism: a->x, b->z WordMorphism: a->y, b->x WordMorphism: a->y, b->y WordMorphism: a->y, b->z WordMorphism: a->z, b->x WordMorphism: a->z, b->y WordMorphism: a->z, b->z WordMorphism: a->xx, b->x TESTS:: sage: list(W.iter_morphisms([1,0])) [WordMorphism: a->a, b->, WordMorphism: a->b, b->] sage: list(W.iter_morphisms([0,0], codomain=Y)) [WordMorphism: a->, b->] sage: list(W.iter_morphisms([0, 1, 2])) Traceback (most recent call last): ... TypeError: arg (=[0, 1, 2]) must be an iterable of 2 integers sage: list(W.iter_morphisms([0, 'a'])) Traceback (most recent call last): ... TypeError: arg (=[0, 'a']) must be an iterable of 2 integers sage: list(W.iter_morphisms([0, 1], codomain='a')) Traceback (most recent call last): ... TypeError: codomain (=a) must be an instance of Words_over_OrderedAlphabet The argument ``l`` is now deprecated:: sage: W = Words('ab') sage: it = W.iter_morphisms(l=None) doctest:...: DeprecationWarning: use the option 'arg' instead of 'l' See http://trac.sagemath.org/10134 for details. """ n = self.size_of_alphabet() # create an iterable of compositions (all "compositions" if arg is # None, or [arg] otherwise) if arg is None: from sage.combinat.integer_list import IntegerListsLex compositions = IntegerListsLex(itertools.count(), length=n, min_part=max(0, min_length)) elif isinstance(arg, tuple): if not len(arg) == 2 or not all( isinstance(a, (int, Integer)) for a in arg): raise TypeError("arg (=%s) must be a tuple of 2 integers" % arg) from sage.combinat.integer_list import IntegerListsLex compositions = IntegerListsLex(range(*arg), length=n, min_part=max(0, min_length)) else: arg = list(arg) if (not len(arg) == n or not all(isinstance(a, (int, Integer)) for a in arg)): raise TypeError( "arg (=%s) must be an iterable of %s integers" % (arg, n)) compositions = [arg] # set the codomain if codomain is None: codomain = self elif not isinstance(codomain, Words_over_OrderedAlphabet): raise TypeError, "codomain (=%s) must be an instance of Words_over_OrderedAlphabet" % codomain # iterate through the morphisms from sage.combinat.words.morphism import WordMorphism for composition in compositions: cuts = [0] + list(composition) for i in range(1, len(cuts)): cuts[i] += cuts[i - 1] s = cuts[-1] # same but better than s = sum(composition) for big_word in codomain.iterate_by_length(s): d = {} i = 0 for a in self.alphabet(): d[a] = big_word[cuts[i]:cuts[i + 1]] i += 1 yield WordMorphism(d, codomain=codomain)