Пример #1
0
 def __init__(self, underlying, numbering):
     Fatgraph.__init__(self, underlying)
     self.underlying = underlying
     assert len(numbering) == self.num_boundary_cycles
     self.numbering = dict(numbering)
     if __debug__:
         count = [0 for x in xrange(self.num_boundary_cycles)]
         for (bcy, n) in self.numbering.iteritems():
             assert type(n) is types.IntType, \
                 "NumberedFatgraph.__init__: 2nd argument has wrong type:" \
                 " expecting (BoundaryCycle, Int) pair, got `(%s, %s)`." \
                 " Reversed-order arguments?" \
                 % (bcy, n)
             assert isinstance(bcy, BoundaryCycle), \
                 "NumberedFatgraph.__init__: 1st argument has wrong type:" \
                 " expecting (BoundaryCycle, Int) pair, got `(%s, %s)`." \
                 " Reversed-order arguments?" \
                 % (bcy, n)
             assert bcy in self.boundary_cycles, \
                 "NumberedFatgraph.__init__():" \
                 " Cycle `%s` is no boundary cycle of graph `%s` " \
                 % (bcy, self.underlying)
             count[n] += 1
             if count[n] > 1:
                 raise AssertionError("NumberedFatgraph.__init__():" \
                                      " Duplicate key %d" % n)
         assert sum(count) != self.num_boundary_cycles - 1, \
             "NumberedFatgraph.__init__():" \
             " Initializer does not exhaust range `0..%d`: %s" \
             % (self.num_boundary_cycles - 1, numbering)
Пример #2
0
 def __init__(self, underlying, numbering):
     Fatgraph.__init__(self, underlying)
     self.underlying = underlying
     assert len(numbering) == self.num_boundary_cycles
     self.numbering = dict(numbering)
     if __debug__:
         count = [ 0 for x in xrange(self.num_boundary_cycles) ]
         for (bcy,n) in self.numbering.iteritems():
             assert type(n) is types.IntType, \
                    "NumberedFatgraph.__init__: 2nd argument has wrong type:" \
                    " expecting (BoundaryCycle, Int) pair, got `(%s, %s)`." \
                    " Reversed-order arguments?" \
                    % (bcy, n)
             assert isinstance(bcy, BoundaryCycle), \
                    "NumberedFatgraph.__init__: 1st argument has wrong type:" \
                    " expecting (BoundaryCycle, Int) pair, got `(%s, %s)`." \
                    " Reversed-order arguments?" \
                    % (bcy, n)
             assert bcy in self.boundary_cycles, \
                    "NumberedFatgraph.__init__():" \
                    " Cycle `%s` is no boundary cycle of graph `%s` " \
                    % (bcy, self.underlying)
             count[n] += 1
             if count[n] > 1:
                 raise AssertionError("NumberedFatgraph.__init__():" \
                                      " Duplicate key %d" % n)
         assert sum(count) != self.num_boundary_cycles - 1, \
                "NumberedFatgraph.__init__():" \
                " Initializer does not exhaust range `0..%d`: %s" \
                % (self.num_boundary_cycles - 1, numbering)
