def make_continent_naive(veering_isosig, max_num_tetrahedra=50): tri, angle = isosig_to_tri_angle(veering_isosig) vt = veering_triangulation(tri, angle) #, tet_shapes = tet_shapes) initial_tet_face = tet_face(vt, 0, 0, verts_pos=[None, None, None, None]) con = continent(vt, initial_tet_face) #, desired_vertices = desired_vertices ) con.build_naive(max_num_tetrahedra=max_num_tetrahedra) con.make_convex() print(len(con.triangles), len(con.vertices), len(con.tetrahedra)) return con
def fan_stacks(tri, angle): """Find lists of tetrahedra that make up the fans, including the toggles at either end""" vt = veering_triangulation(tri, angle) veering_colours = vt.veering_colours ## "red" or "blue" tet_types = vt.tet_types ### "toggle", "red" or "blue" tet_vert_coors = vt.coorientations toggle_nums = [ i for i in range(len(tet_types)) if tet_types[i] == "toggle" ] out = [] for toggle_num in toggle_nums: tops, bottoms = get_top_and_bottom_nums(tet_vert_coors, toggle_num) for i in range(2): tet_nums = [toggle_num] trailing_vertex = bottoms[i] other_b = bottoms[(i + 1) % 2] t0, t1 = tops if vt.get_edge_between_verts_colour( toggle_num, (t0, t1)) == vt.get_edge_between_verts_colour( toggle_num, (t0, other_b)): leading_vertex = t0 else: leading_vertex = t1 tet = tri.tetrahedron(toggle_num) while True: gluing = tet.adjacentGluing(trailing_vertex) tet = tet.adjacentTetrahedron(trailing_vertex) # e0, e1 = gluing[e0], gluing[e1] leading_vertex, trailing_vertex = gluing[ trailing_vertex], gluing[leading_vertex] tet_nums.append(tet.index()) if tet_types[tet.index()] == "toggle": break out.append(tet_nums) return out
def excise_fans(tri, angle, fan_nums=None): vt = veering_triangulation(tri, angle) veering_colours = vt.veering_colours ## "red" or "blue" tet_types = vt.tet_types ### "toggle", "red" or "blue" tet_vert_coors = vt.coorientations if fan_nums == None: ## do all fans fan_nums = [ n for n in range(len(tet_types)) if tet_types[n] != "toggle" ] excisedAngle = angle[:] for fan_num in sorted(fan_nums, reverse=True): del excisedAngle[fan_num] minority_edge_pairs = [] for fan_num in fan_nums: assert tet_types[fan_num] != "toggle" tops, bottoms = get_top_and_bottom_nums(tet_vert_coors, fan_num) if 0 in tops: pi_pair = list(tops) else: pi_pair = list(bottoms) other = pi_pair[(pi_pair.index(0) + 1) % 2] for i in range(1, 4): if i != other: if vt.get_edge_between_verts_colour( fan_num, (0, i)) != tet_types[fan_num]: ### "red" or "blue" last = 6 - i - other ## the fourth vertex minority_edge_pairs.append([(0, i), (other, last)]) break excisedTri = regina.Triangulation3(tri) ### copy fan_tets = [excisedTri.tetrahedron(fan_num) for fan_num in fan_nums] for k, tet in enumerate(fan_tets): # print 'k', k, 'tet.index', tet.index() minority_edge_pair = minority_edge_pairs[k] # print minority_edge_pair ### record gluings for neighbours neighbours = [tet.adjacentSimplex(i) for i in range(4)] gluings = [tet.adjacentGluing(i) for i in range(4)] # print [neighbour.index() for neighbour in neighbours] # print gluings tet.isolate() ## now glue neighbours to each other if tet not in neighbours: to_glue = [0, 1, 2, 3] while to_glue != []: i = to_glue.pop() if i in minority_edge_pair[0]: j = minority_edge_pair[0][(minority_edge_pair[0].index(i) + 1) % 2] else: j = minority_edge_pair[1][(minority_edge_pair[1].index(i) + 1) % 2] teti = neighbours[i] tetj = neighbours[j] teti.join( gluings[i][i], tetj, gluings[j] * regina.Perm4(j, i) * (gluings[i].inverse())) to_glue.remove(j) # print i,j else: ### tet is glued to itself, which makes it trickier to remove ### first, find self-gluings self_gluings = [] self_gluings = [neighbours.index(tet)] self_gluings.append(neighbours.index(tet, self_gluings[0] + 1)) ### add second entry other_gluings = [0, 1, 2, 3] other_gluings.remove(self_gluings[0]) other_gluings.remove(self_gluings[1]) i, j = other_gluings teti = neighbours[i] tetj = neighbours[j] if i in minority_edge_pair[0]: p = minority_edge_pair[0][(minority_edge_pair[0].index(i) + 1) % 2] q = minority_edge_pair[1][(minority_edge_pair[1].index(j) + 1) % 2] else: p = minority_edge_pair[1][(minority_edge_pair[1].index(i) + 1) % 2] q = minority_edge_pair[0][(minority_edge_pair[0].index(j) + 1) % 2] teti.join( gluings[i][i], tetj, gluings[j] * regina.Perm4(j, q) * gluings[p] * regina.Perm4(p, i) * (gluings[i].inverse())) excisedTri.removeTetrahedron(tet) return excisedTri, excisedAngle
def make_continent_drill_flow_cycle(veering_isosig, flow_cycle, num_steps): tri, angle = isosig_to_tri_angle(veering_isosig) vt = veering_triangulation(tri, angle) #, tet_shapes = tet_shapes) ### format for loops: it is a list of tuples, ### each tuple is (tet_index, edge_index within this tet that we exit through) tet_0 = flow_cycle[0][0] face_0 = 0 ### only used for initial numbering of vertices of the continent, so who cares initial_tet_face = tet_face(vt, tet_0, face_0, verts_pos=[None, None, None, None]) con = continent(vt, initial_tet_face) side_face_collections, side_tet_collections = edge_side_face_collections( vt.tri, vt.angle, tet_vert_coorientations=vt.coorientations, return_tets=True, order_bottom_to_top=False) # print('sfc', side_face_collections) # print('stc', side_tet_collections) ### identify the next edge in the cycle init_tetrahedron = con.tetrahedra[0] init_edge = flow_edge_in_continent(init_tetrahedron, flow_cycle[0][1]) flow_tetrahedra = [init_tetrahedron] flow_edges = [init_edge] ### both in the continent upwards_flow_index = 0 downwards_flow_index = 0 for i in range(num_steps): # print(i) if i % 2 == 0: ### go up edge = flow_edges[-1] last_tet = None while True: con.update_boundary() upper_boundary_triangles = [ t for t in edge.boundary_triangles if t.is_upper ] if len(upper_boundary_triangles) == 0: break ## out of while loop last_tet = con.bury(upper_boundary_triangles[0]) assert last_tet != None upwards_flow_index = (upwards_flow_index + 1) % len(flow_cycle) assert last_tet.index == flow_cycle[upwards_flow_index][0] flow_tetrahedra.append(last_tet) flow_edges.append( flow_edge_in_continent(last_tet, flow_cycle[upwards_flow_index][1])) con.make_convex( ) ### could this take us further up dual_cycle? We don't think so else: ### go down tet = flow_tetrahedra[0] edge = tet.lower_edge() flow_edges = [edge] + flow_edges ### now build the continent to get new_lowest_tet manifold_edge = tri.edge(edge.index) downwards_flow_index = (downwards_flow_index - 1) % len(flow_cycle) ### is the flow cycle vertical through the tetrahedron? tet_below = get_tet_above_edge( vt.tri, vt.angle, manifold_edge, tet_vert_coorientations=vt.coorientations, get_tet_below_edge=True) tet_below_num = tet_below.index() top_vert_nums = get_tet_top_vert_nums(vt.coorientations, tet_below_num) top_edge_num = vert_pair_to_edge_num[tuple(top_vert_nums)] if (tet_below_num, top_edge_num) == flow_cycle[ downwards_flow_index]: ### flow cycle went straight up while True: con.update_boundary() lower_boundary_triangles = [ t for t in edge.boundary_triangles if not t.is_upper ] if len(lower_boundary_triangles) == 0: break ## out of while loop last_tet = con.bury(lower_boundary_triangles[0]) else: ### find which side of the edge our tet is in # print('edge index', edge.index) side_tet_collections_at_edge = side_tet_collections[ edge.index] ## index in the manifold side_face_collections_at_edge = side_face_collections[ edge.index] downward_path = None flow_step = flow_cycle[downwards_flow_index] for i, side_tet_collection in enumerate( side_tet_collections_at_edge): if flow_step in side_tet_collection: downward_path = side_tet_collection[: side_tet_collection .index(flow_step) + 1] downward_path_faces = side_face_collections_at_edge[ i][:side_tet_collection.index(flow_step) + 1] assert downward_path != None for j, (tet_num, edge_num) in enumerate(downward_path): con.update_boundary() lower_boundary_triangles = [ t for t in edge.boundary_triangles if not t.is_upper and t.index == downward_path_faces[j][0] ] assert len(lower_boundary_triangles) == 1 last_tet = con.bury(lower_boundary_triangles[0]) assert last_tet != None flow_tetrahedra = [last_tet] + flow_tetrahedra con.make_convex( ) ### could this take us further down dual_cycle? We don't think so con.update_boundary() return con, flow_tetrahedra, flow_edges
def make_continent_drill_dual_cycle(veering_isosig, dual_cycle, num_steps): tri, angle = isosig_to_tri_angle(veering_isosig) vt = veering_triangulation(tri, angle) #, tet_shapes = tet_shapes) ### initial tetrahedron is above face 0 of the dual cycle face0 = vt.tri.triangles()[dual_cycle[0]] embeds = face0.embeddings() tet_0, face_0 = None, None for embed in embeds: tet_num = embed.simplex().index() face_num = embed.face() if vt.coorientations[tet_num][face_num] == -1: ## into the tet tet_0, face_0 = tet_num, face_num break assert tet_0 != None and face_0 != None initial_tet_face = tet_face(vt, tet_0, face_0, verts_pos=[None, None, None, None]) con = continent(vt, initial_tet_face) ### identify the triangle corresponding to dual_cycle[1] for triangle in con.triangles: if not triangle.is_upper and triangle.index == dual_cycle[0]: lowest_triangle = triangle if triangle.is_upper and triangle.index == dual_cycle[1]: highest_triangle = triangle triangle_path = [lowest_triangle, highest_triangle] lowest_path_index = 0 highest_path_index = 1 # for i in range(num_steps): # last_added_tet = con.bury(highest_triangle) # ### find next_triangle # highest_triangle = None # for triangle in last_added_tet.upper_triangles: # if triangle.index == dual_cycle[(path_index + 1) % len(dual_cycle)]: # highest_triangle = triangle # break # assert highest_triangle != None # triangle_path.append(highest_triangle) # path_index = path_index + 1 # con.make_convex() ### could this take us further up dual_cycle? for i in range(num_steps): if i % 2 == 0: last_added_tet = con.bury(triangle_path[-1]) ## go up for triangle in last_added_tet.upper_triangles: if triangle.index == dual_cycle[(highest_path_index + 1) % len(dual_cycle)]: triangle_path.append(triangle) break highest_path_index = highest_path_index + 1 else: last_added_tet = con.bury(triangle_path[0]) ## go down for triangle in last_added_tet.lower_triangles: if triangle.index == dual_cycle[(lowest_path_index - 1) % len(dual_cycle)]: triangle_path.insert(0, triangle) break lowest_path_index = lowest_path_index - 1 con.make_convex() ### could this take us further up dual_cycle? con.update_boundary() con.triangle_path = triangle_path return con
def initial_path_single(tri, angle, tet_shapes, verbose=0.0): vt = veering_triangulation(tri, angle, tet_shapes=tet_shapes) lower_faces = [i for i in range(4) if vt.coorientations[0][i] == -1] verts_CP1 = [[1, 0], [0, 1], [1, 1], [tet_shapes[0], 1]] return ([ct_edge(0, lower_faces[1], verts_CP1, 0, vt)], [])
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 drill_midsurface_bdy(tri, angle): vt = veering_triangulation(tri, angle) veering_colours = vt.veering_colours ## "red" or "blue" tet_types = vt.tet_types ### "toggle", "red" or "blue" tet_vert_coors = vt.coorientations drilledTri = regina.Triangulation3() subtet_indices = [] subtet_addresses = [] num_toggles = tet_types.count("toggle") drilled_num_tet = 8 * num_toggles + 4 * (tri.countTetrahedra() - num_toggles) meridians = [] ### in each toggle, we record the meridians for the two boundary components we are drilling ### note that this repeats the same meridian many times - the construction here is purely local: it is much ### easier to figure out which are duplicates of which later for i in range(tri.countTetrahedra()): tet = tri.tetrahedron(i) first_subtet_index = drilledTri.countTetrahedra() if tet_types[i] == 'toggle': num_to_add = 8 else: num_to_add = 4 for j in range(num_to_add): drilledTri.newTetrahedron() subtet_indices.append( range(first_subtet_index, first_subtet_index + num_to_add)) ### glue sub-tetrahedra together. Note that some tetrahedra will be negatively oriented [(t0, t1), (b0, b1)] = get_top_and_bottom_nums(tet_vert_coors, i) this_tet_subtet_addresses = {} if tet_types[i] == 'toggle': ## first two subtetrahedra are top two, second two are bottom two, then the four side tetrahedra tet_t0, tet_t1 = drilledTri.tetrahedron( first_subtet_index), drilledTri.tetrahedron( first_subtet_index + 1) # tet_ti is opposite vertex bi tet_b0, tet_b1 = drilledTri.tetrahedron(first_subtet_index + 2), drilledTri.tetrahedron( first_subtet_index + 3) # tet_bi is opposite vertex ti tet_s00 = drilledTri.tetrahedron(first_subtet_index + 4) tet_s01 = drilledTri.tetrahedron(first_subtet_index + 5) tet_s10 = drilledTri.tetrahedron(first_subtet_index + 6) tet_s11 = drilledTri.tetrahedron(first_subtet_index + 7) # tet_sij meets vertices ti and bj ## keys are (vert not on face, vert not on edge), returns the subtet which meets the face, edge this_tet_subtet_addresses[(b0, b1)] = tet_t0 this_tet_subtet_addresses[(b1, b0)] = tet_t1 this_tet_subtet_addresses[(t0, t1)] = tet_b0 this_tet_subtet_addresses[(t1, t0)] = tet_b1 this_tet_subtet_addresses[(t1, b1)] = tet_s00 this_tet_subtet_addresses[(b1, t1)] = tet_s00 this_tet_subtet_addresses[(t1, b0)] = tet_s01 this_tet_subtet_addresses[(b0, t1)] = tet_s01 this_tet_subtet_addresses[(t0, b1)] = tet_s10 this_tet_subtet_addresses[(b1, t0)] = tet_s10 this_tet_subtet_addresses[(t0, b0)] = tet_s11 this_tet_subtet_addresses[(b0, t0)] = tet_s11 tet_t0.join(b0, tet_t1, regina.Perm4(b0, b1, b1, b0, t0, t0, t1, t1)) ## b0 <-> b1, t0 and t1 fixed tet_b0.join(t0, tet_b1, regina.Perm4(t0, t1, t1, t0, b0, b0, b1, b1)) ## t0 <-> t1, b0 and b1 fixed tet_s00.join(b0, tet_t1, regina.Perm4(t0, t0, b1, b1, t1, b0, b0, t1)) tet_s00.join(t0, tet_b1, regina.Perm4(b0, b0, t1, t1, b1, t0, t0, b1)) tet_s01.join(b1, tet_t0, regina.Perm4(t0, t0, b0, b0, t1, b1, b1, t1)) tet_s01.join(t0, tet_b1, regina.Perm4(b1, b1, t1, t1, b0, t0, t0, b0)) tet_s10.join(b0, tet_t1, regina.Perm4(t1, t1, b1, b1, t0, b0, b0, t0)) tet_s10.join(t1, tet_b0, regina.Perm4(b0, b0, t0, t0, b1, t1, t1, b1)) tet_s11.join(b1, tet_t0, regina.Perm4(t1, t1, b0, b0, t0, b1, b1, t0)) tet_s11.join(t1, tet_b0, regina.Perm4(b1, b1, t0, t0, b0, t1, t1, b0)) ### meridian around upper hole meridian = [0] * (3 * drilled_num_tet) meridian[3 * tet_t1.index() + unsorted_vert_pair_to_edge_pair[b0, t0]] = -1 meridian[3 * tet_s00.index() + unsorted_vert_pair_to_edge_pair[b1, t1]] = 1 meridian[3 * tet_b1.index() + unsorted_vert_pair_to_edge_pair[t0, t1]] = 1 meridian[3 * tet_s01.index() + unsorted_vert_pair_to_edge_pair[b0, t1]] = 1 meridian[3 * tet_t0.index() + unsorted_vert_pair_to_edge_pair[b1, t0]] = -1 meridians.append(meridian) ### meridian around lower hole - swap b with t everywhere. Note this also swaps s01 with s10 meridian = [0] * (3 * drilled_num_tet) meridian[3 * tet_b1.index() + unsorted_vert_pair_to_edge_pair[t0, b0]] = -1 meridian[3 * tet_s00.index() + unsorted_vert_pair_to_edge_pair[t1, b1]] = 1 meridian[3 * tet_t1.index() + unsorted_vert_pair_to_edge_pair[b0, b1]] = 1 meridian[3 * tet_s10.index() + unsorted_vert_pair_to_edge_pair[t0, b1]] = 1 meridian[3 * tet_b0.index() + unsorted_vert_pair_to_edge_pair[t1, b0]] = -1 meridians.append(meridian) else: ## fan # first two tetrahedra are the top and bottom tet_t, tet_b = drilledTri.tetrahedron( first_subtet_index), drilledTri.tetrahedron( first_subtet_index + 1) # second two tetrahedra are the sides, s0 meets vertex t0, s1 meets vertex t1 tet_s0, tet_s1 = drilledTri.tetrahedron(first_subtet_index + 2), drilledTri.tetrahedron( first_subtet_index + 3) # tet_types[i] == 'blue' this_tet_subtet_addresses[(b0, b1)] = tet_t this_tet_subtet_addresses[(b1, b0)] = tet_t this_tet_subtet_addresses[(t0, t1)] = tet_b this_tet_subtet_addresses[(t1, t0)] = tet_b ### if this is a blue fan tet then each side tet meet a blue edge # tet_types[i] could be 'blue' or 'red' ### find which bottom vertex, when linked to t0, gives an edge of the correct colour if vt.get_edge_between_verts_colour(i, (t0, b0)) == tet_types[i]: s0b = b0 ## bottom vert of s0 s1b = b1 ## bottom vert of s1 this_tet_subtet_addresses[(t0, b0)] = tet_s1 this_tet_subtet_addresses[(b0, t0)] = tet_s1 this_tet_subtet_addresses[(t1, b1)] = tet_s0 this_tet_subtet_addresses[(b1, t1)] = tet_s0 else: s0b = b1 ## bottom vert of s0 s1b = b0 ## bottom vert of s1 this_tet_subtet_addresses[(t0, b1)] = tet_s1 this_tet_subtet_addresses[(b1, t0)] = tet_s1 this_tet_subtet_addresses[(t1, b0)] = tet_s0 this_tet_subtet_addresses[(b0, t1)] = tet_s0 tet_s0.join(s0b, tet_t, regina.Perm4(t0, t0, s0b, t1, s1b, s1b, t1, s0b)) tet_s0.join(t0, tet_b, regina.Perm4(s0b, s0b, t0, s1b, t1, t1, s1b, t0)) tet_s1.join(s1b, tet_t, regina.Perm4(t1, t1, s1b, t0, s0b, s0b, t0, s1b)) tet_s1.join(t1, tet_b, regina.Perm4(s1b, s1b, t1, s0b, t0, t0, s0b, t1)) subtet_addresses.append(this_tet_subtet_addresses) ### now glue subtetrahedra from different original tetrahedra together ### fan tetrahedra only glue two of three subtetrahedra on each face. ### toggles glue one of their subfaces all the way through to the next toggle... unglued_flags = [] for f in range(tri.countTriangles()): for e in range(3): unglued_flags.append((f, e)) while unglued_flags != []: (f, e) = unglued_flags.pop() # print f,e face = tri.triangle(f) embed0 = face.embedding(0) embed1 = face.embedding(1) tet_index_0 = embed0.simplex().index() tet_index_1 = embed1.simplex().index() face0 = embed0.face() face1 = embed1.face() vertperm0 = embed0.vertices() vertperm1 = embed1.vertices() edge0 = vertperm0[e] edge1 = vertperm1[e] otherverts0 = [0, 1, 2, 3] otherverts0.remove(face0) otherverts0.remove(edge0) otherverts1 = [0, 1, 2, 3] otherverts1.remove(face1) otherverts1.remove(edge1) if (face0, edge0) in subtet_addresses[tet_index_0]: subtet0 = subtet_addresses[tet_index_0][(face0, edge0)] else: subtet0 = None if (face1, edge1) in subtet_addresses[tet_index_1]: subtet1 = subtet_addresses[tet_index_1][(face1, edge1)] else: subtet1 = None if subtet0 == None and subtet1 == None: pass ### both are fans, with no subtet, skip elif subtet0 != None and subtet1 != None: ### glue u, v = otherverts0 tet0perm = regina.Perm4(face0, edge0, edge0, face0, u, u, v, v) u, v = otherverts1 tet1perm = regina.Perm4(face1, edge1, edge1, face1, u, u, v, v) gluing = embed0.simplex().adjacentGluing(face0) subtet0.join(edge0, subtet1, tet1perm * gluing * tet0perm) ### perms act on left else: ### we have to walk around to find the right place to glue this toggle subtet, and remove the other unglued flag from the list if subtet1 == None: assert tet_types[tet_index_0] == 'toggle' toggle_tet_index = tet_index_0 toggle_face = face0 toggle_edge = edge0 subtet = subtet0 else: assert tet_types[tet_index_1] == 'toggle' toggle_tet_index = tet_index_1 toggle_face = face1 toggle_edge = edge1 subtet = subtet1 edge_verts = [0, 1, 2, 3] edge_verts.remove(toggle_edge) edge_verts.remove(toggle_face) toggle_e0, toggle_e1 = edge_verts e0, e1 = toggle_e0, toggle_e1 leading_vertex = toggle_edge trailing_vertex = toggle_face tet = tri.tetrahedron(toggle_tet_index) while True: gluing = tet.adjacentGluing(trailing_vertex) tet = tet.adjacentTetrahedron(trailing_vertex) e0, e1 = gluing[e0], gluing[e1] leading_vertex, trailing_vertex = gluing[ trailing_vertex], gluing[leading_vertex] if tet_types[tet.index()] == "toggle": break other_toggle_tet_index = tet.index() other_toggle_face = leading_vertex other_toggle_edge = trailing_vertex other_toggle_e0 = e0 other_toggle_e1 = e1 other_subtet = subtet_addresses[other_toggle_tet_index][( other_toggle_face, other_toggle_edge)] tetperm = regina.Perm4(toggle_face, toggle_edge, toggle_edge, toggle_face, toggle_e0, toggle_e0, toggle_e1, toggle_e1) other_tetperm = regina.Perm4(other_toggle_face, other_toggle_edge, other_toggle_edge, other_toggle_face, other_toggle_e0, other_toggle_e0, other_toggle_e1, other_toggle_e1) gluing = regina.Perm4(toggle_e0, other_toggle_e0, toggle_e1, other_toggle_e1, toggle_face, other_toggle_face, toggle_edge, other_toggle_edge) subtet.join(toggle_edge, other_subtet, other_tetperm * gluing * tetperm) return drilledTri, meridians