Esempio n. 1
0
def go_deep():
    sig = 'dLQacccjsnk_200'
    branch = [4, 11, 0]
    tri, angle = isosig_to_tri_angle(sig)
    for j in range(20):
        print(j)
        tri, branch = go_deeper(tri, branch)
        if not has_non_sing_semiflow(tri, branch):
            print(tri, branch)
Esempio n. 2
0
def test_semiflow_on_drillings(sig):
    
    tri, angle = isosig_to_tri_angle(sig)
    branch = upper_branched_surface(tri, angle)
    loops = find_flow_cycles(tri, branch)
    tri_loops = [flow_cycle_to_triangle_loop(tri, branch, loop) for loop in loops]
    
    for tri_loop in tri_loops:
        if tri_loop != False: # False means that tri_loop goes more than once  along the same triangle - not currently implemented
            tri, angle = isosig_to_tri_angle(sig)
            if tri_loop_is_boundary_parallel(tri_loop, tri) == False: # if a loop is boundary parallel then we don't drill
                tri, angle = isosig_to_tri_angle(sig)
                branch = upper_branched_surface(tri, angle)
                drill(tri, tri_loop, angle, branch)
                assert has_non_sing_semiflow(tri, branch)
Esempio n. 3
0
def threeTwoMove(tri, branch, edge_num, return_triangle=False):
    """Apply a 3-2 move to a triangulation with a branched surface, if possible. 
    If perform = False, returns if the move is possible.
    modifies tri, returns (tri, branch) for the performed move"""

    ### note if this function does not return False, then there is only one
    ### possible branch so we just return it rather than a list

    # assert is_branched(tri, branch)
    assert has_non_sing_semiflow(tri, branch)

    edge = tri.edge(edge_num)
    if edge.degree() != 3:
        return False

    tets = []
    tet_nums = []
    vertices = []
    for i in range(3):
        embed = edge.embedding(i)
        tets.append(embed.simplex())
        tet_nums.append(tets[i].index())
        vertices.append(embed.vertices())

    if len(set([tet.index() for tet in tets])) != 3:
        return False  ### tetrahedra must be distinct

    ### check we do the same as regina...
    tri2 = regina.Triangulation3(tri)  ## make a copy
    tri2.pachner(tri2.edge(edge_num))

    ## record the tetrahedra and gluings adjacent to the tets

    gluings = []
    for i in range(3):
        tet_gluings = []
        for j in range(2):
            tet_gluings.append([
                tets[i].adjacentTetrahedron(vertices[i][j]),
                tets[i].adjacentGluing(vertices[i][j])
            ])
        gluings.append(tet_gluings)

    for i in range(3):
        assert tets[i].adjacentTetrahedron(vertices[i][2]) == tets[
            (i + 1) % 3]  ### The edge embeddings should be ordered this way...

    large_edges = []  ### record large edge info for the outer faces
    for i in range(3):
        this_tet_large_edges = []
        for j in range(2):
            this_tet_large_edges.append(
                large_edge_of_face(branch[tets[i].index()], vertices[i][j]))
        large_edges.append(this_tet_large_edges)

    ### add new tetrahedra
    new_tets = []
    for i in range(2):
        new_tets.append(tri.newTetrahedron())

    ### glue across face
    new_tets[0].join(3, new_tets[1], regina.Perm4(0, 2, 1, 3))

    ### replace mapping info with corresponding info for the 2 tet. Self gluings will be annoying...

    ### write vertices[i][j] as vij

    ###                 tets[0]                                   new_tet1
    ###                _________                                 _________
    ###              ,'\`.v00,'/`.                             ,'\       /`.
    ###            ,'   \ `.' /   `.                         ,'   \  3  /   `.
    ###          ,'   v10\ | /v20   `.                     ,'      \   /      `.
    ###         /|\       \|/       /|\                   / \       \ /       / \
    ###        / | \       *       / | \                 /   \       *       /   \
    ###    v12/  |  \..... | ...../  |  \v23            /  1 _\..... | ...../_ 2  \
    ###      /  ,'  /      *      \  `.  \             /_--"" /      *      \ ""--_\
    ###      \`.|  /v03   /|\   v02\  |,'/      `.     \`. 2 /      / \      \ 1 ,'/
    ###       \ `./      / | \      \,' /     ----}     \ `./      /   \      \,' /
    ###        \|/`.    /  |  \    ,'\|/        ,'       \ /`.    /  0  \    ,'\ /
    ###         \   `. /   |   \ ,'   /                   \   `. /       \ ,'   /
    ###          \    * v13|v22 *    /                     \    `---------'    /
    ###           \    \   |   /    /                       \    \       /    /
    ###            \    \  |  /    /                         \    \  0  /    /
    ###             \    \ | /    /                           \    \   /    /
    ###    tets[1]   \    \|/    /   tets[2]                   \    \ /    /
    ###               \    *    /                               \    *    / new_tet0
    ###                \..v01../                                 \...|.../
    ###                 \`.|.'/                                   \  |  /
    ###               v11\ | /v21                                  \ 3 /
    ###                   \|/                                       \|/
    ###                    *                                         *

    # permutations taking the vertices for a face of the 2-tet ball to the
    # vertices of the same face for the 3-tet ball

    # these should be even in order to preserve orientability.

    # perms = [[regina.Perm4( vertices[0][0], vertices[0][2], vertices[0][3], vertices[0][1] ),   ### opposite v00
    #           regina.Perm4( vertices[0][1], vertices[0][3], vertices[0][2], vertices[0][0] )    ### opposite v01
    #           ],
    #          [regina.Perm4( vertices[1][3], vertices[1][0], vertices[1][2], vertices[1][1] ),   ### opposite v10
    #           regina.Perm4( vertices[1][3], vertices[1][2], vertices[1][1], vertices[1][0] )    ### opposite v11
    #           ],
    #          [regina.Perm4( vertices[2][2], vertices[2][3], vertices[2][0], vertices[2][1] ),   ### opposite v20
    #           regina.Perm4( vertices[2][2], vertices[2][1], vertices[2][3], vertices[2][0] )    ### opposite v21
    #           ]
    #         ]

    perms = [
        [
            vertices[0] * regina.Perm4(0, 2, 3, 1),  ### opposite v00
            vertices[0] * regina.Perm4(1, 3, 2, 0)  ### opposite v01
        ],
        [
            vertices[1] * regina.Perm4(3, 0, 2, 1),  ### opposite v10
            vertices[1] * regina.Perm4(3, 2, 1, 0)  ### opposite v11
        ],
        [
            vertices[2] * regina.Perm4(2, 3, 0, 1),  ### opposite v20
            vertices[2] * regina.Perm4(2, 1, 3, 0)  ### opposite v21
        ]
    ]

    for i in range(3):
        for j in range(2):
            gluing = gluings[i][j]
            if gluing != None:
                if gluing[0] not in tets:  ### not a self gluing
                    gluing[1] = gluing[1] * perms[i][j]
                else:
                    i_other = tets.index(gluing[0])
                    otherfacenum = gluing[1][vertices[i][j]]
                    j_other = [vertices[i_other][k]
                               for k in range(4)].index(otherfacenum)
                    assert gluings[i_other][j_other][0] == tets[i]
                    assert gluings[i_other][j_other][1].inverse(
                    ) == gluings[i][j][1]

                    gluings[i_other][
                        j_other] = None  ### only do a self gluing from one side
                    gluing[0] = new_tets[
                        j_other]  ### j refers to the vertex on the same 3 side
                    gluing[1] = perms[i_other][j_other].inverse(
                    ) * gluing[1] * perms[i][j]

    ### unglue three tetrahedra
    for tet in tets:
        tet.isolate()

    ### remove the tetrahedra
    for tet in tets:
        tri.removeSimplex(tet)

    ### make the gluings on the boundary of the new ball
    for i in range(3):
        for j in range(2):
            if gluings[i][j] != None:
                if j == 0 or i == 0:
                    assert new_tets[j].adjacentTetrahedron(
                        i) == None  ## not glued
                    assert gluings[i][j][0].adjacentTetrahedron(
                        gluings[i][j][1][i]) == None
                    new_tets[j].join(i, gluings[i][j][0], gluings[i][j][1])
                else:
                    assert new_tets[j].adjacentTetrahedron(
                        3 - i) == None  ## not glued
                    assert gluings[i][j][0].adjacentTetrahedron(
                        gluings[i][j][1][3 - i]) == None
                    new_tets[j].join(3 - i, gluings[i][j][0],
                                     gluings[i][j][1])  ## swap 1 and 2

    assert tri.isIsomorphicTo(tri2)
    assert tri.isOriented()

    # ### update the branched surface
    tet_nums.sort()
    branch.pop(tet_nums[2])
    branch.pop(tet_nums[1])
    branch.pop(tet_nums[0])  ## remove from the list in the correct order!

    ### update the branched surface

    ### for each of the two new tetrahedra, figure out what their outer face train tracks are

    large_edges_new = []  ### record large edge info for the outer faces
    for i in range(3):
        this_tet_large_edges_new = []
        for j in range(2):
            new_large_edge = perms[i][j].inverse()[large_edges[i][j]]
            # assert new_large_edge != i ??
            this_tet_large_edges_new.append(new_large_edge)
        large_edges_new.append(this_tet_large_edges_new)

    large_edges_new_transposed = [list(i) for i in zip(*large_edges_new)]

    branch0 = determine_possible_branch_given_three_faces(
        [0, 1, 2], large_edges_new_transposed[0])
    branch1 = determine_possible_branch_given_three_faces(
        [0, 2, 1], large_edges_new_transposed[1])
    if branch0 == None or branch1 == None:
        return False
    large_edge_for_new_tet0 = large_edge_of_face(branch0, 3)
    large_edge_for_new_tet1 = large_edge_of_face(branch1, 3)
    if large_edge_for_new_tet0 == large_edge_for_new_tet1:
        if large_edge_for_new_tet0 != 0:
            return False
    else:
        if large_edge_for_new_tet0 + large_edge_for_new_tet1 != 3:  ### one must be 1, one must be 2
            return False

    branch.extend([branch0, branch1])

    # if not is_branched(tri, branch):
    if not has_non_sing_semiflow(tri, branch):
        return False

    if not return_triangle:
        return (tri, branch)
    else:
        return (tri, branch, new_tets[0].triangle(3).index())
