Beispiel #1
0
    def generate_neighbour_isoSigs(self, ceiling, floor):
        # tri, angle = self.getTriang()
        tri, angle = self.tri, self.angle
        num_tetrahedra = tri.countTetrahedra()
        assert num_tetrahedra <= ceiling
        assert num_tetrahedra >= floor

        if num_tetrahedra < ceiling:
            for face_index in range(tri.countTriangles()):
                tri_copy = regina.Triangulation3(tri)
                angle_copy = angle[:]
                output = twoThreeMove(tri_copy, angle_copy, face_index)
                if output != False:
                    tri_new, angle_new = output
                    self.neighbour_moves_up_faces[isosig_from_tri_angle(
                        tri_new, angle_new)] = 'f' + str(face_index)
                    self.neighbour_moves_up_tri_angles[isosig_from_tri_angle(
                        tri_new, angle_new)] = (tri_new, angle_new)

        if num_tetrahedra > floor:
            for edge_index in range(tri.countEdges()):
                tri_copy = regina.Triangulation3(tri)
                angle_copy = angle[:]
                output = threeTwoMove(tri_copy, angle_copy, edge_index)
                if output != False:
                    tri_new, angle_new = output
                    self.neighbour_moves_down_edges[isosig_from_tri_angle(
                        tri_new, angle_new)] = 'e' + str(edge_index)
                    self.neighbour_moves_down_tri_angles[isosig_from_tri_angle(
                        tri_new, angle_new)] = (tri_new, angle_new)
        self.neighbour_moves_tri_angles = {
            **self.neighbour_moves_up_tri_angles,
            **self.neighbour_moves_down_tri_angles
        }  ## union of the dictionaries
    def generate_neighbour_isoSigs(self, ceiling, floor):
        # tri, angle = self.getTriang()
        tri, angle, branch = self.tri, self.angle, self.branch
        num_tetrahedra = tri.countTetrahedra()
        assert num_tetrahedra <= ceiling
        assert num_tetrahedra >= floor

        if num_tetrahedra < ceiling:
            for face_index in range(tri.countTriangles()):
                # print('face_index', face_index)
                tri_copy = regina.Triangulation3(tri)
                angle_copy = angle[:]
                output_taut = taut_pachner.twoThreeMove(
                    tri_copy, angle_copy, face_index)
                # print('output_taut', output_taut)
                tri_copy2 = regina.Triangulation3(tri)
                branch_copy = branch[:]
                output_branch = branched_pachner.twoThreeMove(
                    tri_copy2, branch_copy, face_index)
                # print('output_branch', output_branch)
                if output_taut != False and output_branch != False:
                    tri_new, angle_new = output_taut
                    _, branch_new_list = output_branch
                    for branch_new in branch_new_list:
                        new_isosig = isosig_from_tri_angle_branch(
                            tri_new, angle_new, branch_new)
                        # print('new_isosig', new_isosig)
                        self.neighbour_moves_up_faces[new_isosig] = 'f' + str(
                            face_index)
                        self.neighbour_moves_up_tri_angle_branches[
                            new_isosig] = (tri_new, angle_new, branch_new)

        if num_tetrahedra > floor:
            # print('now do threeTwoMove')
            for edge_index in range(tri.countEdges()):
                # print('edge_index', edge_index)
                tri_copy = regina.Triangulation3(tri)
                angle_copy = angle[:]
                output_taut = taut_pachner.threeTwoMove(
                    tri_copy, angle_copy, edge_index)
                # print('output_taut', output_taut)
                tri_copy2 = regina.Triangulation3(tri)
                branch_copy = branch[:]
                output_branch = branched_pachner.threeTwoMove(
                    tri_copy2, branch_copy, edge_index)
                # print('output_branch', output_branch)
                if output_taut != False and output_branch != False:
                    tri_new, angle_new = output_taut
                    _, branch_new = output_branch
                    new_isosig = isosig_from_tri_angle_branch(
                        tri_new, angle_new, branch_new)
                    # print('new_isosig', new_isosig)
                    self.neighbour_moves_down_edges[new_isosig] = 'e' + str(
                        edge_index)
                    self.neighbour_moves_down_tri_angle_branches[
                        new_isosig] = (tri_new, angle_new, branch_new)
        self.neighbour_moves_tri_angle_branches = {
            **self.neighbour_moves_up_tri_angle_branches,
            **self.neighbour_moves_down_tri_angle_branches
        }  ## union of the dictionaries
Beispiel #3
0
def main():
    # for i in range(4):
    #     print(i)
    #     # tri, angle = isosig_to_tri_angle('cPcbbbiht_12')
    # sigs = ['dLQacccjsnk_200', 'dLQbccchhfo_122','dLQbccchhsj_122']
    # for sig in sigs:
    #     print(sig)
    #     tri, angle = isosig_to_tri_angle(sig)
    #     for branch in all_branched_surfaces(tri):
    #         print(lex_smallest_branched_surface(tri, branch))

    sig = 'dLQacccjsnk_200'
    for i in range(6):
        # print(i)
        tri, angle = isosig_to_tri_angle(sig)
        tri_original = regina.Triangulation3(tri)  #copy

        out = twoThreeMove(tri, [4, 11, 0], i, return_edge=True)
        if out != False:
            tri, possible_branches, edge_num = out
            # print('possible_branches', possible_branches)
            # print('all branches', all_branched_surfaces(tri))
            tri, branch = threeTwoMove(tri, possible_branches[0], edge_num)
            all_isoms = tri.findAllIsomorphisms(tri_original)
            all_branches = [
                apply_isom_to_branched_surface(branch, isom)
                for isom in all_isoms
            ]
            assert [4, 11, 0] in all_branches
Beispiel #4
0
def num_veering_structs(M, angles=None, use_flipper=True):
    """
    Tries to count them (in a very naive way). 
    Tries to eliminate overcounting due to symmetries (in reduced_angles)
    but will fail if one of the symmetries is hidden by retriangulation. 
    If use_flipper = False then we get a (true) lower bound, since then it only
    counts veering structures on the given triangulation.
    """
    if angles == None:
        angles = reduced_angles(M)
    tri = regina.Triangulation3(M)
    for angle in angles:
        if not is_taut(tri, angle):
            return None
    for angle in angles:
        if not (is_veering(tri, angle) or is_layered(tri, angle)):
            return None
    total = 0
    for angle in angles:
        if is_veering(tri, angle):
            total = total + 1
        elif use_flipper:
            assert is_layered(tri, angle)
            print(M.name(), angle, "needs flipper")
            try:
                if not has_internal_singularities(tri, angle):
                    total = total + 1
            except:
                print("flipper failed")
    return total
