def __init__(self,data,group=None): """ Builds a FreeGroupAutomorphism from data. INPUT: - ``data`` - the data used to build the morphism - ``group`` - an optional free group """ if group is not None and not isinstance(group, FreeGroup): raise ValueError, "the group must be a Free Group" WordMorphism.__init__(self,data) if group is None: A = AlphabetWithInverses(self.domain().alphabet()) F = FreeGroup(A) else: F = group A = group.alphabet() self._domain = F self._codomain = F # unuseful... consistency with WordMorphism for letter in self._morph.keys(): self._morph[letter]=F.reduce(self._morph[letter]) self._morph[A.inverse_letter(letter)] = F.inverse_word(self._morph[letter])
def __init__(self, data, group=None): """ Builds a FreeGroupAutomorphism from data. INPUT: - ``data`` - the data used to build the morphism - ``group`` - an optional free group """ if group is not None and not isinstance(group, FreeGroup): raise ValueError, "the group must be a Free Group" WordMorphism.__init__(self, data) if group is None: A = AlphabetWithInverses(self.domain().alphabet()) F = FreeGroup(A) else: F = group A = group.alphabet() self._domain = F self._codomain = F # unuseful... consistency with WordMorphism for letter in self._morph.keys(): self._morph[letter] = F.reduce(self._morph[letter]) self._morph[A.inverse_letter(letter)] = F.inverse_word( self._morph[letter])
def __init__(self,data=None,alphabet=None): self._initial={} self._terminal={} letters=[] if isinstance(data,dict): new_data=dict() for a in data: letters.append(a) if data[a][0] in new_data: if data[a][1] in new_data[data[a][0]]: new_data[data[a][0]][data[a][1]].append(a) else: new_data[data[a][0]][data[a][1]]=[a] else: new_data[data[a][0]]={data[a][1]:[a]} data=new_data elif isinstance(data,list): new_data=dict() for e in data: letters.append(e[2]) if e[0] in new_data: if e[1] in new_data[e[0]]: new_data[e[0]][e[1]].append(e[2]) else: new_data[e[0]][e[1]]=[e[2]] else: new_data[e[0]]={e[1]:[e[2]]} data=new_data if alphabet is None: from inverse_alphabet import AlphabetWithInverses alphabet = AlphabetWithInverses(self._initial.keys()) self._alphabet=alphabet DiGraph.__init__(self,data=data,loops=True,multiedges=True,vertex_labels=True,pos=None,format=None,\ boundary=[],weighted=None,implementation='c_graph',sparse=True) for e in self.edges(): self._initial[e[2]]=e[0] self._terminal[e[2]]=e[1] self._initial[alphabet.inverse_letter(e[2])]=e[1] self._terminal[alphabet.inverse_letter(e[2])]=e[0]
def __init__(self, data=None, alphabet=None): self._initial = {} self._terminal = {} letters = [] if isinstance(data, dict): new_data = dict() for a in data: letters.append(a) if data[a][0] in new_data: if data[a][1] in new_data[data[a][0]]: new_data[data[a][0]][data[a][1]].append(a) else: new_data[data[a][0]][data[a][1]] = [a] else: new_data[data[a][0]] = {data[a][1]: [a]} data = new_data elif isinstance(data, list): new_data = dict() for e in data: letters.append(e[2]) if e[0] in new_data: if e[1] in new_data[e[0]]: new_data[e[0]][e[1]].append(e[2]) else: new_data[e[0]][e[1]] = [e[2]] else: new_data[e[0]] = {e[1]: [e[2]]} data = new_data if alphabet is None: from inverse_alphabet import AlphabetWithInverses alphabet = AlphabetWithInverses(self._initial.keys()) self._alphabet = alphabet DiGraph.__init__(self,data=data,loops=True,multiedges=True,vertex_labels=True,pos=None,format=None,\ boundary=[],weighted=None,implementation='c_graph',sparse=True) for e in self.edges(): self._initial[e[2]] = e[0] self._terminal[e[2]] = e[1] self._initial[alphabet.inverse_letter(e[2])] = e[1] self._terminal[alphabet.inverse_letter(e[2])] = e[0]
def Handel_Mosher_axes_3_4(): """ Automorphism given in Section 3.4 of [HM-axes] This automorphism is iwip, not geometric and is train-track on the rose. It has expansion factor 4.0795. Its inverse is not train-track on the rose and has expansion factor 2.46557. It also appears in Section 5.5 of the paper. REFERENCES: [HM-axes] M. Handel, L. Mosher, axes in Outer space, Mem. Amer. Math. Soc. 213, 2011. """ A = AlphabetWithInverses(['a', 'g', 'f'], ['A', 'G', 'F']) return FreeGroupAutomorphism("a->afgfgf,f->fgf,g->gfafg", FreeGroup(A))
def valence_3(rank): """ A strongly connected graph with all vertices of valence 3 and of given rank. ``rank`` is assumed to be greater or equal than 2. """ graph = dict() A = AlphabetWithInverses(3 * rank - 3) for i in xrange(rank - 2): graph[A[2 * i]] = (2 * i + 1, 2 * i + 3) graph[A[2 * i + 1]] = (2 * i + 1, 2 * i + 2) graph[A[i + 2 * rank - 4]] = (2 * i, 2 * i + 2) graph[A[3 * rank - 6]] = (2 * rank - 4, 2 * rank - 3) graph[A[3 * rank - 5]] = (0, 2 * rank - 3) graph[A[3 * rank - 4]] = (0, 1) return GraphWithInverses(graph, A)
def __init__(self, data=None, alphabet=None): """ INPUT: - ``data`` -- (default None) a list or dictionary from a list of edges: ``[initial_vertex,terminal_vertex,letter]``. a dictionnary that maps letters of the alphabet to lists ``(initial_vertex,terminal_vertex)`` - ``alphabet`` -- (default None ) alphabet AlphabetWithInverses by default """ self._initial = {} self._terminal = {} letters = [] if isinstance(data, dict): new_data = dict() for a in data: letters.append(a) if data[a][0] in new_data: if data[a][1] in new_data[data[a][0]]: new_data[data[a][0]][data[a][1]].append(a) else: new_data[data[a][0]][data[a][1]] = [a] else: new_data[data[a][0]] = {data[a][1]: [a]} data = new_data elif isinstance(data, list): new_data = dict() for e in data: letters.append(e[2]) if e[0] in new_data: if e[1] in new_data[e[0]]: new_data[e[0]][e[1]].append(e[2]) else: new_data[e[0]][e[1]] = [e[2]] else: new_data[e[0]] = {e[1]: [e[2]]} data = new_data if alphabet is None: from inverse_alphabet import AlphabetWithInverses alphabet = AlphabetWithInverses(letters) self._alphabet = alphabet DiGraph.__init__(self, data=data, loops=True, multiedges=True, vertex_labels=True, pos=None, format=None, weighted=None, implementation='c_graph', sparse=True) # DiGraph.__init__(self,data=data, loops=True,multiedges=True, # vertex_labels=True, pos=None, format=None, # boundary=[], weighted=None, # implementation='c_graph', sparse=True) for e in self.edges(): self._initial[e[2]] = e[0] self._terminal[e[2]] = e[1] self._initial[alphabet.inverse_letter(e[2])] = e[1] self._terminal[alphabet.inverse_letter(e[2])] = e[0]
def inverse(self): """A homotopy inverse of ``self``. For ``t1=self.domain().spanning_tree()`` and ``t2=self.codomain().spanning_tree()``. The alphabet ``A`` is made of edges not in ``t1`` identified (by their order in the alphabets of the domain and codomain) to letters not in ``t2``. The automorphism ``phi`` of ``FreeGroup(A)`` is defined using ``t1`` and ``t2``. The inverse map is given by ``phi.inverse()`` using ``t1`` and edges from ``t2`` are mapped to a single point (the root of ``t1``). In particular the inverse maps all vertices to the root of ``t1``. WARNING: ``self`` is assumed to be a homotopy equivalence. """ from free_group import FreeGroup from free_group_automorphism import FreeGroupAutomorphism G1=self.domain() A1=G1.alphabet() t1=G1.spanning_tree() G2=self.codomain() A2=G2.alphabet() t2=G2.spanning_tree() A=AlphabetWithInverses(len(A1)-len(G1.vertices())+1) F=FreeGroup(A) map=dict() translate=dict() i=0 for a in A1.positive_letters(): l=len(t1[G1.initial_vertex(a)])-len(t1[G1.terminal_vertex(a)]) if (l!=1 or t1[G1.initial_vertex(a)][-1]!=A1.inverse_letter(a)) and\ (l!=-1 or t1[G1.terminal_vertex(a)][-1]!=a): # a is not in the spanning tree map[A[i]]=self(t1[G1.initial_vertex(a)]*Word([a])*G1.reverse_path(t1[G1.terminal_vertex(a)])) translate[A[i]]=a translate[A.inverse_letter(A[i])]=A1.inverse_letter(a) i+=1 rename=dict() edge_map=dict() i=0 for a in A2.positive_letters(): l=len(t2[G2.initial_vertex(a)])-len(t2[G2.terminal_vertex(a)]) if (l!=1 or t2[G2.initial_vertex(a)][-1]!=A2.inverse_letter(a)) and\ (l!=-1 or t2[G2.terminal_vertex(a)][-1]!=a): # a is not in the spanning tree rename[a]=A[i] rename[A2.inverse_letter(a)]=A.inverse_letter(A[i]) i+=1 else: edge_map[a]=Word() for a in map: map[a]=F([rename[b] for b in map[a] if b in rename]) phi=FreeGroupAutomorphism(map,F) psi=phi.inverse() i=0 for a in A2.positive_letters(): if a not in edge_map: result=Word() for b in psi.image(A[i]): c=translate[b] result=result*t1[G1.initial_vertex(c)]*Word([c])*G1.reverse_path(t1[G1.terminal_vertex(c)]) edge_map[a]=G1.reduce_path(result) i+=1 return GraphMap(G2,G1,edge_map)
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:G3 -> 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 G2->G with ``self``: a graph map G1->G, OUTPUT: A ``GraphMap`` f EXAMPLES:: 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: Graph with inverses: 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) Graph with inverses: 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 inverse(self): """A homotopy inverse of ``self``. For ``t1=self.domain().spanning_tree()`` and ``t2=self.codomain().spanning_tree()``. The alphabet ``A`` is made of edges not in ``t1`` identified (by their order in the alphabets of the domain and codomain) to letters not in ``t2``. The automorphism ``phi`` of ``FreeGroup(A)`` is defined using ``t1`` and ``t2``. The inverse map is given by ``phi.inverse()`` using ``t1`` and edges from ``t2`` are mapped to a single point (the root of ``t1``). In particular the inverse maps all vertices to the root of ``t1``. OUTPUT: A homotopy inverse of ``self``. WARNING: ``self`` is assumed to be a homotopy equivalence. EXAMPLES:: 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: print f.inverse() Graph map: Graph with inverses: a: 0->0, b: 0->0 Graph with inverses: a: 0->0, b: 0->1, c: 1->1 edge map: a->abcB, b->bCB """ from free_group import FreeGroup from free_group_automorphism import FreeGroupAutomorphism g1 = self.domain() a1 = g1.alphabet() t1 = g1.spanning_tree() g2 = self.codomain() a2 = g2.alphabet() t2 = g2.spanning_tree() A = AlphabetWithInverses(len(a1) - len(g1.vertices()) + 1) f = FreeGroup(A) map = dict() translate = dict() i = 0 for a in a1.positive_letters(): l = len(t1[g1.initial_vertex(a)]) - len(t1[g1.terminal_vertex(a)]) if (l != 1 or t1[g1.initial_vertex(a)][-1] != a1.inverse_letter( a)) and (l != -1 or t1[g1.terminal_vertex(a)][-1] != a): # a is not in the spanning tree map[A[i]] = self( t1[g1.initial_vertex(a)] * Word([a]) * g1.reverse_path( t1[g1.terminal_vertex(a)])) translate[A[i]] = a translate[A.inverse_letter(A[i])] = a1.inverse_letter(a) i += 1 rename = dict() edge_map = dict() i = 0 for a in a2.positive_letters(): l = len(t2[g2.initial_vertex(a)]) - len(t2[g2.terminal_vertex(a)]) if (l != 1 or t2[g2.initial_vertex(a)][-1] != a2.inverse_letter( a)) and (l != -1 or t2[g2.terminal_vertex(a)][-1] != a): # a is not in the spanning tree rename[a] = A[i] rename[a2.inverse_letter(a)] = A.inverse_letter(A[i]) i += 1 else: edge_map[a] = Word() for a in map: map[a] = f([rename[b] for b in map[a] if b in rename]) phi = FreeGroupAutomorphism(map, f) psi = phi.inverse() i = 0 for a in a2.positive_letters(): if a not in edge_map: result = Word() for b in psi.image(A[i]): c = translate[b] result = result * t1[g1.initial_vertex(c)] * Word( [c]) * g1.reverse_path(t1[g1.terminal_vertex(c)]) edge_map[a] = g1.reduce_path(result) i += 1 return GraphMap(g2, g1, edge_map)
def __init__(self,alphabet): if not isinstance(alphabet,AlphabetWithInverses): alphabet=AlphabetWithInverses(alphabet) FiniteWords_over_OrderedAlphabet.__init__(self,alphabet)
def inverse(self): """A homotopy inverse of ``self``. For ``t1=self.domain().spanning_tree()`` and ``t2=self.codomain().spanning_tree()``. The alphabet ``A`` is made of edges not in ``t1`` identified (by their order in the alphabets of the domain and codomain) to letters not in ``t2``. The automorphism ``phi`` of ``FreeGroup(A)`` is defined using ``t1`` and ``t2``. The inverse map is given by ``phi.inverse()`` using ``t1`` and edges from ``t2`` are mapped to a single point (the root of ``t1``). In particular the inverse maps all vertices to the root of ``t1``. WARNING: ``self`` is assumed to be a homotopy equivalence. """ from free_group import FreeGroup from free_group_automorphism import FreeGroupAutomorphism G1 = self.domain() A1 = G1.alphabet() t1 = G1.spanning_tree() G2 = self.codomain() A2 = G2.alphabet() t2 = G2.spanning_tree() A = AlphabetWithInverses(len(A1) - len(G1.vertices()) + 1) F = FreeGroup(A) map = dict() translate = dict() i = 0 for a in A1.positive_letters(): l = len(t1[G1.initial_vertex(a)]) - len(t1[G1.terminal_vertex(a)]) if (l!=1 or t1[G1.initial_vertex(a)][-1]!=A1.inverse_letter(a)) and\ (l!=-1 or t1[G1.terminal_vertex(a)][-1]!=a): # a is not in the spanning tree map[A[i]] = self(t1[G1.initial_vertex(a)] * Word([a]) * G1.reverse_path(t1[G1.terminal_vertex(a)])) translate[A[i]] = a translate[A.inverse_letter(A[i])] = A1.inverse_letter(a) i += 1 rename = dict() edge_map = dict() i = 0 for a in A2.positive_letters(): l = len(t2[G2.initial_vertex(a)]) - len(t2[G2.terminal_vertex(a)]) if (l!=1 or t2[G2.initial_vertex(a)][-1]!=A2.inverse_letter(a)) and\ (l!=-1 or t2[G2.terminal_vertex(a)][-1]!=a): # a is not in the spanning tree rename[a] = A[i] rename[A2.inverse_letter(a)] = A.inverse_letter(A[i]) i += 1 else: edge_map[a] = Word() for a in map: map[a] = F([rename[b] for b in map[a] if b in rename]) phi = FreeGroupAutomorphism(map, F) psi = phi.inverse() i = 0 for a in A2.positive_letters(): if a not in edge_map: result = Word() for b in psi.image(A[i]): c = translate[b] result = result * t1[G1.initial_vertex(c)] * Word( [c]) * G1.reverse_path(t1[G1.terminal_vertex(c)]) edge_map[a] = G1.reduce_path(result) i += 1 return GraphMap(G2, G1, edge_map)