Esempio n. 4
0
def twoThreeMove(tri, branch, face_num, perform=True, return_edge=False):
    """Apply a 2-3 move to a triangulation with a branched surface, if possible. 
    If perform = False, returns if the move is possible.
    If perform = True, modifies tri, returns (tri, possible_branches) for the performed move"""

    ### possible_branches is a list

    # assert is_branched(tri, branch)
    assert has_non_sing_semiflow(tri, branch)

    face = tri.triangle(face_num)

    embed0 = face.embedding(0)
    tet0 = embed0.simplex()
    tet_num0 = tet0.index()
    tet_0_face_num = embed0.face()
    vertices0 = embed0.vertices(
    )  # Maps vertices (0,1,2) of face to the corresponding vertex numbers of tet0

    embed1 = face.embedding(1)
    tet1 = embed1.simplex()
    tet_num1 = tet1.index()
    tet_1_face_num = embed1.face()
    vertices1 = embed1.vertices(
    )  # Maps vertices (0,1,2) of face to the corresponding vertex numbers of tet1

    if tet0 == tet1:  ### Cannot perform a 2-3 move across a self-gluing
        return False

    ### are all moves valid for the branched surface?
    ### for now, lets assume yes

    ### check we do the same as regina...
    tri2 = regina.Triangulation3(tri)  ## make a copy
    tri2.pachner(tri2.triangle(face_num))

    ### We have to implement twoThreeMove ourselves. e.g. we do a 2-3 move to canonical fig 8 knot complement triangulation.
    ### All of the original tetrahedra are removed. I don't see any way to carry the angle structure through without knowing
    ### exactly how Ben's implementation works.

    ## record the tetrahedra and gluings adjacent to tet0 and tet1

    tets = [tet0, tet1]
    vertices = [vertices0, vertices1]

    # print('2-3 vertices signs')
    # print([v.sign() for v in vertices])

    gluings = []
    for i in range(2):
        tet_gluings = []
        for j in range(3):
            tet_gluings.append([
                tets[i].adjacentTetrahedron(vertices[i][j]),
                tets[i].adjacentGluing(vertices[i][j])
            ])
            # if tets[i].adjacentTetrahedron(vertices[i][j]) in tets:
            #     print('self gluing')
        gluings.append(tet_gluings)

    large_edges = []  ### record large edge info for the outer faces
    for i in range(2):
        this_tet_large_edges = []
        for j in range(3):
            this_tet_large_edges.append(
                large_edge_of_face(branch[tets[i].index()], vertices[i][j]))
        large_edges.append(this_tet_large_edges)

    ### add new tetrahedra
    new_tets = []
    for i in range(3):
        new_tets.append(tri.newTetrahedron())

    ### glue around degree 3 edge
    for i in range(3):
        new_tets[i].join(2, new_tets[(i + 1) % 3], regina.Perm4(0, 1, 3, 2))

    ### replace mapping info with corresponding info for the 3 tet. Self gluings will be annoying...

    ### write verticesi[j] as vij

    ###                  tet0                                    new_tet0
    ###                _________                                 _________
    ###              ,'\       /`.                             ,'\`.   ,'/`.
    ###            ,'   \ v03 /   `.                         ,'   \ `0' /   `.
    ###          ,'      \   /      `.                     ,'      \ | /      `.
    ###         / \       \ /       / \                   /|\       \|/       /|\
    ###        /v02\       *       /v01\                 / | \       *       / | \
    ###       /    _\..... | ...../_    \               /  | 3\..... | ...../2 |  \
    ###      /_--"" /      *      \ ""--_\             /2 ,'  /      *      \  `. 3\
    ###      \`.v12/      / \      \v11,'/      `.     \`.|  /      /|\      \  |,'/
    ###       \ `./      /   \      \,' /     ----}     \ `./      / | \      \,' /
    ###        \ /`.    / v00 \    ,'\ /        ,'       \|/`.    /  |  \    ,'\|/
    ###         \   `. /       \ ,'   /                   \   `. /   |   \ ,'   /
    ###          \    `---------'    /                     \    * 3  |  2 *    /
    ###           \    \       /    /                       \    \   |   /    /
    ###            \    \ v10 /    /               new_tet1  \    \  |  /    /  new_tet2
    ###             \    \   /    /                           \    \ | /    /
    ###              \    \ /    /                             \    \|/    /
    ###               \    *    /                               \    *    /
    ###         tet1   \...|.../                                 \...|.../
    ###                 \  |  /                                   \`.|.'/
    ###                  \v13/                                     \ 1 /
    ###                   \|/                                       \|/
    ###                    *                                         *

    # permutations taking the vertices for a face of the 3-tet ball to the
    # vertices of the same face for the 2-tet ball

    # these should be even in order to preserve orientability.
    # exactly one of vertices[0] and vertices[1] is even, but it seems to depend on the face.

    # perms = [[regina.Perm4( vertices[0][3], vertices[0][0], vertices[0][1], vertices[0][2] ),   ### opposite v00
    #           regina.Perm4( vertices[0][3], vertices[0][1], vertices[0][2], vertices[0][0] ),   ### opposite v01
    #           regina.Perm4( vertices[0][3], vertices[0][2], vertices[0][0], vertices[0][1] )    ### opposite v02
    #           ],
    #          [regina.Perm4( vertices[1][0], vertices[1][3], vertices[1][1], vertices[1][2] ),   ### opposite v10
    #           regina.Perm4( vertices[1][1], vertices[1][3], vertices[1][2], vertices[1][0] ),   ### opposite v11
    #           regina.Perm4( vertices[1][2], vertices[1][3], vertices[1][0], vertices[1][1] )    ### opposite v12
    #           ]
    #         ]

    perms = [
        [
            vertices[0] * regina.Perm4(3, 0, 1, 2),  ### opposite v00
            vertices[0] * regina.Perm4(3, 1, 2, 0),  ### opposite v01
            vertices[0] * regina.Perm4(3, 2, 0, 1)  ### opposite v02
        ],
        [
            vertices[1] * regina.Perm4(0, 3, 1, 2),  ### opposite v10
            vertices[1] * regina.Perm4(1, 3, 2, 0),  ### opposite v11
            vertices[1] * regina.Perm4(2, 3, 0, 1)  ### opposite v12
        ]
    ]
    flip = perms[0][0].sign() == -1
    if flip:  #then all of the signs are wrong, switch 0 and 1 on input
        perms = [[p * regina.Perm4(1, 0, 2, 3) for p in a] for a in perms]
    #     print('flip')
    # else:
    #     print('no flip')

    # print('2-3 perms signs')
    # print([[p.sign() for p in a] for a in perms])

    for i in range(2):
        for j in range(3):
            gluing = gluings[i][j]
            if gluing != None:
                if gluing[0] not in tets:  ### not a self gluing
                    gluing[1] = gluing[1] * perms[i][j]
                else:
                    i_other = tets.index(gluing[0])
                    otherfacenum = gluing[1][vertices[i][j]]
                    j_other = [vertices[i_other][k]
                               for k in range(4)].index(otherfacenum)
                    assert gluings[i_other][j_other][0] == tets[i]
                    assert gluings[i_other][j_other][1].inverse(
                    ) == gluings[i][j][1]

                    gluings[i_other][
                        j_other] = None  ### only do a self gluing from one side
                    gluing[0] = new_tets[j_other]
                    gluing[1] = perms[i_other][j_other].inverse(
                    ) * gluing[1] * perms[i][j]

    ### unglue two tetrahedra
    tet0.isolate()
    tet1.isolate()

    ### remove the tetrahedra
    tri.removeSimplex(tet0)
    tri.removeSimplex(tet1)

    ### make the gluings on the boundary of the new ball
    for i in range(2):
        for j in range(3):
            if gluings[i][j] != None:
                if flip:
                    new_tets[j].join(i, gluings[i][j][0], gluings[i][j][1])
                else:
                    new_tets[j].join(1 - i, gluings[i][j][0], gluings[i][j][1])

    assert tri.isIsomorphicTo(tri2)
    assert tri.isOriented()

    ### update the branched surface

    ### for each of the three new tetrahedra, figure out what their outer face train tracks are

    large_edges_new = []  ### record large edge info for the outer faces
    for i in range(2):
        this_tet_large_edges_new = []
        for j in range(3):
            new_large_edge = perms[i][j].inverse()[large_edges[i][j]]
            if flip:
                assert new_large_edge != i  ### the face number cannot be the large vertex for that face
            else:
                assert new_large_edge != 1 - i
            this_tet_large_edges_new.append(new_large_edge)
        large_edges_new.append(this_tet_large_edges_new)

    candidate_branches = []
    for j in range(3):
        if flip:
            candidate_branches.append(
                determine_possible_branch_given_two_faces(
                    (0, 1), (large_edges_new[0][j], large_edges_new[1][j])))
        else:
            candidate_branches.append(
                determine_possible_branch_given_two_faces(
                    (1, 0), (large_edges_new[0][j], large_edges_new[1][j])))

    ### update the branch structure, many possible ways
    tet_indices = [tet_num0, tet_num1]
    tet_indices.sort()
    branch.pop(tet_indices[1])
    branch.pop(tet_indices[0])  ## remove from the list in the correct order!

    out = []
    for cand0 in candidate_branches[0]:
        for cand1 in candidate_branches[1]:
            for cand2 in candidate_branches[2]:
                candidate = branch[:] + [cand0, cand1, cand2]
                # print('candidate', candidate)
                # if is_branched(tri, candidate):
                if has_non_sing_semiflow(tri, candidate):
                    out.append(candidate)
    # assert len(out) > 0  ### this works if we check is_branched three lines above, but not if we check has_non_sing_semiflow
    if len(
            out
    ) == 0:  ### with has_non_sing_semiflow instead, we might not get any
        return False
    if not return_edge:
        return (tri, out)
    else:
        return (tri, out, new_tets[0].edge(0).index())