Пример #3
0
    def isomorphisms(G1, G2):
        """Iterate over isomorphisms from `G1` to `G2`.

        See `Fatgraph.isomrphisms` for a discussion of the
        representation of isomorphisms and example usage.

        A concrete example taken from `M_{1,4}`:latex: ::

          >>> g1 = NumberedFatgraph(
          ...         Fatgraph([Vertex([1, 0, 2]), Vertex([2, 1, 5]), Vertex([0, 4, 3]), Vertex([8, 5, 6]), Vertex([3, 6, 7, 7]), Vertex([8, 4, 9, 9])]),
          ...         numbering={
          ...             BoundaryCycle([(0, 0, 1), (0, 1, 2), (0, 2, 0), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 1), (2, 2, 0), (3, 0, 1), (3, 1, 2), (4, 1, 2), (4, 3, 0), (5, 1, 2), (5, 3, 0)]):0,
          ...             BoundaryCycle([(2, 1, 2), (3, 2, 0), (4, 0, 1), (5, 0, 1)]):1,
          ...             BoundaryCycle([(4, 2, 3)]):2,
          ...             BoundaryCycle([(5, 2, 3)]):3,
          ...       })
          >>> g2 = NumberedFatgraph(
          ...         Fatgraph([Vertex([1, 0, 5, 6]), Vertex([1, 0, 2]), Vertex([5, 2, 3]), Vertex([8, 4, 3]), Vertex([7, 7, 6]), Vertex([4, 8, 9, 9])]),
          ...         numbering={
          ...             BoundaryCycle([(0, 0, 1), (0, 1, 2), (0, 2, 3), (0, 3, 0), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 1), (2, 1, 2), (2, 2, 0), (3, 1, 2), (3, 2, 0), (4, 1, 2), (4, 2, 0), (5, 1, 2), (5, 3, 0)]):0,
          ...             BoundaryCycle([(3, 0, 1), (5, 0, 1)]):1,
          ...             BoundaryCycle([(4, 0, 1)]):2,
          ...             BoundaryCycle([(5, 2, 3)]):3,
          ...      })
          >>> len(list(NumberedFatgraph.isomorphisms(g1, g2)))
          0

        """
        for iso in Fatgraph.isomorphisms(G1.underlying, G2.underlying):
            pe_does_not_preserve_bc = False
            for bc1 in G1.underlying.boundary_cycles:
                bc2 = iso.transform_boundary_cycle(bc1)
                # there are cases (see examples in the
                # `Fatgraph.__eq__` docstring, in which the above
                # algorithm may find a valid mapping, changing from
                # `g1` to an *alternate* representation of `g2` -
                # these should fail as they don't preserve the
                # boundary cycles, so we catch them here.
                if (bc2 not in G2.numbering) \
                        or (G1.numbering[bc1] != G2.numbering[bc2]):
                    pe_does_not_preserve_bc = True
                    # if bc2 not in G2.numbering:
                    #     print ("DEBUG: Rejecting isomorphism %r between marked fatgraphs %r and %r:"
                    #            " %r not in destination boundary cycles"
                    #            % (iso, G1, G2, bc2))
                    # else:
                    #     print ("DEBUG: Rejecting isomorphism %r between marked fatgraphs %r and %r:"
                    #            " boundary cycle %r has number %d in G1 and %d in G2"
                    #            % (iso, G1, G2, bc2, G1.numbering[bc1], G2.numbering[bc2]))
                    break
            if pe_does_not_preserve_bc:
                continue  # to next underlying graph isomorphism
            yield iso
