Esempio n. 1
0
    def _build_core(self):
        """
        Builds the core.  Called when Core is initialized.
        """
        ends={} # remove signs from ends
        for x in self._signed_ends.keys():
            ends[x]=[]
            for e in self._signed_ends[x]:
                ends[x].append(e[1:] if e[0]=='-' else e)
        inv_alph=self._inv_graph_map.domain()._alphabet

        for x in self._graph_map.domain().edge_labels():
            slice_x=GraphWithInverses(alphabet=inv_alph)
            # find common prefix
            if ends[x]: common=ends[x][0]
            for e in ends[x]:
                common_len=self._inv_graph_map.domain().common_prefix_length(e,common)
                common=common[:common_len]
            # build slice
            for e in ends[x]:
                v_label=common
                for a in e[common_len:-1]:
                    if v_label not in slice_x.vertices():
                        slice_x.add_vertex(v_label)
                    t_label=v_label+a
                    if t_label not in slice_x.vertices():
                        slice_x.add_vertex(t_label)
                    edge=(v_label,t_label,a)
                    if edge not in slice_x.edges():
                        slice_x.add_edge(edge)
                    v_label=t_label
            self._core_slice[x]=slice_x
    def HNN_splitting(A):
        """
        The marked metric graph corresponding to the HNN splitting
        F_N=F_{N-1}*<t>.

        This is rose marked graph with all edges of length 0 except ``A[0]``
        which is of length 1.

        INPUT:

        - ``A`` alphabet

        OUTPUT:

        The marked metric graph corresponding to the HNN splitting

        EXAMPLES::

            sage: A=AlphabetWithInverses(4)
            sage: print MarkedMetricGraph.HNN_splitting(A)
            Marked graph: a: 0->0, b: 0->0, c: 0->0, d: 0->0
            Marking: a->a, b->b, c->c, d->d
            Length: a:1, b:0, c:0, d:0
        """

        length = dict((a, 0) for a in A.positive_letters())
        length[A[0]] = 1

        RA = GraphWithInverses.rose_graph(A)
        RAA = GraphWithInverses.rose_graph(A)
        marking = GraphMap(RA, RAA, dict(
                               (a, Word([a])) for a in A.positive_letters()))

        return MarkedMetricGraph(marking=marking, length=length)
    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: 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 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``, ``edges_partial`` are list of edges (each
          possibly empty, but the union must have at least two edges).


          OUTPUT:

          A dictionnary that maps old edges to new graph paths.

          SEE ALSO:

          ``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 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.

        SEE ALSO:

        ``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 rose_marked_graph(alphabet):
          """
          The rose on ``alphabet`` marked with the identity.
          """

          marking=dict((a,a) for a in alphabet.positive_letters())
          return MarkedGraph(graph=GraphWithInverses.rose_graph(alphabet),marking=marking,marking_alphabet=alphabet)
     def HNN_splitting(A):
          """
          The marked metric graph corresponding to the HNN splitting
          F_N=F_{N-1}*<t>.

          The rose marked graph with all edges of length 0 except ``A[0]``
          which is of length 1.
          """

          length=dict((a,0) for a in A.positive_letters())
          length[A[0]]=1

          RA=GraphWithInverses.rose_graph(A)
          RAA=GraphWithInverses.rose_graph(A)
          marking=GraphMap(RA,RAA,edge_map=dict((a,Word([a])) for a in A.positive_letters()))

          return MarkedMetricGraph(marking=marking,length=length)
Esempio n. 8
0
    def rose_map(automorphism):
        """
        Returns the core of the rose and the rose acted upon by the corresponding
        automorphism.
        """

        graph=GraphWithInverses.rose_graph(automorphism._domain._alphabet.copy())
        inv_automorphism=automorphism.inverse()
        return Core(graph,graph,automorphism,inv_automorphism)
Esempio n. 9
0
    def rose_map(automorphism):
        """
        Returns the core of the rose and the rose acted upon by the corresponding
        automorphism.
        """

        graph = GraphWithInverses.rose_graph(
            automorphism._domain._alphabet.copy())
        inv_automorphism = automorphism.inverse()
        return Core(graph, graph, automorphism, inv_automorphism)
    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: 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

        SEE ALSO:

        ``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 rose_conjugacy_representative(self):
        """
        Topological representative of the conjugacy class of ``self``.

        SEE ALSO:

        This is the same as ``self.rose_representative()`` but the
        base graph of the ``TopologicalRepresentative`` is a
        ``GraphWithInverses`` instead of a ``MarkedGraph``.
        """
        from topological_representative import TopologicalRepresentative
        from inverse_graph import GraphWithInverses

        return TopologicalRepresentative(GraphWithInverses.rose_graph(self._domain.alphabet()),self)