Beispiel #5
0
def fix_orientations(tri, angle, return_isom=False):
    """
    Fix the orientations of the tetrahedra in triangulation so that
    they are consistently oriented.  We choose how to flip each
    tetrahedron so that the angle structure list does not change.
    If return_isom, return the isomorphism from the original tri to the fixed orientations tri
    """
    orig_tri = regina.Triangulation3(tri)

    old_orientations = find_orientations(tri)
    swaps = []
    for i, orientation in enumerate(old_orientations):
        if orientation == -1:
            swaps.append(
                reverse_tet_orientation(tri, tri.tetrahedron(i), angle[i]))
        else:
            swaps.append(regina.Perm4())  ## identity

    if return_isom:
        out_isom = regina.Isomorphism3.identity(len(swaps))
        for i, p in enumerate(swaps):
            out_isom.setFacetPerm(i, p)
        return out_isom

        ### Regina 6 didn't let us build the isom directly from perms in python. Regina 7 does, using setFacetPerm
        ### old:
        # all_isoms = orig_tri.findAllIsomorphisms(tri) ### we will be order two, so we dont care which way this goes
        # for isom in all_isoms:
        #     if not moves_tetrahedra(isom):
        #         for i in range(tri.countTetrahedra()):
        #             assert swaps[i] == isom.facetPerm(i)
        #             assert isom == out_isom
        #         return isom
        assert False  ## should never get here
Beispiel #6
0
def go_deeper(tri, branch):
    for i in range(tri.countTriangles()):
        tri_copy = regina.Triangulation3(tri)  #copy
        branch_copy = branch[:]
        out = twoThreeMove(tri_copy, branch_copy, i)
        if out != False:
            tri_copy2, possible_branches = out
            return tri_copy2, possible_branches[0]
Beispiel #7
0
def main():
    tri, angle = isosig_to_tri_angle('jLLAvQQbcdeihhiihtsfxedxhdt_201021201')
    # tri, angle = isosig_to_tri_angle('cPcbbbiht_12')
    for i in range(tri.countTriangles()):
        print('triangle_num', i)
        tri2 = regina.Triangulation3(tri)
        angle2 = angle[:]
        tri3, angle3, edge_num = twoThreeMove(tri2, angle2, i, return_edge = True) 

        print('angle3', angle3, 'edge_num', edge_num)
        threeTwoMove(tri3, angle3, edge_num)
def do_all_veering_n_surgeries(tri, angle, n=1):
    # apply veering mobius dehn surgery n times to all Mobius strips in tri, print the results
    out = []
    print(("mob strip faces: " + str(get_mobius_strip_indices(tri))))
    for face_num in get_mobius_strip_indices(tri):
        print(("face_num", face_num))
        tri2 = regina.Triangulation3(tri)  # make a copy
        tri_surg, angle_surg, face_num_surg = veering_n_mobius_dehn_surgery(
            tri2, angle, face_num, n)
        sig = isosig_from_tri_angle(tri_surg, angle_surg)
        print(sig)
        out.append(sig)
    return out
Beispiel #9
0
def can_deal_with_reduced_angles(M, report=False):
    """
    Returns True if we can deal with all of the reduced angles. 
    """
    angles = reduced_angles(M)
    tri = regina.Triangulation3(M)
    if report:
        nv = num_veering_structs(M, angles=angles, use_flipper=False)
        return all(
            can_deal_with_reduced_angle(tri, angle)
            for angle in angles), len(angles), nv
    else:
        return all(can_deal_with_reduced_angle(tri, angle) for angle in angles)
Beispiel #10
0
def leading_trailing_deformations(M):
    tri = regina.Triangulation3(M)
    num_tet = tri.countTetrahedra()
    out = []
    for e in tri.edges():
        defm = [0] * (3 * num_tet)
        for i in range(e.degree()):
            emb = e.embedding(i)
            tet_num = emb.simplex().index()
            v0, v1, v2 = emb.vertices()[0], emb.vertices()[1], emb.vertices(
            )[2]
            a = unsorted_vert_pair_to_edge_pair[(v0, v2)]
            b = unsorted_vert_pair_to_edge_pair[(v1, v2)]
            defm[3 * tet_num + a] += 1
            defm[3 * tet_num + b] -= 1
        out.append(vector(defm))
    return out
Beispiel #11
0
def is_fibered(snappy_name, tries=1000, with_data=False):
    """
    Given an snappy_name, tries to find a layered transverse taut
    triangulation for the manifold via random search.
    """
    sigs = get_many_sigs(snappy_name, tries)
    for sig in sigs:
        T = regina.Triangulation3(sig)
        T.orient()
        angle_strs = list(regina.AngleStructures.enumerate(T, True))
        angle_strs = [
            taut_regina_angle_struct_to_taut_struct(angle_str)
            for angle_str in angle_strs
        ]
        for angle in angle_strs:
            # we are not interested in semi-fibered manifolds so:
            if is_transverse_taut(T, angle) and is_layered(T, angle):
                if with_data:
                    return (True, T.isoSig(), angle)
                return True

    return False
Beispiel #12
0
def reduced_angles(M):
    """
    Given a snappy manifold M, compute the reduced charges, convert to
    angle structures, remove repeated structures (using symmetries of
    the triangulation), remove non-trivial structures (in cohomology),
    and return what remains.
    """
    charges = reduced_charges(M)
    tri = regina.Triangulation3(M)
    angles = [charge_to_angle(c) for c in charges]

    # remove symmetries
    lex_angles = [lex_smallest_angle_structure(tri, angle) for angle in angles]
    angles = []
    for angle in lex_angles:
        if angle not in angles:
            angles.append(angle)

    # remove the angles that flip a triangle over
    angles = [
        angle for angle in angles if is_trivial_in_cohomology(tri, angle)
    ]
    return angles
Beispiel #13
0
    def generate_neighbour_isoSigs(self, ceiling, floor):
        # tri, angle = self.getTriang()
        tri, angle, branch = self.tri, self.angle, self.branch
        num_tetrahedra = tri.countTetrahedra()
        assert num_tetrahedra <= ceiling
        assert num_tetrahedra >= floor

        if num_tetrahedra < ceiling:
            for face_index in range(tri.countTriangles()):
                ### only go this way if it expands the drilled cusp
                face = tri.triangles()[face_index]
                embed0 = face.embedding(0)
                tet0 = embed0.simplex()
                tet_0_face_num = embed0.face()
                embed1 = face.embedding(1)
                tet1 = embed1.simplex()
                tet_1_face_num = embed1.face()
                if (tet0.vertex(tet_0_face_num).index()
                        == self.drilled_cusp_index) or (
                            tet1.vertex(tet_1_face_num).index()
                            == self.drilled_cusp_index):
                    tri_copy = regina.Triangulation3(tri)
                    angle_copy = angle[:]
                    branch_copy = branch[:]
                    output = twoThreeMove(tri_copy,
                                          face_index,
                                          angle=angle_copy,
                                          branch=branch_copy,
                                          return_vertex_perm=True)
                    # print('2-3', face_index, output)
                    if output != False:
                        tri_new, angle_new, branches_new, vertex_perm = output
                        drilled_cusp_index_new = vertex_perm[
                            self.drilled_cusp_index]
                        for branch_new in branches_new:
                            sig_new = isosig_from_tri_angle_branch(
                                tri_new, angle_new, branch_new)
                            self.neighbour_moves_up_faces[sig_new] = 'f' + str(
                                face_index)
                            self.neighbour_moves_up_tri_angle_branch[
                                sig_new] = (tri_new, angle_new, branch_new,
                                            drilled_cusp_index_new)

        if num_tetrahedra > floor:
            for edge_index in range(tri.countEdges()):
                ### only go this way if it expands the drilled cusp
                edge = tri.edges()[edge_index]
                if (edge.vertex(0).index() != self.drilled_cusp_index) and (
                        edge.vertex(1).index() != self.drilled_cusp_index):
                    tri_copy = regina.Triangulation3(tri)
                    angle_copy = angle[:]
                    branch_copy = branch[:]
                    output = threeTwoMove(tri_copy,
                                          edge_index,
                                          angle=angle_copy,
                                          branch=branch_copy,
                                          return_vertex_perm=True)
                    if output != False:
                        tri_new, angle_new, branch_new, vertex_perm = output
                        drilled_cusp_index_new = vertex_perm[
                            self.drilled_cusp_index]
                        sig_new = isosig_from_tri_angle_branch(
                            tri_new, angle_new, branch_new)
                        self.neighbour_moves_down_edges[sig_new] = 'e' + str(
                            edge_index)
                        self.neighbour_moves_down_tri_angle_branch[sig_new] = (
                            tri_new, angle_new, branch_new,
                            drilled_cusp_index_new)
        self.neighbour_moves_tri_angle_branch = {
            **self.neighbour_moves_up_tri_angle_branch,
            **self.neighbour_moves_down_tri_angle_branch
        }  ## union of the dictionaries
