Example #1
0
def glue(g,h,n):
    """
    Glues two ribbon graphs together.

    INPUT:

    - A ribbon graph
    - A ribbon graph
    - A non-negative integer.

    OUTPUT: A ribbon graph

    >>> f = RibbonGraph.vertex(4)
    >>> g = RibbonGraph.vertex(3)
    >>> glue(f,g,0) #doctest: +ELLIPSIS
    <__main__.RibbonGraph object at 0...>

    """
    if n < 0:
        raise ValueError("Need a non-negative integer.")
    if n > len(g.bd) or n > len(h.bd):
        raise ValueError("Cannot glue this many points.")

    a = g.copy()
    b = h.copy()
    he = a.jg.he.union(b.jg.he)
    jg = ribbon.justgraph(he)

    for i in xrange(n):
        x = a.bd.pop()
        y = b.bd.popleft()
        jg.stitch(x,y)

    return RibbonGraph(jg,list(a.bd)+list(b.bd))
Example #2
0
    def compose(self,other):
        """Composition of morphisms.

        INPUT: Two morphisms

        OUTPUT: A morphism

        EXAMPLES:

        >>> s1 = Artin_generator(3,1)
        >>> s2 = Artin_generator(3,2)
        >>> s1.compose(s2) # doctest:+ELLIPSIS
        <__main__.Morphism instance at 0x...>

        >>> s1 = Artin_generator(3,1)
        >>> t1 = Artin_generator(3,-1)
        >>> s1.compose(t1) # doctest:+ELLIPSIS
        <__main__.Morphism instance at 0x...>

        """

        if len(self.codomain) != len(other.domain):
            raise ValueError

        sm = self.copy()
        om = other.copy()
        jg = ribbon.justgraph(sm.graph.he.union(om.graph.he))
        for u, v in zip(sm.codomain,om.domain):
            jg.stitch(u, v)
        jg.normal()
        return Morphism(jg,sm.domain,om.codomain)
Example #3
0
    def vertex(x):
        """
        Constructs a single vertex of valency n.
        INPUT:
        A positive integer n, at least 1 or a list of features.

        OUTPUT:
        A closed ribbon graph.

        EXAMPLES:

        >>> RibbonGraph.vertex(3) # doctest:+ELLIPSIS
        <__main__.RibbonGraph object at 0x...>

        """

        if isinstance(x, Iterable):
            n = len(x)
        else:
            n = x
        if n<1: raise ValueError("Not enough points.")

        a = [ ribbon.halfedge() for i in xrange(n) ]
        for i in xrange(n-1):
            a[i].c = a[i+1]
        a[n-1].c = a[0]
        if isinstance(x, Iterable):
            for i, r in zip(xrange(n),x):
                if not isinstance(r, ribbon.features):
                    raise ValueError
                a[i].decorations = r

        h = ribbon.justgraph(a)
        return RibbonGraph(h,a)
Example #4
0
    def polygon(n):
        """
        This constructs a polygon with n sides.

        INPUT:
        A positive integer n, at least 1.

        OUTPUT:
        A closed ribbon graph.

        EXAMPLE:

        >>> RibbonGraph.polygon(4) # doctest:+ELLIPSIS
        <__main__.RibbonGraph object at 0x...>
        """

        if n<1: raise ValueError
        a =  [ ribbon.halfedge() for i in xrange(n) ]
        b1 = [ ribbon.halfedge() for i in xrange(n) ]
        b2 = [ ribbon.halfedge() for i in xrange(n) ]
        for i in xrange(n-1):
            b1[i].e = b2[i+1]
            b2[i+1].e = b1[i]
        b1[n-1].e = b2[0]
        b2[0].e = b1[n-1]
        for i in xrange(n):
            a[i].c = b1[i]
            b1[i].c = b2[i]
            b2[i].c = a[i]
        h = ribbon.justgraph(a+b1+b2)
        return RibbonGraph(h,a)
