def WeightedRipsFiltration(X, F, p, dimension_max=2, filtration_max=np.inf): ''' Compute the weighted Rips filtration of a point cloud, weighted with the values F, and with parameter p. Requires sklearn.metrics.pairwise.euclidean_distances to compute pairwise distances between points. Input: X (np.array): size Nxn, representing N points in R^n. F (np.array):size 1xN, representing the values of a function on X. p (float): a parameter in [0, +inf) or np.inf. dimension_max (int, optional): maximal dimension to expand the complex. filtration_max (float, optional): maximal filtration value of the filtration. Output: st (gudhi.SimplexTree): the weighted Rips filtration. ''' N_tot = X.shape[0] distances = euclidean_distances(X) #compute the pairwise distances st = gudhi.SimplexTree() #create an empty simplex tree for i in range(N_tot): #add vertices to the simplex tree value = F[i] if value < filtration_max: st.insert([i], filtration=F[i]) for i in range(N_tot): #add edges to the simplex tree for j in range(i): value = WeightedRipsFiltrationValue(p, F[i], F[j], distances[i][j]) if value < filtration_max: st.insert([i, j], filtration=value) st.expansion(dimension_max) # expand the simplex tree result_str = 'Weighted Rips Complex is of dimension ' + repr(st.dimension()) + ' - ' + \ repr(st.num_simplices()) + ' simplices - ' + \ repr(st.num_vertices()) + ' vertices.' +\ ' Filtration maximal value is ' + str(filtration_max) + '.' print(result_str, flush=True) return st
def main(): import persistence import matplotlib.pyplot as plt try: import gudhi except: print('gudhi package not installed') return # definir la filtration : on a un triangle vide et un triangle plein qui partagent une arete st = gudhi.SimplexTree() n_pts = 5 time = 0.0 for n in range(n_pts): st.insert([n], time) time += 1.0 for n in range(n_pts - 1, 0, -1): st.insert([0, n], time) time += 1.0 diag = st.persistence(persistence_dim_max=True) gudhi.plot_persistence_diagram(diag) plt.show() diag2 = clean_diag(diag) landscape = persistence.persistence_landscape(diag2) persistence.plot_persistence_landscape(landscape) plt.show()
def RipsComplex(X, filtration_max=np.inf, dimension_max=3): ''' Build the Rips filtration over X, until time filtration_max. Input: X (np.array): size (N x M), representing a point cloud of R^M. filtration_max (float): filtration maximal value. dimension_max (int): maximal dimension to expand the complex. Must be k+1 to read k homology Output: st (gudhi.SimplexTree): the Rips filtration of X. ''' N = X.shape[0] distances = euclidean_distances(X) #pairwise distances st = gudhi.SimplexTree() #create an empty simplex tree for i in range(N): #add vertices to the simplex tree st.insert([i], filtration=0) distances_threshold = distances < 2 * filtration_max indices = zip(*distances_threshold.nonzero()) for u in indices: #add edges to the simplex tree i = u[0] j = u[1] if i < j: #add only edges [i,j] with i<j st.insert([i, j], filtration=distances[i, j] / 2) st.expansion(dimension_max) #expand the flag complex result_str = 'Rips Complex is of dimension ' + repr(st.dimension()) + ' - ' + \ repr(st.num_simplices()) + ' simplices - ' + \ repr(st.num_vertices()) + ' vertices.' +\ ' Filtration maximal value is ' + repr(filtration_max) + '.' print(result_str, flush=True) return st
def persistence_diagram(self): list_dgm = [] num_cols = self.clus_colors[list(self.clus_colors.keys())[0]].shape[0] for c in range(num_cols): col_vals = [] for key, elem in self.clus_colors.items(): col_vals.append(elem[c]) st = gd.SimplexTree() list_simplices, list_vertices = self.st_.get_skeleton( 1), self.st_.get_skeleton(0) for simplex in list_simplices: st.insert(simplex[0] + [-2], filtration=-3) min_val, max_val = min(col_vals), max(col_vals) for vertex in list_vertices: if st.find(vertex[0]): st.assign_filtration(vertex[0], filtration=-2 + (col_vals[vertex[0][0]] - min_val) / (max_val - min_val)) st.assign_filtration(vertex[0] + [-2], filtration=2 - (col_vals[vertex[0][0]] - min_val) / (max_val - min_val)) st.make_filtration_non_decreasing() dgm = st.persistence() for point in range(len(dgm)): b, d = dgm[point][1][0], dgm[point][1][1] b, d = min_val + (2 - abs(b)) * ( max_val - min_val), min_val + (2 - abs(d)) * (max_val - min_val) dgm[point] = tuple([dgm[point][0], tuple([b, d])]) list_dgm.append(dgm) return list_dgm
def makePlane(n: int, m: int): simp = gd.SimplexTree() counter = 0 for i in range(0, n * m): simp.insert([counter], counter) counter += 1 for j in range(0, m - 1): for i in range(0, n - 1): a = i + j * n b = i + 1 + j * n c = i + (j + 1) * n d = i + 1 + (j + 1) * n #print((a,b,c,d)) simp.insert([a, b], counter) counter += 1 simp.insert([a, c], counter) counter += 1 simp.insert([a, d], counter) counter += 1 simp.insert([b, d], counter) counter += 1 simp.insert([c, d], counter) counter += 1 simp.insert([a, b, d], counter) counter += 1 simp.insert([a, c, d], counter) return simp
def build_gudhi_tree(patterns): print("building tree...") tree = gudhi.SimplexTree() for simplex, frequency in patterns.items(): tree.insert(simplex, 1.0 / frequency) print("tree built...") return tree
def makeTorus(n: int, m: int): simp = gd.SimplexTree() counter = 0 for i in range(0, n * m): simp.insert([counter], counter) counter += 1 for j in range(0, m): for i in range(0, n): a = (i + j * n) % (m * n) b = ((i + 1 % n) + j * n) % (m * n) c = (i + ((j + 1) % m) * n) % (m * n) d = ((i + 1 % n) + ((j + 1) % m) * n) % (m * n) #print((a,b,c,d)) simp.insert([a, b], counter) counter += 1 simp.insert([a, c], counter) counter += 1 simp.insert([a, d], counter) counter += 1 for j in range(0, m): for i in range(0, n): a = (i + j * n) % (m * n) b = ((i + 1 % n) + j * n) % (m * n) c = (i + ((j + 1) % m) * n) % (m * n) d = ((i + 1 % n) + ((j + 1) % m) * n) % (m * n) #print((a,b,c,d)) simp.insert([a, b, d], counter) counter += 1 simp.insert([a, c, d], counter) counter += 1 return simp
def Build_Cliques(G): cliques = list(nx.algorithms.clique.enumerate_all_cliques(G)) st = gudhi.SimplexTree() for clique in cliques: st.insert(clique) return st
def test_empty(): st = gudhi.SimplexTree() st.persistence() assert st.lower_star_persistence_generators() == ([], []) g = st.flag_persistence_generators() assert np.array_equal(g[0], np.empty((0, 3))) assert g[1] == [] assert np.array_equal(g[2], []) assert g[3] == []
def _get_base_simplex(A): num_vertices = A.shape[0] st = gd.SimplexTree() for i in range(num_vertices): st.insert([i], filtration=-1e10) for j in range(i + 1, num_vertices): if A[i, j] > 0: st.insert([i, j], filtration=-1e10) return st.get_filtration()
def init_and_build_simplex_tree(self): self.st = gd.SimplexTree() for i in range(len(self.X) - 1): self.st.insert([i], filtration = self.Y[i]) self.st.insert([i, i + 1], filtration = max(self.Y[i], self.Y[i + 1])) self.st.insert([i + 1], filtration = self.Y[i + 1]) self.pd = PersistenceDiagram(self.st)
def __init__(self, graph): simplex_tree = gudhi.SimplexTree() for node in graph: simplex_tree.insert([node], filtration=node) for el in (graph[node]): simplex_tree.insert([node, el], filtration=node + el) self.simplex_tree = simplex_tree
def build_cochains(simplex_tree, signals_top, function=np.sum): """Build the k-cochains using the weights on X (form the X-Y bipartite graph) and a chosen aggregating function. Features are aggregated by the provided functions. The function takes as input a list of values and must return a single number. Parameters ---------- simplex_tree : Gudhi simplex tree signals_top : ndarray Features for every maximal dimensional simplex = weights on the nodes of X (from bipartite graph X-Y) function : callable Functions that will aggregate the features to build the k-coachains, default=np.sum Returns ------- signals : list of dictionaries List of dictionaries, one per dimension k. The dictionary's keys are the k-simplices. The dictionary's values are the k-cochains signals_top: Features for every maximal dimensional simplex """ signal = [dict() for _ in range(simplex_tree.dimension() + 1)] for d in range(len(signals_top) - 1, -1, -1): for simplex, values in signals_top[d].items(): st = gudhi.SimplexTree() st.insert(simplex) for face, _ in st.get_skeleton(st.dimension()): face = frozenset(face) signal[len(face) - 1].setdefault(face, []).extend( signals_top[d][simplex]) for d in range(len(signals_top) - 1, -1, -1): for simplex, values in signals_top[d].items(): st = gudhi.SimplexTree() st.insert(simplex) for face, _ in st.get_skeleton(st.dimension()): face = frozenset(face) value = np.array(signal[len(face) - 1][face]) signal[len(face) - 1][face] = int( function(value)) ##Choose propagation function # signal[len(face)-1][face]=np.mean(value[value>0]) return signal, signals_top
def MappingCylinderFiltration(st_X, st_Y, g): ''' Creates a filtration of the mapping cylinder of g: st_X --> st_Y. Input: st_X (gudhi.SimplexTree): simplex tree, domain of g. st_Y (gudhi.SimplexTree): simplex tree, codomain of g. g (dict int:int): a simplicial map { (vertices of st_X):(vertices of st_Y)}. Output: st_cyl (gudhi.SimplexTree): the mapping cylinder of g. Example: st_X = gudhi.SimplexTree() st_X.insert([0,1,2], 0) st_X.remove_maximal_simplex([0,1,2]) #st is sphere g = {0:0,1:1,2:2} st_cyl = velour.MappingCylinderFiltration(st_X, st_X, g) #st_cyl is a cylinder st_cyl.persistence(persistence_dim_max=True, homology_coeff_field = 2) ---> [(1, (0.0, inf)), (0, (0.0, inf))] ''' NewVerticesList = [tuple([v, 0]) for v in GetVerticesSimplexTree(st_X)] + [ tuple([v, 1]) for v in GetVerticesSimplexTree(st_Y) ] NewVertices = {NewVerticesList[i]: i for i in range(len(NewVerticesList))} st_cyl = gudhi.SimplexTree() #insert st_X for filtr in st_X.get_filtration(): simplex = filtr[0] simplex_cyl = [NewVertices[tuple([v, 0])] for v in simplex] st_cyl.insert(simplex_cyl, filtr[1]) #insert st_Y for filtr in st_Y.get_filtration(): simplex = filtr[0] simplex_cyl = [NewVertices[tuple([v, 1])] for v in simplex] st_cyl.insert(simplex_cyl, filtr[1]) #connect st_X to st_Y for filtr in st_X.get_filtration(): simplex_X = filtr[0] simplex_Y = [g[v] for v in simplex_X] simplex_X_cyl = [NewVertices[tuple([v, 0])] for v in simplex_X] simplex_Y_cyl = [NewVertices[tuple([v, 1])] for v in simplex_Y] simplex_cyl = simplex_X_cyl + simplex_Y_cyl #union of the simplices simplex_cyl = list(set(simplex_cyl)) #unique st_cyl.insert(simplex_cyl, filtr[1]) result_str = 'Mapping Cylinder Complex is of dimension ' + repr(st_cyl.dimension()) + ' - ' + \ repr(st_cyl.num_simplices()) + ' simplices - ' + \ repr(st_cyl.num_vertices()) + ' vertices.' print(result_str, flush=True) return st_cyl
def simplex_tree_constructor_ord(G): ''' G a graph, list of edges [v,w] output gudhi simplex tree object ''' st = gudhi.SimplexTree() for e in G: st.insert(e, 0.0) return st
def StructureW(X, F, distances, edge_max, dimension_max = 2): ''' Compute the Rips-W filtration of a point cloud, weighted with the DTM values st = StructureW(X, F, distances, dimension_max = 2) Input: + X: a nxd numpy array representing n points in R^d + F: the values of a function over the set X + dim: the dimension of the skeleton of the Rips (dim_max = 1 or 2) Output: + st: a gd.SimplexTree with the constructed filtration (require Gudhi) ''' nPts = X.shape[0] alpha_complex = gd.AlphaComplex(points=X) st = alpha_complex.create_simplex_tree() stDTM = gd.SimplexTree() for simplex in st.get_filtration(): if len(simplex[0])==1: i = simplex[0][0] stDTM.insert([i], filtration = F[i]) if len(simplex[0])==2: i = simplex[0][0] j = simplex[0][1] if (j < i): filtr = (distances[i][j] + F[i] + F[j])/2 else: filtr = (distances[j][i] + F[i] + F[j])/2 stDTM.insert([i,j], filtration = filtr) stDTM.expansion(dimension_max) st = stDTM """ st = gd.SimplexTree() for i in range(nPts): st.insert([i], filtration = F[i]) for i in range(nPts): for j in range(i): if distances[i][j]<edge_max: val = (distances[i][j] + F[i] + F[j])/2 filtr = max([F[i], F[j], val]) st.insert([i,j], filtration = filtr) st.expansion(dimension_max) """ result_str = 'Complex W is of dimension ' + repr(st.dimension()) + ' - ' + \ repr(st.num_simplices()) + ' simplices - ' + \ repr(st.num_vertices()) + ' vertices.' return st
def compute_extended_persistence_diagrams(graph, attribute): """ Compute the 4 extended persistence diagrams for a graph, using Gudhi. """ st = gd.SimplexTree() for u in graph.vs: st.insert([u.index], filtration=u[attribute]) for e in graph.es: st.insert([e.source, e.target], filtration=e[attribute]) st.extend_filtration() dgs = st.extended_persistence() return [[t[1] for t in dg] for dg in dgs]
def compute_persistence_diagrams(self): """ Compute the extended persistence diagrams of the Mapper simplicial complex associated to each color function. Returns: list_dgm (list of gudhi persistence diagrams): output extended persistence diagrams. There is one per color function. """ num_cols, list_dgm = self.colors.shape[1], [] # Compute an extended persistence diagram for each color for c in range(num_cols): # Retrieve all color values col_vals = { node_name: self.node_info_[node_name]["colors"][c] for node_name in self.node_info_.keys() } # Create a new simplicial complex by coning the Mapper with an extra point with name -2 st = gd.SimplexTree() list_simplices, list_vertices = self.mapper_.get_skeleton( 1), self.mapper_.get_skeleton(0) for (simplex, f) in list_simplices: st.insert(simplex + [-2], filtration=-3) # Assign ascending filtration values on the original simplices and descending filtration values on the coned simplices min_val, max_val = min(col_vals), max(col_vals) for (vertex, f) in list_vertices: if st.find(vertex): st.assign_filtration(vertex, filtration=-2 + (col_vals[vertex[0]] - min_val) / (max_val - min_val)) st.assign_filtration(vertex + [-2], filtration=2 - (col_vals[vertex[0]] - min_val) / (max_val - min_val)) # Compute persistence st.make_filtration_non_decreasing() dgm = st.persistence() # Output extended persistence diagrams for point in range(len(dgm)): b, d = dgm[point][1][0], dgm[point][1][1] b, d = min_val + (2 - abs(b)) * ( max_val - min_val), min_val + (2 - abs(d)) * (max_val - min_val) dgm[point] = tuple([dgm[point][0], tuple([b, d])]) list_dgm.append(dgm) return list_dgm
def compute_betti(facetlist, highest_dim): """ This function computes the betti numbers of the j-skeleton of the simplicial complex given in the shape of a facet list. Parameters ---------- facetlist (list of list) : Sublists are facets with the index of the nodes in the facet. highest_dim (int) : Highest dimension allowed for the facets. If a facet is of higher dimension in the facet list, the algorithm breaks it down into it's N choose k faces. The resulting simplicial complexe is called the j-skeleton, where j = highest_dim. Returns ------- The (highest_dim - 1) Betti numbers of the simplicial complexe. """ st = gudhi.SimplexTree() for facet in facetlist: # This condition and the loop it contains break a facet in all its n choose k faces. This is more # memory efficient than inserting a big facet because of the way GUDHI's SimplexTree works. The # dimension of the faces has to be at least one unit bigger than the skeleton we use. if len(facet) > highest_dim + 1: for face in itertools.combinations(facet, highest_dim + 1): st.insert(face) else: st.insert(facet) # We need to add a facet that has a size equivalent to the Betti we want to compute + 2 because GUDHI cannot compute # Betti N if there are no facets of size N + 2. As an example, if we take the 1-skeleton, there are, of course, no # facet of size higher than 2 and does not compute Betti 1. As a result, highest_dim needs to be 1 unit higher than # the maximum Betti number we want to compute. Moreover, we need to manually add a facet of size N + 2 (highest_dim + 1) # for the case where there are no such facets in the original complex. As an example, if we have two empty triangles, # GUDHI kinda assumes it's a 1-skeleton, and just compute Betti 0, although we would like to know that Betti 1 = 2. # The only way, it seems, to do so it to add a facet of size N + 2 (highest_dim + 1) # It also need to be disconnected from our simplicial complex because we don't want it to create unintentional holes. # This has the effect to add a componant in the network, hence why we substract 1 from Betti 0 (see last line of the function) # We add + 1 + 2 for the following reasons. First, we need a new facet of size highest_dim + 1 because of the reason # above. The last number in arange is not considered (ex : np.arange(0, 3) = [0, 1, 2], so we need to add 1 again. # Moreover, we cannot start at index 0 (because 0 is in the complex) nor -1 because GUDHI returns an error code 139. # If we could start at -1, it would be ok only with highest_dim + 3, but since we start at -2, we need to go one # index further, hence + 1. disconnected_facet = [ label for label in np.arange(-2, -(highest_dim + 1 + 2), -1) ] st.insert(disconnected_facet) # This function has to be launched in order to compute the Betti numbers st.persistence() bettis = st.betti_numbers() bettis[0] = bettis[0] - 1 return bettis
def get_persistence_from_audio(audio_wave, sample=22050, length=5, graph=False): simplex_up = gd.SimplexTree() # for the upper level persistence data simplex_dw = gd.SimplexTree() # for the lower level persistence data for i in np.arange(len(audio_wave)): simplex_up.insert([i, i + 1], filtration=audio_wave[i]) simplex_dw.insert([i, i + 1], filtration=-audio_wave[i]) for i in np.arange(len(audio_wave) - 1): simplex_up.insert([i, i + 1], filtration=audio_wave[i]) simplex_dw.insert([i, i + 1], filtration=-audio_wave[i]) simplex_up.initialize_filtration() simplex_dw.initialize_filtration() dig_up = simplex_up.persistence() dig_dw = simplex_dw.persistence() if graph: plt.figure() lr_disp.waveplot(audio_wave, sr=sample) plt.title("Audio Wave") plt.show() plt.figure() gd.plot_persistence_barcode(dig_up) plt.title("Upper Level Persistence Barcode") plt.show() plt.figure() gd.plot_persistence_barcode(dig_dw) plt.title("Sub Levels Persistence Barcode") plt.show() plt.figure() gd.plot_persistence_diagram(dig_up) plt.title("Upper Level Persistence Diagram") plt.show() plt.figure() gd.plot_persistence_diagram(dig_dw) plt.title("Sub Levels Persistence Diagram") plt.show() return dig_up, dig_dw # NOTE: this does not filter out infinite values
def build_simplex_tree(facetlist, highest_dim): st = gudhi.SimplexTree() for facet in facetlist: # This condition and the loop it contains break a facet in all its n choose k faces. This is more # memory efficient than inserting a big facet because of the way GUDHI's SimplexTree works. The # dimension of the faces has to be at least one unit bigger than the skeleton we use. if len(facet) > highest_dim + 1: for face in itertools.combinations(facet, highest_dim + 1): st.insert(face) else: st.insert(facet) return st
def SimplexTree(stbase, fct, dim, card): # Parameters: stbase (array containing the name of the file where the simplex tree is located) # fct (function values on the vertices of stbase), # dim (homological dimension), # card (number of persistence diagram points, sorted by distance-to-diagonal) # Copy stbase in another simplex tree st st = gd.SimplexTree() f = open(stbase[0], "r") for line in f: ints = line.split(" ") s = [int(v) for v in ints[:-1]] st.insert(s, -1e10) f.close() # Assign new filtration values for i in range(st.num_vertices()): st.assign_filtration([i], fct[i]) st.make_filtration_non_decreasing() # Compute persistence diagram dgm = st.persistence() # Get vertex pairs for optimization. First, get all simplex pairs pairs = st.persistence_pairs() # Then, loop over all simplex pairs indices, pers = [], [] for s1, s2 in pairs: # Select pairs with good homological dimension and finite lifetime if len(s1) == dim + 1 and len(s2) > 0: # Get IDs of the vertices corresponding to the filtration values of the simplices l1, l2 = np.array(s1), np.array(s2) i1 = l1[np.argmax(fct[l1])] i2 = l2[np.argmax(fct[l2])] indices.append(i1) indices.append(i2) # Compute lifetime pers.append(st.filtration(s2) - st.filtration(s1)) # Sort vertex pairs wrt lifetime perm = np.argsort(pers) indices = list(np.reshape(indices, [-1, 2])[perm][::-1, :].flatten()) # Pad vertex pairs indices = indices[:2 * card] + [ 0 for _ in range(0, max(0, 2 * card - len(indices))) ] return list(np.array(indices, dtype=np.int32))
def init_freudenthal_2d(width, height): """ Freudenthal triangulation of 2d grid """ # row-major format # 0-cells # global st st = gudhi.SimplexTree() count = 0 for i in range(height): for j in range(width): ind = i * width + j st.insert([ind], max_value([ind])) count += 1 # 1-cells # pdb.set_trace() for i in range(height): for j in range(width - 1): ind = i * width + j st.insert([ind, ind + 1], max_value([ind, ind + 1])) count += 1 # pdb.set_trace() for i in range(height - 1): for j in range(width): ind = i * width + j st.insert([ind, ind + width], max_value([ind, ind + width])) count += 1 # pdb.set_trace() # 2-cells + diagonal 1-cells for i in range(height - 1): for j in range(width - 1): ind = i * width + j # diagonal st.insert([ind, ind + width + 1], max_value([ind, ind + width + 1])) count += 1 # 2-cells st.insert([ind, ind + 1, ind + width + 1], max_value([ind, ind + 1, ind + width + 1])) count += 1 st.insert([ind, ind + width, ind + width + 1], max_value([ind, ind + width, ind + width + 1])) count += 1 # pdb.set_trace() print(count, "counts") return st
def AlphaWeightedRipsFiltration(X, F, p, dimension_max=2, filtration_max=np.inf): ''' /!\ this is a heuristic method, to speed-up the computation It computes the weighted Rips filtration as a subset of the alpha complex Input: X: a nxd numpy array representing n points in R^d F: an array of length n, representing the values of a function on X p: a parameter in [0, +inf) or np.inf filtration_max: maximal filtration value of simplices when building the complex dimension_max: maximal dimension to expand the complex Output: st: a gudhi.SimplexTree ''' N_tot = X.shape[0] distances = euclidean_distances(X) # compute the pairwise distances st_alpha = gudhi.AlphaComplex(points=X).create_simplex_tree() st = gudhi.SimplexTree() # create an empty simplex tree for simplex in st_alpha.get_skeleton( 2): # add vertices with corresponding filtration value if len(simplex[0]) == 1: i = simplex[0][0] st.insert([i], filtration=F[i]) if len(simplex[0] ) == 2: # add edges with corresponding filtration value i = simplex[0][0] j = simplex[0][1] value = Filtration_value(p, F[i], F[j], distances[i][j]) st.insert([i, j], filtration=value) st.expansion(dimension_max) # expand the complex result_str = 'Alpha Weighted Rips Complex is of dimension '+repr(st.dimension())+' - '+ \ repr(st.num_simplices())+' simplices - '+ \ repr(st.num_vertices())+' vertices.'+ \ ' Filtration maximal value is '+str(filtration_max)+'.' print(result_str) return st
def test_lower_star_generators(): st = gudhi.SimplexTree() st.insert([0, 1, 2], -10) st.insert([0, 3], -10) st.insert([1, 3], -10) st.assign_filtration([2], -1) st.assign_filtration([3], 0) st.assign_filtration([0], 1) st.assign_filtration([1], 2) st.make_filtration_non_decreasing() st.persistence(min_persistence=-1) g = st.lower_star_persistence_generators() assert len(g[0]) == 2 assert np.array_equal(g[0][0], [[0, 0], [3, 0], [1, 1]]) assert np.array_equal(g[0][1], [[1, 1]]) assert len(g[1]) == 2 assert np.array_equal(g[1][0], [2]) assert np.array_equal(g[1][1], [1])
def AlphaDTMFiltration(X, m, p, dimension_max=2, filtration_max=np.inf): ''' /!\ this is a heuristic method, that speeds-up the computation. It computes the DTM-filtration seen as a subset of the Delaunay filtration. Input: X (np.array): size Nxn, representing N points in R^n. m (float): parameter of the DTM, in [0,1). p (float): parameter of the DTM-filtration, in [0, +inf) or np.inf. dimension_max (int, optional): maximal dimension to expand the complex. filtration_max (float, optional): maximal filtration value of the filtration. Output: st (gudhi.SimplexTree): the alpha-DTM filtration. ''' N_tot = X.shape[0] alpha_complex = gudhi.AlphaComplex(points=X) st_alpha = alpha_complex.create_simplex_tree() Y = np.array([alpha_complex.get_point(i) for i in range(N_tot)]) distances = euclidean_distances(Y) #computes the pairwise distances DTM_values = DTM( X, Y, m ) #/!\ in 3D, gudhi.AlphaComplex may change the ordering of the points st = gudhi.SimplexTree() #creates an empty simplex tree for simplex in st_alpha.get_skeleton( 2): #adds vertices with corresponding filtration value if len(simplex[0]) == 1: i = simplex[0][0] st.insert([i], filtration=DTM_values[i]) if len(simplex[0] ) == 2: #adds edges with corresponding filtration value i = simplex[0][0] j = simplex[0][1] value = WeightedRipsFiltrationValue(p, DTM_values[i], DTM_values[j], distances[i][j]) st.insert([i, j], filtration=value) st.expansion(dimension_max) #expands the complex result_str = 'Alpha Weighted Rips Complex is of dimension ' + repr(st.dimension()) + ' - ' + \ repr(st.num_simplices()) + ' simplices - ' + \ repr(st.num_vertices()) + ' vertices.' +\ ' Filtration maximal value is ' + str(filtration_max) + '.' print(result_str) return st
def apply_graph_extended_persistence(A, filtration_val): num_vertices = A.shape[0] (xs, ys) = np.where(np.triu(A)) st = gd.SimplexTree() for i in range(num_vertices): st.insert([i], filtration=-1e10) for idx, x in enumerate(xs): st.insert([x, ys[idx]], filtration=-1e10) for i in range(num_vertices): st.assign_filtration([i], filtration_val[i]) st.make_filtration_non_decreasing() st.extend_filtration() LD = st.extended_persistence() dgmOrd0, dgmRel1, dgmExt0, dgmExt1 = LD[0], LD[1], LD[2], LD[3] dgmOrd0 = np.vstack([np.array([[ min(p[1][0],p[1][1]), max(p[1][0],p[1][1]) ]]) for p in dgmOrd0 if p[0] == 0]) if len(dgmOrd0) else np.empty([0,2]) dgmRel1 = np.vstack([np.array([[ min(p[1][0],p[1][1]), max(p[1][0],p[1][1]) ]]) for p in dgmRel1 if p[0] == 1]) if len(dgmRel1) else np.empty([0,2]) dgmExt0 = np.vstack([np.array([[ min(p[1][0],p[1][1]), max(p[1][0],p[1][1]) ]]) for p in dgmExt0 if p[0] == 0]) if len(dgmExt0) else np.empty([0,2]) dgmExt1 = np.vstack([np.array([[ min(p[1][0],p[1][1]), max(p[1][0],p[1][1]) ]]) for p in dgmExt1 if p[0] == 1]) if len(dgmExt1) else np.empty([0,2]) return dgmOrd0, dgmExt0, dgmRel1, dgmExt1
def build_series(G, functions, start, time_step, time_count): """ Args: G : networkX.Graph(), a graph functions : list of functions (one function for each node of G) T : list of time slices (for which we sample the functions) Returns: This function returns a sequence of node labelings on K, where K is the clique complex built on the graph G (there is a node labeling for each time slice in T), put in the form of an array np.ndarray() of size |G| x |T|. """ T = [] for i in range(int(time_count)): T.append(start) start += time_step n = G.number_of_nodes() time_count = len(T) assert n == len(functions), "There has to be a function for each node." K = gd.SimplexTree() for e in G.edges: K.insert(e) for n in G.nodes: K.insert([n]) K.expansion(3) complex_series = [] complex_series += time_count * [K] for j in range(time_count): nodes = list(complex_series[j].get_skeleton(0)) for i in range(n): complex_series[j].assign_filtration(nodes[i][0], functions[i](T[j])) return complex_series
def CopySimplexTree(st): ''' Hard copy of a simplex tree. Input: st (gudhi.SimplexTree): the simplex tree to be copied. Output: st1 (gudhi.SimplexTree): a copy of the simplex tree. Example: st = gudhi.SimplexTree() print(st) ---> <gudhi.SimplexTree object at 0x7fded6968e30> velour.CopySimplexTree(st) ---> <gudhi.SimplexTree at 0x7fded6968d90> ''' st2 = gudhi.SimplexTree() for filtr in st.get_filtration(): st2.insert(filtr[0], filtr[1]) return st2
def WeightedRipsFiltration(X, F, p, dimension_max=2, filtration_max=np.inf): ''' Compute the weighted Rips filtration of a point cloud, weighted with the values F, and with parameter p Input: X: a nxd numpy array representing n points in R^d F: an array of length n, representing the values of a function on X p: a parameter in [0, +inf) or np.inf filtration_max: maximal filtration value of simplices when building the complex dimension_max: maximal dimension to expand the complex Output: st: a gudhi.SimplexTree ''' N_tot = X.shape[0] distances = euclidean_distances(X) # compute the pairwise distances st = gudhi.SimplexTree() # create an empty simplex tree for i in range(N_tot): # add vertices to the simplex tree value = F[i] if value < filtration_max: st.insert([i], filtration=F[i]) for i in range(N_tot): # add edges to the simplex tree for j in range(i): value = WeightedRipsFiltrationValue(p, F[i], F[j], distances[i][j]) if value < filtration_max: st.insert([i, j], filtration=value) st.expansion(dimension_max) # expand the simplex tree result_str = 'Weighted Rips Complex is of dimension ' + repr(st.dimension()) + ' - ' + \ repr(st.num_simplices()) + ' simplices - ' + \ repr(st.num_vertices()) + ' vertices.' +\ ' Filtration maximal value is ' + str(filtration_max) + '.' print(result_str) return st