Beispiel #14
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())
Beispiel #15
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())
def drill_midsurface_bdy(tri, angle):
    vt = veering_triangulation(tri, angle)
    veering_colours = vt.veering_colours  ## "red" or "blue"
    tet_types = vt.tet_types  ### "toggle", "red" or "blue"
    tet_vert_coors = vt.coorientations

    drilledTri = regina.Triangulation3()
    subtet_indices = []
    subtet_addresses = []

    num_toggles = tet_types.count("toggle")
    drilled_num_tet = 8 * num_toggles + 4 * (tri.countTetrahedra() -
                                             num_toggles)

    meridians = []
    ### in each toggle, we record the meridians for the two boundary components we are drilling
    ### note that this repeats the same meridian many times - the construction here is purely local: it is much
    ### easier to figure out which are duplicates of which later

    for i in range(tri.countTetrahedra()):
        tet = tri.tetrahedron(i)
        first_subtet_index = drilledTri.countTetrahedra()
        if tet_types[i] == 'toggle':
            num_to_add = 8
        else:
            num_to_add = 4
        for j in range(num_to_add):
            drilledTri.newTetrahedron()

        subtet_indices.append(
            range(first_subtet_index, first_subtet_index + num_to_add))

        ### glue sub-tetrahedra together. Note that some tetrahedra will be negatively oriented

        [(t0, t1), (b0, b1)] = get_top_and_bottom_nums(tet_vert_coors, i)
        this_tet_subtet_addresses = {}
        if tet_types[i] == 'toggle':
            ## first two subtetrahedra are top two, second two are bottom two, then the four side tetrahedra
            tet_t0, tet_t1 = drilledTri.tetrahedron(
                first_subtet_index), drilledTri.tetrahedron(
                    first_subtet_index + 1)
            # tet_ti is opposite vertex bi
            tet_b0, tet_b1 = drilledTri.tetrahedron(first_subtet_index +
                                                    2), drilledTri.tetrahedron(
                                                        first_subtet_index + 3)
            # tet_bi is opposite vertex ti
            tet_s00 = drilledTri.tetrahedron(first_subtet_index + 4)
            tet_s01 = drilledTri.tetrahedron(first_subtet_index + 5)
            tet_s10 = drilledTri.tetrahedron(first_subtet_index + 6)
            tet_s11 = drilledTri.tetrahedron(first_subtet_index + 7)
            # tet_sij meets vertices ti and bj

            ## keys are (vert not on face, vert not on edge), returns the subtet which meets the face, edge
            this_tet_subtet_addresses[(b0, b1)] = tet_t0
            this_tet_subtet_addresses[(b1, b0)] = tet_t1
            this_tet_subtet_addresses[(t0, t1)] = tet_b0
            this_tet_subtet_addresses[(t1, t0)] = tet_b1
            this_tet_subtet_addresses[(t1, b1)] = tet_s00
            this_tet_subtet_addresses[(b1, t1)] = tet_s00
            this_tet_subtet_addresses[(t1, b0)] = tet_s01
            this_tet_subtet_addresses[(b0, t1)] = tet_s01
            this_tet_subtet_addresses[(t0, b1)] = tet_s10
            this_tet_subtet_addresses[(b1, t0)] = tet_s10
            this_tet_subtet_addresses[(t0, b0)] = tet_s11
            this_tet_subtet_addresses[(b0, t0)] = tet_s11

            tet_t0.join(b0, tet_t1,
                        regina.Perm4(b0, b1, b1, b0, t0, t0, t1,
                                     t1))  ## b0 <-> b1, t0 and t1 fixed
            tet_b0.join(t0, tet_b1,
                        regina.Perm4(t0, t1, t1, t0, b0, b0, b1,
                                     b1))  ## t0 <-> t1, b0 and b1 fixed

            tet_s00.join(b0, tet_t1,
                         regina.Perm4(t0, t0, b1, b1, t1, b0, b0, t1))
            tet_s00.join(t0, tet_b1,
                         regina.Perm4(b0, b0, t1, t1, b1, t0, t0, b1))

            tet_s01.join(b1, tet_t0,
                         regina.Perm4(t0, t0, b0, b0, t1, b1, b1, t1))
            tet_s01.join(t0, tet_b1,
                         regina.Perm4(b1, b1, t1, t1, b0, t0, t0, b0))

            tet_s10.join(b0, tet_t1,
                         regina.Perm4(t1, t1, b1, b1, t0, b0, b0, t0))
            tet_s10.join(t1, tet_b0,
                         regina.Perm4(b0, b0, t0, t0, b1, t1, t1, b1))

            tet_s11.join(b1, tet_t0,
                         regina.Perm4(t1, t1, b0, b0, t0, b1, b1, t0))
            tet_s11.join(t1, tet_b0,
                         regina.Perm4(b1, b1, t0, t0, b0, t1, t1, b0))

            ### meridian around upper hole
            meridian = [0] * (3 * drilled_num_tet)
            meridian[3 * tet_t1.index() +
                     unsorted_vert_pair_to_edge_pair[b0, t0]] = -1
            meridian[3 * tet_s00.index() +
                     unsorted_vert_pair_to_edge_pair[b1, t1]] = 1
            meridian[3 * tet_b1.index() +
                     unsorted_vert_pair_to_edge_pair[t0, t1]] = 1
            meridian[3 * tet_s01.index() +
                     unsorted_vert_pair_to_edge_pair[b0, t1]] = 1
            meridian[3 * tet_t0.index() +
                     unsorted_vert_pair_to_edge_pair[b1, t0]] = -1
            meridians.append(meridian)

            ### meridian around lower hole - swap b with t everywhere. Note this also swaps s01 with s10
            meridian = [0] * (3 * drilled_num_tet)
            meridian[3 * tet_b1.index() +
                     unsorted_vert_pair_to_edge_pair[t0, b0]] = -1
            meridian[3 * tet_s00.index() +
                     unsorted_vert_pair_to_edge_pair[t1, b1]] = 1
            meridian[3 * tet_t1.index() +
                     unsorted_vert_pair_to_edge_pair[b0, b1]] = 1
            meridian[3 * tet_s10.index() +
                     unsorted_vert_pair_to_edge_pair[t0, b1]] = 1
            meridian[3 * tet_b0.index() +
                     unsorted_vert_pair_to_edge_pair[t1, b0]] = -1
            meridians.append(meridian)

        else:  ## fan
            # first two tetrahedra are the top and bottom
            tet_t, tet_b = drilledTri.tetrahedron(
                first_subtet_index), drilledTri.tetrahedron(
                    first_subtet_index + 1)
            # second two tetrahedra are the sides, s0 meets vertex t0, s1 meets vertex t1
            tet_s0, tet_s1 = drilledTri.tetrahedron(first_subtet_index +
                                                    2), drilledTri.tetrahedron(
                                                        first_subtet_index + 3)

            # tet_types[i] == 'blue'
            this_tet_subtet_addresses[(b0, b1)] = tet_t
            this_tet_subtet_addresses[(b1, b0)] = tet_t
            this_tet_subtet_addresses[(t0, t1)] = tet_b
            this_tet_subtet_addresses[(t1, t0)] = tet_b
            ### if this is a blue fan tet then each side tet meet a blue edge
            # tet_types[i] could be 'blue' or 'red'

            ### find which bottom vertex, when linked to t0, gives an edge of the correct colour
            if vt.get_edge_between_verts_colour(i, (t0, b0)) == tet_types[i]:
                s0b = b0  ## bottom vert of s0
                s1b = b1  ## bottom vert of s1
                this_tet_subtet_addresses[(t0, b0)] = tet_s1
                this_tet_subtet_addresses[(b0, t0)] = tet_s1
                this_tet_subtet_addresses[(t1, b1)] = tet_s0
                this_tet_subtet_addresses[(b1, t1)] = tet_s0
            else:
                s0b = b1  ## bottom vert of s0
                s1b = b0  ## bottom vert of s1
                this_tet_subtet_addresses[(t0, b1)] = tet_s1
                this_tet_subtet_addresses[(b1, t0)] = tet_s1
                this_tet_subtet_addresses[(t1, b0)] = tet_s0
                this_tet_subtet_addresses[(b0, t1)] = tet_s0

            tet_s0.join(s0b, tet_t,
                        regina.Perm4(t0, t0, s0b, t1, s1b, s1b, t1, s0b))
            tet_s0.join(t0, tet_b,
                        regina.Perm4(s0b, s0b, t0, s1b, t1, t1, s1b, t0))
            tet_s1.join(s1b, tet_t,
                        regina.Perm4(t1, t1, s1b, t0, s0b, s0b, t0, s1b))
            tet_s1.join(t1, tet_b,
                        regina.Perm4(s1b, s1b, t1, s0b, t0, t0, s0b, t1))

        subtet_addresses.append(this_tet_subtet_addresses)

    ### now glue subtetrahedra from different original tetrahedra together
    ### fan tetrahedra only glue two of three subtetrahedra on each face.
    ### toggles glue one of their subfaces all the way through to the next toggle...

    unglued_flags = []
    for f in range(tri.countTriangles()):
        for e in range(3):
            unglued_flags.append((f, e))

    while unglued_flags != []:
        (f, e) = unglued_flags.pop()
        # print f,e
        face = tri.triangle(f)
        embed0 = face.embedding(0)
        embed1 = face.embedding(1)
        tet_index_0 = embed0.simplex().index()
        tet_index_1 = embed1.simplex().index()
        face0 = embed0.face()
        face1 = embed1.face()
        vertperm0 = embed0.vertices()
        vertperm1 = embed1.vertices()

        edge0 = vertperm0[e]
        edge1 = vertperm1[e]

        otherverts0 = [0, 1, 2, 3]
        otherverts0.remove(face0)
        otherverts0.remove(edge0)
        otherverts1 = [0, 1, 2, 3]
        otherverts1.remove(face1)
        otherverts1.remove(edge1)

        if (face0, edge0) in subtet_addresses[tet_index_0]:
            subtet0 = subtet_addresses[tet_index_0][(face0, edge0)]
        else:
            subtet0 = None
        if (face1, edge1) in subtet_addresses[tet_index_1]:
            subtet1 = subtet_addresses[tet_index_1][(face1, edge1)]
        else:
            subtet1 = None

        if subtet0 == None and subtet1 == None:
            pass  ### both are fans, with no subtet, skip
        elif subtet0 != None and subtet1 != None:
            ### glue
            u, v = otherverts0
            tet0perm = regina.Perm4(face0, edge0, edge0, face0, u, u, v, v)
            u, v = otherverts1
            tet1perm = regina.Perm4(face1, edge1, edge1, face1, u, u, v, v)
            gluing = embed0.simplex().adjacentGluing(face0)
            subtet0.join(edge0, subtet1,
                         tet1perm * gluing * tet0perm)  ### perms act on left

        else:  ### we have to walk around to find the right place to glue this toggle subtet, and remove the other unglued flag from the list
            if subtet1 == None:
                assert tet_types[tet_index_0] == 'toggle'
                toggle_tet_index = tet_index_0
                toggle_face = face0
                toggle_edge = edge0
                subtet = subtet0
            else:
                assert tet_types[tet_index_1] == 'toggle'
                toggle_tet_index = tet_index_1
                toggle_face = face1
                toggle_edge = edge1
                subtet = subtet1
            edge_verts = [0, 1, 2, 3]
            edge_verts.remove(toggle_edge)
            edge_verts.remove(toggle_face)
            toggle_e0, toggle_e1 = edge_verts

            e0, e1 = toggle_e0, toggle_e1
            leading_vertex = toggle_edge
            trailing_vertex = toggle_face
            tet = tri.tetrahedron(toggle_tet_index)
            while True:
                gluing = tet.adjacentGluing(trailing_vertex)
                tet = tet.adjacentTetrahedron(trailing_vertex)
                e0, e1 = gluing[e0], gluing[e1]
                leading_vertex, trailing_vertex = gluing[
                    trailing_vertex], gluing[leading_vertex]
                if tet_types[tet.index()] == "toggle":
                    break
            other_toggle_tet_index = tet.index()
            other_toggle_face = leading_vertex
            other_toggle_edge = trailing_vertex
            other_toggle_e0 = e0
            other_toggle_e1 = e1
            other_subtet = subtet_addresses[other_toggle_tet_index][(
                other_toggle_face, other_toggle_edge)]
            tetperm = regina.Perm4(toggle_face, toggle_edge, toggle_edge,
                                   toggle_face, toggle_e0, toggle_e0,
                                   toggle_e1, toggle_e1)
            other_tetperm = regina.Perm4(other_toggle_face, other_toggle_edge,
                                         other_toggle_edge, other_toggle_face,
                                         other_toggle_e0, other_toggle_e0,
                                         other_toggle_e1, other_toggle_e1)
            gluing = regina.Perm4(toggle_e0, other_toggle_e0, toggle_e1,
                                  other_toggle_e1, toggle_face,
                                  other_toggle_face, toggle_edge,
                                  other_toggle_edge)
            subtet.join(toggle_edge, other_subtet,
                        other_tetperm * gluing * tetperm)

    return drilledTri, meridians