Пример #4
0
    def isomorphisms(G1, G2):
        """Iterate over isomorphisms from `G1` to `G2`.

        See `Fatgraph.isomrphisms` for a discussion of the
        representation of isomorphisms and example usage.

        A concrete example taken from `M_{1,4}`:latex: ::

          >>> g1 = NumberedFatgraph(
          ...         Fatgraph([Vertex([1, 0, 2]), Vertex([2, 1, 5]), Vertex([0, 4, 3]), Vertex([8, 5, 6]), Vertex([3, 6, 7, 7]), Vertex([8, 4, 9, 9])]),
          ...         numbering={
          ...             BoundaryCycle([(0, 0, 1), (0, 1, 2), (0, 2, 0), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 1), (2, 2, 0), (3, 0, 1), (3, 1, 2), (4, 1, 2), (4, 3, 0), (5, 1, 2), (5, 3, 0)]):0,
          ...             BoundaryCycle([(2, 1, 2), (3, 2, 0), (4, 0, 1), (5, 0, 1)]):1,
          ...             BoundaryCycle([(4, 2, 3)]):2,
          ...             BoundaryCycle([(5, 2, 3)]):3,
          ...       })
          >>> g2 = NumberedFatgraph(
          ...         Fatgraph([Vertex([1, 0, 5, 6]), Vertex([1, 0, 2]), Vertex([5, 2, 3]), Vertex([8, 4, 3]), Vertex([7, 7, 6]), Vertex([4, 8, 9, 9])]),
          ...         numbering={
          ...             BoundaryCycle([(0, 0, 1), (0, 1, 2), (0, 2, 3), (0, 3, 0), (1, 0, 1), (1, 1, 2), (1, 2, 0), (2, 0, 1), (2, 1, 2), (2, 2, 0), (3, 1, 2), (3, 2, 0), (4, 1, 2), (4, 2, 0), (5, 1, 2), (5, 3, 0)]):0,
          ...             BoundaryCycle([(3, 0, 1), (5, 0, 1)]):1,
          ...             BoundaryCycle([(4, 0, 1)]):2,
          ...             BoundaryCycle([(5, 2, 3)]):3,
          ...      })
          >>> len(list(NumberedFatgraph.isomorphisms(g1, g2)))
          0

        """
        for iso in Fatgraph.isomorphisms(G1.underlying, G2.underlying):
            pe_does_not_preserve_bc = False
            for bc1 in G1.underlying.boundary_cycles:
                bc2 = iso.transform_boundary_cycle(bc1)
                # there are cases (see examples in the
                # `Fatgraph.__eq__` docstring, in which the above
                # algorithm may find a valid mapping, changing from
                # `g1` to an *alternate* representation of `g2` -
                # these should fail as they don't preserve the
                # boundary cycles, so we catch them here.
                if (bc2 not in G2.numbering) \
                       or (G1.numbering[bc1] != G2.numbering[bc2]):
                    pe_does_not_preserve_bc = True
                    # if bc2 not in G2.numbering:
                    #     print ("DEBUG: Rejecting isomorphism %r between marked fatgraphs %r and %r:"
                    #            " %r not in destination boundary cycles"
                    #            % (iso, G1, G2, bc2))
                    # else:
                    #     print ("DEBUG: Rejecting isomorphism %r between marked fatgraphs %r and %r:"
                    #            " boundary cycle %r has number %d in G1 and %d in G2"
                    #            % (iso, G1, G2, bc2, G1.numbering[bc1], G2.numbering[bc2]))
                    break
            if pe_does_not_preserve_bc:
                continue # to next underlying graph isomorphism
            yield iso