Esempio n. 12
0
    def rose_conjugacy_representative(self):
        """
        Topological representative of the conjugacy class of ``self``.

        SEE ALSO:

        This is the same as ``self.rose_representative()`` but the
        base graph of the ``TopologicalRepresentative`` is a
        ``GraphWithInverses`` instead of a ``MarkedGraph``.
        """
        from topological_representative import TopologicalRepresentative
        from inverse_graph import GraphWithInverses

        return TopologicalRepresentative(
            GraphWithInverses.rose_graph(self._domain.alphabet()), self)
     def fold(self,edges_full,edges_partial):
          """
          Folds the edges.

          OUTPUT:

          A dictionnary that maps old edges to new graph paths.

          SEE ALSO:

          ``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 subdivide(self,edge_list):
          """
          Subdivides edges in the edge_list into two edges.

          WARNING:

          each edge in ``edge_list`` must appear only once.

          SEE ALSO:

          ``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 contract_forest(self,forest):
          """
          Contract the forest.

          OUTPUT:

          A dictionnary that maps old edges to new edges.

          SEE ALSO:

          ``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 rose_marked_graph(alphabet):
        """
        The rose on ``alphabet`` marked with the identity.

        INPUT:

        - ``alphabet`` a alphabet

        OUTPUT:

        The rose on ``alphabet`` marked with the identity.

        EXAMPLES::

            sage: print MarkedGraph.rose_marked_graph(AlphabetWithInverses(2))
            Marked graph: a: 0->0, b: 0->0
            Marking: a->a, b->b
        """

        marking = dict((a, Word([a])) for a in alphabet.positive_letters())
        return MarkedGraph(graph=GraphWithInverses.rose_graph(alphabet),
                           marking=marking, marking_alphabet=alphabet)
    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: 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

        SEE ALSO::

        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 rose_map(automorphism):
        """
        The graph map of the rose representing the automorphism.

        The rose is built on a copy of the alphabet of the domain of
        ``automorphism``.

        OUTPUT:

        The graph map of the rose representing the automorphism.

        EXAMPLES::

            sage: phi=FreeGroupAutomorphism('a->ab,b->ac,c->a')
            sage: print GraphMap.rose_map(phi)
            Graph map:
            Graph with inverses: a: 0->0, b: 0->0, c: 0->0
            Graph with inverses: a: 0->0, b: 0->0, c: 0->0
            edge map: a->ab, b->ac, c->a
        """

        graph = GraphWithInverses.rose_graph(
            automorphism.domain().alphabet().copy())
        return GraphMap(graph, graph, automorphism)
     def __init__(self,graph=None,marking=None,alphabet=None,marking_alphabet=None):
         if isinstance(marking,GraphMap):
              GraphWithInverses.__init__(self,marking.codomain(),marking.codomain().alphabet())
              self._marking=marking
         else:
              if isinstance(graph,GraphWithInverses):
                   alphabet=graph._alphabet
              GraphWithInverses.__init__(self,graph,alphabet)

              if marking is None: #computes a (random) marking from a rose equivalent to graph

                   A=graph.alphabet()
                   tree=graph.spanning_tree()

                   j=0
                   letter=dict()
                   for a in A.positive_letters():
                        vi=graph.initial_vertex(a)
                        vt=graph.terminal_vertex(a)
                        if (len(tree[vi])==0 or tree[vi][-1]!=A.inverse_letter(a)) and\
                                 (len(tree[vt])==0 or tree[vt][-1]!=a):
                             letter[j]=a
                             j=j+1



                   B=AlphabetWithInverses(j)
                   RB=GraphWithInverses.rose_graph(B)

                   edge_map=dict()

                   for i in xrange(j):
                        a=letter[i]
                        edge_map[B[i]]=graph.reduce_path(tree[graph.initial_vertex(a)]\
                                                           *Word([a])\
                                                           *graph.reverse_path(tree[graph.terminal_vertex(a)]))

                        marking=GraphMap(RB,graph,edge_map)
              else:
                   marking=GraphMap(GraphWithInverses.rose_graph(marking_alphabet),self,marking)
              self._marking=marking
    def __init__(self, graph=None, marking=None, alphabet=None,
                 marking_alphabet=None):
        """
        INPUT:

        - ``graph`` -- (default None) GraphWithInverses is expected
          or will be combute from ''grap''
        - ``marking`` -- (default None) GraphMap is expected
          or will be compute
        - ``alphabet`` -- (default None) if ``graph`` is GraphWithInverses
          ``alphabet`` will be use for ``self``
        - ``marking_alphabet`` -- (default None) alphabet used in the case of a
          ``MarkedGraph`` is created from a GraphWithInverses`` by
          computing (randomly) a rose equivalent to the graph.

        EXAMPLES::

            sage: G = GraphWithInverses({'a':(0,0),'b':(0,1),'c':(1,0)})
            sage: M = MarkedGraph(graph=G)
            sage: print M
            Marked graph: a: 0->0, c: 1->0, b: 0->1
            Marking: a->a, b->bc
            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: M = MarkedGraph(marking=f)
            sage: print M
            Marked graph: a: 0->0, b: 0->0
            Marking: a->aba, b->ab
            sage: print MarkedGraph(marking=f, alphabet=A)
            Marked graph: a: 0->0, b: 0->0
            Marking: a->aba, b->ab
            sage: print MarkedGraph(marking=f, marking_alphabet=A)
            Marked graph: a: 0->0, b: 0->0
            Marking: a->aba, b->ab
        """
        if isinstance(marking, GraphMap):
            GraphWithInverses.__init__(self,
                                       marking.codomain(),
                                       marking.codomain().alphabet())
            self._marking = marking
        else:
            if isinstance(graph, GraphWithInverses):
                alphabet = graph.alphabet()
            GraphWithInverses.__init__(self, graph, alphabet)

            if marking is None:  # computes a (random) marking
                # from a rose equivalent to graph

                A = graph.alphabet()
                tree = graph.spanning_tree()

                j = 0
                letter = dict()
                for a in A.positive_letters():
                    vi = graph.initial_vertex(a)
                    vt = graph.terminal_vertex(a)
                    if (len(tree[vi]) == 0 or
                            tree[vi][-1] != A.inverse_letter(a)) \
                            and (len(tree[vt]) == 0 or tree[vt][-1] != a):
                        letter[j] = a
                        j = j + 1

                B = AlphabetWithInverses(j)
                RB = GraphWithInverses.rose_graph(B)

                edge_map = dict()

                for i in xrange(j):
                    a = letter[i]
                    edge_map[B[i]] = graph.reduce_path(
                        tree[graph.initial_vertex(a)] * Word([a]) *
                        graph.reverse_path(tree[graph.terminal_vertex(a)]))
                marking = GraphMap(RB, graph, edge_map)
            else:
                marking = GraphMap(
                    GraphWithInverses.rose_graph(marking_alphabet),
                    self, marking)
            self._marking = marking
Esempio n. 21
0
    def _build_core(self):
        """
        Builds the core.  Called when Core is initialized.
        """
        ends = {}  # remove signs from ends
        for x in self._signed_ends.keys():
            ends[x] = []
            for e in self._signed_ends[x]:
                ends[x].append(e[1:] if e[0] == '-' else e)
        inv_alph = self._inv_graph_map.domain()._alphabet

        for x in self._graph_map.domain().edge_labels():
            slice_x = GraphWithInverses(alphabet=inv_alph)
            # find common prefix
            if ends[x]: common = ends[x][0]
            for e in ends[x]:
                common_len = self._inv_graph_map.domain().common_prefix_length(
                    e, common)
                common = common[:common_len]
            # build slice
            for e in ends[x]:
                v_label = common
                for a in e[common_len:-1]:
                    if v_label not in slice_x.vertices():
                        slice_x.add_vertex(v_label)
                    t_label = v_label + a
                    if t_label not in slice_x.vertices():
                        slice_x.add_vertex(t_label)
                    edge = (v_label, t_label, a)
                    if edge not in slice_x.edges():
                        slice_x.add_edge(edge)
                    v_label = t_label
            self._core_slice[x] = slice_x
    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)