def oriented_quads_mat(qtons): s = qtons T = s.triangulation() return Matrix([[ get_oriented_quads(s, i, j, k) for (j, k) in [(0, 1), (2, 3), (0, 2), (1, 3), (0, 3), (1, 2)] ] for i in range(T.size())])
def is_embedded(qtons, TN_wrapper): W = TN_wrapper S = qtons T = W.triangulation() if not W.manifold_is_closed(): if not ends_embedded(S, W): return False oriented_projection = orient(S) if oriented_projection != False: k = len(oriented_projection) q_mat = oriented_quads_mat(S) orientations = list(itertools.product([1, -1], repeat=k)) for nu in orientations: nu_oriented_proj = Matrix([[0 for i in range(6)] for j in range(T.size())]) for i in range(k): if nu[i] == -1: comp_orientation = opp_oriented_quads_mat( oriented_projection[i]) else: comp_orientation = oriented_projection[i] nu_oriented_proj += comp_orientation if nu_oriented_proj == q_mat: return True return False
def has_mixed_bdy(self, qtons): if self.manifold_is_closed(): return False else: try: ind = int(qtons) except TypeError: ind = int(qtons.name()) if not ind in self._has_mixed_bdy: pos_bdy, neg_bdy = self.boundary_slopes(ind).values() if Matrix(pos_bdy).norm() == 0 or Matrix(neg_bdy).norm() == 0: self._has_mixed_bdy[ind] = False else: self._has_mixed_bdy[ind] = True return self._has_mixed_bdy[ind]
def opp_oriented_quads_mat(oriented_quads_mat): opp_oriented_quads_mat = [] for row in oriented_quads_mat: opp_row = [] for i in [0, 2, 4]: opp_row.append(row[i + 1]) opp_row.append(row[i]) opp_oriented_quads_mat.append(opp_row) return Matrix(opp_oriented_quads_mat)
def quads_mat(spun_surface): s = spun_surface T = s.triangulation() return Matrix( [[get_quads(s, i, j, k) for (j, k) in [ (0, 1), (0, 2), (0, 3), ]] for i in range(T.size())])
def peripheral_curve_mats(M, T): mats = [] for cusp in range(T.countCusps()): tets = T.size() try: # for newer versions of SnapPy mat = M._get_cusp_indices_and_peripheral_curve_data()[1] except AttributeError: # for older versions of SnapPy mat = M._get_peripheral_curve_data() mat_l = Matrix([[ mat[i][j] * int(in_cusp(T, i // 4, j // 4, cusp)) for j in range(16) ] for i in range(2, 4 * tets, 4)]) mat_m = Matrix([[ mat[i][j] * int(in_cusp(T, i // 4, j // 4, cusp)) for j in range(16) ] for i in range(0, 4 * tets, 4)]) mats.append((mat_l, mat_m)) return mats
def quad_bdy_mat(qtons, cusp): s = qtons T = s.triangulation() quads_mat = [] for i in range(T.size()): row = [] for (j, k) in [(0, 1), (2, 3), (0, 2), (1, 3), (0, 3), (1, 2)]: if (in_cusp(T, i, j, cusp) or in_cusp(T, i, k, cusp)): row.append(get_oriented_quads(s, i, j, k)) else: row.append(0) quads_mat.append(row) return Matrix(quads_mat)
def solve_lin_gluing_eq(T): GE = regina_to_sage_mat(T.gluingEquations()) for i in range(T.size()): # append equations log(z1)+log(z2)+log(z3)=\pi GE = GE.insert_row(GE.dimensions()[0],[Integer(0) for j in range(3*i)]+[Integer(1) for j in range(3)] + [Integer(0) for j in range(3*T.size()-3*(i+1))]) ##### for sympy implementation #GE = GE.col_join((Matrix([[Integer(0) for j in range(3*i)]+[Integer(1) for j in range(3)] + [Integer(0) for j in range(3*T.size()-3*(i+1))]]))) edges = T.countEdges() cusps = T.countCusps() # 2pi for edge equations, 0 for cusp equations, pi for three dihedral angles of a tetrahedron (then divide out pi) v= vector([2 for i in range(edges)]+[0 for i in range(2*cusps)]+[1 for i in range(T.size())]) assert len(v) == len(GE.rows()) angles_vec = GE.solve_right(v) ###### for sympy implementation # assert v.rows == GE.rows # solutions, params = GE.gauss_jordan_solve(v) # solve the system of equations, returns parametrization of solutions # param_zeroes = { tau:0 for tau in params } #set parameters to 0 to get a solution which will hopefully be an integer solution. # angles_vec = solutions.xreplace(param_zeroes) angles = Matrix([[angles_vec[j+i] for i in range(3)] for j in range(0,len(angles_vec),3)]) return angles
def abstract_nbhds(spun_normal_surface): S = spun_normal_surface T = S.triangulation() quad_counts = {} tets_around_edges = {} # First we'll gather, for each edge, information about the quads in the tetrahedra around the edge. # The tetrahedra are cylically ordered counter-clockwise w.r.t. the orientation of the edge with # vertex on top. for edge in T.faces(int(1)): # for each tet around edge, how many quads and what is slope (sign). quad_count = [] # for each tet around edge, a tuple (tet_index, edge_type, edge embedding permutation) tets_around_edge = [] embs = cyclically_order_embeddings(edge.embeddings(),T) for f in embs: t = f.tetrahedron() e = f.edge() p = f.vertices() tets_around_edge.append((t.index(),e,p)) types = {0:[1,2], 5:[1,2], 1:[2,0], 4:[2,0], 2:[0,1], 3:[0,1]} for i in types[e]: count = regina_to_sage_int(S.quads(t.index(),i)) if count != 0: if i == types[e][1]: count = -count break quad_count.append(count) quad_counts[edge.index()] = quad_count tets_around_edges[edge.index()] = tets_around_edge # width is the number of connected components of the surface in the abstract nbhd at the edge, # ind is the index in the cyclic ordering around the edge where adding up crossing counts # from i to some later j gives the width. widths = {} for edge in quad_counts: width, ind = 0, 0 L = len(quad_counts[edge]) for i in range(L): for j in range(i,L): this_width = int(sum(quad_counts[edge][i:j+1])) if abs(this_width) > width: if this_width < 0: width, ind = abs(this_width), j+1 else: width, ind = this_width, i widths[edge] = (ind, width) # for each edge, abst_nbhds stores the info we need about the quads in its abstract nbhd. abst_nbhds = {} for edge in T.faces(int(1)): # for this edge, info we want to store about the abstract nbhd abstract_nbhd = [] # get the counts for this edge, computed above counts = quad_counts[edge.index()] L = len(counts) # get ind and width for this edge, computed above ind, width = widths[edge.index()] # get tets_around_edge for this edge, computed above tets = tets_around_edges[edge.index()] # if there is anything in this abstract nbhd, continue if width > 0: # if there are no quads in the tet at ind in the ordering, then increasing ind until there are while counts[ind] == 0: ind = (ind + 1) % L assert counts[ind] > 0 # For the first tet cyclically forward from ind containing quads, the quads will have positive # slope (because of how ind was chosen above), and will be as far toward vertex 1 of the edge as # possible (i.e., in the top connected component of the intersection of surface with abstract nbhd.) slope = 1 tet, edge_type, perm = tets[ind] # For a permutation perm that described how edge is embedded in tet (or, equivalently, how tet # is embedded in the abstract nbhd), this function tells us if the 0 vertex of tet is below the quad # (in which case alignment(perm)=-1) or above the quad (in which case alignment(perm)=1). This can # be interpreted as a transerve orientation of the quad, which gives a frame of reference. When we # orient the quads, we can describe the orientation based on whether or not it agrees with this # orientation. def alignment(perm): if perm[0]==0 or (slope==-1 and perm[3]==0) or (slope==1 and perm[2]==0): return 1 else: return -1 align = alignment(perm) # arranged quads is a vector of length width, whose components are in range(counts[ind]) for a quad, and -1 for a triangle. # e.g., vector [-1,0,1,-1] means in the tet at ind in cyclic ordering, top layer is a triangle, next down is # the 0th quad, next is 1st quad, next is a triangle (as we go along the edge from vertex 1 to vertex 0) arranged_quads = quad_range(counts[ind],align) + [-1]*(width-counts[ind]) # where in the vertical ordering of the surface components are the first and last quad. first_quad, last_quad = 0,counts[ind]-1 abstract_nbhd.append((tet,slope,align,arranged_quads,perm)) # now continue, in the cyclic ordering, until we go all the way around the edge. while len(abstract_nbhd) < L: prev_slope = slope if counts[ind] != 0: prev_count = counts[ind] ind = (ind + 1) % L if counts[ind] > 0: slope = 1 elif counts[ind] < 0: slope = -1 else: slope = prev_slope tet, edge_type, perm = tets[ind] align = alignment(perm) if counts[ind] == 0: arranged_quads = [-1]*width else: if prev_slope == 1 and slope == 1: arranged_quads = pad_with_tris([-1]*(last_quad+1) + quad_range(counts[ind],align), width) elif prev_slope == 1 and slope == -1: arranged_quads = pad_with_tris([-1]*((prev_count + counts[ind]) + first_quad) + quad_range(counts[ind],align), width) elif prev_slope == -1 and slope == 1: arranged_quads = pad_with_tris([-1]*(first_quad) + quad_range(counts[ind],align), width) elif prev_slope == -1 and slope == -1: arranged_quads = pad_with_tris([-1]*(first_quad + counts[ind]) + quad_range(counts[ind],align), width) first_quad, last_quad = first_non_neg(arranged_quads), last_non_neg(arranged_quads) abstract_nbhd.append((tet,slope,align,arranged_quads,perm)) N = abstract_nbhd tets = [N[i][0] for i in range(len(N))] perms = [N[i][4] for i in range(len(N))] slopes = [N[i][1] for i in range(len(N))] align = [N[i][2] for i in range(len(N))] components = Matrix([[N[i][3][j] for i in range(len(N))] for j in range(width)]) # check to make sure in each component the quads alternate between positive and negative slopes. If # they don't then there is a problem, as in that case matching equations would not hold. for comp in components: total_sign=0 for i in range(len(comp)): if comp[i] != -1: total_sign += slopes[i] assert total_sign in [-1,0,1], 'something is wrong---please let the developer know about this!' assert total_sign == 0, 'something is wrong---please let the developer know about this!' abst_nbhds[edge.index()] = {'perms':perms,'tets':tets,'slopes':slopes,'align':align,'components':components} return abst_nbhds
def orient(spun_normal_surface): S = spun_normal_surface T = S.triangulation() # We'll create a dictionary that keeps track of quad orientations wrt tets. For a given tet, # with k quads, the quads are numbered from 0 to k-1, with the 0th quad being the one that is closest # to vertex 0 of tet. Then oriented_quads[tet] is a list of length k whose i^th entry is +1 if the # orientation of the i^th quad agrees with the local orientation ("alignment"), and -1 if it disagrees. # At first, all entries are 0 to indicate that orientation is unknown (or really, not yet determined). abs_nbhds = abstract_nbhds(S) # abs_nbhds stores quads grouped according to the tetrahedron they are in. For below, it will be more # useful to store them according to which connected component of the intersection of the surface and # the abstract neighborhood they are in. abstract_comps = [] for e in abs_nbhds: N = abs_nbhds[e] for comp in N['components']: Qs = [i for i in range(len(comp)) if comp[i] != -1] abstract_comps.append({ 'tets': [N['tets'][i] for i in Qs], 'slopes': [N['slopes'][i] for i in Qs], 'align': [N['align'][i] for i in Qs], 'components': [comp[i] for i in Qs] }) # iterator for assigning orientations to quads. See below. def iterate(comp, abstract_comps, oriented_quads, orientn_wrt_nbd=1): orientable = True # we iterate through the normal quadrilateral disks in the component comp, # triangular disks are skips because we have already removed them from comp for i in range(len(comp['tets'])): # set orientation of this quad with respect to the tet orientn_wrt_tet = comp['align'][i] * orientn_wrt_nbd # if the orientation of the quad is not already set in "oriented_quads", # then set it to orientation found above if oriented_quads[comp['tets'][i]][comp['components'][i]] == 0: oriented_quads[comp['tets'][i]][comp['components'] [i]] = orientn_wrt_tet # if there is already an orientation on the quad set in "oriented_quads", # then make sure it matches the orientation found above. If it doesn't, # then set orientalbe to False, and break. else: if oriented_quads[comp['tets'][i]][comp['components'] [i]] != orientn_wrt_tet: orientable = False break # for each quad in comp, look for other components that it appears in, and add each # of them, along with the orientation comp induces on them, to the list "new_comps" new_comps = [] for other_comp in abstract_comps: for j in range(len(other_comp['tets'])): if comp['tets'][i] == other_comp['tets'][j] and comp[ 'components'][i] == other_comp['components'][j]: new_orientn_wrt_nbd = orientn_wrt_tet * other_comp[ 'align'][j] new_comps.append((other_comp, new_orientn_wrt_nbd)) break # remove from abstract_comps all the components we just added to new_comps for new_comp in new_comps: abstract_comps.remove(new_comp[0]) # for each component in new_comps, run iterate again. If new_comps is empty, then # we have exhausted this connected component, so we return for new_comp in new_comps: abstract_comps, oriented_quads, orientable = iterate( new_comp[0], abstract_comps, oriented_quads, new_comp[1]) if not orientable: return abstract_comps, oriented_quads, orientable return abstract_comps, oriented_quads, orientable # pick a first abstract component, and impose a transverse orientation. A transverse orientation is # induced on each quad in the component. For each of these quads, we search for all remaining abstract # components in which they appear, and give that component the orientation induced by the orientation # on the quad. Iterate until we run out of components, or until we come to a component where the # orientation induced by the quad that brought us there disagrees with the orientation of another quad # which has already been oriented (in which cases the surface is not orientable). oriented_quads_matrices = [] while len(abstract_comps) > 0: # get a component. When we call iterate() below, we stay in this call to iterate # until we get orientations that are not compatible, or the component containing # comp is completely oriented. oriented_quads = {} for i in range(T.size()): num_quads = regina_to_sage_int( S.quads(i, 0) + S.quads(i, 1) + S.quads(i, 2)) oriented_quads[i] = [0] * num_quads comp = abstract_comps.pop() abstract_comps, oriented_quads, orientable = iterate( comp, abstract_comps, oriented_quads) if not orientable: break # if orientable is still set to True, then this component is orientable, so we # compute the oriented_quads_mat for the component and store it in oriented_quads_matrices. if orientable: quads = quads_mat(S) emb_oriented_quads_mat = [] for i in range(T.size()): row = [] for j in [0, 1, 2]: if quads[i][j] != 0: pos = sum([k for k in oriented_quads[i] if k == 1]) neg = -sum([k for k in oriented_quads[i] if k == -1]) #assert pos+neg == quads[i][j] row.append(pos) row.append(neg) else: row.append(0) row.append(0) emb_oriented_quads_mat.append(row) oriented_quads_matrices.append(Matrix(emb_oriented_quads_mat)) if orientable: return oriented_quads_matrices else: return False
def norm_ball(self): """ Return the Thurston norm ball. """ if not self._QUIET: print('Computing Thurston norm unit ball... ', end='') try: sys.stdout.flush() except AttributeError: pass pts_dict, rays_dict = self._norm_ball_points ### If M is not hyperbolic then rays_dict should be non-empty, and the norm ball should be non-compact. ### The below attempts to compute the non-compact norm ball, but it may be wrong. This is because ### (it seems) some surfaces may not be realized as spun normal surfaces. if len(rays_dict) != 0: V = VectorSpace(RR, self.betti_number()) ray_span = V.span([V(r) for r in rays_dict]) keys = [p for p in pts_dict] for p in keys: if V(p) in ray_span: _ = pts_dict.pop(p) polyhedron = Polyhedron(vertices=Matrix(pts_dict.keys()), rays=Matrix(rays_dict.keys()), base_ring=QQ, backend='cdd') rays = tuple([(rays_dict[tuple(i * vector(v))], i * vector(v)) for v in polyhedron.lines_list() for i in [1, -1]]) Rays = [ Ray(i, rays[i][0], rays[i][1], self) for i in range(len(rays)) ] vertices = tuple([(pts_dict[tuple(v)], vector(v)) for v in polyhedron.vertices_list() if not vector(v).is_zero()]) Vertices = [ NBVertex(i, vertices[i][0], vertices[i][1], self) for i in range(len(vertices)) ] # projected_verts = {} # Vertices = [] # for i,v in vertices: # v = vector(v) # v_proj, coeffs = orthogonal_proj(v, polyhedron.lines_list()) # v_proj = tuple(v_proj) # boundary_slopes = [] # num_boundary_comps = 0 # euler_char = coeffs[0]*self.euler_char(pts_dict[tuple(v)]) # for j in range(self.manifold().num_cusps()): # slope = vector((0,0)) # slope += vector(self.boundary_slopes(i)[j])*coeffs[0] # for k in range(len(polyhedron().lines_list())): # slope += vector(self.boundary_slopes(rays_dict[tuple(polyhedron.lines_list()[k])])[j])*coeffs[k+1] # boundary_slopes.append(slope) # num_boundary_comps += gcd(slope[0],slope[1]) # if v_proj not in projected_verts: # projected_verts[v_proj] = (num_boundary_comps, euler_char, boundary_slopes) # polyhedron = Polyhedron(vertices=Matrix(projected_verts.keys()), rays=Matrix(rays_dict.keys()), base_ring=QQ, backend='cdd') # for i in range(len(polyhedron.vertices_list())): # v = tuple(polyhedron.vertices_list()[i]) # if v in pts_dict: # Vertices.append(NBVertex(i, pts_dict[v], vector(v), self)) # else: # Vertices.append(NBVertex(i, None, vector(v), self, (projected_verts[v][0], projected_verts[v][1], projected_verts[v][2]))) else: polyhedron = Polyhedron(vertices=Matrix(pts_dict.keys()), base_ring=QQ) vertices = tuple([(pts_dict[tuple(v)], vector(v)) for v in polyhedron.vertices_list()]) Vertices = [ NBVertex(i, vertices[i][0], vertices[i][1], self) for i in range(len(vertices)) ] Rays = [] ball = TNormBall(Vertices, Rays, polyhedron) if self.num_cusps() == 1 and self.betti_number() == 1: M = self.manifold().copy() (p, q) = self.manifold().homological_longitude() elem_divs = [ div for div in M.homology().elementary_divisors() if div != 0 ] M.dehn_fill((p, q)) filled_elem_divs = [ div for div in M.homology().elementary_divisors() if div != 0 ] b = 1 for div in elem_divs: b *= div for div in filled_elem_divs: b /= div A = self.manifold().alexander_polynomial() A_norm = QQ(A.degree() - 1) try: v = ball.vertices()[0] except IndexError: pass ### below needs to be fixed. Currently does not account for virtual fibers. if A_norm <= 0 or not A.is_monic(): self._is_fibered = False ball._confirmed = True elif len(ball.vertices()) == 0: self._is_fibered = True polyhedron = Polyhedron(vertices=[[A_norm], [-A_norm]], base_ring=QQ) Vertices = [ NBVertex(0, None, (-1 / A_norm, ), self, (b, -A_norm, { 'outward': [(0, 1)], 'inward': [(0, 0)] })), NBVertex(1, None, (1 / A_norm, ), self, (b, -A_norm, { 'outward': [(0, -1)], 'inward': [(0, 0)] })) ] ball = TNormBall(Vertices, Rays, polyhedron) ball._confirmed = True elif A_norm == abs(v.euler_char()): assert self.num_H1bdy_comps(v.qtons_index()) == b self._is_fibered = 'unknown' ball._confirmed = True elif A_norm < abs(v.euler_char()): #M = self.manifold().copy() #M.dehn_fill(M.homological_longitude()) Mf = M.filled_triangulation() T = regina.Triangulation3(Mf._to_string()) boo = T.intelligentSimplify() ns = regina.NormalSurfaces.enumerate(T, regina.NS_QUAD, regina.NS_VERTEX, regina.NS_ALG_DEFAULT) non_trivial = [ i for i in range(ns.size()) if ns.surface(i).isOrientable() ] if len(non_trivial) > 1: non_trivial = [ i for i in non_trivial if ns.surface(i).cutAlong().isConnected() ] if len(non_trivial) == 0: ## something is wrong! print( 'Warning: failed to confirm that norm ball is correct (error: len(non_trivial)==0, culprit:M={}.' .format(self.manifold().name())) ball._confirmed = False elif len(non_trivial) >= 1: genus = min([ (2 - regina_to_sage_int(ns.surface(i).eulerChar())) / 2 for i in non_trivial ]) if 2 * genus - 2 + b == abs(v.euler_char()): self._is_fibered = False ball._confirmed = True elif 2 * genus - 2 + b == A_norm: multiplier = 1 / QQ(abs(v.euler_char()) / A_norm) if self.uses_simplicial_homology() == True: simplicial_class = multiplier * self.simplicial_class( v.qtons_index()) else: simplicial_class = None self._is_fibered == True polyhedron = Polyhedron(vertices=[[A_norm], [-A_norm]], base_ring=QQ) Vertices = [ NBVertex(0, None, (-1 / A_norm, ), self, (b, -A_norm, { 'outward': [(0, 1)], 'inward': [(0, 0)] })), NBVertex(1, None, (1 / A_norm, ), self, (b, -A_norm, { 'outward': [(0, -1)], 'inward': [(0, 0)] })) ] ball = TNormBall(Vertices, Rays, polyhedron) ball._confirmed = True else: print( 'Warning: failed to confirm that norm ball is correct (error: 2g-1!=A_norm or abs(X(S)), culprit:M={}.' .format(self.manifold().name())) ball._confirmed = False else: # if we are here, then something is wrong, and the following will cause an error to be thrown. assert A_norm <= abs( v.euler_char()) # (this should never happen) if not self._QUIET: print('Done.') try: sys.stdout.flush() except AttributeError: pass return ball
def __init__(self, manifold, qtons=None, quiet=False, tracker=False, allows_non_admissible=False, force_simplicial_homology=False): self._triangulation = None if isinstance(manifold, str): self._manifold = snappy.Manifold(manifold) elif isinstance(manifold, regina.engine.SnapPeaTriangulation): self._manifold = snappy.Manifold(manifold.snapPea()) self._triangulation = manifold elif isinstance(manifold, regina.engine.Triangulation3): self._manifold = snappy.Manifold(manifold.snapPea()) self._triangulation = manifold elif isinstance(manifold, snappy.Manifold): self._manifold = manifold elif isinstance(manifold, snappy.Triangulation): self._manifold = manifold for c in self._manifold.cusp_info(): if c.is_complete == False: self._manifold = self._manifold.filled_triangulation() break self._QUIET = quiet self._force_simplicial_homology = force_simplicial_homology self._num_cusps = self._manifold.num_cusps() if self._num_cusps != 0: try: L = self._manifold.link() self._knows_link_complement = True except ValueError: self._knows_link_complement = False #if self._knows_link_complement and bdy_H1_basis == 'natural': # self._bdy_H1_basis = 'natural' #else: # if self._manifold.verify_hyperbolicity()[0]: # self._manifold.set_peripheral_curves('shortest') # self._bdy_H1_basis = 'shortest' self._triangulation = regina.SnapPeaTriangulation( self._manifold._to_string()) self._angle_structure = solve_lin_gluing_eq(self._triangulation) self._peripheral_curve_mats = peripheral_curve_mats( self._manifold, self._triangulation) self._manifold_is_closed = False # check to make sure peripheral basis curves actually give a basis check, message = periph_basis_intersections(self) assert check, message # check to make sure each peripheral basis curve is connected (extra trivial loops # will mess up Euler char calculations). check, message = periph_basis_connected(self) assert check, message else: if self._triangulation == None: self._triangulation = regina.Triangulation3( self._manifold._to_string()) for _ in range(10): self._triangulation.intelligentSimplify() self._manifold_is_closed = True #self._bdy_H1_basis = None if not self._triangulation.isOriented(): self._triangulation.orient() self._qtons = qtons self._tkr = False self._allows_non_admissible = allows_non_admissible self._is_fibered = 'unknown' self._betti_number = self._triangulation.homologyH1().rank() # qtons memoization caches----- self._euler_char = {} self._map_to_ball = {} self._map_to_H2 = {} self._num_boundary_comps = {} self._over_facet = {} self._is_norm_minimizing = {} self._is_admissible = {} self._qtons_image_in_C2 = {} self._num_H1bdy_comps = {} self._has_mixed_bdy = {} self._is_embedded = {} self._ends_embedded = {} self._oriented_quads_mat = {} if self._manifold.num_cusps() > 0: self._boundary_slopes = {} self._spinning_slopes = {} self._map_to_H1bdy = {} if not self._QUIET: print( 'Enumerating quad transversely oriented normal surfaces (qtons)... ', end='') try: sys.stdout.flush() except AttributeError: pass if tracker: self._tkr = regina.ProgressTracker() # compute transversely oriented normal surfaces self._qtons = self.qtons() # name the surfaces by their index in the NormalSurfaces list for i in range(self._qtons.size()): self._qtons.surface(i).setName(str(i)) if not self._QUIET: print('Done.') try: sys.stdout.flush() except AttributeError: pass if self._betti_number > self._num_cusps: self._has_internal_homology = True else: self._has_internal_homology = False # if the manifold has internal homology or force_simplicial_homology==True then we need to use simplicial homology. if self._has_internal_homology or self._force_simplicial_homology: self._uses_simplicial_homology = True if not self._QUIET: print('computing simplicial homology...', end='') try: sys.stdout.flush() except AttributeError: pass self._face_map_to_C2 = get_face_map_to_C2(self._triangulation) self._quad_map_to_C2 = get_quad_map_to_C2(self._triangulation, self._face_map_to_C2) H2_basis_in_C2, P, qtons_image = H2_as_subspace_of_C2( self, self._face_map_to_C2, self._quad_map_to_C2) self._project_to_im_del3 = P self._qtons_image_in_C2 = { i: qtons_image[i] for i in range(len(qtons_image)) } assert len( H2_basis_in_C2) == self._betti_number, self._manifold.name( ) + ', force_simplicial_homology={}'.format( self._force_simplicial_homology) # raise an error if the qtons do not generate H2. This should only happen for a knot in a # rational homology sphere, with force_simplicial_homology=True. I = Matrix.identity(self._betti_number) B = Matrix(H2_basis_in_C2).transpose() A = B.solve_left(I) self._map_H2_to_standard_basis = A if not self._QUIET: print('Done.') try: sys.stdout.flush() except AttributeError: pass else: self._uses_simplicial_homology = False
for (j, k) in [(0, 1), (2, 3), (0, 2), (1, 3), (0, 3), (1, 2)]: if (in_cusp(T, i, j, cusp) or in_cusp(T, i, k, cusp)): row.append(get_oriented_quads(s, i, j, k)) else: row.append(0) quads_mat.append(row) return Matrix(quads_mat) ### Matrix that encodes how curves intersect with quad types, restricted to intersections ### that contribute to the positive boundary (see Cooper--Tillmann--Worden Fig 5).def pos_intx_matrix(): POS_INTERSECTION_MAT = Matrix([[0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0], [1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1], [0, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]) ### Matrix that encodes how curves intersect with quad types, restricted to intersections ### that contribute to the negative boundary (see Cooper--Tillmann--Worden Fig 5). NEG_INTERSECTION_MAT = Matrix([[0, 0, 0, 0, 0, 0], [0, -1, 0, 0, 0, 0], [0, 0, 0, -1, 0, 0], [0, 0, 0, 0, 0, -1], [0, -1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, -1, 0], [0, 0, -1, 0, 0, 0], [0, 0, 0, -1, 0, 0], [0, 0, 0, 0, -1, 0], [0, 0, 0, 0, 0, 0], [-1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, -1], [0, 0, -1, 0, 0, 0], [-1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]) ### Matrix that encodes how curves intersect with quad types (see Cooper--Tillmann--Worden Fig 5).