Example #5
0
def Artin_generator(n,k):
    """Constructs the Artin generator s_k of n string braif group.

    INPUT: n a positive integer, k an integer with 0 < |k| < n

    OUTPUT: A Morphism

    >>> s1 = Artin_generator(3,1)
    >>> s2 = Artin_generator(3,2)
    """
    if not 0 < abs(k) < n:
        raise ValueError

    # These are also defined in knots.py
    in_over = ribbon.Features('head','blue',True)
    in_under = ribbon.Features('head','blue',False)
    out_over = ribbon.Features('tail','blue',True)
    out_under = ribbon.Features('tail','blue',False)

    r = range(n)
    do = [ ribbon.halfedge() for i in r ]
    co = [ ribbon.halfedge() for i in r ]
    for a in do:
        a.decorations = in_over
        a.IsI = True
    for a in co:
        a.decorations = out_over
        a.IsI = True
    for i in r:
        do[i].c = co[i]
        co[i].c = do[i]
        
    p =abs(k)
    do[p-1].IsI = False
    do[p].IsI = False
    co[p-1].IsI = False
    co[p].IsI = False
    do[p-1].c = do[p]
    do[p].c = co[p]
    co[p].c = co[p-1]
    co[p-1].c = do[p-1]

    if k > 0:
        do[k-1].decorations = in_over
        do[k].decorations = in_under
        co[k-1].decorations = out_under
        co[k].decorations = out_over
    elif k < 0:
        do[p-1].decorations = in_under
        do[p].decorations = in_over
        co[p-1].decorations = out_over
        co[p].decorations = out_under
    else:
        raise RuntimeError

    g = ribbon.justgraph( set(do+co) )
    return Morphism(g, do, co)
Example #6
0
    def from_PlanarDiagram(PD):
        """Produces a LinkDiagram from the planar diagram notation.

        INPUT: A list or set of 4-tuples

        OUTPUT: A closed justgraph

        EXAMPLES:

        >>> LinkDiagram.from_PlanarDiagram([[1,2,3,4],[4,3,6,5],[2,1,5,6]]) #doctest: +ELLIPSIS
        <__main__.LinkDiagram object at 0x...>

        >>> g = LinkDiagram.from_PlanarDiagram([[1,2,3,4],[4,3,6,5],[2,1,5,6]])
        >>> [ len(v) for v in g.vertices ]
        [4, 4, 4]
        >>> [ len(v) for v in g.edges ]
        [2, 2, 2, 2, 2, 2]
        >>> x = [ len(v) for v in g.faces ]
        >>> x.sort()
        >>> x
        [2, 2, 2, 3, 3]
        >>> x = [ len(v) for v in g.graph.get_orbits( lambda a: a.e.c ) ]
        >>> x.sort()
        >>> x
        [2, 2, 2, 3, 3]


        """
        if not all([ len(x) == 4 for x in PD ]):
            raise ValueError("Not a valid planar diagram code")
        for i in range(2*len(PD)):
            p = [ x for x in PD if i+1 in x ]
            if len(p) != 2:
                raise ValueError("Not a valid planar diagram code")

        D = dict()
        he = set()
        for x in PD:
            a = [ ribbon.halfedge() for i in range(4) ]
            for i in range(4):
                a[i-1].c = a[i]
            D[tuple(x)] = a
            he = he.union(set(a))

        for i in range(2*len(PD)):
            p = [ x for x in PD if i+1 in x ]
            a0 = D[tuple(p[0])][p[0].index(i+1)]
            a1 = D[tuple(p[1])][p[1].index(i+1)]
            a0.e = a1
            a1.e = a0

        g = ribbon.justgraph(he)
        outside = g.get_orbits( lambda a: a.e.c )[0]
        return LinkDiagram(g,outside)
