Esempio n. 1
0
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())])
Esempio n. 2
0
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
Esempio n. 3
0
    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]
Esempio n. 4
0
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)
Esempio n. 5
0
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())])
Esempio n. 6
0
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
Esempio n. 7
0
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)
Esempio n. 8
0
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
Esempio n. 9
0
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
Esempio n. 10
0
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
Esempio n. 11
0
    def norm_ball(self):
        """
		Return the Thurston norm ball.
		"""
        if not self._QUIET:
            print('Computing Thurston norm unit ball... ', end='')
            try:
                sys.stdout.flush()
            except AttributeError:
                pass
        pts_dict, rays_dict = self._norm_ball_points

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

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

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


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

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

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

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

            except IndexError:
                pass

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

            elif len(ball.vertices()) == 0:
                self._is_fibered = True
                polyhedron = Polyhedron(vertices=[[A_norm], [-A_norm]],
                                        base_ring=QQ)
                Vertices = [
                    NBVertex(0, None, (-1 / A_norm, ), self, (b, -A_norm, {
                        'outward': [(0, 1)],
                        'inward': [(0, 0)]
                    })),
                    NBVertex(1, None, (1 / A_norm, ), self, (b, -A_norm, {
                        'outward': [(0, -1)],
                        'inward': [(0, 0)]
                    }))
                ]
                ball = TNormBall(Vertices, Rays, polyhedron)
                ball._confirmed = True
            elif A_norm == abs(v.euler_char()):
                assert self.num_H1bdy_comps(v.qtons_index()) == b
                self._is_fibered = 'unknown'
                ball._confirmed = True
            elif A_norm < abs(v.euler_char()):
                #M = self.manifold().copy()
                #M.dehn_fill(M.homological_longitude())
                Mf = M.filled_triangulation()
                T = regina.Triangulation3(Mf._to_string())
                boo = T.intelligentSimplify()
                ns = regina.NormalSurfaces.enumerate(T, regina.NS_QUAD,
                                                     regina.NS_VERTEX,
                                                     regina.NS_ALG_DEFAULT)
                non_trivial = [
                    i for i in range(ns.size())
                    if ns.surface(i).isOrientable()
                ]
                if len(non_trivial) > 1:
                    non_trivial = [
                        i for i in non_trivial
                        if ns.surface(i).cutAlong().isConnected()
                    ]
                if len(non_trivial) == 0:  ## something is wrong!
                    print(
                        'Warning: failed to confirm that norm ball is correct (error: len(non_trivial)==0, culprit:M={}.'
                        .format(self.manifold().name()))
                    ball._confirmed = False
                elif len(non_trivial) >= 1:
                    genus = min([
                        (2 - regina_to_sage_int(ns.surface(i).eulerChar())) / 2
                        for i in non_trivial
                    ])
                    if 2 * genus - 2 + b == abs(v.euler_char()):
                        self._is_fibered = False
                        ball._confirmed = True
                    elif 2 * genus - 2 + b == A_norm:
                        multiplier = 1 / QQ(abs(v.euler_char()) / A_norm)
                        if self.uses_simplicial_homology() == True:
                            simplicial_class = multiplier * self.simplicial_class(
                                v.qtons_index())
                        else:
                            simplicial_class = None
                        self._is_fibered == True
                        polyhedron = Polyhedron(vertices=[[A_norm], [-A_norm]],
                                                base_ring=QQ)
                        Vertices = [
                            NBVertex(0, None, (-1 / A_norm, ), self,
                                     (b, -A_norm, {
                                         'outward': [(0, 1)],
                                         'inward': [(0, 0)]
                                     })),
                            NBVertex(1, None, (1 / A_norm, ), self,
                                     (b, -A_norm, {
                                         'outward': [(0, -1)],
                                         'inward': [(0, 0)]
                                     }))
                        ]
                        ball = TNormBall(Vertices, Rays, polyhedron)
                        ball._confirmed = True
                    else:
                        print(
                            'Warning: failed to confirm that norm ball is correct (error: 2g-1!=A_norm or abs(X(S)), culprit:M={}.'
                            .format(self.manifold().name()))
                        ball._confirmed = False
            else:
                # if we are here, then something is wrong, and the following will cause an error to be thrown.
                assert A_norm <= abs(
                    v.euler_char())  # (this should never happen)
        if not self._QUIET:
            print('Done.')
            try:
                sys.stdout.flush()
            except AttributeError:
                pass
        return ball
Esempio n. 12
0
    def __init__(self,
                 manifold,
                 qtons=None,
                 quiet=False,
                 tracker=False,
                 allows_non_admissible=False,
                 force_simplicial_homology=False):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            self._uses_simplicial_homology = True

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

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

            if not self._QUIET:
                print('Done.')
                try:
                    sys.stdout.flush()
                except AttributeError:
                    pass
        else:
            self._uses_simplicial_homology = False
Esempio n. 13
0
        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).