Пример #5
0
    def facets(self, edge, other):
        """Iterate over facets obtained by contracting `edge` and
        projecting onto `other`.

        Each returned item is a triple `(j, k, s)`, where:
          - `j` is the index of a `NumberedFatgraph` in `self`;
          - `k` is the index of a `NumberedFatgraph` in `other`;
          - `s` is the sign by which `self[j].contract(edge)` projects onto `other[k]`.
        Only triples for which `s != 0` are returned.

        Examples::
        
          >>> p0 = NumberedFatgraphPool(Fatgraph([Vertex([1, 2, 0, 1, 0]), Vertex([3, 3, 2])]))
          >>> p1 = NumberedFatgraphPool(Fatgraph([Vertex([0, 1, 0, 1, 2, 2])]))
          >>> list(NumberedFatgraphPool.facets(p0, 2, p1))
          [(0, 0, 1), (1, 1, 1)]
        """
        assert not self.graph.is_loop(edge)
        assert self.is_orientable
        assert other.is_orientable

        g0 = self.graph
        g1 = g0.contract(edge)
        g2 = other.graph
        assert len(g1.boundary_cycles) == len(g2.boundary_cycles)

        # compute isomorphism map `f1` from `g1` to `g2`: if there is
        # no such isomorphisms, then stop iteration (do this first so
        # then we do not waste time on computing if we need to abort
        # anyway)
        f1 = Fatgraph.isomorphisms(g1, g2).next()

        ## 1. compute map `phi0` induced on `g0.boundary_cycles` from the
        ##    graph map `f0` which contracts `edge`.
        ##
        (e1, e2) = g0.endpoints(edge)
        assert set(g1.boundary_cycles) == set([g0.contract_boundary_cycle(bcy, e1, e2)
                                               for bcy in g0.boundary_cycles]), \
            "NumberedFatgraphPool.facets():" \
            " Boundary cycles of contracted graph are not the same" \
            " as contracted boundary cycles of parent graph:" \
            " `%s` vs `%s`" % (g1.boundary_cycles,
                               [g0.contract_boundary_cycle(bcy, e1, e2)
                                for bcy in g0.boundary_cycles])
        phi0_inv = Permutation((i1, i0) for (i0, i1) in enumerate(
            g1.boundary_cycles.index(g0.contract_boundary_cycle(bc0, e1, e2))
            for bc0 in g0.boundary_cycles))
        ## 2. compute map `phi1` induced by isomorphism map `f1` on
        ##    the boundary cycles of `g1` and `g2`.
        ##
        phi1_inv = Permutation((i1, i0) for (i0, i1) in enumerate(
            g2.boundary_cycles.index(f1.transform_boundary_cycle(bc1))
            for bc1 in g1.boundary_cycles))
        assert len(phi1_inv) == len(g1.boundary_cycles)
        assert len(phi1_inv) == len(g2.boundary_cycles)
        ## 3. Compute the composite map `f1^(-1) * f0`.
        ##

        ## For every numbering `nb` on `g0`, compute the (index of)
        ## corresponding numbering on `g2` (under the composition map
        ## `f1^(-1) * f0`) and return a triple `(index of nb, index of
        ## push-forward, sign)`.
        ##
        ## In the following:
        ##
        ## - `j` is the index of a numbering `nb` in `self.numberings`;
        ## - `k` is the index of the corresponding numbering in `other.numberings`,
        ##   under the composition map `f1^(-1) * f0`;
        ## - `a` is the the unique automorphism `a` of `other.graph` such that::
        ##
        ##       self.numberings[j] = pull_back(<permutation induced by `a` applied to> other.numberings[k])
        ##
        ## - `s` is the pull-back sign (see below).
        ##
        ## The pair `k`,`a` is computed using the
        ## `NumberedFatgraphPool._index` (which see), applied to each
        ## of `self.numberings`, rearranged according to the
        ## permutation of boundary cycles induced by `f1^(-1) * f0`.
        ##
        for (j, (k, a)) in enumerate(
                other._index(phi1_inv.rearranged(phi0_inv.rearranged(nb)))
                for nb in self.numberings):
            ## there are three components to the sign `s`:
            ##   - the sign given by the ismorphism `f1`
            ##   - the sign of the automorphism of `g2` that transforms the
            ##     push-forward numbering into the chosen representative in the same orbit
            ##   - the alternating sign from the homology differential
            s = f1.compare_orientations() \
                * a.compare_orientations() \
                * minus_one_exp(g0.edge_numbering[edge])
            yield (j, k, s)