Example #7
0
        def vogel_move(f,u,v):
            # This goes into an infinite loop
            phi = f.graph.copy()
            new = phi.codomain
            a = phi.map[u]
            b = a.e
            c = phi.map[v]
            d = c.e
            x = [ ribbon.halfedge() for i in range(4) ]
            for i in range(4):
                x[i-1].c = x[i]
            y = [ ribbon.halfedge() for i in range(4) ]
            for i in range(4):
                y[i-1].c = y[i]
            a.e = x[0]; x[0].e = a
            d.e = x[3]; x[3].e = d
            b.e = y[1]; y[1].e = b
            c.e = y[2]; y[2].e = c
            x[1].e = y[0]; y[0].e = x[1]
            x[2].e = y[3]; y[3].e = x[2]
            switch = {'head':'tail','neither':'neither','tail':'head'}
            ut = a.decorations.directed
            ot = switch[ut]

            if False:
                x[0].decorations._replace(a.decorations.colour)
                x[1].decorations._replace(c.decorations.colour)
                x[2].decorations._replace(a.decorations.colour)
                x[3].decorations._replace(c.decorations.colour)
                y[0].decorations._replace(d.decorations.colour)
                y[1].decorations._replace(b.decorations.colour)
                y[2].decorations._replace(d.decorations.colour)
                y[3].decorations._replace(b.decorations.colour)

            for a in x:
                a.decorations._replace(colour='red')
            for a in y:
                a.decorations._replace(colour='red')


            g = ribbon.justgraph( new.he.union( set(x+y) ) )

            u = phi.map[f.outside[0]]
            outside = [u]
            s = u.e.c
            while s != u:
                outside.append(s)
                s = s.e.c

            return LinkDiagram(g,outside)
    def map(self):
        labels = ('VE','VF','EV','EF','FV','FE',)
        flags = dict([])
        for l in labels:
            for i in range(self.degree):
                flags[(i,l,)] = ribbon.halfedge()

        for i in range(self.degree):
            flags[(i,'VE',)].e = flags[(i,'EV',)]
            flags[(i,'EV',)].e = flags[(i,'VE',)]
            flags[(i,'VF',)].e = flags[(i,'FV',)]
            flags[(i,'FV',)].e = flags[(i,'VF',)]
            flags[(i,'FE',)].e = flags[(i,'EF',)]
            flags[(i,'EF',)].e = flags[(i,'FE',)]

            #flags[(i,'VE',)]).c = flags[(self.sigma[i],'VF',)]
            flags[(self.sigma[i],'VE',)].c = flags[(i,'VF',)]
            flags[(i,'EV',)].c = flags[(i,'EF',)]
            flags[(i,'VF',)].c = flags[(i,'VE',)]
            flags[(self.phi[i],'FV',)].c = flags[(i,'FE',)]
            flags[(i,'FE',)].c = flags[(i,'FV',)]
            #flags[(i,'EF',)].c = flags[(self.alpha[i],'EV',)]
            flags[(self.alpha[i],'EF',)].c = flags[(i,'EV',)]

        for i in range(self.degree):
            flags[(i,'EV',)].decorations = ribbon.Features('neither','red',True)
            flags[(i,'VE',)].decorations = ribbon.Features('neither','red',True)
            flags[(i,'FV',)].decorations = ribbon.Features('neither','blue',True)
            flags[(i,'VF',)].decorations = ribbon.Features('neither','blue',True)
            flags[(i,'FE',)].decorations = ribbon.Features('neither','green',True)
            flags[(i,'EF',)].decorations = ribbon.Features('neither','green',True)
            
        g = ribbon.justgraph(set(flags.values()))

        a = self.faces[0]
        x = flags[(a[0],'FE',)]
        out = [x]
        y = x.c
        while y != x:
            out.append(y)
            y = y.c
        out.reverse()

        sut = [ flags[(i,'FE',)] for i in a ] + [ flags[(i,'FV',)] for i in a ]
        if set(sut) != set(out):
            raise RuntimeError

        return g, out
    def ribbon(self):
        g1, out1 = self.map()
        #g1.inspect()
        g2, inc1 = g1.subdivision()
        #g2.inspect()
        out2 = [ inc1[x] for x in out1 ]
        g3, inc2 = g2.subdivision()
        out3 = [ inc2[x] for x in out2 ]
        #g3.inspect()

        #g, out = g3, out3
        g, out = g2, out2

        bd = [ x.e for x in out ]
        he = g.he
        for x in out:
            x.e.e = None
            he.remove(x)

        gj = ribbon.justgraph(he)

        return spider.RibbonGraph(gj,bd)
Example #10
0
    def connected_sum(self,other):
        """Constructs the connected sum of two link diagrams.

        EXAMPLE:

        >>> c = DT([4,6,2])
        >>> g = LinkDiagram.from_DT(c)
        >>> g.connected_sum(g) #doctest: +ELLIPSIS
        <__main__.LinkDiagram object at 0x...>
        >>> g.connected_sum(g).genus
        2

        """
        phi = self.graph.copy()
        psi = other.graph.copy()

        he = phi.codomain.he.union(psi.codomain.he)

        u = phi.map[self.outside[0]]
        v = psi.map[other.outside[0]]
        x = u.e; y = v.e

        if u.decorations.over == v.decorations.over:
            u.e = y; y.e = u
            v.e = x; x.e = v
        else:
            u.e = x; x.e = u
            v.e = y; y.e = v


        outside = [u]
        s = u.e.c
        while s != u:
            outside.append(s)
            s = s.e.c

        return LinkDiagram( ribbon.justgraph(he), outside )