Esempio n. 5
0
def drill(tri,
          loop,
          angle=None,
          branch=None,
          sig=None):  # sig just for diagnostics
    """
    Returns the new cusp formed by drilling
    """
    if angle != None:
        face_coorientations = is_transverse_taut(
            tri, angle, return_type="face_coorientations")
        assert face_coorientations != False
        tet_vert_coorientations = is_transverse_taut(
            tri, angle, return_type="tet_vert_coorientations")
        assert tet_vert_coorientations != False

    original_tri = regina.Triangulation3(tri)

    original_countTetrahedra = tri.countTetrahedra()
    original_countBoundaryComponents = tri.countBoundaryComponents()
    ### add new tetrahedra
    new_0_tets = []
    new_1_tets = []  ## both relative to regina's two embeddings for the face
    for i in range(len(loop)):
        new_0_tets.append(tri.newTetrahedron())
        new_1_tets.append(tri.newTetrahedron())

    ### we will glue tetrahedra together with a numbering that is convenient but
    ### unfortunately not oriented. We will orient later.

    #           1  pivot
    #          /|\
    #         / | \
    #        / ,3. \
    #       /,'   `.\
    #      0---------2 leading
    # trailing

    for i in range(len(loop)):
        new_0_tets[i].join(1, new_1_tets[i], regina.Perm4(0, 1, 2, 3))

    ### now glue along the loop path, need to worry about regina's embeddings for neighbouring triangles

    loop_face_tets0 = []
    loop_face_vertices0 = []
    loop_face_tets1 = []
    loop_face_vertices1 = [
    ]  ### need to store these because they cannot be recomputed once we start ungluing faces

    for i in range(len(loop)):
        # print('collect info: i', i)
        face_data = loop[i]
        face_index = face_data[0]
        vert_nums = face_data[1]
        face = tri.triangles()[face_index]
        face_embed0 = face.embedding(0)
        face_tet = face_embed0.simplex()
        face_vertices = face_embed0.vertices()
        face_opposite_vert = face_vertices[3]
        face_other_non_edge_vert = face_vertices[
            vert_nums[0]]  ## opposite trailing vertex

        face_data_next = loop[(i + 1) % len(loop)]
        face_index_next = face_data_next[0]
        vert_nums_next = face_data_next[1]
        face_next = tri.triangles()[face_index_next]
        face_next_embed0 = face_next.embedding(0)
        face_next_tet = face_next_embed0.simplex()
        face_next_vertices = face_next_embed0.vertices()
        face_next_opposite_vert = face_next_vertices[3]
        face_next_other_non_edge_vert = face_next_vertices[
            vert_nums_next[2]]  ## opposite leading vertex

        ### things to store for later

        loop_face_tets0.append(face_tet)  # store for later
        loop_face_vertices0.append(face_vertices)  #store for later
        face_embed1 = face.embedding(1)
        loop_face_tets1.append(face_embed1.simplex())
        loop_face_vertices1.append(face_embed1.vertices())

        edge = face.edge(vert_nums[0])  ## opposite trailing vertex
        # print('edge index', edge.index())
        assert edge == face_next.edge(
            vert_nums_next[2])  ## opposite leading vertex

        edgemapping = face.faceMapping(1, vert_nums[0])
        next_edgemapping = face_next.faceMapping(1, vert_nums_next[2])
        face_gluing_regina_numbering = next_edgemapping * (
            edgemapping.inverse()
        )  ### maps vertices 0,1,2 on face to corresponding vertices on face_next
        assert face_gluing_regina_numbering[3] == 3
        face_gluing_regina_numbering = regina.Perm3(
            face_gluing_regina_numbering[0], face_gluing_regina_numbering[1],
            face_gluing_regina_numbering[2])
        assert face_gluing_regina_numbering[vert_nums[0]] == vert_nums_next[2]
        face_gluing = vert_nums_next.inverse(
        ) * face_gluing_regina_numbering * vert_nums
        assert face_gluing[0] == 2

        signs = []
        edge_embeddings = edge.embeddings()
        for embed in edge_embeddings:
            if embed.simplex() == face_tet:
                if set([embed.vertices()[2],
                        embed.vertices()[3]]) == set(
                            [face_opposite_vert, face_other_non_edge_vert]):
                    # print('embed data face_tet', embed.simplex().index(), embed.vertices())
                    # print('embed edge vert nums', embed.vertices()[0], embed.vertices()[1])
                    signs.append(face_opposite_vert == embed.vertices()[2])
            if embed.simplex() == face_next_tet:
                if set([embed.vertices()[2],
                        embed.vertices()[3]]) == set([
                            face_next_opposite_vert,
                            face_next_other_non_edge_vert
                        ]):
                    # print('embed data face_next_tet', embed.simplex().index(), embed.vertices())
                    # print('embed edge vert nums', embed.vertices()[0], embed.vertices()[1])
                    signs.append(
                        face_next_opposite_vert == embed.vertices()[2])
        # print('signs', signs)
        assert len(signs) == 2
        if signs[0] == signs[
                1]:  ### coorientations are same around the edge (not a transverse taut coorientation!)
            new_0_tets[i].join(
                0, new_1_tets[(i + 1) % len(loop)],
                regina.Perm4(2, face_gluing[1], face_gluing[2], 3))
            new_1_tets[i].join(
                0, new_0_tets[(i + 1) % len(loop)],
                regina.Perm4(2, face_gluing[1], face_gluing[2], 3))
        else:
            new_0_tets[i].join(
                0, new_0_tets[(i + 1) % len(loop)],
                regina.Perm4(2, face_gluing[1], face_gluing[2], 3))
            new_1_tets[i].join(
                0, new_1_tets[(i + 1) % len(loop)],
                regina.Perm4(2, face_gluing[1], face_gluing[2], 3))

    ### now unglue tri along the loop and glue in the new tetrahedra

    for i in range(len(loop)):
        # print('modify triangulation: i', i)
        vert_nums = loop[i][1]

        face_tet0 = loop_face_tets0[i]
        face_vertices0 = loop_face_vertices0[i]
        face_tet1 = loop_face_tets1[i]
        face_vertices1 = loop_face_vertices1[i]

        face_opposite_vert0 = face_vertices0[3]
        face_tet0.unjoin(face_opposite_vert0)
        ### glue torus shell to the old tetrahedra

        vert_nums_Perm4 = regina.Perm4(vert_nums[0], vert_nums[1],
                                       vert_nums[2], 3)
        new_0_tets[i].join(3, face_tet0, face_vertices0 * vert_nums_Perm4)
        new_1_tets[i].join(3, face_tet1, face_vertices1 * vert_nums_Perm4)

    assert tri.isValid()
    assert tri.countBoundaryComponents(
    ) == original_countBoundaryComponents + 1

    if angle != None:
        for i in range(len(loop)):
            face_data = loop[i]
            face_index = face_data[0]
            face = original_tri.triangles()[face_index]
            vert_nums = face_data[1]

            face_embed0 = face.embedding(0)
            face_tet = face_embed0.simplex()
            face_vertices = face_embed0.vertices()
            face_opposite_vert = face_vertices[3]
            coor_points_out_of_tet0 = (tet_vert_coorientations[
                face_tet.index()][face_opposite_vert] == +1)

            # if (flow_agrees_with_regina_numbers != face_cor_agrees_with_regina_numbers) != coor_points_out_of_tet0:
            if coor_points_out_of_tet0:
                angle.extend([0, 2])
            else:
                angle.extend([2, 0])

        # print(sig, loop, angle, is_taut(tri, angle))
        assert is_taut(tri, angle)

    if branch != None:
        for i in range(len(loop)):
            branch.extend([1, 1])

        assert is_branched(tri, branch)

    M = snappy.Manifold(tri)
    if M.volume() < 1.0:
        print('not hyperbolic', sig, loop, angle, M.volume())
        assert False
        # print(M.verify_hyperbolicity())  ### very slow
        # print(M.volume())
        # assert M.volume() > 1.0 ###

    ### now orient
    swaps = [regina.Perm4()
             ] * original_countTetrahedra  ### identity permutations
    for i in range(len(loop)):
        if new_0_tets[i].adjacentGluing(3).sign() == 1:
            if angle != None:
                this_tet_angle = angle[original_countTetrahedra + 2 * i]
            else:
                this_tet_angle = 0  ### pi_location = 0 is an arbitrary choice
            swaps.append(
                reverse_tet_orientation(tri, new_0_tets[i], this_tet_angle))
        else:
            swaps.append(regina.Perm4())
        if new_1_tets[i].adjacentGluing(3).sign() == 1:
            if angle != None:
                this_tet_angle = angle[original_countTetrahedra + 2 * i + 1]
            else:
                this_tet_angle = 0  ### pi_location = 0 is an arbitrary choice
            swaps.append(
                reverse_tet_orientation(tri, new_1_tets[i], this_tet_angle))
        else:
            swaps.append(regina.Perm4())
    assert tri.isOriented()

    if angle != None:
        assert is_taut(tri, angle)
    if branch != None:
        # print('loop, branch, swaps', loop, branch, swaps)
        apply_swaps_to_branched_surface(branch, swaps)
        # print('loop, branch, swaps', loop, branch, swaps)
        assert is_branched(tri, branch)
        assert has_non_sing_semiflow(tri, branch)

    ### return the vertex of the triangulation corresponding to the drilled cusp
    drilled_cusp = new_0_tets[0].vertex(swaps[new_0_tets[0].index()][3])
    assert drilled_cusp.degree() == len(new_0_tets) + len(new_1_tets)
    return drilled_cusp.index()
