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]
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
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)
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
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
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
# 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)
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()
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()
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() ]
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())
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
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
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")
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