Example #1
0
    def exotic_angles(self):
        """Given a transverse taut veering triangulation, find the exotic taut structures (as defined in Futer-Gueritaud)"""

        ### From Futer-Gueritaud's leading-trailing deformations, the exotic angles should be derivable even without
        ### a transverse-taut structure, but it seems easier with.

        exotic_upper = []
        exotic_lower = []
        for i, tet in enumerate(self.tri.tetrahedra()):
            top_edge, bottom_edge = get_tet_top_and_bottom_edges(
                self.coorientations, tet)
            top_colour, bottom_colour = self.veering_colours[
                top_edge.index()], self.veering_colours[bottom_edge.index()]
            tet_angle = self.angle[i]

            equator = (tet_angle + 1) % 3
            equator_colour = self.veering_colours[tet.edge(equator).index()]
            assert self.veering_colours[tet.edge(
                5 - equator).index()] == equator_colour
            if equator_colour == top_colour:
                exotic_upper.append(equator)
            else:
                exotic_upper.append((equator + 1) % 3)

            if equator_colour == bottom_colour:
                exotic_lower.append(equator)
            else:
                exotic_lower.append((equator + 1) % 3)
        assert is_taut(self.tri, exotic_upper) and is_taut(
            self.tri, exotic_lower)
        return [exotic_upper, exotic_lower]
Example #2
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
Example #3
0
def mutate(tri,
           angle,
           weights,
           isom,
           tet_vert_coorientations=None,
           quiet=False):

    if tet_vert_coorientations == None:
        tet_vert_coorientations = is_transverse_taut(
            tri, angle, return_type="tet_vert_coorientations")

    regluing = surface_isom_to_regluing_pattern(tri, angle, weights, isom,
                                                tet_vert_coorientations)
    #print('regluing data:', regluing)

    # first unglue all faces that need to be unglued, then reglue them via isom
    # can't do both at the same step, because regina goes crazy when you try to glue a face to a face which is already glued somewhere else
    for x in regluing:
        tet_below_top_triangle = tri.tetrahedron(x[0])
        which_face = x[1]
        tet_below_top_triangle.unjoin(which_face)
        #print('unglued face', which_face, 'of tet', tet_below_top_triangle.index())

    for x in regluing:
        tet_below_top_triangle = tri.tetrahedron(x[0])
        which_face = x[1]
        tet_above_image_triangle = tri.tetrahedron(x[2])
        perm = x[3]
        tet_below_top_triangle.join(which_face, tet_above_image_triangle, perm)
        #print('glued face', which_face, 'of tet', tet_below_top_triangle.index(), 'to tet', tet_above_image_triangle.index(), 'via', perm)

    assert tri.isValid()
    assert tri.countBoundaryFacets() == 0

    if quiet == False:
        print('mutant isosig:', tri.isoSig())
        print('taut:', is_taut(tri, angle))
        if is_taut(tri, angle):
            print('transverse taut:', is_transverse_taut(tri, angle))
            print('veering:', is_veering(tri, angle))
        else:
            edge_num = tri.countEdges()
            print('Got', edge_num, 'edges out of', tri.countTetrahedra())
            totals = is_taut(tri, angle, return_totals=True)
            print('Angles:', totals)
Example #4
0
 def __init__(self, tri, angle, tet_shapes=None):
     self.tri = tri
     self.angle = angle
     assert is_taut(tri, angle)
     self.coorientations = is_transverse_taut(
         tri, angle, return_type="tet_vert_coorientations")
     assert self.coorientations != False
     self.veering_colours = is_veering(tri,
                                       angle,
                                       return_type="veering_colours")
     assert self.veering_colours != False
     self.tet_types = is_veering(tri, angle, return_type="tet_types")
     self.tet_shapes = tet_shapes
Example #5
0
def can_deal_with_reduced_angle(tri, angle):
    """
    Returns True or False, as our techniques can recognise the given
    reduced angle structure.
    """
    if not is_taut(tri, angle):
        return False
    if is_veering(tri, angle):
        return True
    if is_layered(tri,
                  angle):  # has_internal_singularities is not needed here.
        return True
    return False
Example #6
0
def is_transverse_taut(triangulation,
                       taut_angle_struct,
                       return_type="boolean"):
    assert is_taut(triangulation, taut_angle_struct)
    assert triangulation.isOriented()
    coorientations = []
    for i in range(triangulation.countTetrahedra()):
        coorientations.append([0, 0, 0, 0])  # +1 is out of tet, -1 is into tet
    # first decide coorientation for tet 0
    edgepair = taut_angle_struct[0]
    if not add_coors_with_check(triangulation, coorientations, 0, edgepair,
                                +1):  # choose arbitrary direction for tet 0
        return False  # tet 0 could have self gluings incompatible with the transverse taut structure, so we could fail here

    explored_tetrahedra = [0]
    frontier_tet_faces = [(0, 0), (0, 1), (0, 2), (0, 3)]
    while len(frontier_tet_faces) > 0:
        my_tet_num, my_face_num = frontier_tet_faces.pop()
        my_tet = triangulation.tetrahedron(my_tet_num)
        neighbour_tet = my_tet.adjacentSimplex(my_face_num)
        neighbour_face_num = my_tet.adjacentGluing(my_face_num)[my_face_num]
        if neighbour_tet.index() in explored_tetrahedra:
            frontier_tet_faces.remove(
                (neighbour_tet.index(), neighbour_face_num))
        else:
            newcoor = -coorientations[my_tet_num][
                my_face_num]  # opposite of the adjacent face's coorientation
            edgepair = taut_angle_struct[neighbour_tet.index()]
            # now calculate the direction that the flow points through neighbour_tet
            if neighbour_face_num in [
                    vertexSplit[edgepair][i] for i in range(2)
            ]:
                direction = newcoor  # agrees with newcoor
            else:
                direction = -newcoor
            if not add_coors_with_check(
                    triangulation, coorientations, neighbour_tet.index(),
                    taut_angle_struct[neighbour_tet.index()], direction):
                return False

            frontier_tet_faces.extend([(neighbour_tet.index(), i)
                                       for i in range(4)
                                       if i != neighbour_face_num])
            explored_tetrahedra.append(neighbour_tet.index())
    if return_type == "tet_vert_coorientations":
        return coorientations
    elif return_type == "face_coorientations":
        return convert_tetrahedron_coorientations_to_faces(
            triangulation, coorientations)
    else:
        return True