Esempio n. 6
0
def threeTwoMove(tri, edge_num, angle = None, branch = None, perform = True, return_triangle = False, return_vertex_perm = False):
    """Apply a 3-2 move to a triangulation with a taut structure and/or branched surface, if possible. 
    If perform = False, returns if the move is possible.
    modifies tri, returns (tri, angle, branch) for the performed move.
    If return_vertex_perm, tells you how the vertices of the old triangulation correspond to the vertices of the new.
    If return_edge_consequences, tells you what happened to the edges: if an edge e survived then edge_consequences[e.index()] gives you the new index, if not then it returns None."""

    ### perform = True isn't yet implemented for branch

    ### note if branch != None and this function does not return False, then there is only one possible branch 

    # if branch != None:
    #     assert has_non_sing_semiflow(tri, branch)   ### we are checking this on the output of pachner moves so we don't need to check it here

    edge = tri.edge(edge_num)
    if edge.degree() != 3:
        return False

    tets = []
    tet_nums = []
    vertices = []
    if angle != None:
        non_pi_tet_num = None
    for i in range(3):
        embed = edge.embedding(i)
        tets.append(embed.simplex())
        tet_nums.append(tets[i].index())
        vertices.append(embed.vertices())
        if angle != None:
            if not there_is_a_pi_here(angle, embed):
                assert non_pi_tet_num == None
                non_pi_tet_num = embed.simplex().index()
                local_non_pi_tet_num = i
    tet_nums.sort()

    if len(set([tet.index() for tet in tets])) != 3: 
        return False  ### tetrahedra must be distinct
     
    if branch == None and not perform:
        return True  ### taut 3-2 move is always possible if the 3-2 move is.

    if angle != None:
        ### record the "slope" of the pis on the non_pi_tet. This is a boolean
        non_pi_tet_positive = unsorted_vert_pair_to_edge_pair[ ( vertices[local_non_pi_tet_num][0], vertices[local_non_pi_tet_num][2] ) ]
        is_positive_slope = (angle[non_pi_tet_num] == non_pi_tet_positive)

    ### check we do the same as regina... 
    tri2 = regina.Triangulation3(tri)  ## make a copy
    tri2.pachner(tri2.edge(edge_num))

    if return_vertex_perm:
        vertex_representatives = []
        for c in tri.vertices():
            embed = c.embedding(0)
            vertex_representatives.append((embed.simplex(), embed.face())) ### pair (tet, vert_num_of_that_tet)
        ### for testing:
        vertex_degrees = [v.degree() for v in tri.vertices()]

    ## record the tetrahedra and gluings adjacent to the tets 

    gluings = [] 
    for i in range(3):
        tet_gluings = []
        for j in range(2):
            tet_gluings.append( [ tets[i].adjacentTetrahedron(vertices[i][j]),  tets[i].adjacentGluing(vertices[i][j])] )
        gluings.append(tet_gluings)

    for i in range(3):
        assert tets[i].adjacentTetrahedron(vertices[i][2]) == tets[(i+1)%3]  ### The edge embeddings should be ordered this way...

    if branch != None:
        large_edges = []  ### record large edge info for the outer faces
        for i in range(3):
            this_tet_large_edges = []
            for j in range(2):
                this_tet_large_edges.append(large_edge_of_face( branch[tets[i].index()], vertices[i][j] ))
            large_edges.append(this_tet_large_edges)

    ### add new tetrahedra
    new_tets = []
    for i in range(2):
        new_tets.append(tri.newTetrahedron())

    ### glue across face
    new_tets[0].join(3, new_tets[1], regina.Perm4(0,2,1,3))

    ### replace mapping info with corresponding info for the 2 tet. Self gluings will be annoying...

    ### write vertices[i][j] as vij

    ###                 tets[0]                                   new_tet1
    ###                _________                                 _________
    ###              ,'\`.v00,'/`.                             ,'\       /`.
    ###            ,'   \ `.' /   `.                         ,'   \  3  /   `. 
    ###          ,'   v10\ | /v20   `.                     ,'      \   /      `.
    ###         /|\       \|/       /|\                   / \       \ /       / \
    ###        / | \       *       / | \                 /   \       *       /   \
    ###    v12/  |  \..... | ...../  |  \v23            /  1 _\..... | ...../_ 2  \ 
    ###      /  ,'  /      *      \  `.  \             /_--"" /      *      \ ""--_\
    ###      \`.|  /v03   /|\   v02\  |,'/      `.     \`. 2 /      / \      \ 1 ,'/ 
    ###       \ `./      / | \      \,' /     ----}     \ `./      /   \      \,' /
    ###        \|/`.    /  |  \    ,'\|/        ,'       \ /`.    /  0  \    ,'\ /
    ###         \   `. /   |   \ ,'   /                   \   `. /       \ ,'   /
    ###          \    * v13|v22 *    /                     \    `---------'    /
    ###           \    \   |   /    /                       \    \       /    /
    ###            \    \  |  /    /                         \    \  0  /    /
    ###             \    \ | /    /                           \    \   /    /  
    ###    tets[1]   \    \|/    /   tets[2]                   \    \ /    /
    ###               \    *    /                               \    *    / new_tet0
    ###                \..v01../                                 \...|.../
    ###                 \`.|.'/                                   \  |  /
    ###               v11\ | /v21                                  \ 3 /
    ###                   \|/                                       \|/
    ###                    *                                         *

    # permutations taking the vertices for a face of the 2-tet ball to the 
    # vertices of the same face for the 3-tet ball

    # these should be even in order to preserve orientability.

    # perms = [[regina.Perm4( vertices[0][0], vertices[0][2], vertices[0][3], vertices[0][1] ),   ### opposite v00
    #           regina.Perm4( vertices[0][1], vertices[0][3], vertices[0][2], vertices[0][0] )    ### opposite v01
    #           ],
    #          [regina.Perm4( vertices[1][3], vertices[1][0], vertices[1][2], vertices[1][1] ),   ### opposite v10
    #           regina.Perm4( vertices[1][3], vertices[1][2], vertices[1][1], vertices[1][0] )    ### opposite v11
    #           ],
    #          [regina.Perm4( vertices[2][2], vertices[2][3], vertices[2][0], vertices[2][1] ),   ### opposite v20
    #           regina.Perm4( vertices[2][2], vertices[2][1], vertices[2][3], vertices[2][0] )    ### opposite v21
    #           ]
    #         ]

    perms = [[vertices[0] * regina.Perm4( 0, 2, 3, 1 ),   ### opposite v00
              vertices[0] * regina.Perm4( 1, 3, 2, 0 )    ### opposite v01
              ],
             [vertices[1] * regina.Perm4( 3, 0, 2, 1 ),   ### opposite v10
              vertices[1] * regina.Perm4( 3, 2, 1, 0 )    ### opposite v11
              ],
             [vertices[2] * regina.Perm4( 2, 3, 0, 1 ),   ### opposite v20
              vertices[2] * regina.Perm4( 2, 1, 3, 0 )    ### opposite v21
              ]
            ]

    for i in range(3):
        for j in range(2):
            gluing = gluings[i][j]
            if gluing != None:
                if gluing[0] not in tets:  ### not a self gluing
                    gluing[1] = gluing[1] * perms[i][j]
                else:
                    i_other = tets.index( gluing[0] )
                    otherfacenum = gluing[1][vertices[i][j]]
                    j_other = [vertices[i_other][k] for k in range(4)].index(otherfacenum) 
                    assert gluings[i_other][j_other][0] == tets[i]
                    assert gluings[i_other][j_other][1].inverse() == gluings[i][j][1]

                    gluings[i_other][j_other] = None ### only do a self gluing from one side 
                    gluing[0] = new_tets[j_other]  ### j refers to the vertex on the same 3 side
                    gluing[1] = perms[i_other][j_other].inverse() * gluing[1] * perms[i][j] 

    if return_vertex_perm:
        new_vertex_representatives = []
        for (tet, vert_num) in vertex_representatives:
            if tet in tets:
                which_tet = tets.index(tet)
                vert_num = vertices[which_tet].inverse()[vert_num]
                if vert_num == 0:
                    new_vertex_representatives.append((new_tets[1], 3))
                elif vert_num == 1:
                    new_vertex_representatives.append((new_tets[0], 3))
                elif vert_num == 2:
                    new_vertex_representatives.append((new_tets[0], (which_tet + 1) % 3 ))
                elif vert_num == 3:
                    new_vertex_representatives.append((new_tets[0], (which_tet - 1) % 3 ))
            else:
                new_vertex_representatives.append((tet, vert_num)) ### not changed by the move
        # ### for testing:
        polar_cusp_indices = [ tets[0].vertex( vertices[0][0] ).index(), tets[0].vertex( vertices[0][1] ).index() ]

    ### unglue three tetrahedra
    for tet in tets:
        tet.isolate()

    ### remove the tetrahedra
    for tet in tets:
        tri.removeSimplex(tet)

    ### make the gluings on the boundary of the new ball
    for i in range(3):
        for j in range(2):
            if gluings[i][j] != None:
                if j == 0 or i == 0:
                    assert new_tets[j].adjacentTetrahedron(i) == None ## not glued
                    assert gluings[i][j][0].adjacentTetrahedron(gluings[i][j][1][i]) == None
                    new_tets[j].join(i, gluings[i][j][0], gluings[i][j][1])
                else:
                    assert new_tets[j].adjacentTetrahedron(3 - i) == None ## not glued
                    assert gluings[i][j][0].adjacentTetrahedron(gluings[i][j][1][3 - i]) == None
                    new_tets[j].join(3 - i, gluings[i][j][0], gluings[i][j][1])  ## swap 1 and 2

    assert tri.isIsomorphicTo(tri2)
    assert tri.isOriented()

    if angle != None:
        ### update the angle structure
        angle.pop(tet_nums[2])
        angle.pop(tet_nums[1])
        angle.pop(tet_nums[0])  ## remove from the list in the correct order!

        if local_non_pi_tet_num == 0:
            if is_positive_slope:
                new_angle = [0, 0]
            else:
                new_angle = [1, 1]
        elif local_non_pi_tet_num == 1:
            if is_positive_slope:
                new_angle = [2, 1]
            else:
                new_angle = [0, 2]
        else:
            assert local_non_pi_tet_num == 2
            if is_positive_slope:
                new_angle = [1, 2]
            else:
                new_angle = [2, 0]
        
        angle.extend(new_angle)

        assert is_taut(tri, angle)

    if branch != None:
        ### update the branched surface
        branch.pop(tet_nums[2])
        branch.pop(tet_nums[1])
        branch.pop(tet_nums[0])  ## remove from the list in the correct order!

        ### for each of the two new tetrahedra, figure out what their outer face train tracks are

        large_edges_new = []  ### record large edge info for the outer faces
        for i in range(3):
            this_tet_large_edges_new = []
            for j in range(2):
                new_large_edge = perms[i][j].inverse()[ large_edges[i][j] ]
                this_tet_large_edges_new.append( new_large_edge )
            large_edges_new.append(this_tet_large_edges_new)

        large_edges_new_transposed = [list(i) for i in zip(*large_edges_new)]

        branch0 = determine_possible_branch_given_three_faces([0,1,2], large_edges_new_transposed[0])
        branch1 = determine_possible_branch_given_three_faces([0,2,1], large_edges_new_transposed[1])
        if branch0 == None or branch1 == None:
            return False
        large_edge_for_new_tet0 = large_edge_of_face( branch0, 3 )
        large_edge_for_new_tet1 = large_edge_of_face( branch1, 3 )
        if large_edge_for_new_tet0 == large_edge_for_new_tet1:
            if large_edge_for_new_tet0 != 0:
                return False
        else:
            if large_edge_for_new_tet0 + large_edge_for_new_tet1 != 3:  ### one must be 1, one must be 2
                return False

        branch.extend([branch0, branch1])

        assert is_branched(tri, branch)
        if not has_non_sing_semiflow(tri, branch):
            return False

    if return_vertex_perm:
        vertex_permutation = []
        for (tet, vert_num) in new_vertex_representatives:
            vertex_permutation.append(tet.vertex(vert_num).index())
        ### note that Regina's permutations can have up to 16 entries, lets just use lists for this
        ### for testing:
        new_vertex_degrees = [v.degree() for v in tri.vertices()]
        new_vertex_degrees_pulled_back = [new_vertex_degrees[vertex_permutation[i]] for i in range(len(vertex_degrees))]
        #if new_vertex_degrees != new_vertex_degrees_pulled_back:
        # print(vertex_permutation)
        # if vertex_permutation != list(range(len(new_vertex_representatives))):
        #     print('3-2 permuted vertices', vertex_permutation, new_vertex_degrees, new_vertex_degrees_pulled_back, vertex_degrees)
        for i in polar_cusp_indices:
            vertex_degrees[i] -= 2
        assert vertex_degrees == new_vertex_degrees_pulled_back
        # print(new_vertex_degrees_pulled_back)

    output = [tri]
    if angle != None:
        output.append(angle)
    if branch != None:
        output.append(branch)      
    if return_triangle:
        output.append(new_tets[0].triangle(3).index())
    if return_vertex_perm:
        output.append(vertex_permutation)
    return output