Example #11
0
    def closure(self, bv=None):
        """Construct the closure of a web.

        INPUT: A web and a boundary vector.

        OUTPUT: A closed web.

        EXAMPLES:

        >>> RibbonGraph.polygon(5).closure() #doctest: +ELLIPSIS
        <closedgraph.ClosedGraph object at 0x...>

        >>> RibbonGraph.polygon(5).closure().graph.count_vertices()
        [0, 0, 5, 10]

        """
        if bv == None:
            bv = [1] * len(self.bd)
        if sum(bv) != len(self.bd):
            raise ValueError("Boundary vector is not consistent with web.")
        if len(bv) < 3:
            raise ValueError("Not enough corners.")

        C = len(bv)
        B = len(self.bd)

        phi = self.jg.copy()
        he = phi.codomain.he

        rim = ribbon.Features('neither','black',True)
        switch = {'head':'tail','neither':'neither','tail':'head'}
        
        ci = [ ribbon.halfedge() for i in xrange(C) ]
        for a in ci:
            a.decorations = rim
        co = [ ribbon.halfedge() for i in xrange(C) ]
        for a in co:
            a.decorations = rim
        he = he.union(ci+co)
        bi = [ ribbon.halfedge() for i in xrange(B) ]
        for a in bi:
            a.decorations = rim
        bo = [ ribbon.halfedge() for i in xrange(B) ]
        for a in bo:
            a.decorations = rim
        bc = [ ribbon.halfedge() for i in xrange(B) ]
        he = he.union(bi+bo+bc)

        for i in xrange(C):
            ci[i].c = co[i]
            co[i].c = ci[i]

        nb = [ phi.map[a] for a in self.bd ]
        for i in xrange(B):
            bi[i].c = bo[i]
            bo[i].c = bc[i]
            bc[i].c = bi[i]
            bc[i].e = nb[i]
            nb[i].e = bc[i]

        for a in bc:
            f = a.e.decorations
            a.decorations = ribbon.Features(switch[f.directed],f.colour,True)

        p = 0
        for i, a in enumerate(bv):
            r = co[i-1]
            for j in xrange(a):
                bi[p].e = r
                r.e = bi[p]
                r = bo[p]
                p += 1
            r.e = ci[i]
            ci[i].e = r

        ng = ribbon.justgraph(he)

        u = co[0]
        outside = [u]
        s = u.e.c
        while s != u:
            outside.append(s)
            s = s.e.c

        return closedgraph.ClosedGraph(ng, outside)