Example #7
0
    # sig = 'eLMkbcddddedde_2100'
    # sig = 'eLMkbcdddhxqlm_1200'
    # sig = 'eLAkaccddjsnak_2001'
    # sig = 'eLAkbbcdddhwqj_2102'
    # sig = 'fLLQcbcdeeelonlel_02211'  ## should have three different midsurface bdy components, so four total after drilling
    # sig = 'fLLQcbddeeehrcdui_12000' ## five boundary components
    # sig = 'fLAMcbbcdeedhwqqs_21020'
    # sig = 'qvvLPQwAPLQkfhgffijlkmnopoppoaaaaaaaaaaaaadddd_1020212200111100'
    # t = excise_fans(sig)
    # t.save('excise_fans_' + sig + '.rga')

    # t0, _ = isosig_to_tri_angle('cPcbbbdxm_10')
    # t1, _ = isosig_to_tri_angle('cPcbbbiht_12')

    # print t.isIsomorphicTo(t0) != None or t.isIsomorphicTo(t1) != None

    # print fan_stacks(sig)

    import veering
    import transverse_taut
    import taut
    from file_io import parse_data_file
    census = parse_data_file('Data/veering_census.txt')

    for sig in census[:200]:
        tri, angle = excise_fans(sig)
        print((sig, tri.countTetrahedra(), angle, taut.is_taut(tri, angle)))
        assert veering.is_veering(tri, angle)
        assert transverse_taut.is_transverse_taut(tri, angle)
        # print sig, fan_stacks(sig)
Example #8
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()
Example #9
0
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()
Example #10
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() ]
Example #11
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())    
Example #12
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
Example #13
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
Example #14
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")
Example #15
0
def is_veering(tri, angle, return_type="boolean"):
    """
    checks to see if this triangulation with taut angle structure is
    veering
    """
    # return type can be "boolean", "veering_colours", "veering_partition" or "num_toggles"
    # all return False if it is not veering. If it is veering:
    # "veering_colours" returns the data of which edges are "blue" (bLue - Left veering) or "red" (Right veering) veering,
    # "veering_partition" rewrites this as a list of the "blue" edges followed by a list of the "red" edges
    # "num_toggles" returns the number of toggle tetrahedra
    # "tet_types" returns list of tets, either "toggle", "red" or "blue" veering

    assert is_taut(tri, angle)

    # first do quick test on edge degrees
    for e in tri.edges():
        if e.degree() < 4:
            return False

    # now do proper check
    veering_colours = ["-"] * tri.countEdges()
    for tet_num in range(tri.countTetrahedra()):
        tet = tri.tetrahedron(tet_num)
        edgepair = angle[tet_num]
        # now label edges L or R
        L_edgepair = (
            edgepair + 1
        ) % 3  # L or R depends on orientation, this takes us from pi edge pair to L edge pair
        L_edge_1, L_edge_2 = L_edgepair, 5 - L_edgepair
        L_edge_1_index = tet.edge(L_edge_1).index()
        L_edge_2_index = tet.edge(L_edge_2).index()
        if veering_colours[L_edge_1_index] == "red" or veering_colours[
                L_edge_2_index] == "red":
            return False
        else:
            veering_colours[L_edge_1_index] = "blue"
            veering_colours[L_edge_2_index] = "blue"
        R_edgepair = (edgepair + 2) % 3
        R_edge_1, R_edge_2 = R_edgepair, 5 - R_edgepair
        R_edge_1_index = tet.edge(R_edge_1).index()
        R_edge_2_index = tet.edge(R_edge_2).index()
        if veering_colours[R_edge_1_index] == "blue" or veering_colours[
                R_edge_2_index] == "blue":
            return False
        else:
            veering_colours[R_edge_1_index] = "red"
            veering_colours[R_edge_2_index] = "red"
    if return_type == "veering_colours":
        return veering_colours
    elif return_type == "tet_types" or return_type == "num_toggles":
        tet_types = []
        for tet_num in range(tri.countTetrahedra()):
            tet_types.append(tet_type(tri, tet_num, veering_colours))
        if return_type == "tet_types":
            return tet_types
        else:
            return tet_types.count("toggle")
    elif return_type == "veering_partition":
        Llist = []
        Rlist = []
        for i, direction in enumerate(veering_colours):
            if direction == "blue":
                Llist.append(i)
            else:
                Rlist.append(i)
        return (Llist, Rlist)
    else:
        assert return_type == 'boolean'
        return True