Beispiel #17
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()
Beispiel #18
0
# All gems should have the same first permutation (third colour)
# Example: all (1^1,3^k)-type 3-crystallisation for n=7
n = 7
gems = surv.family_three_crystallisations(7,
                                          sphere1=[[1], [0, 4, 2], [3, 5, 6]])

idn = list(range(n))
mu = surv.mu_online(n)
sigmas = surv.cycle_to_oneline(gems[0][0], n)
taus = [surv.cycle_to_oneline(g[1]) for g in gems]
triangulations = []

for tau in taus:
    tau = []
    perms = [idn, mu, sigma, tau]
    T = regina.Triangulation3()
    simplices = [T.newTetrahedron() for i in range(2 * n)]
    id4 = regina.Perm4()
    for c in range(4):
        for i in range(n):
            simplices[i].join(c, simplices[perms[c][i] + n], id4)
    triangulations.append(T)

# Example: computing each unique isomorphism signature for triangulations

unique_sigs = []
for T in triangulations:
    s = T.isoSig()
    if s not in unique_sigs:
        unique_sigs.append(s)
def excise_fans(tri, angle, fan_nums=None):
    vt = veering_triangulation(tri, angle)
    veering_colours = vt.veering_colours  ## "red" or "blue"
    tet_types = vt.tet_types  ### "toggle", "red" or "blue"
    tet_vert_coors = vt.coorientations
    if fan_nums == None:  ## do all fans
        fan_nums = [
            n for n in range(len(tet_types)) if tet_types[n] != "toggle"
        ]

    excisedAngle = angle[:]
    for fan_num in sorted(fan_nums, reverse=True):
        del excisedAngle[fan_num]

    minority_edge_pairs = []
    for fan_num in fan_nums:
        assert tet_types[fan_num] != "toggle"
        tops, bottoms = get_top_and_bottom_nums(tet_vert_coors, fan_num)
        if 0 in tops:
            pi_pair = list(tops)
        else:
            pi_pair = list(bottoms)
        other = pi_pair[(pi_pair.index(0) + 1) % 2]
        for i in range(1, 4):
            if i != other:
                if vt.get_edge_between_verts_colour(
                        fan_num,
                    (0, i)) != tet_types[fan_num]:  ### "red" or "blue"
                    last = 6 - i - other  ## the fourth vertex
                    minority_edge_pairs.append([(0, i), (other, last)])
                    break
    excisedTri = regina.Triangulation3(tri)  ### copy

    fan_tets = [excisedTri.tetrahedron(fan_num) for fan_num in fan_nums]
    for k, tet in enumerate(fan_tets):
        # print 'k', k, 'tet.index', tet.index()
        minority_edge_pair = minority_edge_pairs[k]
        # print minority_edge_pair
        ### record gluings for neighbours
        neighbours = [tet.adjacentSimplex(i) for i in range(4)]
        gluings = [tet.adjacentGluing(i) for i in range(4)]

        # print [neighbour.index() for neighbour in neighbours]
        # print gluings

        tet.isolate()

        ## now glue neighbours to each other

        if tet not in neighbours:
            to_glue = [0, 1, 2, 3]
            while to_glue != []:
                i = to_glue.pop()
                if i in minority_edge_pair[0]:
                    j = minority_edge_pair[0][(minority_edge_pair[0].index(i) +
                                               1) % 2]
                else:
                    j = minority_edge_pair[1][(minority_edge_pair[1].index(i) +
                                               1) % 2]

                teti = neighbours[i]
                tetj = neighbours[j]
                teti.join(
                    gluings[i][i], tetj,
                    gluings[j] * regina.Perm4(j, i) * (gluings[i].inverse()))
                to_glue.remove(j)
                # print i,j
        else:  ### tet is glued to itself, which makes it trickier to remove
            ### first, find self-gluings
            self_gluings = []
            self_gluings = [neighbours.index(tet)]
            self_gluings.append(neighbours.index(tet, self_gluings[0] +
                                                 1))  ### add second entry

            other_gluings = [0, 1, 2, 3]
            other_gluings.remove(self_gluings[0])
            other_gluings.remove(self_gluings[1])
            i, j = other_gluings
            teti = neighbours[i]
            tetj = neighbours[j]
            if i in minority_edge_pair[0]:
                p = minority_edge_pair[0][(minority_edge_pair[0].index(i) + 1)
                                          % 2]
                q = minority_edge_pair[1][(minority_edge_pair[1].index(j) + 1)
                                          % 2]
            else:
                p = minority_edge_pair[1][(minority_edge_pair[1].index(i) + 1)
                                          % 2]
                q = minority_edge_pair[0][(minority_edge_pair[0].index(j) + 1)
                                          % 2]
            teti.join(
                gluings[i][i], tetj, gluings[j] * regina.Perm4(j, q) *
                gluings[p] * regina.Perm4(p, i) * (gluings[i].inverse()))

        excisedTri.removeTetrahedron(tet)

    return excisedTri, excisedAngle
