PersistentLaplacians
sheaf_simplex_tree.hpp
Go to the documentation of this file.
1 #ifndef SST_H
2 #define SST_H
3 // wrap gudhi simplex tree with extra data
5 
6 
7 #include <gudhi/Simplex_tree.h>
8 #include <gudhi/Rips_complex.h>
9 #include <gudhi/distance_functions.h>
10 #include <vector>
11 #include "Eigen/SparseCore"
12 #include <unordered_map>
13 #include <cmath>
14 #include <iostream>
15 
16 namespace PersistentLaplacians{
17  using Simplex_tree = Gudhi::Simplex_tree<Gudhi::Simplex_tree_options_default>;
19  using Distance_matrix = std::vector<std::vector<Filtration_value>>;
21  public:
23  std::unordered_map<Simplex_tree::Simplex_key, std::vector<float>> extra_data;
24  std::function<float(Simplex_tree::Simplex_handle, Simplex_tree::Simplex_handle, sheaf_simplex_tree&)> restriction;
26  std::unordered_map<Simplex_tree::Simplex_key, std::vector<float>> _extra_data,
27  std::function<float(Simplex_tree::Simplex_handle, Simplex_tree::Simplex_handle, sheaf_simplex_tree&)> _restriction){
28  st = Simplex_tree(_st);
29  extra_data = _extra_data;
30  restriction = _restriction;
31  }
32  int coface_index(Simplex_tree::Simplex_handle simplex, Simplex_tree::Simplex_handle coface){
33  // get iterator for vertices of simplex and coface
34  Simplex_tree::Simplex_vertex_range vertex_range_simplex = this->st.simplex_vertex_range(simplex);
35  Simplex_tree::Simplex_vertex_range vertex_range_coface = this->st.simplex_vertex_range(coface);
36 
37  // convert to vector
38  std::vector<Simplex_tree::Vertex_handle> vec_simplex(vertex_range_simplex.begin(), vertex_range_simplex.end());
39  std::vector<Simplex_tree::Vertex_handle> vec_coface(vertex_range_coface.begin(), vertex_range_coface.end());
40 
41  // sort
42  // std::sort(vec_simplex.begin(),vec_simplex.end());
43  // std::sort(vec_coface.begin(),vec_coface.end());
44 
45  // loop until no match
46  for (int i = 0; i < (int) vec_simplex.size(); i++){
47  if (vec_simplex[i] != vec_coface[i])
48  return i;
49  }
50  return vec_coface.size() - 1; // missing vertex is in last position
51  }
52 
53  std::vector<FilteredBoundaryMatrix<float>> apply_restriction_function(){
54  // std::function<float(Simplex_tree::Simplex_handle, Simplex_tree::Simplex_handle, sheaf_simplex_tree&)> f){
55  int complex_dim = this->st.dimension();
56  std::vector<size_t> dims = this->st.num_simplices_by_dimension();
57 
58  // intermediate sparse matrix storage as (row, col, coefficient)
59  // this is later reindexed then given to Eigen to create a SparseMatrixFloat (MatrixXf)
60  std::vector<std::vector<std::tuple<int64_t,int64_t,float>>> boundaries_triples;
61  std::vector<std::vector<std::pair<int,double>>> filtrations;
62 
63  std::vector<SparseMatrixFloat> coboundary_matrices;
64  std::vector<std::vector<double>> reindexed_filtrations;
65 
66  // Wrapper to keep explicit boundary matrices with the filtration of each simplex
67  std::vector<FilteredBoundaryMatrix<float>> coboundaries;
68 
69  // Initialize vectors for filtrations and boundary matrices
70  for (int dim = 0; dim < (int) complex_dim; dim++){
71  filtrations.push_back({});
72  boundaries_triples.push_back({});
73  }
74  filtrations.push_back({}); //for top dim
75 
76  // loop over all simplices
77 
78  for (auto sh : st.filtration_simplex_range()){
79  int dim = st.dimension(sh);
80  filtrations[dim].push_back(std::make_pair<int, float>(st.key(sh),st.filtration(sh)));
81  if (dim == complex_dim){ // zero coboundary from top dimension
82  continue;
83  }
84 
85  // loop over all codimension-1 cofaces
86  for (Simplex_tree::Simplex_handle c : st.cofaces_simplex_range(sh, 1)){
87 
88  float sign = (float) std::pow(-1.0, (double) (this->coface_index(sh, c) % 2));
89 
90  // apply the restriction function
91  float coeff = sign*restriction(sh, c, *this);
92 
93  // store as (row, col, coefficient)
94  boundaries_triples[dim].push_back(std::tuple<int64_t,int64_t,float>(st.key(c),st.key(sh),coeff));
95  }
96  }
97  reindex_boundaries_map(boundaries_triples,coboundary_matrices,filtrations, reindexed_filtrations);
98 
99  // convert from reindexed (row, col, coeff) to FilteredBoundaryMatrix (wraps Eigen::MatrixXf)
100  for (int dim = 0; dim < (int) complex_dim; dim++){
101  SparseMatrixFloat d = coboundary_matrices[dim];
102  std::vector<double> filt_domain = reindexed_filtrations[dim];
103  std::vector<double> filt_range = reindexed_filtrations[dim+1];
104  FilteredBoundaryMatrix<float> fbm(coboundary_matrices[dim],filt_domain, filt_range);
105  coboundaries.push_back(fbm);
106  }
107  return coboundaries;
108  }
109  private:
110  void reindex_boundaries_map(std::vector<std::vector<std::tuple<int64_t,int64_t,float>>> &boundaries_triples,std::vector<SparseMatrixFloat> &reindexed_boundaries,
111  std::vector<std::vector<std::pair<int,double>>> &filtrations, std::vector<std::vector<double>> &reindexed_filtrations){
112  std::vector<std::set<int64_t>> indices_of_actual_simplices_set(boundaries_triples.size()+1);
113  std::vector<std::vector<int>> indices_of_actual_simplices_vector(boundaries_triples.size()+1);
114 
115  // get the sets of all simplices that met threshold
116  for (int i = 0; i < (int) boundaries_triples.size(); i++){
117  int dim = i + 1;
118 
119  std::vector<std::tuple<int64_t, int64_t, float>> boundary_triples = boundaries_triples[i]; // is this an unnecessary copy?
120 
121  int max_ind_row = 0;
122  int max_ind_col = 0;
123  for (int j = 0; j < (int) boundary_triples.size(); j++){
124  int64_t row = std::get<0>(boundary_triples[j]);
125  int64_t col = std::get<1>(boundary_triples[j]);
126 
127  indices_of_actual_simplices_set[dim-1].insert(col);
128  indices_of_actual_simplices_set[dim].insert(row);
129  }
130  }
131 
132  // create mapping from old indices to new (e.g. ripser lexicographic -> minimal with same matrix)
133  std::vector<std::unordered_map<int64_t,int64_t>> index_maps(indices_of_actual_simplices_set.size());
134  for (int i = 0; i < (int) indices_of_actual_simplices_set.size(); i++){
135  std::vector<int64_t> temp_indices;
136  temp_indices.assign(indices_of_actual_simplices_set[i].begin(), indices_of_actual_simplices_set[i].end());
137  // indices_of_actual_simplices_vector[i] = temp_indices;
138  std::unordered_map<int64_t, int64_t> temp_map(temp_indices.size());
139  for (int j = 0; j < (int) temp_indices.size(); j++){
140  temp_map[temp_indices[j]] = j;
141  }
142  index_maps[i] = temp_map;
143  }
144 
145  // actually make the boundary matrices
146  for (int i = 0; i < (int) boundaries_triples.size(); i++){
147  int dim = i + 1;
148  std::vector<Eigen::Triplet<float>> tripletList;
149  std::vector<std::tuple<int64_t, int64_t, float>> boundary_triples = boundaries_triples[i]; // is this an unnecessary copy?
150  // convert to eigen triple format and use the mapping
151  for (int j = 0; j < (int) boundary_triples.size(); j++){
152  int col = index_maps[dim-1][std::get<1>(boundary_triples[j])];
153  int row = index_maps[dim][std::get<0>(boundary_triples[j])];
154  float coeff = std::get<2>(boundary_triples[j]);
155  tripletList.push_back(Eigen::Triplet<float>(row, col, coeff));
156  // std::cout << "triplet (" << row << ", " << col << ", " << coeff << ")" << std::endl;
157  }
158 
159  SparseMatrixFloat boundary(index_maps[dim].size(), index_maps[dim-1].size());
160  boundary.setFromTriplets(tripletList.begin(), tripletList.end());
161  reindexed_boundaries.push_back(boundary);
162  }
163 
164  for (int dim = 0; dim < (int) filtrations.size(); dim++){
165  std::vector<double> temp(filtrations[dim].size(),0.0);
166 
167  for (int i = 0; i < (int) filtrations[dim].size(); i++){
168  temp[index_maps[dim][std::get<0>(filtrations[dim][i])]] = std::get<1>(filtrations[dim][i]);
169  }
170  reindexed_filtrations.push_back(temp);
171  }
172  }
173 
174  };
175 
176  PersistentLaplacians::sheaf_simplex_tree rips_sheaf_simplex_tree(std::vector<std::vector<float>> points, Filtration_value max_length,
177  std::function<float(Simplex_tree::Simplex_handle, Simplex_tree::Simplex_handle, sheaf_simplex_tree&)> restriction){
178  using Rips_complex = Gudhi::rips_complex::Rips_complex<Filtration_value>;
179 
180  // This is an example of a function that constructs a sheaf simplex tree
181 
182 
183  // Important: We must wrap the Gudhi Rips_complex rips in our final sheaf_simplex_tree sst
184  // BEFORE calling rips.create_complex(sst.st, dim_max).
185  // This is because of how Gudhi copies certain information.
186 
187  // Declare a rips complex with the given point cloud,
188  // but do NOT call rips.create_complex yet.
189  Rips_complex rips(points,max_length,Gudhi::Euclidean_distance());
190 
191  // Declare but do NOT give a value to a Simplex_tree
193  int dim_max = 3;
194 
195  // Extra data is essentially a dictionary with keys of type Simplex_key and values of vector<float>
196  std::unordered_map<PersistentLaplacians::Simplex_tree::Simplex_key, std::vector<float>> extra_data;
197 
198  // Construct sst as wrapping the declared simplex tree and extra data
199  PersistentLaplacians::sheaf_simplex_tree sst(st, extra_data, restriction);
200 
201  // Store the rips complex in the simplex tree that has been wrapped.
202  rips.create_complex(sst.st, dim_max);
203 
204  // Assuming that Gudhi's rips complex preserves the order of points
205  int counter = 0;
206 
207  // Assign vertex keys and add (x,y,z) coordinates to extra_data
208  for (Simplex_tree::Vertex_handle v : sst.st.complex_vertex_range()){
209  Simplex_tree::Simplex_handle as_sh = sst.st.find({v});
210  sst.st.assign_key(as_sh, counter);
211  sst.extra_data[sst.st.key(as_sh)] = {points[counter][0],
212  points[counter][1],
213  points[counter][2]};
214  counter++;
215  }
216  // Assign the dimension >= 1 simplex keys
217  for (Simplex_tree::Simplex_handle sh : sst.st.filtration_simplex_range()){
218  if (sst.st.dimension(sh) == 0)
219  continue;
220  sst.st.assign_key(sh, counter++);
221  }
222  return sst;
223  }
224 
225 
226 
227 
228 }
229 
230 #endif
Definition: FilteredBoundaryMatrix.hpp:16
Definition: sheaf_simplex_tree.hpp:20
Simplex_tree st
Definition: sheaf_simplex_tree.hpp:22
std::function< float(Simplex_tree::Simplex_handle, Simplex_tree::Simplex_handle, sheaf_simplex_tree &)> restriction
Definition: sheaf_simplex_tree.hpp:24
std::vector< FilteredBoundaryMatrix< float > > apply_restriction_function()
Definition: sheaf_simplex_tree.hpp:53
int coface_index(Simplex_tree::Simplex_handle simplex, Simplex_tree::Simplex_handle coface)
Definition: sheaf_simplex_tree.hpp:32
std::unordered_map< Simplex_tree::Simplex_key, std::vector< float > > extra_data
Definition: sheaf_simplex_tree.hpp:23
sheaf_simplex_tree(Simplex_tree _st, std::unordered_map< Simplex_tree::Simplex_key, std::vector< float >> _extra_data, std::function< float(Simplex_tree::Simplex_handle, Simplex_tree::Simplex_handle, sheaf_simplex_tree &)> _restriction)
Definition: sheaf_simplex_tree.hpp:25
Definition: PersistentLaplacians.cpp:7
Simplex_tree::Filtration_value Filtration_value
Definition: sheaf_simplex_tree.hpp:18
PersistentLaplacians::sheaf_simplex_tree rips_sheaf_simplex_tree(std::vector< std::vector< float >> points, Filtration_value max_length, std::function< float(Simplex_tree::Simplex_handle, Simplex_tree::Simplex_handle, sheaf_simplex_tree &)> restriction)
Definition: sheaf_simplex_tree.hpp:176
std::vector< std::vector< Filtration_value > > Distance_matrix
Definition: sheaf_simplex_tree.hpp:19
Gudhi::Simplex_tree< Gudhi::Simplex_tree_options_default > Simplex_tree
Definition: sheaf_simplex_tree.hpp:17
Eigen::SparseMatrix< float, Eigen::ColMajor > SparseMatrixFloat
Definition: typedefs.hpp:31