Esempio n. 7
0
def twoThreeMove(tri, face_num, angle = None, branch = None, perform = True, return_edge = False, return_vertex_perm = False, return_edge_consequences = False):
    """Apply a 2-3 move to a triangulation with a taut structure and/or branched surface, if possible. 
    If perform = False, returns if the move is possible.
    If perform = True, modifies tri, returns (tri, angle, possible_branches) for the performed move
    If return_edge, tells you the index of the newly created edge in the triangulation.
    If return_vertex_perm, tells you how the vertices of the old triangulation correspond to the vertices of the new.
    If return_edge_consequences, tells you what happened to the edges: edge_consequences[e.index()] gives you the new index."""

    ### possible_branches is a list
    
    # if branch != None:
    #     assert has_non_sing_semiflow(tri, branch)    ### we are checking this on the output of pachner moves so we don't need to check it here
        ## Joe Christy says [p764, Branched surfaces and attractors I: Dynamic Branched Surfaces] that if the branched surface carries the stable lamination of a pseudo-Anosov flow then it has a non singular semi flow
        ## We hope that we can move to a veering triangulation through such branched surfaces

    face = tri.triangle(face_num)

    embed0 = face.embedding(0)
    tet0 = embed0.simplex()
    tet_num0 = tet0.index()
    tet_0_face_num = embed0.face()
    vertices0 = embed0.vertices() # Maps vertices (0,1,2) of face to the corresponding vertex numbers of tet0

    embed1 = face.embedding(1)
    tet1 = embed1.simplex()
    tet_num1 = tet1.index()
    tet_1_face_num = embed1.face()
    vertices1 = embed1.vertices() # Maps vertices (0,1,2) of face to the corresponding vertex numbers of tet1

    if tet0 == tet1:  ### Cannot perform a 2-3 move across a self-gluing
        return False

    if angle != None:
        ### taut 2-3 move is valid if the pis are on different edges of face
        ### this never happens if we start with a veering triangulation.
        ### for veering, the two-tetrahedron ball is always a continent.

        for i in range(3):
            j = (i+1) % 3
            k = (i+2) % 3
            if angle[tet_num0] == unsorted_vert_pair_to_edge_pair[(vertices0[j], vertices0[k])]:
                pi_num_0 = i
            if angle[tet_num1] == unsorted_vert_pair_to_edge_pair[(vertices1[j], vertices1[k])]:
                pi_num_1 = i
        if pi_num_0 == pi_num_1:
            return False
        if perform == False:
            return True

    ### are all moves valid for the branched surface?
    ### for now, lets assume yes

    ### check we do the same as regina... 
    tri2 = regina.Triangulation3(tri)  ## make a copy
    tri2.pachner(tri2.triangle(face_num))

    if return_vertex_perm:
        vertex_representatives = []
        for c in tri.vertices():
            embed = c.embedding(0)
            vertex_representatives.append((embed.simplex(), embed.face())) ### pair (tet, vert_num_of_that_tet)
        ### for testing:
        vertex_degrees = [v.degree() for v in tri.vertices()]

    if return_edge_consequences:
        edge_representatives = []
        for e in tri.edges():
            embed = e.embedding(0)
            ### HERE
            ##edge_representatives.append((embed.simplex(), embed.face())) ### pair (tet, vert_num_of_that_tet)

    ### We have to implement twoThreeMove ourselves. e.g. we do a 2-3 move to canonical fig 8 knot complement triangulation. 
    ### All of the original tetrahedra are removed. I don't see any way to carry the angle structure through without knowing
    ### exactly how Ben's implementation works.

    ## record the tetrahedra and gluings adjacent to tet0 and tet1

    tets = [tet0, tet1]
    vertices = [vertices0, vertices1]

    gluings = [] 
    for i in range(2):
        tet_gluings = []
        for j in range(3):
            tet_gluings.append( [ tets[i].adjacentTetrahedron(vertices[i][j]),  tets[i].adjacentGluing(vertices[i][j])] )
            # if tets[i].adjacentTetrahedron(vertices[i][j]) in tets:
            #     print('self gluing')
        gluings.append(tet_gluings)

    if branch != None:
        large_edges = []  ### record large edge info for the outer faces
        for i in range(2):
            this_tet_large_edges = []
            for j in range(3):
                this_tet_large_edges.append(large_edge_of_face( branch[tets[i].index()], vertices[i][j] ))
            large_edges.append(this_tet_large_edges)

    ### add new tetrahedra
    new_tets = []
    for i in range(3):
        new_tets.append(tri.newTetrahedron())

    ### glue around degree 3 edge
    for i in range(3):
        new_tets[i].join(2, new_tets[(i+1)%3], regina.Perm4(0,1,3,2))

    ### replace mapping info with corresponding info for the 3 tet. Self gluings will be annoying...

    ### write verticesi[j] as vij

    ###                  tet0                                    new_tets[0]
    ###                _________                                 _________
    ###              ,'\       /`.                             ,'\`.   ,'/`.
    ###            ,'   \ v03 /   `.                         ,'   \ `0' /   `. 
    ###          ,'      \   /      `.                     ,'      \ | /      `.
    ###         / \       \ /       / \                   /|\       \|/       /|\
    ###        /v02\       *       /v01\                 / | \       *       / | \
    ###       /    _\..... | ...../_    \               /  | 3\..... | ...../2 |  \ 
    ###      /_--"" /      *      \ ""--_\             /2 ,'  /      *      \  `. 3\
    ###      \`.v12/      / \      \v11,'/      `.     \`.|  /      /|\      \  |,'/ 
    ###       \ `./      /   \      \,' /     ----}     \ `./      / | \      \,' /
    ###        \ /`.    / v00 \    ,'\ /        ,'       \|/`.    /  |  \    ,'\|/
    ###         \   `. /       \ ,'   /                   \   `. /   |   \ ,'   /
    ###          \    `---------'    /                     \    * 3  |  2 *    /
    ###           \    \       /    /                       \    \   |   /    /
    ###            \    \ v10 /    /            new_tets[1]  \    \  |  /    /  new_tets[2]
    ###             \    \   /    /                           \    \ | /    /  
    ###              \    \ /    /                             \    \|/    /
    ###               \    *    /                               \    *    /
    ###         tet1   \...|.../                                 \...|.../
    ###                 \  |  /                                   \`.|.'/
    ###                  \v13/                                     \ 1 /
    ###                   \|/                                       \|/
    ###                    *                                         *


    # permutations taking the vertices for a face of the 3-tet ball to the 
    # vertices of the same face for the 2-tet ball

    # these should be even in order to preserve orientability.
    # exactly one of vertices[0] and vertices[1] is even, but it seems to depend on the face.

    perms = [[vertices[0] * regina.Perm4( 3,0,1,2 ),   ### opposite v00
              vertices[0] * regina.Perm4( 3,1,2,0 ),   ### opposite v01
              vertices[0] * regina.Perm4( 3,2,0,1 )    ### opposite v02
              ],  
             [vertices[1] * regina.Perm4( 0,3,1,2 ),   ### opposite v10
              vertices[1] * regina.Perm4( 1,3,2,0 ),   ### opposite v11
              vertices[1] * regina.Perm4( 2,3,0,1 )    ### opposite v12
              ]
            ]
    flip = perms[0][0].sign() == -1
    if flip:  #then all of the signs are wrong, switch 0 and 1 on input
        perms = [[p * regina.Perm4( 1,0,2,3 ) for p in a] for a in perms]

    for i in range(2):
        for j in range(3):
            gluing = gluings[i][j]
            if gluing != None:
                if gluing[0] not in tets:  ### not a self gluing
                    gluing[1] = gluing[1] * perms[i][j]
                else:
                    i_other = tets.index( gluing[0] )
                    otherfacenum = gluing[1][vertices[i][j]]
                    j_other = [vertices[i_other][k] for k in range(4)].index(otherfacenum)
                    assert gluings[i_other][j_other][0] == tets[i]
                    assert gluings[i_other][j_other][1].inverse() == gluings[i][j][1]

                    gluings[i_other][j_other] = None ### only do a self gluing from one side 
                    gluing[0] = new_tets[j_other]
                    gluing[1] = perms[i_other][j_other].inverse() * gluing[1] * perms[i][j] 

    if return_vertex_perm:
        new_vertex_representatives = []
        for (tet, vert_num) in vertex_representatives:
            if tet == tet0:
                triangle_vert_num = vertices[0].inverse()[vert_num]
                if triangle_vert_num == 3:
                    if flip:
                        new_vertex_representatives.append((new_tets[0], 1))
                    else:
                        new_vertex_representatives.append((new_tets[0], 0))
                else:
                    new_vertex_representatives.append((new_tets[(triangle_vert_num + 1) % 3], 3))
            elif tet == tet1:
                triangle_vert_num = vertices[1].inverse()[vert_num]
                if triangle_vert_num == 3:
                    if flip:
                        new_vertex_representatives.append((new_tets[0], 0))
                    else:
                        new_vertex_representatives.append((new_tets[0], 1))
                else:
                    new_vertex_representatives.append((new_tets[(triangle_vert_num + 1) % 3], 3))
            else:
                new_vertex_representatives.append((tet, vert_num)) ### not changed by the move
        ### for testing:
        polar_cusp_indices = [ tet0.vertex( vertices[0][3] ).index(), tet1.vertex( vertices[1][3] ).index() ]

    ### unglue two tetrahedra
    tet0.isolate()
    tet1.isolate()

    ### remove the tetrahedra
    tri.removeSimplex(tet0)
    tri.removeSimplex(tet1)

    ### make the gluings on the boundary of the new ball
    for i in range(2):
        for j in range(3):
            if gluings[i][j] != None:
                if flip:
                    new_tets[j].join(i, gluings[i][j][0], gluings[i][j][1])
                else:
                    new_tets[j].join(1 - i, gluings[i][j][0], gluings[i][j][1])

    assert tri.isIsomorphicTo(tri2)
    assert tri.isOriented()

    if angle != None:
        ### update the angle structure
        tet_indices = [tet_num0, tet_num1]
        tet_indices.sort()
        angle.pop(tet_indices[1])
        angle.pop(tet_indices[0])  ## remove from the list in the correct order!

        new_angle = [None, None, None]
        new_angle[pi_num_0] = 0
        new_angle[pi_num_1] = 0 ### these two tetrahedra have their pi's on the new degree three edge

        third_index = 3 - (pi_num_0 + pi_num_1)
        if (pi_num_0 - third_index) % 3 == 1:
            new_angle[third_index] = 1
        else:
            assert (pi_num_0 - third_index) % 3 == 2
            new_angle[third_index] = 2
        if flip:
            new_angle[third_index] = 3 - new_angle[third_index]
        
        angle.extend(new_angle)

        assert is_taut(tri, angle)

    if branch != None:
        ### update the branched surface
        ### for each of the three new tetrahedra, figure out what their outer face train tracks are

        large_edges_new = []  ### record large edge info for the outer faces
        for i in range(2):
            this_tet_large_edges_new = []
            for j in range(3):
                new_large_edge = perms[i][j].inverse()[ large_edges[i][j] ]
                if flip:
                    assert new_large_edge != i ### the face number cannot be the large vertex for that face
                else:
                    assert new_large_edge != 1 - i 
                this_tet_large_edges_new.append( new_large_edge )
            large_edges_new.append(this_tet_large_edges_new)

        candidate_branches = []
        for j in range(3):
            if flip:   
                candidate_branches.append( determine_possible_branch_given_two_faces((0,1), (large_edges_new[0][j], large_edges_new[1][j]) ) )
            else:
                candidate_branches.append( determine_possible_branch_given_two_faces((1,0), (large_edges_new[0][j], large_edges_new[1][j]) ) )

        ### update the branch structure, many possible ways
        tet_indices = [tet_num0, tet_num1]
        tet_indices.sort()
        branch.pop(tet_indices[1])
        branch.pop(tet_indices[0])  ## remove from the list in the correct order!

        out_branches = []
        for cand0 in candidate_branches[0]:
            for cand1 in candidate_branches[1]:
                for cand2 in candidate_branches[2]:
                    candidate = branch[:] + [cand0, cand1, cand2]
                    # print('candidate', candidate)
                    # if is_branched(tri, candidate):
                    if has_non_sing_semiflow(tri, candidate):
                        out_branches.append(candidate)
        # assert len(out) > 0  ### this works if we check is_branched three lines above, but not if we check has_non_sing_semiflow
        if len(out_branches) == 0: ### with has_non_sing_semiflow instead, we might not get any
            return False

    if return_vertex_perm:
        vertex_permutation = []
        for (tet, vert_num) in new_vertex_representatives:
            vertex_permutation.append(tet.vertex(vert_num).index())
        ### note that Regina's permutations can have up to 16 entries, lets just use lists for this
        ### for testing:
        new_vertex_degrees = [v.degree() for v in tri.vertices()]
        new_vertex_degrees_pulled_back = [new_vertex_degrees[vertex_permutation[i]] for i in range(len(vertex_degrees))]

        # print(vertex_permutation)
        # if vertex_permutation != list(range(len(new_vertex_representatives))):
        #     print('2-3 permuted vertices', vertex_permutation, new_vertex_degrees, new_vertex_degrees_pulled_back, vertex_degrees)

        for i in polar_cusp_indices:
            vertex_degrees[i] += 2
        assert vertex_degrees == new_vertex_degrees_pulled_back
        # print(new_vertex_degrees_pulled_back)


    output = [tri]
    if angle != None:
        output.append(angle)
    if branch != None:
        output.append(out_branches)      
    if return_edge:
        output.append(new_tets[0].edge(0).index())
    if return_vertex_perm:
        output.append(vertex_permutation)
    return output