Example #12
0
    def from_DT(DT):
        """Construct a knot diagram from the Dowker-Thistlewaite code.

        The theory is given in the original paper:

        Classification of knot projections
        C. H. Dowker & Morwen B. Thistlewaite
        Topology and its Applications 16 (1983), 19--31

        INPUT:
            A list of positive even integers

        OUTPUT:
            A closed justgraph

        EXAMPLES:
            >>> LinkDiagram.from_DT(DT([4,6,2])) #doctest: +ELLIPSIS
            <__main__.LinkDiagram object at 0x...>
            >>> LinkDiagram.from_DT(DT([4,6,8,2])) #doctest: +ELLIPSIS
            <__main__.LinkDiagram object at 0x...>
            >>> LinkDiagram.from_DT(DT([4,8,10,2,6])) #doctest: +ELLIPSIS
            <__main__.LinkDiagram object at 0x...>
            >>> LinkDiagram.from_DT(DT([6,8,10,2,4])) #doctest: +ELLIPSIS
            <__main__.LinkDiagram object at 0x...>

            >>> g = LinkDiagram.from_DT(DT([4,6,2]))
            >>> [ len(v) for v in g.vertices ]
            [4, 4, 4]
            >>> [ len(v) for v in g.edges ]
            [2, 2, 2, 2, 2, 2]
            >>> x = [ len(v) for v in g.faces ]
            >>> x.sort()
            >>> x
            [2, 2, 2, 3, 3]

            >>> g = LinkDiagram.from_DT(DT([4,6,8,2]))
            >>> [ len(v) for v in g.vertices ]
            [4, 4, 4, 4]
            >>> [ len(v) for v in g.edges ]
            [2, 2, 2, 2, 2, 2, 2, 2]
            >>> x = [ len(v) for v in g.faces ]
            >>> x.sort()
            >>> x
            [2, 2, 3, 3, 3, 3]
            >>> x = [ len(v) for v in g.graph.get_orbits( lambda a: a.c.e ) ]
            >>> x.sort()
            >>> x
            [2, 2, 3, 3, 3, 3]

            >>> g = LinkDiagram.from_DT(DT([4,8,10,2,6]))
            >>> [ len(v) for v in g.vertices ]
            [4, 4, 4, 4, 4]
            >>> [ len(v) for v in g.edges ]
            [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
            >>> x = [ len(v) for v in g.faces ]
            >>> x.sort()
            >>> x
            [2, 2, 2, 3, 3, 4, 4]
            >>> x = [ len(v) for v in g.graph.get_orbits( lambda a: a.c.e ) ]
            >>> x.sort()
            >>> x
            [2, 2, 2, 3, 3, 4, 4]

            >>> g = LinkDiagram.from_DT(DT([8,10,2,12,4,6]))
            >>> x = [ len(v) for v in g.vertices ]
            >>> x.sort()
            >>> x
            [4, 4, 4, 4, 4, 4]
            >>> [ len(v) for v in g.edges ]
            [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
            >>> x = [ len(v) for v in g.faces ]
            >>> x.sort()
            >>> x
            [2, 2, 2, 3, 3, 3, 4, 5]
            >>> x = [ len(v) for v in g.graph.get_orbits( lambda a: a.c.e ) ]
            >>> x.sort()
            >>> x
            [2, 2, 2, 3, 3, 3, 4, 5]

        REFS:
            http://katlas.org/wiki/DT_%28Dowker-Thistlethwaite%29_Codes
        """

        emb = DT.orientation

        m = 2 * len(emb)

        left =  [ ribbon.halfedge() for i in range(m) ]
        right = [ ribbon.halfedge() for i in range(m) ]
        for i in range(m):
            left[i].e = right[i-1]
            right[i-1].e = left[i]
        for i, k in enumerate(DT.code):
            a = abs(k)-1
            x = [ left[2*i], left[a], right[2*i], right[a] ]
            if k > 0:
                x[0].decorations = in_over
                x[1].decorations = in_under
                x[2].decorations = out_over
                x[3].decorations = out_under
            elif k < 0:
                x[0].decorations = in_under
                x[1].decorations = in_over
                x[2].decorations = out_under
                x[3].decorations = out_over
            else:
                raise ValueError("Not a valid Dowker-Thistlewaite code")
            if emb[i] == -1:
                x.reverse()
            elif emb[i] != 1:
                raise RuntimeError
            for i in xrange(4):
                x[i-1].c = x[i]

        g = ribbon.justgraph( set(left).union(set(right)) )
        outside = g.get_orbits( lambda a: a.e.c )[0]
        return LinkDiagram(g,outside)
