def test_simple(self): V = array([[0,0],[1,0],[0,1]]) S = array([[0,1,2]]) sc = simplicial_complex((V,S)) rows,cols = massmatrix_rowcols(sc,0) assert_equal(rows,array([0,0,0,1,1,1,2,2,2])) assert_equal(cols,array([0,1,2,0,1,2,0,1,2])) rows,cols = massmatrix_rowcols(sc,1) edges = [simplex(x) for x in combinations(range(3),2)] edge0 = sc[1].simplex_to_index[edges[0]] edge1 = sc[1].simplex_to_index[edges[1]] edge2 = sc[1].simplex_to_index[edges[2]] assert_equal(rows,array([edge0,edge0,edge0,edge1,edge1,edge1,edge2,edge2,edge2])) assert_equal(cols,array([edge0,edge1,edge2,edge0,edge1,edge2,edge0,edge1,edge2])) rows,cols = massmatrix_rowcols(sc,2) assert_equal(rows,array([0])) assert_equal(cols,array([0]))
def test_simple(self): V = array([[0, 0], [1, 0], [0, 1]]) S = array([[0, 1, 2]]) sc = simplicial_complex((V, S)) rows, cols = massmatrix_rowcols(sc, 0) assert_equal(rows, array([0, 0, 0, 1, 1, 1, 2, 2, 2])) assert_equal(cols, array([0, 1, 2, 0, 1, 2, 0, 1, 2])) rows, cols = massmatrix_rowcols(sc, 1) edges = [simplex(x) for x in combinations(range(3), 2)] edge0 = sc[1].simplex_to_index[edges[0]] edge1 = sc[1].simplex_to_index[edges[1]] edge2 = sc[1].simplex_to_index[edges[2]] assert_equal( rows, array([ edge0, edge0, edge0, edge1, edge1, edge1, edge2, edge2, edge2 ])) assert_equal( cols, array([ edge0, edge1, edge2, edge0, edge1, edge2, edge0, edge1, edge2 ])) rows, cols = massmatrix_rowcols(sc, 2) assert_equal(rows, array([0])) assert_equal(cols, array([0]))
def massmatrix_rowcols(complex, k): """ Compute the row and column arrays in the COO format of the Whitney form mass matrix """ simplices = complex[-1].simplices num_simplices = simplices.shape[0] p = complex.complex_dimension() if k == p: #top dimension rows = arange(num_simplices, dtype=simplices.dtype) cols = arange(num_simplices, dtype=simplices.dtype) return rows, cols k_faces = [tuple(x) for x in combinations(range(p + 1), k + 1)] faces_per_simplex = len(k_faces) num_faces = num_simplices * faces_per_simplex faces = empty((num_faces, k + 1), dtype=simplices.dtype) for n, face in enumerate(k_faces): for m, i in enumerate(face): faces[n::faces_per_simplex, m] = simplices[:, i] #faces.sort() #we can't assume that the p-simplices are sorted indices = simplex_array_searchsorted(complex[k].simplices, faces) rows = tile(indices.reshape((-1, 1)), (faces_per_simplex, )).flatten() cols = tile(indices.reshape((-1, faces_per_simplex)), (faces_per_simplex, )).flatten() return rows, cols
def massmatrix_rowcols(complex,k): """ Compute the row and column arrays in the COO format of the Whitney form mass matrix """ simplices = complex[-1].simplices num_simplices = simplices.shape[0] p = complex.complex_dimension() if k == p: #top dimension rows = arange(num_simplices,dtype=simplices.dtype) cols = arange(num_simplices,dtype=simplices.dtype) return rows,cols k_faces = [tuple(x) for x in combinations(range(p+1),k+1)] faces_per_simplex = len(k_faces) num_faces = num_simplices*faces_per_simplex faces = empty((num_faces,k+1),dtype=simplices.dtype) for n,face in enumerate(k_faces): for m,i in enumerate(face): faces[n::faces_per_simplex,m] = simplices[:,i] #faces.sort() #we can't assume that the p-simplices are sorted indices = simplex_array_searchsorted(complex[k].simplices,faces) rows = tile(indices.reshape((-1,1)),(faces_per_simplex,)).flatten() cols = tile(indices.reshape((-1,faces_per_simplex)),(faces_per_simplex,)).flatten() return rows,cols
def test_combinations(): for N in range(6): L = list(range(N)) for K in range(N + 1): C = list(combinations(L, K)) S = set([frozenset(x) for x in C]) ##Check order assert_equal(C, sorted(C)) ##Check number of elements assert_equal(len(C), comb(N, K, exact=True)) ##Make sure each element is unique assert_equal(len(S), comb(N, K, exact=True))
def test_combinations(): for N in xrange(6): L = range(N) for K in xrange(N+1): C = list(combinations(L,K)) S = set([ frozenset(x) for x in C]) ##Check order assert_equal(C, sorted(C)) ##Check number of elements assert_equal(len(C), comb(N,K,exact=True)) ##Make sure each element is unique assert_equal(len(S), comb(N,K,exact=True))
def loop_subdivision(vertices, simplices): """ Given a triangle mesh represented by the matrices (vertices,simplices), return new vertex and simplex arrays for the Loop subdivided mesh. """ #all edges in the mesh edges = set() for s in simplices: edges.update([frozenset(x) for x in combinations(ravel(s), 2)]) edge_index_map = {} for n, e in enumerate(edges): edge_index_map[e] = len(vertices) + n edge_vertices = [] for e in edges: e0, e1 = sorted(e) edge_vertices.append(0.5 * (vertices[e0] + vertices[e1])) new_vertices = concatenate((vertices, array(edge_vertices))) new_simplices = [] for n, s in enumerate(simplices): v0, v1, v2 = ravel(s) e01 = edge_index_map[frozenset((v0, v1))] e12 = edge_index_map[frozenset((v1, v2))] e20 = edge_index_map[frozenset((v2, v0))] new_simplices.append([v0, e01, e20]) new_simplices.append([v1, e12, e01]) new_simplices.append([v2, e20, e12]) new_simplices.append([e01, e12, e20]) new_simplices = array(new_simplices) return new_vertices, new_simplices
def loop_subdivision(vertices,simplices): """ Given a triangle mesh represented by the matrices (vertices,simplices), return new vertex and simplex arrays for the Loop subdivided mesh. """ #all edges in the mesh edges = set() for s in simplices: edges.update([frozenset(x) for x in combinations(ravel(s),2)]) edge_index_map = {} for n,e in enumerate(edges): edge_index_map[e] = len(vertices) + n edge_vertices = [] for e in edges: e0,e1 = sorted(e) edge_vertices.append(0.5*(vertices[e0] + vertices[e1])) new_vertices = concatenate((vertices,array(edge_vertices))) new_simplices = [] for n,s in enumerate(simplices): v0,v1,v2 = ravel(s) e01 = edge_index_map[frozenset((v0,v1))] e12 = edge_index_map[frozenset((v1,v2))] e20 = edge_index_map[frozenset((v2,v0))] new_simplices.append([v0,e01,e20]) new_simplices.append([v1,e12,e01]) new_simplices.append([v2,e20,e12]) new_simplices.append([e01,e12,e20]) new_simplices = array(new_simplices) return new_vertices,new_simplices
def whitney_innerproduct(complex, k): """ For a given SimplicialComplex, compute a matrix representing the innerproduct of Whitney k-forms """ assert (k >= 0 and k <= complex.complex_dimension()) ## MASS MATRIX COO DATA rows, cols = massmatrix_rowcols(complex, k) data = empty(rows.shape) ## PRECOMPUTATION p = complex.complex_dimension() scale_integration = (factorial(k)**2) / ((p + 2) * (p + 1)) k_forms = [tuple(x) for x in combinations(range(p + 1), k)] k_faces = [tuple(x) for x in combinations(range(p + 1), k + 1)] num_k_forms = len(k_forms) num_k_faces = len(k_faces) k_form_pairs = [tuple(x) for x in combinations(k_forms, 2)] + [(x, x) for x in k_forms] num_k_form_pairs = len(k_form_pairs) k_form_pairs_to_index = dict(zip(k_form_pairs, range(num_k_form_pairs))) k_form_pairs_to_index.update( zip([x[::-1] for x in k_form_pairs], range(num_k_form_pairs))) num_k_face_pairs = num_k_faces**2 if k > 0: k_form_pairs_array = array(k_form_pairs) #maps flat vector of determinants to the flattened matrix entries dets_to_vals = scipy.sparse.lil_matrix( (num_k_face_pairs, num_k_form_pairs)) k_face_pairs = [] for face1 in k_faces: for face2 in k_faces: row_index = len(k_face_pairs) k_face_pairs.append((face1, face2)) for n in range(k + 1): for m in range(k + 1): form1 = face1[:n] + face1[n + 1:] form2 = face2[:m] + face2[m + 1:] col_index = k_form_pairs_to_index[(form1, form2)] dets_to_vals[row_index, col_index] += (-1)**(n + m) * ( (face1[n] == face2[m]) + 1) k_face_pairs_to_index = dict(zip(k_face_pairs, range(num_k_faces**2))) dets_to_vals = dets_to_vals.tocsr() ## END PRECOMPUTATION ## COMPUTATION if k == 1: Fdet = lambda x: x #det for 1x1 matrices - extend else: Fdet, = scipy.linalg.flinalg.get_flinalg_funcs(('det', ), (complex.vertices, )) dets = ones(num_k_form_pairs) for i, s in enumerate(complex[-1].simplices): # for k=1, dets is already correct i.e. dets[:] = 1 if k > 0: # lambda_i denotes the scalar barycentric basis function of the i-th vertex of this simplex # d(lambda_i) is the 1 form (gradient) of the i-th scalar basis function within this simplex pts = complex.vertices[s, :] d_lambda = barycentric_gradients(pts) mtxs = d_lambda[ k_form_pairs_array] # these lines are equivalent to: for n, (A, B) in enumerate( mtxs): # for n,(form1,form2) in enumerate(k_form_pairs): dets[n] = Fdet( inner(A, B) )[0] # dets[n] = det(dot(d_lambda[form1,:],d_lambda[form2,:].T)) volume = complex[-1].primal_volume[i] vals = dets_to_vals * dets vals *= volume * scale_integration #scale by the volume, barycentric weights, and account for the p! in each whitney form #put values into appropriate entries of the COO data array data[i * num_k_face_pairs:(i + 1) * num_k_face_pairs] = vals #now rows,cols,data form a COOrdinate representation for the mass matrix shape = (complex[k].num_simplices, complex[k].num_simplices) return coo_matrix((data, (rows, cols)), shape).tocsr()
def whitney_innerproduct(complex,k): """ For a given SimplicialComplex, compute a matrix representing the innerproduct of Whitney k-forms """ assert(k >= 0 and k <= complex.complex_dimension()) ## MASS MATRIX COO DATA rows,cols = massmatrix_rowcols(complex,k) data = empty(rows.shape) ## PRECOMPUTATION p = complex.complex_dimension() scale_integration = (factorial(k)**2)/((p + 2)*(p + 1)) k_forms = [tuple(x) for x in combinations(range(p+1),k)] k_faces = [tuple(x) for x in combinations(range(p+1),k+1)] num_k_forms = len(k_forms) num_k_faces = len(k_faces) k_form_pairs = [tuple(x) for x in combinations(k_forms,2)] + [(x,x) for x in k_forms] num_k_form_pairs = len(k_form_pairs) k_form_pairs_to_index = dict(zip(k_form_pairs,range(num_k_form_pairs))) k_form_pairs_to_index.update(zip([x[::-1] for x in k_form_pairs],range(num_k_form_pairs))) num_k_face_pairs = num_k_faces**2 if k > 0: k_form_pairs_array = array(k_form_pairs) #maps flat vector of determinants to the flattened matrix entries dets_to_vals = scipy.sparse.lil_matrix((num_k_face_pairs,num_k_form_pairs)) k_face_pairs = [] for face1 in k_faces: for face2 in k_faces: row_index = len(k_face_pairs) k_face_pairs.append((face1,face2)) for n in range(k+1): for m in range(k+1): form1 = face1[:n] + face1[n+1:] form2 = face2[:m] + face2[m+1:] col_index = k_form_pairs_to_index[(form1,form2)] dets_to_vals[row_index,col_index] += (-1)**(n+m)*((face1[n] == face2[m]) + 1) k_face_pairs_to_index = dict(zip(k_face_pairs,range(num_k_faces**2))) dets_to_vals = dets_to_vals.tocsr() ## END PRECOMPUTATION ## COMPUTATION if k == 1: Fdet = lambda x : x #det for 1x1 matrices - extend else: Fdet, = scipy.linalg.flinalg.get_flinalg_funcs(('det',),(complex.vertices,)) dets = ones(num_k_form_pairs) for i,s in enumerate(complex[-1].simplices): # for k=1, dets is already correct i.e. dets[:] = 1 if k > 0: # lambda_i denotes the scalar barycentric basis function of the i-th vertex of this simplex # d(lambda_i) is the 1 form (gradient) of the i-th scalar basis function within this simplex pts = complex.vertices[s,:] d_lambda = barycentric_gradients(pts) mtxs = d_lambda[k_form_pairs_array] # these lines are equivalent to: for n,(A,B) in enumerate(mtxs): # for n,(form1,form2) in enumerate(k_form_pairs): dets[n] = Fdet(inner(A,B))[0] # dets[n] = det(dot(d_lambda[form1,:],d_lambda[form2,:].T)) volume = complex[-1].primal_volume[i] vals = dets_to_vals * dets vals *= volume * scale_integration #scale by the volume, barycentric weights, and account for the p! in each whitney form #put values into appropriate entries of the COO data array data[i*num_k_face_pairs:(i+1)*num_k_face_pairs] = vals #now rows,cols,data form a COOrdinate representation for the mass matrix shape = (complex[k].num_simplices,complex[k].num_simplices) return coo_matrix((data,(rows,cols)), shape).tocsr()