Beispiel #20
0
def twoThreeMove(tri, angle, face_num, perform = True, return_edge = False):
    """Apply a 2-3 move to a taut triangulation, if possible. 
    If perform = False, returns if the move is possible.
    If perform = True, modifies tri, returns (tri, angle) for the performed move"""
    
    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

    ### 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

    ### 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)

    ### 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('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 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 not return_edge:
        return [ tri, angle ]
    else:
        return [ tri, angle, new_tets[0].edge(0).index() ]
Beispiel #21
0
def threeTwoMove(tri, angle, edge_num, perform = True, return_triangle = False):
    """Apply a 3-2 move to a taut triangulation, if possible. 
    If perform = False, returns if the move is possible.
    If perform = True, modifies tri, returns (tri, angle) for the performed move"""

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

    tets = []
    tet_nums = []
    vertices = []
    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 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

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

    if not perform:
        return True  ### taut 3-2 move is always possible if the 3-2 move is.

    ### 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))

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

    ### 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 angle structure
    tet_nums.sort()
    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 not return_triangle:
        return (tri, angle)    
    else:
        return (tri, angle, new_tets[0].triangle(3).index())    
Beispiel #22
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
Beispiel #23
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
Beispiel #24
0
    def __init__(self,
                 manifold,
                 qtons=None,
                 quiet=False,
                 tracker=False,
                 allows_non_admissible=False,
                 force_simplicial_homology=False):

        self._triangulation = None
        if isinstance(manifold, str):
            self._manifold = snappy.Manifold(manifold)
        elif isinstance(manifold, regina.engine.SnapPeaTriangulation):
            self._manifold = snappy.Manifold(manifold.snapPea())
            self._triangulation = manifold
        elif isinstance(manifold, regina.engine.Triangulation3):
            self._manifold = snappy.Manifold(manifold.snapPea())
            self._triangulation = manifold
        elif isinstance(manifold, snappy.Manifold):
            self._manifold = manifold
        elif isinstance(manifold, snappy.Triangulation):
            self._manifold = manifold

        for c in self._manifold.cusp_info():
            if c.is_complete == False:
                self._manifold = self._manifold.filled_triangulation()
                break

        self._QUIET = quiet
        self._force_simplicial_homology = force_simplicial_homology
        self._num_cusps = self._manifold.num_cusps()
        if self._num_cusps != 0:
            try:
                L = self._manifold.link()
                self._knows_link_complement = True
            except ValueError:
                self._knows_link_complement = False
            #if self._knows_link_complement and bdy_H1_basis == 'natural':
            #	self._bdy_H1_basis = 'natural'
            #else:
            #	if self._manifold.verify_hyperbolicity()[0]:
            #		self._manifold.set_peripheral_curves('shortest')
            #		self._bdy_H1_basis = 'shortest'

            self._triangulation = regina.SnapPeaTriangulation(
                self._manifold._to_string())
            self._angle_structure = solve_lin_gluing_eq(self._triangulation)
            self._peripheral_curve_mats = peripheral_curve_mats(
                self._manifold, self._triangulation)
            self._manifold_is_closed = False

            # check to make sure peripheral basis curves actually give a basis
            check, message = periph_basis_intersections(self)
            assert check, message

            # check to make sure each peripheral basis curve is connected (extra trivial loops
            # will mess up Euler char calculations).
            check, message = periph_basis_connected(self)
            assert check, message

        else:
            if self._triangulation == None:
                self._triangulation = regina.Triangulation3(
                    self._manifold._to_string())
                for _ in range(10):
                    self._triangulation.intelligentSimplify()
            self._manifold_is_closed = True
            #self._bdy_H1_basis = None
        if not self._triangulation.isOriented():
            self._triangulation.orient()
        self._qtons = qtons
        self._tkr = False
        self._allows_non_admissible = allows_non_admissible
        self._is_fibered = 'unknown'
        self._betti_number = self._triangulation.homologyH1().rank()

        # qtons memoization caches-----
        self._euler_char = {}
        self._map_to_ball = {}
        self._map_to_H2 = {}
        self._num_boundary_comps = {}
        self._over_facet = {}
        self._is_norm_minimizing = {}
        self._is_admissible = {}
        self._qtons_image_in_C2 = {}
        self._num_H1bdy_comps = {}
        self._has_mixed_bdy = {}
        self._is_embedded = {}
        self._ends_embedded = {}
        self._oriented_quads_mat = {}

        if self._manifold.num_cusps() > 0:
            self._boundary_slopes = {}
            self._spinning_slopes = {}
            self._map_to_H1bdy = {}

        if not self._QUIET:
            print(
                'Enumerating quad transversely oriented normal surfaces (qtons)... ',
                end='')
            try:
                sys.stdout.flush()
            except AttributeError:
                pass

        if tracker:
            self._tkr = regina.ProgressTracker()

        # compute transversely oriented normal surfaces
        self._qtons = self.qtons()

        # name the surfaces by their index in the NormalSurfaces list
        for i in range(self._qtons.size()):
            self._qtons.surface(i).setName(str(i))

        if not self._QUIET:
            print('Done.')
            try:
                sys.stdout.flush()
            except AttributeError:
                pass

        if self._betti_number > self._num_cusps:
            self._has_internal_homology = True
        else:
            self._has_internal_homology = False

        # if the manifold has internal homology or force_simplicial_homology==True then we need to use simplicial homology.
        if self._has_internal_homology or self._force_simplicial_homology:

            self._uses_simplicial_homology = True

            if not self._QUIET:
                print('computing simplicial homology...', end='')
                try:
                    sys.stdout.flush()
                except AttributeError:
                    pass

            self._face_map_to_C2 = get_face_map_to_C2(self._triangulation)
            self._quad_map_to_C2 = get_quad_map_to_C2(self._triangulation,
                                                      self._face_map_to_C2)
            H2_basis_in_C2, P, qtons_image = H2_as_subspace_of_C2(
                self, self._face_map_to_C2, self._quad_map_to_C2)
            self._project_to_im_del3 = P
            self._qtons_image_in_C2 = {
                i: qtons_image[i]
                for i in range(len(qtons_image))
            }
            assert len(
                H2_basis_in_C2) == self._betti_number, self._manifold.name(
                ) + ', force_simplicial_homology={}'.format(
                    self._force_simplicial_homology)
            # raise an error if the qtons do not generate H2. This should only happen for a knot in a
            # rational homology sphere, with force_simplicial_homology=True.
            I = Matrix.identity(self._betti_number)
            B = Matrix(H2_basis_in_C2).transpose()
            A = B.solve_left(I)
            self._map_H2_to_standard_basis = A

            if not self._QUIET:
                print('Done.')
                try:
                    sys.stdout.flush()
                except AttributeError:
                    pass
        else:
            self._uses_simplicial_homology = False