Esempio n. 8
0
def run_tests(num_to_check=10, smaller_num_to_check = 10):

    import taut
    veering_isosigs = parse_data_file("Data/veering_census.txt")
    print("testing is_taut")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        assert taut.is_taut(tri, angle), sig

    print("testing isosig round trip")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        recovered_sig = taut.isosig_from_tri_angle(tri, angle)
        assert sig == recovered_sig, sig
        # we only test this round trip - the other round trip does not
        # make sense because tri->isosig is many to one.

    import transverse_taut
    print("testing is_transverse_taut")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        assert transverse_taut.is_transverse_taut(tri, angle), sig

    non_transverse_taut_isosigs = parse_data_file("Data/veering_non_transverse_taut_examples.txt")
    print("testing not is_transverse_taut")
    for sig in non_transverse_taut_isosigs:
        tri, angle = taut.isosig_to_tri_angle(sig)
        assert not transverse_taut.is_transverse_taut(tri, angle), sig

    import veering
    print("testing is_veering")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        assert veering.is_veering(tri, angle), sig

    # tri, angle = taut.isosig_to_tri_angle("cPcbbbdxm_10")
    # explore_mobius_surgery_graph(tri, angle, max_tetrahedra = 12)
    # # tests to see that it makes only veering triangulations as it goes

    import veering_dehn_surgery
    print("testing veering_dehn_surgery")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        for face_num in veering_dehn_surgery.get_mobius_strip_indices(tri):
            (tri_s, angle_s, face_num_s) = veering_dehn_surgery.veering_mobius_dehn_surgery(tri, angle, face_num)
            assert veering.is_veering(tri_s, angle_s), sig
            
    import veering_fan_excision
    print("testing veering_fan_excision")
    m003, _ = taut.isosig_to_tri_angle('cPcbbbdxm_10')
    m004, _ = taut.isosig_to_tri_angle('cPcbbbiht_12')
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        tet_types = veering.is_veering(tri, angle, return_type = "tet_types")
        if tet_types.count("toggle") == 2:
            excised_tri, _ = veering_fan_excision.excise_fans(tri, angle)
            assert ( excised_tri.isIsomorphicTo(m003) != None or
                     excised_tri.isIsomorphicTo(m004) != None ), sig

    import pachner
    print("testing pachner with taut structure")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        face_num = random.randrange(tri.countTriangles())
        result = pachner.twoThreeMove(tri, face_num, angle = angle, return_edge = True)  
        if result != False: 
            tri2, angle2, edge_num = result
            tri3, angle3 = pachner.threeTwoMove(tri2, edge_num, angle = angle2)
            assert taut.isosig_from_tri_angle(tri, angle) == taut.isosig_from_tri_angle(tri3, angle3), sig

    import branched_surface
    import regina
    print("testing branched_surface and pachner with branched surface")
    for sig in random.sample(veering_isosigs, num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        tri_original = regina.Triangulation3(tri) #copy
        branch = branched_surface.upper_branched_surface(tri, angle, return_lower = random.choice([True, False]))
        
        ### test branch isosig round trip
        sig_with_branch = branched_surface.isosig_from_tri_angle_branch(tri, angle, branch)
        tri2, angle2, branch2 = branched_surface.isosig_to_tri_angle_branch(sig_with_branch)
        assert (branch == branch2) and (angle == angle2), sig

        branch_original = branch[:] #copy
        face_num = random.randrange(tri.countTriangles())
        out = pachner.twoThreeMove(tri, face_num, branch = branch, return_edge = True)
        if out != False:
            tri, possible_branches, edge_num = out
            tri, branch = pachner.threeTwoMove(tri, edge_num, branch = possible_branches[0])
            all_isoms = tri.findAllIsomorphisms(tri_original)
            all_branches = [branched_surface.apply_isom_to_branched_surface(branch, isom) for isom in all_isoms]
            assert branch_original in all_branches, sig

    import flow_cycles
    import drill
    print("testing taut and branched drill + semiflows on drillings")
    for sig in random.sample(veering_isosigs, smaller_num_to_check):
        tri, angle = taut.isosig_to_tri_angle(sig)
        branch = branched_surface.upper_branched_surface(tri, angle) ### also checks for veering and transverse taut
        found_loops = flow_cycles.find_flow_cycles(tri, branch)
        for loop in random.sample(found_loops, min(len(found_loops), 5)):  ## drill along at most 5 loops
            tri, angle = taut.isosig_to_tri_angle(sig)
            branch = branched_surface.upper_branched_surface(tri, angle) 
            tri_loop = flow_cycles.flow_cycle_to_triangle_loop(tri, branch, loop)
            if tri_loop != False: 
                if not flow_cycles.tri_loop_is_boundary_parallel(tri_loop, tri):
                    drill.drill(tri, tri_loop, angle = angle, branch = branch, sig = sig)
                    assert branched_surface.has_non_sing_semiflow(tri, branch), sig

    print("all basic tests passed")

    try:
        import snappy
        import snappy_util
        snappy_working = True
    except:
        print("failed to import from snappy?")
        snappy_working = False

    if snappy_working:        
        print("testing algebraic intersection")
        census = snappy.OrientableCuspedCensus() # not a set or list, so can't use random.sample
        for i in range(10):
            M = random.choice(census)
            n = M.num_cusps()
            peripheral_curves = M.gluing_equations()[-2*n:]
            for i in range(2*n):
                for j in range(i, 2*n):
                    alg_int = snappy_util.algebraic_intersection(peripheral_curves[i], peripheral_curves[j])
                    if i % 2 == 0 and j == i + 1:
                        assert alg_int == 1, M.name()
                    else:
                        assert alg_int == 0, M.name()
                       
    if snappy_working:
        import veering_drill_midsurface_bdy
        print("testing veering drilling and filling")
        for sig in random.sample(veering_isosigs[:3000], num_to_check):
            T, per = veering_drill_midsurface_bdy.drill_midsurface_bdy(sig)
            M = snappy.Manifold(T.snapPea())
            M.set_peripheral_curves("shortest")
            L = snappy_util.get_slopes_from_peripherals(M, per)
            M.dehn_fill(L)
            N = snappy.Manifold(sig.split("_")[0])
            assert M.is_isometric_to(N), sig

    if snappy_working:
        print("all tests depending on snappy passed")
   
    # try:
    #     from hashlib import md5
    #     from os import remove
    #     import pyx
    #     from boundary_triangulation import draw_triangulation_boundary_from_veering_isosig
    #     pyx_working = True
    # except:
    #     print("failed to import from pyx?")
    #     pyx_working = False

    # ladders_style_sigs = {
    #     "cPcbbbiht_12": "f34c1fdf65db9d02994752814803ae01",
    #     "gLLAQbecdfffhhnkqnc_120012": "091c85b4f4877276bfd8a955b769b496",
    #     "kLALPPzkcbbegfhgijjhhrwaaxnxxn_1221100101": "a0f15a8454f715f492c74ce1073a13a4",
    # }

    # geometric_style_sigs = {
    #     "cPcbbbiht_12": "1e74d0b68160c4922e85a5adb20a0f1d",
    #     "gLLAQbecdfffhhnkqnc_120012": "856a1fce74eb64f519bcda083303bd8f",
    #     "kLALPPzkcbbegfhgijjhhrwaaxnxxn_1221100101": "33bd23b34c5d977a103fa50ffe63120a",
    # }

    # args = {
    #     "draw_boundary_triangulation":True,
    #     "draw_triangles_near_poles": False,
    #     "ct_depth":-1,
    #     "ct_epsilon":0.03,
    #     "global_drawing_scale": 4,
    #     "delta": 0.2,
    #     "ladder_width": 10.0,
    #     "ladder_height": 20.0,
    #     "draw_labels": True,
    # }

    # shapes_data = read_from_pickle("Data/veering_shapes_up_to_ten_tetrahedra.pkl")

    # if pyx_working:
    #     for sig in ladders_style_sigs:
    #         print("testing boundary triangulation pictures, ladder style", sig)
    #         args["tet_shapes"] = shapes_data[sig]
    #         args["style"] = "ladders"
    #         file_name = draw_triangulation_boundary_from_veering_isosig(sig, args = args) 
    #         f = open(file_name, "rb")
    #         file_hash = md5(f.read())
    #         assert file_hash.hexdigest() == ladders_style_sigs[sig]
    #         f.close()
    #         remove(file_name)
        
    # if pyx_working:
    #     for sig in geometric_style_sigs:
    #         print("testing boundary triangulation pictures, ladder style", sig)
    #         args["tet_shapes"] = shapes_data[sig]
    #         args["style"] = "geometric"
    #         file_name = draw_triangulation_boundary_from_veering_isosig(sig, args = args) 
    #         f = open(file_name, "rb")
    #         file_hash = md5(f.read())
    #         assert file_hash.hexdigest() == geometric_style_sigs[sig]
    #         f.close()
    #         remove(file_name)

    # if pyx_working: 
    #     print("all tests depending on pyx passed")

    veering_polys = {
        "cPcbbbiht_12": [-4, -1, 1, 4],
        "eLMkbcddddedde_2100": [-2, -2, -2, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 2, 2],
        "gLLAQbecdfffhhnkqnc_120012": [-1, -1, -1, -1, 1, 1, 1, 1],
        "gLLPQcdfefefuoaaauo_022110": [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1],
    }

    # veering_polys = { ### old
    #     "cPcbbbiht_12": "a^3 - 4*a^2 + 4*a - 1",
    #     "eLMkbcddddedde_2100": "a^6*b - a^6 - 2*a^5*b - a^4*b^2 + a^5 + 2*a^4*b + a^3*b^2 - 2*a^3*b + a^3 + 2*a^2*b + a*b^2 - a^2 - 2*a*b - b^2 + b",
    #     "gLLAQbecdfffhhnkqnc_120012": "a^7 + a^6 + a^5 + a^4 - a^3 - a^2 - a - 1",
    #     "gLLPQcdfefefuoaaauo_022110": "a^12*b^3 - a^11*b^2 - a^10*b^3 - a^10*b^2 - a^7*b^3 - a^7*b^2 - a^6*b^3 + a^7*b + a^5*b^2 - a^6 - a^5*b - a^5 - a^2*b - a^2 - a*b + 1",
    # }

    taut_polys = {
        "cPcbbbiht_12": [-3, 1, 1],
        "eLMkbcddddedde_2100": [-1, -1, -1, 1, 1],
        "iLLAwQcccedfghhhlnhcqeesr_12001122": [],
    }

    # taut_polys = { ### old
    #     "cPcbbbiht_12": "a^2 - 3*a + 1",
    #     "eLMkbcddddedde_2100": "a^2*b - a^2 - a*b - b^2 + b",
    #     "iLLAwQcccedfghhhlnhcqeesr_12001122": "0",
    # }

    torus_bundles = [
        "cPcbbbiht_12",
        "eLMkbcdddhhqqa_1220",
        "gLMzQbcdefffhhqqqdl_122002",
    ]

    measured = [
        "gLLAQbecdfffhhnkqnc_120012",
        "iLLALQcccedhgghhlnxkxrkaa_12001112",
        "iLLAwQcccedfghhhlnhcqeesr_12001122",
    ]

    empties = [
        "fLAMcaccdeejsnaxk_20010",
        "gLALQbcbeeffhhwsras_211220",
        "hLALAkbcbeefgghhwsraqj_2112202",
    ]

    try:
        from sage.rings.integer_ring import ZZ
        sage_working = True
    except:
        print("failed to import from sage?")
        sage_working = False

    if sage_working:
        import taut_polytope
        print("testing is_layered")
        for sig in veering_isosigs[:17]:
            assert taut_polytope.is_layered(sig), sig
        for sig in veering_isosigs[17:21]:
            assert not taut_polytope.is_layered(sig), sig

    if sage_working:
        import fibered
        print("testing is_fibered")
        mflds = parse_data_file("Data/mflds_which_fiber.txt")
        mflds = [line.split("\t")[0:2] for line in mflds]
        for (name, kind) in random.sample(mflds, num_to_check):        
            assert fibered.is_fibered(name) == (kind == "fibered"), name

    if sage_working:
        import veering_polynomial
        import taut_polynomial
        print("testing veering poly")
        for sig in veering_polys:
            p = veering_polynomial.veering_polynomial(sig)
            assert check_polynomial_coefficients(p, veering_polys[sig]), sig
            ### Nov 2021: sage 9.4 changed how smith normal form works, which changed our polynomials
            ### to equivalent but not equal polynomials. To avoid this kind of change breaking things
            ### in the future, we changed to comparing the list of coefficients.
            # assert p.__repr__() == veering_polys[sig]
        print("testing taut poly")
        for sig in taut_polys:
            p = taut_polynomial.taut_polynomial_via_tree(sig)
            assert check_polynomial_coefficients(p, taut_polys[sig]), sig
        #     assert p.__repr__() == taut_polys[sig]
        print("testing divide")
        for sig in random.sample(veering_isosigs[:3000], num_to_check):
            p = veering_polynomial.veering_polynomial(sig)
            q = taut_polynomial.taut_polynomial_via_tree(sig)
            if q == 0:
                assert p == 0, sig
            else:
                assert q.divides(p), sig

    if sage_working:
        print("testing alex")
        for sig in random.sample(veering_isosigs[:3000], num_to_check):        
            snap_sig = sig.split("_")[0]
            M = snappy.Manifold(snap_sig)
            if M.homology().betti_number() == 1:
                assert taut_polynomial.taut_polynomial_via_tree(sig, mode = "alexander") == M.alexander_polynomial(), sig

    if sage_working:
        # would be nice to automate this - need to fetch the angle
        # structure say via z_charge.py...
        print("testing is_torus_bundle")
        for sig in torus_bundles: 
            assert taut_polytope.is_torus_bundle(sig), sig

    if sage_working:
        # ditto
        print("testing is_layered")
        for sig in torus_bundles:
            assert taut_polytope.is_layered(sig), sig
        print("testing measured")
        for sig in measured:
            assert taut_polytope.LMN_tri_angle(sig) == "M", sig
        print("testing empty")
        for sig in empties:
            assert taut_polytope.LMN_tri_angle(sig) == "N", sig

    if sage_working:  # warning - this takes random amounts of time!
        print("testing hom dim")
        for sig in random.sample(veering_isosigs[:3000], 3): # magic number
            # dimension = zero if and only if nothing is carried.
            assert (taut_polytope.taut_cone_homological_dim(sig) == 0) == (taut_polytope.LMN_tri_angle(sig) == "N"), sig

    if sage_working:      

        boundary_cycles = {
            ("eLMkbcddddedde_2100",(2,5,5,1,3,4,7,1)): "((-7, -7, 0, 0, 4, -3, 7, 0), (7, 7, 0, 0, -4, 3, -7, 0))",
            ("iLLLQPcbeegefhhhhhhahahha_01110221",(0,1,0,0,0,1,0,0,0,0,0,0,1,0,1,0)): "((0, 0, -1, 1, 1, 0, 1, 1, -1, 0, 0, 0, 0, 1, 0, 1), (0, 0, 1, -1, -1, 0, -1, -1, 1, 0, 0, 0, 0, -1, 0, -1))",
            ("ivvPQQcfhghgfghfaaaaaaaaa_01122000",(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1)): "((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0), (-2, 0, -3, 1, 2, -1, 0, 2, -1, 0, 3, 1, -2, 1, 0, -1), (0, -2, 1, -3, 0, -1, 2, 0, -1, 2, -1, 1, 0, 1, -2, 3))",
        }

        taut_polys_with_cycles = {
            ("eLMkbcddddedde_2100", ((7, 7, 0, 0, -4, 3, -7, 0),)): [-1, -1, -1, 1, 1],
            ("iLLLQPcbeegefhhhhhhahahha_01110221", ((0, 0, 1, -1, -1, 0, -1, -1, 1, 0, 0, 0, 0, -1, 0, -1),)): [1, 1, 2],
            ("ivvPQQcfhghgfghfaaaaaaaaa_01122000", ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))): [-4, -1, -1, 1, 1],
        }

        # taut_polys_with_cycles = {
        #     ("eLMkbcddddedde_2100", ((7, 7, 0, 0, -4, 3, -7, 0),)): "a^14 - a^8 - a^7 - a^6 + 1",
        #     ("iLLLQPcbeegefhhhhhhahahha_01110221", ((0, 0, 1, -1, -1, 0, -1, -1, 1, 0, 0, 0, 0, -1, 0, -1),)): "a^2 + 2*a + 1",
        #     ("ivvPQQcfhghgfghfaaaaaaaaa_01122000", ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))): "a*b^2 - a^2 - 4*a*b - b^2 + a",
        # }


        taut_polys_image = {
            ('eLMkbcddddedde_2100', ((7, 8, -1, 0, -4, 4, -8, 0),)):[-1, -1, -1, 1, 1],
            ('ivvPQQcfhghgfghfaaaaaaaaa_01122000', ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2),)):[-2, -2, -1, -1, 1, 1],
            ('ivvPQQcfhghgfghfaaaaaaaaa_01122000', ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))):[-4, -1, -1, 1, 1]
        }

        # taut_polys_image = {
        #     ('eLMkbcddddedde_2100', ((7, 8, -1, 0, -4, 4, -8, 0),)):"a^16 - a^9 - a^8 - a^7 + 1",
        #     ('ivvPQQcfhghgfghfaaaaaaaaa_01122000', ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2),)):"a*b^2*c - 2*a*b*c - b^2*c - a^2 - 2*a*b + a",
        #     ('ivvPQQcfhghgfghfaaaaaaaaa_01122000', ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))):"a*b^2 - a^2 - 4*a*b - b^2 + a"
        # }

        alex_polys_with_cycles = {
            ("eLMkbcddddedde_2100",((7, 7, 0, 0, -4, 3, -7, 0),)): [-2, -1, -1, -1, 1, 1, 1, 2],
            ("iLLLQPcbeegefhhhhhhahahha_01110221", ((0, 0, 1, -1, -1, 0, -1, -1, 1, 0, 0, 0, 0, -1, 0, -1),)): [-3, -1, 1, 3],
            ("ivvPQQcfhghgfghfaaaaaaaaa_01122000", ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))): [-1, -1, 1, 1],
        }

        # alex_polys_with_cycles = {
        #     ("eLMkbcddddedde_2100",((7, 7, 0, 0, -4, 3, -7, 0),)): "a^15 - a^14 + a^9 - 2*a^8 + 2*a^7 - a^6 + a - 1",
        #     ("iLLLQPcbeegefhhhhhhahahha_01110221", ((0, 0, 1, -1, -1, 0, -1, -1, 1, 0, 0, 0, 0, -1, 0, -1),)): "3*a^3 - a^2 + a - 3",
        #     ("ivvPQQcfhghgfghfaaaaaaaaa_01122000", ((1, 1, 2, 0, -1, 2, 1, -3, 0, -1, 0, -2, -1, 0, 3, -2), (1, 1, 0, 2, -1, 0, -3, 1, 2, -1, -2, 0, 3, -2, -1, 0))): "a*b^2 - a^2 - b^2 + a",
        # }

    if sage_working:
        import taut_carried     
        print("testing boundary cycles")
        for sig, surface in boundary_cycles:
            surface_list = list(surface)
            cycles = taut_carried.boundary_cycles_from_surface(sig, surface_list)
            cycles = tuple(tuple(cycle) for cycle in cycles)
            assert cycles.__repr__() == boundary_cycles[(sig, surface)], sig

    if sage_working:
        print("testing taut with cycles")
        for sig, cycles in taut_polys_with_cycles:
            cycles_in = [list(cycle) for cycle in cycles]
            p = taut_polynomial.taut_polynomial_via_tree(sig, cycles_in)
            assert check_polynomial_coefficients(p, taut_polys_with_cycles[(sig, cycles)]), sig
            # assert p.__repr__() == taut_polys_with_cycles[(sig, cycles)]

    if sage_working:
        print("testing taut with images")
        for sig, cycles in taut_polys_image:
            cycles_in = [list(cycle) for cycle in cycles]
            p = taut_polynomial.taut_polynomial_image(sig, cycles_in)
            assert check_polynomial_coefficients(p, taut_polys_image[(sig, cycles)]), sig
            # assert p.__repr__() == taut_polys_image[(sig, cycles)]

    if sage_working:
        print("testing alex with cycles")
        for sig, cycles in alex_polys_with_cycles:
            cycles_in = [list(cycle) for cycle in cycles]
            p = taut_polynomial.taut_polynomial_via_tree(sig, cycles_in, mode = "alexander")
            assert check_polynomial_coefficients(p, alex_polys_with_cycles[(sig, cycles)]), sig
            # assert p.__repr__() == alex_polys_with_cycles[(sig, cycles)]

    if sage_working:
        import edge_orientability
        import taut_euler_class
        print("testing euler and edge orientability")
        for sig in random.sample(veering_isosigs[:3000], 3):
            # Theorem: If (tri, angle) is edge orientable then e = 0.
            assert not ( edge_orientability.is_edge_orientable(sig) and
                         (taut_euler_class.order_of_euler_class_wrapper(sig) == 2) ), sig

    if sage_working:
        # Theorem: If (tri, angle) is edge orientable then taut poly = alex poly.
        # taut_polynomial.taut_polynomial_via_tree(sig, mode = "alexander") ==
        #      taut_polynomial.taut_polynomial_via_tree(sig, mode = "taut")
        pass
            
    if sage_working:
        print("testing exotics")
        for sig in random.sample(veering_isosigs[:3000], 3):
            tri, angle = taut.isosig_to_tri_angle(sig)
            T = veering.veering_triangulation(tri, angle)
            is_eo = T.is_edge_orientable()
            for angle in T.exotic_angles():
                assert taut_polytope.taut_cone_homological_dim(tri, angle) == 0, sig
                assert is_eo == transverse_taut.is_transverse_taut(tri, angle), sig

    ### test for drill_midsurface_bdy: drill then fill, check you get the same manifold

    if sage_working:
        from sage.combinat.words.word_generators import words
        from sage.modules.free_module_integer import IntegerLattice
        from sage.modules.free_module import VectorSpace
        from sage.matrix.constructor import Matrix
        import z_charge
        import z2_taut
        import regina

        ZZ2 = ZZ.quotient(ZZ(2))

        sig_starts = ["b+-LR", "b++LR"]

        print("testing lattice for punc torus bundle")
        for i in range(3):
            for sig_start in sig_starts:
                sig = sig_start + str(words.RandomWord(8, 2, "LR"))  # 8 is a magic number
                M = snappy.Manifold(sig)
                tri = regina.Triangulation3(M)
                t, A = z_charge.sol_and_kernel(M)
                B = z_charge.leading_trailing_deformations(M)
                C = z2_taut.cohomology_loops(tri)

                AA = IntegerLattice(A)
                BB = IntegerLattice(B)
                assert AA == BB.saturation(), sig

                dim = 3*M.num_tetrahedra()
                V = VectorSpace(ZZ2, dim)
                AA = V.subspace(A)
                BB = V.subspace(B)
                CM = Matrix(ZZ2, C)
                CC = CM.right_kernel()
                assert AA.intersection(CC) == BB , sig
                ## so l-t defms are the part of the kernel that doesn't flip over

    if sage_working:
        print("testing charges for punc torus bundle")
        for i in range(3):
            for sig_start in sig_starts:
                sig = sig_start + str(words.RandomWord(8, 2, "LR"))  # 8 is a magic number
                M = snappy.Manifold(sig)
                assert z_charge.can_deal_with_reduced_angles(M), sig
    
    if sage_working:
        import carried_surface
        import mutation
        print("testing building carried surfaces and mutations")
        sigs_weights = [
            ['iLLLPQccdgefhhghqrqqssvof_02221000',  (0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0)], 
            ['jLLAvQQcedehihiihiinasmkutn_011220000', (2, 0, 1, 0, 0, 0, 1, 2, 0, 2, 0, 2, 1, 0, 0, 0, 1, 0)],
            ['jLLAvQQcedehihiihiinasmkutn_011220000', (0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0)],
            ['jLLLMPQcdgfhfhiiihshassspiq_122201101', (0, 0, 4, 0, 4, 1, 0, 2, 2, 0, 1, 0, 0, 4, 0, 4, 0, 0)]
        ]
        strata = [
            ((1, 2), [2, 2]), 
            ((2, 4), [5, 5, 1, 1]),
            ((0, 3), [2, 0, 0]),
            ((6, 1), [22])
        ]
        orders_of_veering_symmetry_groups = [4, 2, 2, 2]
        
        for i in range(len(sigs_weights)):
            tri, angle = taut.isosig_to_tri_angle(sigs_weights[i][0])
            weights = sigs_weights[i][1]
            surface, edge_colours = carried_surface.build_surface(tri, angle, weights, return_edge_colours = True)
            assert strata[i] == carried_surface.stratum_from_weights_surface(weights, surface)
            veering_isoms = carried_surface.veering_symmetry_group(surface, edge_colours)
            assert len(veering_isoms) == orders_of_veering_symmetry_groups[i]
            isom = veering_isoms[1]
            mutation.mutate(tri, angle, weights, isom, quiet = True)
            if i == 0:
                assert tri.isoSig() == 'ivLLQQccfhfeghghwadiwadrv'
                #print('svof to wadrv passed')
            elif i == 1:
                assert tri.isoSig() == 'jvLLAQQdfghhfgiiijttmtltrcr'
                #print('smkutn to tltrcr passed')
            elif i == 2:
                assert tri.isoSig() == 'jLLMvQQcedehhiiihiikiwnmtxk'
                #print('smkutn to mtxk passed')
            elif i == 3:
                assert tri.isoSig() == 'jLLALMQcecdhggiiihqrwqwrafo'
                #print('spiq to rafo passed')
                
                        
    if sage_working:
        print("all tests depending on sage passed")