def check_edge_validity(self): """ Checks to make sure that the number of endpoints of edges is the same as the number of vertices. """ spacelike_edges = self.get_spacelike_edges() timelike_edges = self.get_timelike_edges() spacelike_endpoints = [e.get_vertex_ids() for e in spacelike_edges] timelike_endpoints = [e.get_vertex_ids() for e in timelike_edges] spacelike_endpoint_union = ut.set_union(spacelike_endpoints) timelike_endpoint_union = ut.set_union(timelike_endpoints) endpoint_union = spacelike_endpoint_union.union(timelike_endpoint_union) assert endpoint_union == self.get_vertex_ids()
def check_edge_validity(self): """ Checks to make sure that the number of endpoints of edges is the same as the number of vertices. """ edges = self.get_edges() endpoints = [e.get_vertex_ids() for e in edges] endpoint_union = ut.set_union([e.get_vertex_ids() for e in edges]) assert endpoint_union == self.get_vertex_ids()
def extract_vertices(self): """ Extract the vertices from two triangles. """ # Boundary of two triangles endpoints = self.extract_timelike_edges() # Extracted all the vertices of all the timelike edges. extracted_vertices_ids = ut.set_union( [e.get_vertex_ids() for e in endpoints]) extracted_vertices = [ vertex.instances[i] for i in extracted_vertices_ids ] return extracted_vertices
def build_triangle_and_edges(edge_list): """ ONLY USEFUL AT INITIALIZATION. AND USES TTS TRIANGLE Builds a triangle by creating the triangle object. """ # local constants triangle_length = 3 # Total no. of edges in a triangle. # Make sure we actually have a collection of 3 triangle edges. assert len(edge_list) == triangle_length # Make a sets of points that is belongs to a triangle. points = ut.set_union(edge_list) #Make sure we actually have a collection of 3 triangle vertices. assert len(points) == triangle_length # Generate a list of point ids. points = [vertex.parse_input(t).get_id() for t in points] # Type-cast to eliminate duplicates. vertices = set(points) # Build the timelike and spacelike edges contained by triangle of any # and return their ids. # build_sub_edges_of_triangle runs some error checking too. timelike_edge_ids, spacelike_edge_ids = build_sub_edges_of_triangle( edge_list) # RIGHT NOW, WE ARE ONLY USING TTS TRIANGLE FOR INTIALIZATION OF OUR LATTICE # Check to see if the triangle we want to build already exists. tts_duplicates = tts_triangle.find_duplicates(vertices, spacelike_edge_ids, timelike_edge_ids) # If there is one duplicate, work with it. If there is more than # one, raise an error. If there are no duplicates, make that # triangle! if len(tts_duplicates) == 0: # Make a triangle of tts type: t = tts_triangle(vertices, spacelike_edge_ids, timelike_edge_ids) tts_triangle_id = t.id # Ensure we have the id for this triangle tts_triangle.add(t) # Add this triangle to its hash table elif len(tts_duplicates) == 1: triangle_id = tts_duplicates[0] # The id for our triangle else: #If len(tts_duplicates)>1, something went very wrong. assert error.checking.too_many_duplicates('tts_triangle', vertices, tts_duplicates, 1, 'build_triangle_and_edges') return tts_triangle_id
def complex_3_to_1(simplex_id_or_simplex): """ Takes a simplex or simplex id as input and calculates what, if any, complices are topologically acceptable to operate on. The 3->1 move requires a vertex that is attached to 3 simplices. There is a slight danger that volume decreasing moves can make a system topologically unacceptable. If this is the case, then the function returns False. If there is more than one acceptable complex, returns one at random. """ volume_decrease = 2 if not check_area_decreasing_validity(volume_decrease): return False # Extract triangle triangle = extract_triangle(simplex_id_or_simplex) # Extract vertices vertices = triangle.get_vertices() # If a vertex has only three triangles, consider it ids = lambda x: x.get_triangle_ids() # For convenience possibilities = [ids(v) for v in vertices if len(ids(v)) == 3] # Each boundary point on a possible complex must be attached to at # least 4 triangles # Function to extract vertex class objects from a triangle id obj = lambda i: sd.triangle.instances[i].get_vertices() # function to extract the boundary points of a complex possibility boundaries = lambda c: ut.set_union([set(obj(t)) for t in c]) \ - ut.set_intersection([set(obj(t)) for t in c]) # Function that tells us if every boundary vertex of a possibility # is connected to greater than 3 simplex acceptable = lambda p: len([b for b in boundaries(p) if len(b) > 3]) == 3 # Only accept a possibility if each boundary vertex has 4 or more # triangles. complices = [complex(p) for p in possibilities if acceptable(p)] if len(complices) == 1: return complices[0] elif len(complices) > 1: return random.choice(complices) else: return False
def move_3_to_1(cmpx): "Applies a 3->1 move to the input complex, cmpx." # Extract the complex, which contains three triangles. if len(cmpx.get_triangles()) == 3: original_triangles = [sd.triangle.instances[i] \ for i in cmpx.get_triangles()] else: raise ValueError("There should be exactly 3 triangles for the " + "3->1 complex.") # Extract the boundary vertices original_vertices = [set(t.get_vertices()) for t in original_triangles] central_vertex = list(ut.set_intersection(original_vertices))[0] boundary_vertices = ut.set_union(original_vertices) \ - set([central_vertex]) boundary_vertex_ids = [v.get_id() for v in boundary_vertices] # Extract the edges to be removed (there are 3) # Lambda functions for clarity get_edges = lambda i: set(sd.triangle.instances[i].get_edges()) intersected_element = lambda L: ut.only_element(ut.set_intersection(L)) # Nested list comprehensions: take the intersection of the set of # edges associated with pairs of the triangles we care # about. These edges are flagged for deleteion. shared_edges = [intersected_element([get_edges(i) for i in c]) \ for c in ut.k_combinations(cmpx.get_triangles())] # There should only be 3 shared edges. If there are more, we messed up. assert len(shared_edges) == 3 # Make the new triangle new_triangle = sm.build_triangle_and_edges(boundary_vertex_ids) # Clean up for t in original_triangles: # Delete the old triangles: sm.remove_triangle(t) for e in shared_edges: # Delete the old edges. sm.remove_edge(e) sd.vertex.delete(central_vertex) # Delete the old vertex # Set triangles, neihgbors, and check topology set_neighbors_and_triangles(boundary_vertices,[new_triangle]) return True
def find_opposite_vertices_in_complex(triangle_complex): """ Similar to find_opposite_vertices, but looks vertices that don't share a triangle contained in the triangle complex. """ # Parse input triangle_objects = set([sd.triangle.parse_input(t) \ for t in triangle_complex]) # Extract vertices vertices = ut.set_union([set(t.get_vertices()) for t in triangle_objects]) # Vertices that don't share triangles are: shared = [set([v1,v2]) for v1 in vertices for v2 in vertices\ if not shares_triangles_in_complex(v1,v2,triangle_objects)] # Filter out duplicates filtered = [] for vertex_pair in shared: if vertex_pair not in filtered: filtered.append(vertex_pair) return filtered
def find_opposite_vertices(triangle_set): """ A function that looks for a pair of vertices in a set of triangles that don't share a triangle. Returns all such vertex pairs. Takes ids or instances as input. But only takes collections. """ # Parse input triangle_objects = set([sd.triangle.parse_input(t) for t in triangle_set]) # Extract vertices vertices = ut.set_union([set(t.get_vertices()) for t in triangle_objects]) # Finds pairs of vertices that don't share a triangle opposite_vertices = [set([v1,v2]) for v1 in vertices \ for v2 in vertices \ if not v1.shares_a_triangle_with(v2)] # We need to remove duplicates from the list of opposite vertices for vertex_pair in opposite_vertices: if vertex_pair not in filtered_vertices: filtered_vertices.append(vertex_pair) return filtered_vertices
def reindex_sphere_data_list(sphere_data_list): """ Takes an input list of the form [set([v1,v2,v3]),...] and ensures that the list of vertices has no hooes. i.e., if the maximum vertex id is n, then the set of vertices is set(range(1,n+1)). Returns a reindex list. Used for output so that the boundary file contains a list we can give to 2p1-fixed-boundaries as a boundary. """ # Generate a sorted list of all the vertex ids. The reindexed id # of each vertex is just the id of the vertex in the list. vertex_ids = list(ut.set_union(sphere_data_list)) vertex_ids.sort() # It's troublesome to find the index of the id in the list this # way. A better data structure is a dictionary: reindexing_scheme = {v: vertex_ids.index(v) + 1 for v in vertex_ids} # We can now reconstruct our reindexed list by mapping v to # reindexing_scheme[v] sphere_data_list = [set([reindexing_scheme[v] for v in s]) for s in sphere_data_list] return sphere_data_list
def try_3_to_1(simplex_id_or_simplex): """ Tries a 3->1 move and returns the move data that the metropolis algorithm will use to decide whether or not to accept the move. If the move is simply not topologically acceptable, returns false. """ # The complex cmpx = complex_3_to_1(simplex_id_or_simplex) # If there are no topologically acceptable complices, stop right # now and return False. Otherwise, extract the triangles. if cmpx: triangles = cmpx.get_triangles() else: return False # Now that we have the triangle, extract the list of points of # each triangle. old_vertices = [sd.triangle.instances[t].get_vertices() \ for t in triangles] # The central vertex is the intersection of all the vertices in # the old triangles central_vertex = ut.set_intersection([set(t) for t in old_vertices]) # The boundary vertices are the union of the vertices of the old # triangles minus the intersection: boundary_vertices = ut.set_union([set(t) for t in old_vertices]) \ - central_vertex # We're removing the central vertex, but each boundary vertex will # be attached to 1 fewer triangle. # "Replace" the 3 original vertices removed_vertices = [st.imaginary_vertex(len(v),False) \ for v in boundary_vertices] added_vertices = [st.imaginary_vertex(len(v)-1,True) \ for v in boundary_vertices] # "Remove" the center vertex removed_vertices.append(st.imaginary_vertex(3,False)) return st.move_data(removed_vertices + added_vertices,cmpx, move_3_to_1,-2)
def build_sphere_from_data(sphere_data): """ sphere_data should be a list of sets, each with 3 points in it. Resets the sphere and then builds it from this data. """ # First we need to check that sphere_data is what we want. for s in sphere_data: if not (type(s) == set and len(s) == 3): raise TypeError("Sphere data needs to be a list of sets, " +"each of length 3!") for i in s: if not (type(i) == int and i > 0): raise TypeError("The vertices must all be " +"positive integers!") # The vertices in existence will be the union of the triangle # point lists: vertex_ids = ut.set_union(sphere_data) # The last used vertex id is the maximum of the vertex ids last_used_vertex_id = max(vertex_ids) # make sure there are no vertices are missing (print sphere to # file shouldn't let there be any) assert set(range(1,last_used_vertex_id+1)) - vertex_ids == set([]) # Once we're sure the set is what we want, we need to make sure no # sphere is currently initialized. sm.delete_all_geometries() # Make the points we need sm.make_n_vertices(last_used_vertex_id) # Ensure the points we made are the points in our list assert set(vertex.instances.keys()) == vertex_ids # Make the triangles we need out of the points. triangle_ids = [sm.build_triangle_and_edges(t) for t in sphere_data] # Connect all the triangles and tell points what triangles contain them connect_all_triangles() return triangle_ids
def move_1_to_3(cmpx): "Applies a 1->3 move to the input complex, cmpx." # Extract the single triangle required for this move. if len(cmpx.get_triangles()) == 1: triangle_id = list(cmpx.get_triangles())[0] else: raise ValueError("There should be only one "+ "triangle for the 1->3 complex.") triangle = sd.triangle.instances[triangle_id] # Vertices original_vertices = triangle.get_vertices() # Edges original_edges = triangle.get_edges() # Endpoints of edges endpoints = [e.get_vertex_ids() for e in original_edges] # Make the point that will trisect the triangle v = sd.vertex() sd.vertex.add(v) # Generate the vertices for the three new triangles to be created vertex_list = [points | set([v.get_id()]) for points in endpoints] # Make the new triangles new_triangles = [sm.build_triangle_and_edges(tri) for tri in vertex_list] # Delete the old triangle sm.remove_triangle(triangle) # Set neighbors, triangles for each vertex, and do some error checking vertex_ids = ut.set_union(vertex_list) vertices = [sd.vertex.instances[i] for i in vertex_ids] set_neighbors_and_triangles(vertices,new_triangles) return True
def complex_2_to_2(simplex_id_or_simplex): """ Takes a simplex or simplex id as input and calculates what, if any, complices are topologically acceptable to operate on. If there's more than one, return one at random. If there are none, return False. The 2->2 move equires that the new edge not already exist (there are cases when it does) and that the endpoints of the deleted edge each have >=4 triangles attached to them before the move is applied. """ # Declare local constants pair_length = 2 # The length of a pair neighbor_length = 3 # The number of vertices a triangle must have. # Extract data for the first triangle # Extract triangle t1 = extract_triangle(simplex_id_or_simplex) # Extract neighbors t1_neighbors = t1.get_neighbors() # Extract vertices t1_vertices = t1.get_vertices() # If we can find a pair of vertices of t1 that each have >=4 # triangles attached, then these vertices might be good choices of # endpoints for the edge we will delete. possible_endpoints = [v for v in t1_vertices if len(v) >= 4] # If there are fewer than two possible endpoints, no acceptable # edge exists, so me might as well give up. if len(possible_endpoints) < 2: return False else: # If >=2 possible endpoints exist, get all possible pairs: possible_endpoint_pairs = ut.k_combinations(possible_endpoints,2) # The other triangle in the complex will be the neighbor that # shares the chosen pair of of endpoint vertices. We want to keep # track of which pair this is associated with. acceptable_neighbors = [(p,n) for p in possible_endpoint_pairs \ for n in t1_neighbors \ if p.issubset(n.get_vertices())] # We will be making an edge between the unshared vertices of the # two neighboring triangles. We have to make sure that this edge # does not yet exist. # A function to calculate the unshared vertices as a pair. Just # syntactic sugar. unshared_v = lambda p,n: ut.set_union([set(t1.get_vertices()), set(n.get_vertices())]) - set(p) # An acceptable complex has a pair of shared points, a neighbor, # and a pair of unshared points which are not the endpoints of an # existing edge. acceptable_cmpxs = [(p,n,unshared_v(p,n))\ for (p,n) in acceptable_neighbors \ if not sd.edge.exists(unshared_v(p,n))] # Double check to make sure we have what we expect. acceptable_cmpxs = [(p,n,u) for (p,n,u) in acceptable_cmpxs \ if len(p) == pair_length\ and len(n) == neighbor_length\ and len(u) == pair_length] # We have everything. If it exists, stick it in a complex and # return it. Otherwise, return false. if acceptable_cmpxs: chosen_complex = random.choice(acceptable_cmpxs) triangles = [t1,chosen_complex[1]] shared_vertices = chosen_complex[0] unshared_vertices = chosen_complex[2] cmpx = complex22(triangles) cmpx.set_shared_vertices(shared_vertices) cmpx.set_unshared_vertices(unshared_vertices) return cmpx else: return False