def veering_mobius_dehn_surgery(triangulation, angle_struct, face_num):
    tri = regina.Triangulation3(triangulation)  # make a copy
    angle = list(angle_struct)  # make a copy
    face = tri.triangle(face_num)
    assert face.isMobiusBand()
    # Note that dunce caps cannot appear in a veering triangulation

    # Find which vertex is on both copies of the identified edge of the face
    edges = [face.edge(i)
             for i in range(3)]  # edge i is opposite vertex i, i in [0, 1, 2]
    for j in range(3):
        if edges[j] == edges[(j + 1) % 3]:
            B = (j + 2) % 3
            break

    embed0 = face.embedding(0)
    embed1 = face.embedding(1)
    tet0 = embed0.tetrahedron()
    tet1 = embed1.tetrahedron()
    embed0_verts = embed0.vertices()
    embed1_verts = embed1.vertices()

    # In tet0: B gives "b".  Let "c" be the edge sharing a
    # pi with "b".  Let "d" be the vertex not meeting the given face.
    # Let "a" be the remaining vertex.

    b = embed0.vertices()[B]
    c = shares_pi_with(angle[tet0.index()], b)
    d = embed0.vertices()[3]  # ... use the face index
    a = [i for i in [0, 1, 2, 3]
         if i not in [b, c, d]].pop()  # ... whatever is left

    # similarly in tet1 - B gives "q".  Let "r" be the edge sharing a
    # pi with "q".  Let "s" be the vertex not meeting the given face.
    # Let "p" be the remaining vertex.

    q = embed1.vertices()[B]
    r = shares_pi_with(angle[tet1.index()], q)
    s = embed1.vertices()[3]  # ... use the face index
    p = [i for i in [0, 1, 2, 3]
         if i not in [q, r, s]].pop()  # ... whatever is left

    # get colour of mobius strip
    pair_a = [b, c]
    pair_a.sort()
    mob_edge_a = tet0.edge(vert_pair_to_edge_num[tuple(pair_a)])
    pair_c = [a, b]
    pair_c.sort()
    mob_edge_c = tet0.edge(vert_pair_to_edge_num[tuple(pair_c)])
    assert mob_edge_a == mob_edge_c

    veering_colours = is_veering(tri, angle, return_type="veering_colours")
    assert veering_colours != False  # otherwise the triangulation is not veering
    mob_colour = veering_colours[mob_edge_a.index()]

    # Now actually do the surgery
    tet0.unjoin(d)  # same as tet1.unjoin(s)
    tet_new = tri.newTetrahedron()
    if mob_colour == "red":
        tet_new.join(0, tet_new, regina.Perm4(3, 0, 1, 2))
        tet_new.join(1, tet0, regina.Perm4(c, d, a, b))
        tet_new.join(2, tet1, regina.Perm4(q, p, s, r))
    else:
        tet_new.join(1, tet_new, regina.Perm4(1, 3, 0, 2))
        tet_new.join(2, tet0, regina.Perm4(a, b, d, c))
        tet_new.join(0, tet1, regina.Perm4(s, r, p, q))

    angle.append(0)  # this is the correct taut angle for our new tetrahedron
    assert is_taut(tri, angle)
    assert is_veering(tri, angle)
    return tri, angle, tet_new.triangle(3).index()