Пример #6
0
    def facets(self, edge, other):
        """Iterate over facets obtained by contracting `edge` and
        projecting onto `other`.

        Each returned item is a triple `(j, k, s)`, where:
          - `j` is the index of a `NumberedFatgraph` in `self`;
          - `k` is the index of a `NumberedFatgraph` in `other`;
          - `s` is the sign by which `self[j].contract(edge)` projects onto `other[k]`.
        Only triples for which `s != 0` are returned.

        Examples::
        
          >>> p0 = NumberedFatgraphPool(Fatgraph([Vertex([1, 2, 0, 1, 0]), Vertex([3, 3, 2])]))
          >>> p1 = NumberedFatgraphPool(Fatgraph([Vertex([0, 1, 0, 1, 2, 2])]))
          >>> list(NumberedFatgraphPool.facets(p0, 2, p1))
          [(0, 0, 1), (1, 1, 1)]
        """
        assert not self.graph.is_loop(edge)
        assert self.is_orientable
        assert other.is_orientable
        
        g0 = self.graph
        g1 = g0.contract(edge)
        g2 = other.graph
        assert len(g1.boundary_cycles) == len(g2.boundary_cycles)

        # compute isomorphism map `f1` from `g1` to `g2`: if there is
        # no such isomorphisms, then stop iteration (do this first so
        # then we do not waste time on computing if we need to abort
        # anyway)
        f1 = Fatgraph.isomorphisms(g1,g2).next()
        
        ## 1. compute map `phi0` induced on `g0.boundary_cycles` from the
        ##    graph map `f0` which contracts `edge`.
        ##
        (e1, e2) = g0.endpoints(edge)
        assert set(g1.boundary_cycles) == set([ g0.contract_boundary_cycle(bcy, e1, e2)
                                                for bcy in g0.boundary_cycles ]), \
               "NumberedFatgraphPool.facets():" \
               " Boundary cycles of contracted graph are not the same" \
               " as contracted boundary cycles of parent graph:" \
               " `%s` vs `%s`" % (g1.boundary_cycles,
                                  [ g0.contract_boundary_cycle(bcy, e1, e2)
                                    for bcy in g0.boundary_cycles ])
        phi0_inv = Permutation((i1,i0) for (i0,i1) in enumerate(
            g1.boundary_cycles.index(g0.contract_boundary_cycle(bc0, e1, e2))
            for bc0 in g0.boundary_cycles
            ))
        ## 2. compute map `phi1` induced by isomorphism map `f1` on
        ##    the boundary cycles of `g1` and `g2`.
        ##
        phi1_inv = Permutation((i1,i0) for (i0,i1) in enumerate(
            g2.boundary_cycles.index(f1.transform_boundary_cycle(bc1))
            for bc1 in g1.boundary_cycles
            ))
        assert len(phi1_inv) == len(g1.boundary_cycles)
        assert len(phi1_inv) == len(g2.boundary_cycles)
        ## 3. Compute the composite map `f1^(-1) * f0`.
        ##

        ## For every numbering `nb` on `g0`, compute the (index of)
        ## corresponding numbering on `g2` (under the composition map
        ## `f1^(-1) * f0`) and return a triple `(index of nb, index of
        ## push-forward, sign)`.
        ##
        ## In the following:
        ##
        ## - `j` is the index of a numbering `nb` in `self.numberings`;
        ## - `k` is the index of the corresponding numbering in `other.numberings`,
        ##   under the composition map `f1^(-1) * f0`;
        ## - `a` is the the unique automorphism `a` of `other.graph` such that::
        ##
        ##       self.numberings[j] = pull_back(<permutation induced by `a` applied to> other.numberings[k])
        ##
        ## - `s` is the pull-back sign (see below).
        ##
        ## The pair `k`,`a` is computed using the
        ## `NumberedFatgraphPool._index` (which see), applied to each
        ## of `self.numberings`, rearranged according to the
        ## permutation of boundary cycles induced by `f1^(-1) * f0`.
        ##
        for (j, (k, a)) in enumerate(other._index(phi1_inv.rearranged(phi0_inv.rearranged(nb)))
                                     for nb in self.numberings):
            ## there are three components to the sign `s`:
            ##   - the sign given by the ismorphism `f1`
            ##   - the sign of the automorphism of `g2` that transforms the
            ##     push-forward numbering into the chosen representative in the same orbit
            ##   - the alternating sign from the homology differential
            s = f1.compare_orientations() \
                * a.compare_orientations() \
                * minus_one_exp(g0.edge_numbering[edge])
            yield (j, k, s)