Example #13
0
    def braid(self):
        """Given a link diagram find a braid whose closure is the link.

        The existence of the braid is known as Alexander's theorem.
        This is an implementation of Vogel's algorithm. This does not
        change the number of Seifert circles but does increase the number
        of crossings.

        EXAMPLES:

        >>> c = DT([4,6,2])
        >>> g = LinkDiagram.from_DT(c)
        >>> g.braid #doctest: +ELLIPSIS
        <pivotal.Morphism instance at 0x...>

        >>> c = DT([4,6,8,2])
        >>> g = LinkDiagram.from_DT(c)
        >>> g.braid #doctest: +ELLIPSIS
        <pivotal.Morphism instance at 0x...>

        c = DT([4,8,10,2,6])
        g = LinkDiagram.from_DT(c)
        g.braid #doctest: +ELLIPSIS
        <pivotal.Morphism instance at 0x...>

        >>> c = DT([8,10,2,12,4,6])
        >>> g = LinkDiagram.from_DT(c)
        >>> g.braid #doctest: +ELLIPSIS
        <pivotal.Morphism instance at 0x...>

        """

        def get_arc(f):
            """Finds an arc."""
            sf = f.seifert
            DS = dict()
            for x in sf:
                for a in x:
                    DS[a] = x

            # Could use product and ifilter from itertools
            for fc in f.faces:
                for j, v in enumerate(fc):
                    for i in range(j-1):
                        u = fc[i]
                        if DS[u] != DS[v] and u.decorations.directed == v.decorations.directed:
                            return u,v
            return None

        def vogel_move(f,u,v):
            # This goes into an infinite loop
            phi = f.graph.copy()
            new = phi.codomain
            a = phi.map[u]
            b = a.e
            c = phi.map[v]
            d = c.e
            x = [ ribbon.halfedge() for i in range(4) ]
            for i in range(4):
                x[i-1].c = x[i]
            y = [ ribbon.halfedge() for i in range(4) ]
            for i in range(4):
                y[i-1].c = y[i]
            a.e = x[0]; x[0].e = a
            d.e = x[3]; x[3].e = d
            b.e = y[1]; y[1].e = b
            c.e = y[2]; y[2].e = c
            x[1].e = y[0]; y[0].e = x[1]
            x[2].e = y[3]; y[3].e = x[2]
            switch = {'head':'tail','neither':'neither','tail':'head'}
            ut = a.decorations.directed
            ot = switch[ut]

            if False:
                x[0].decorations._replace(a.decorations.colour)
                x[1].decorations._replace(c.decorations.colour)
                x[2].decorations._replace(a.decorations.colour)
                x[3].decorations._replace(c.decorations.colour)
                y[0].decorations._replace(d.decorations.colour)
                y[1].decorations._replace(b.decorations.colour)
                y[2].decorations._replace(d.decorations.colour)
                y[3].decorations._replace(b.decorations.colour)

            for a in x:
                a.decorations._replace(colour='red')
            for a in y:
                a.decorations._replace(colour='red')


            g = ribbon.justgraph( new.he.union( set(x+y) ) )

            u = phi.map[f.outside[0]]
            outside = [u]
            s = u.e.c
            while s != u:
                outside.append(s)
                s = s.e.c

            return LinkDiagram(g,outside)


        f = self
        while 1:
            a = get_arc(f)
            if a == None:
                break
            f = vogel_move(f,a[0],a[1])
            print f.genus
            #f.show()

        # The code below appears to be correct
        faces = f.faces
        fc = [ frozenset(x) for x in faces ]

        def start():
            for x in f.seifert:
                if frozenset(x) in fc:
                    if x[0].decorations.directed == 'head':
                        return x
            raise RuntimeError

        index = dict()
        for a in f.graph.he:
            index[a] = None
        for a in start():
            index[a] = 0
            index[a.e] = 0

        def v_find(i):
            for x in f.vertices:
                u = [ a for a in x if index[a] == None ]
                v = [ a for a in x if index[a] == i ]
                if len(u) == 2 and len(v) == 2:
                    return u,v
            raise RuntimeError

        sf = f.seifert
        DS = dict()
        for x in sf:
            for a in x:
                DS[a] = x

        n = len(sf)/2 # This is #braid strings = #Seifert circles
        for i in range(n-1):
            c = v_find(i)
            for x in c[0]:
                for a in DS[x]:
                    index[a] = i+1
                    index[a.e] = i+1

        if any([ index[a] == None for a in f.graph.he ]):
            raise RuntimeError


        for a in start():
            if a.decorations.directed == 'head':
                cut_path = [ a ]
                break

        DF = dict()
        for x in faces:
            for a in x:
                DF[a] = x

        for i in range(n-1):
            x = DF[ cut_path[i].e ]
            for a in x:
                if index[a] == i+1 and a.decorations.directed == 'head':
                    cut_path.append(a)
                    break

        if len(cut_path) != n:
            raise RuntimeError

        phi = f.graph.copy()
        do = [ phi.map[a] for a in cut_path ]
        co = [ phi.map[a.e] for a in cut_path ]

        for a in cut_path:
            phi.map[a].e = None
            phi.map[a.e].e = None

        g = ribbon.justgraph(phi.codomain.he)

        return pivotal.Morphism(g,do,co)