Beispiel #26
0
    def norm_ball(self):
        """
		Return the Thurston norm ball.
		"""
        if not self._QUIET:
            print('Computing Thurston norm unit ball... ', end='')
            try:
                sys.stdout.flush()
            except AttributeError:
                pass
        pts_dict, rays_dict = self._norm_ball_points

        ### If M is not hyperbolic then rays_dict should be non-empty, and the norm ball should be non-compact.
        ### The below attempts to compute the non-compact norm ball, but it may be wrong. This is because
        ### (it seems) some surfaces may not be realized as spun normal surfaces.

        if len(rays_dict) != 0:
            V = VectorSpace(RR, self.betti_number())
            ray_span = V.span([V(r) for r in rays_dict])
            keys = [p for p in pts_dict]
            for p in keys:
                if V(p) in ray_span:
                    _ = pts_dict.pop(p)

            polyhedron = Polyhedron(vertices=Matrix(pts_dict.keys()),
                                    rays=Matrix(rays_dict.keys()),
                                    base_ring=QQ,
                                    backend='cdd')
            rays = tuple([(rays_dict[tuple(i * vector(v))], i * vector(v))
                          for v in polyhedron.lines_list() for i in [1, -1]])
            Rays = [
                Ray(i, rays[i][0], rays[i][1], self) for i in range(len(rays))
            ]
            vertices = tuple([(pts_dict[tuple(v)], vector(v))
                              for v in polyhedron.vertices_list()
                              if not vector(v).is_zero()])
            Vertices = [
                NBVertex(i, vertices[i][0], vertices[i][1], self)
                for i in range(len(vertices))
            ]


#			projected_verts = {}
#			Vertices = []
#			for i,v in vertices:
#				v = vector(v)
#				v_proj, coeffs = orthogonal_proj(v, polyhedron.lines_list())
#				v_proj = tuple(v_proj)
#				boundary_slopes = []
#				num_boundary_comps = 0
#				euler_char = coeffs[0]*self.euler_char(pts_dict[tuple(v)])
#				for j in range(self.manifold().num_cusps()):
#					slope = vector((0,0))
#					slope += vector(self.boundary_slopes(i)[j])*coeffs[0]
#					for k in range(len(polyhedron().lines_list())):
#						slope += vector(self.boundary_slopes(rays_dict[tuple(polyhedron.lines_list()[k])])[j])*coeffs[k+1]
#					boundary_slopes.append(slope)
#					num_boundary_comps += gcd(slope[0],slope[1])
#				if v_proj not in projected_verts:
#					projected_verts[v_proj] = (num_boundary_comps, euler_char, boundary_slopes)
#			polyhedron = Polyhedron(vertices=Matrix(projected_verts.keys()), rays=Matrix(rays_dict.keys()), base_ring=QQ, backend='cdd')
#			for i in range(len(polyhedron.vertices_list())):
#				v = tuple(polyhedron.vertices_list()[i])
#				if v in pts_dict:
#					Vertices.append(NBVertex(i, pts_dict[v], vector(v), self))
#				else:
#					Vertices.append(NBVertex(i, None, vector(v), self, (projected_verts[v][0], projected_verts[v][1], projected_verts[v][2])))

        else:
            polyhedron = Polyhedron(vertices=Matrix(pts_dict.keys()),
                                    base_ring=QQ)
            vertices = tuple([(pts_dict[tuple(v)], vector(v))
                              for v in polyhedron.vertices_list()])

            Vertices = [
                NBVertex(i, vertices[i][0], vertices[i][1], self)
                for i in range(len(vertices))
            ]
            Rays = []

        ball = TNormBall(Vertices, Rays, polyhedron)
        if self.num_cusps() == 1 and self.betti_number() == 1:
            M = self.manifold().copy()
            (p, q) = self.manifold().homological_longitude()
            elem_divs = [
                div for div in M.homology().elementary_divisors() if div != 0
            ]
            M.dehn_fill((p, q))
            filled_elem_divs = [
                div for div in M.homology().elementary_divisors() if div != 0
            ]
            b = 1
            for div in elem_divs:
                b *= div
            for div in filled_elem_divs:
                b /= div
            A = self.manifold().alexander_polynomial()
            A_norm = QQ(A.degree() - 1)
            try:
                v = ball.vertices()[0]

            except IndexError:
                pass

            ### below needs to be fixed. Currently does not account for virtual fibers.
            if A_norm <= 0 or not A.is_monic():
                self._is_fibered = False
                ball._confirmed = True

            elif len(ball.vertices()) == 0:
                self._is_fibered = True
                polyhedron = Polyhedron(vertices=[[A_norm], [-A_norm]],
                                        base_ring=QQ)
                Vertices = [
                    NBVertex(0, None, (-1 / A_norm, ), self, (b, -A_norm, {
                        'outward': [(0, 1)],
                        'inward': [(0, 0)]
                    })),
                    NBVertex(1, None, (1 / A_norm, ), self, (b, -A_norm, {
                        'outward': [(0, -1)],
                        'inward': [(0, 0)]
                    }))
                ]
                ball = TNormBall(Vertices, Rays, polyhedron)
                ball._confirmed = True
            elif A_norm == abs(v.euler_char()):
                assert self.num_H1bdy_comps(v.qtons_index()) == b
                self._is_fibered = 'unknown'
                ball._confirmed = True
            elif A_norm < abs(v.euler_char()):
                #M = self.manifold().copy()
                #M.dehn_fill(M.homological_longitude())
                Mf = M.filled_triangulation()
                T = regina.Triangulation3(Mf._to_string())
                boo = T.intelligentSimplify()
                ns = regina.NormalSurfaces.enumerate(T, regina.NS_QUAD,
                                                     regina.NS_VERTEX,
                                                     regina.NS_ALG_DEFAULT)
                non_trivial = [
                    i for i in range(ns.size())
                    if ns.surface(i).isOrientable()
                ]
                if len(non_trivial) > 1:
                    non_trivial = [
                        i for i in non_trivial
                        if ns.surface(i).cutAlong().isConnected()
                    ]
                if len(non_trivial) == 0:  ## something is wrong!
                    print(
                        'Warning: failed to confirm that norm ball is correct (error: len(non_trivial)==0, culprit:M={}.'
                        .format(self.manifold().name()))
                    ball._confirmed = False
                elif len(non_trivial) >= 1:
                    genus = min([
                        (2 - regina_to_sage_int(ns.surface(i).eulerChar())) / 2
                        for i in non_trivial
                    ])
                    if 2 * genus - 2 + b == abs(v.euler_char()):
                        self._is_fibered = False
                        ball._confirmed = True
                    elif 2 * genus - 2 + b == A_norm:
                        multiplier = 1 / QQ(abs(v.euler_char()) / A_norm)
                        if self.uses_simplicial_homology() == True:
                            simplicial_class = multiplier * self.simplicial_class(
                                v.qtons_index())
                        else:
                            simplicial_class = None
                        self._is_fibered == True
                        polyhedron = Polyhedron(vertices=[[A_norm], [-A_norm]],
                                                base_ring=QQ)
                        Vertices = [
                            NBVertex(0, None, (-1 / A_norm, ), self,
                                     (b, -A_norm, {
                                         'outward': [(0, 1)],
                                         'inward': [(0, 0)]
                                     })),
                            NBVertex(1, None, (1 / A_norm, ), self,
                                     (b, -A_norm, {
                                         'outward': [(0, -1)],
                                         'inward': [(0, 0)]
                                     }))
                        ]
                        ball = TNormBall(Vertices, Rays, polyhedron)
                        ball._confirmed = True
                    else:
                        print(
                            'Warning: failed to confirm that norm ball is correct (error: 2g-1!=A_norm or abs(X(S)), culprit:M={}.'
                            .format(self.manifold().name()))
                        ball._confirmed = False
            else:
                # if we are here, then something is wrong, and the following will cause an error to be thrown.
                assert A_norm <= abs(
                    v.euler_char())  # (this should never happen)
        if not self._QUIET:
            print('Done.')
            try:
                sys.stdout.flush()
            except AttributeError:
                pass
        return ball
Beispiel #27
0
def is_edge_orientable(tri, angle, return_type="boolean"):
    """
    checks to see if this veering triangulation is edge orientable. 
    If return type is "tri angle" it returns the edge orientable double cover with its angle structure. 
    Note that this is disconnected if and only if the given triangulation is edge orientable
    """
    # return type can be "boolean", "veering_tet_vert_nums", or "tri angle"
    n = tri.countTetrahedra()
    veering_colours = is_veering(tri, angle, return_type="veering_colours")
    assert veering_colours != False  # so we are veering
    tet_vert_coorientations = is_transverse_taut(
        tri, angle, return_type="tet_vert_coorientations")

    ### assumption: the first n tetrahedra have upper edge oriented according to Regina, the last n have it against Regina

    ### build our own model vertex numbering for each tetrahedron as follows:
    ### the top edge e is oriented by regina numbering, and gets vert_nums 1 and 2 in our ordering
    ### an equatorial edge e' of the same colour as e shares a vertex v with it.
    ### e and e' both point away from v or towards it. If away then the other end of e' is 3,
    ### else, the other end is 0

    veering_tet_vert_nums = [
    ]  ### will populate with regina's vert nums, but our order.
    ### that is, veering_tet_vert_nums[1] and [2] will be the regina vert nums for the ends of the top edge

    for i in range(n):
        tet = tri.tetrahedron(i)
        veering_vert_nums = [None, None, None, None]
        top_vert_pair = get_tet_top_vert_nums(tet_vert_coorientations, i)
        # print(i, top_vert_pair)
        if not regina_edge_orientation_agrees(tet, top_vert_pair):
            top_vert_pair.reverse()
            assert regina_edge_orientation_agrees(tet, top_vert_pair)
        veering_vert_nums[1] = top_vert_pair[0]
        veering_vert_nums[2] = top_vert_pair[1]

        top_edge_num = vert_pair_to_edge_num[tuple(top_vert_pair)]
        top_edge_col = veering_colours[tet.edge(top_edge_num).index()]

        bottom_vert_pair = list(set(range(4)) - set(top_vert_pair))
        bv, bv2 = bottom_vert_pair  ## choose arbitrarily, now find which edge from the top vertices has same colour as bv
        for j, tv in enumerate(top_vert_pair):
            edge_col = veering_colours[tet.edge(
                vert_pair_to_edge_num[(bv, tv)]).index()]
            if edge_col == top_edge_col:
                if j == 0:  ### bv shares an edge of same colour as top with tail of top edge
                    veering_vert_nums[3] = bv
                    veering_vert_nums[0] = bv2
                else:  ### bv shares an edge of same colour as top with head of top edge
                    veering_vert_nums[0] = bv
                    veering_vert_nums[3] = bv2
        veering_tet_vert_nums.append(veering_vert_nums)
    # print('veering_tet_vert_nums', veering_tet_vert_nums)
    if return_type == "veering_tet_vert_nums":
        return veering_tet_vert_nums

    ### Now, when we glue two tetrahedra together along a face, the first of the three vertices in the veering_vert_num order
    ### on that tet's face glues to the first of the three vertices on the other tet's face, or to the third.
    ### depending on this, we go to the other part of the double cover, or not

    cover_tri = regina.Triangulation3()  ## starts empty
    for i in range(2 * n):
        cover_tri.newTetrahedron()
    tet_faces = []
    for i in range(n):
        for j in range(4):
            tet_faces.append((i, j))
    while len(tet_faces) > 0:
        i, j = tet_faces.pop()
        tet = tri.tetrahedron(i)
        adjtet, adjgluing = tet.adjacentTetrahedron(j), tet.adjacentGluing(j)
        iN, jN = adjtet.index(), adjgluing[j]
        tet_faces.remove((iN, jN))

        cover_tets = [cover_tri.tetrahedron(i), cover_tri.tetrahedron(i + n)]
        cover_tetsN = [
            cover_tri.tetrahedron(iN),
            cover_tri.tetrahedron(iN + n)
        ]
        ### find the veering indices for the verts in the gluing on this tet and on adjtet

        face_veering_nums = veering_tet_vert_nums[i][:]
        face_veering_nums.remove(j)
        a, b, c = face_veering_nums
        neighbour_face_veering_nums = veering_tet_vert_nums[iN][:]
        neighbour_face_veering_nums.remove(jN)
        aN, bN, cN = neighbour_face_veering_nums
        assert adjgluing[b] == bN  ### middles should match
        if adjgluing[a] == aN:  ### veering orderings agree across the gluing
            assert adjgluing[c] == cN
            for k in range(2):
                cover_tets[k].join(j, cover_tetsN[k], adjgluing)
        else:  ### veering orderings disagree across the gluing
            assert adjgluing[a] == cN and adjgluing[c] == aN
            for k in range(2):
                cover_tets[k].join(j, cover_tetsN[(k + 1) % 2], adjgluing)
    assert not cover_tri.hasBoundaryFacets()
    assert is_veering(cover_tri, angle + angle)

    if return_type == "boolean":
        return not cover_tri.isConnected(
        )  ### not connected if the original veering triangulation is edge orientable
    else:
        assert return_type == "tri angle"
        return cover_tri, angle + angle
Beispiel #28
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")