Sparse_rips_complex.h
1 /* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT.
2  * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details.
3  * Author(s): Marc Glisse
4  *
5  * Copyright (C) 2018 Inria
6  *
7  * Modification(s):
8  * - YYYY/MM Author: Description of the modification
9  */
10 
11 #ifndef SPARSE_RIPS_COMPLEX_H_
12 #define SPARSE_RIPS_COMPLEX_H_
13 
14 #include <gudhi/Debug_utils.h>
16 #include <gudhi/choose_n_farthest_points.h>
17 
18 #include <boost/graph/graph_traits.hpp>
19 #include <boost/range/metafunctions.hpp>
20 #include <boost/iterator/counting_iterator.hpp>
21 
22 #include <vector>
23 
24 namespace Gudhi {
25 namespace rips_complex {
26 // A custom graph class, because boost::adjacency_list does not conveniently allow to choose vertex descriptors
27 template <class Vertex_handle, class Filtration_value>
28 struct Graph {
29  typedef std::vector<Vertex_handle> VList;
30  typedef std::vector<std::tuple<Vertex_handle, Vertex_handle, Filtration_value>> EList;
31  typedef typename VList::const_iterator vertex_iterator;
32  typedef boost::counting_iterator<std::size_t> edge_iterator;
33  VList vlist;
34  EList elist;
35 };
36 template <class Vertex_handle, class Filtration_value>
37 void add_vertex(Vertex_handle v, Graph<Vertex_handle, Filtration_value>&g) { g.vlist.push_back(v); }
38 template <class Vertex_handle, class Filtration_value>
39 void add_edge(Vertex_handle u, Vertex_handle v, Filtration_value f, Graph<Vertex_handle, Filtration_value>&g) { g.elist.emplace_back(u, v, f); }
40 template <class Vertex_handle, class Filtration_value>
41 std::size_t num_vertices(Graph<Vertex_handle, Filtration_value> const&g) { return g.vlist.size(); }
42 template <class Vertex_handle, class Filtration_value>
43 std::size_t num_edges(Graph<Vertex_handle, Filtration_value> const&g) { return g.elist.size(); }
44 template <class Vertex_handle, class Filtration_value, class Iter = typename Graph<Vertex_handle, Filtration_value>::vertex_iterator>
45 std::pair<Iter, Iter>
46 vertices(Graph<Vertex_handle, Filtration_value> const&g) {
47  return { g.vlist.begin(), g.vlist.end() };
48 }
49 template <class Vertex_handle, class Filtration_value>
50 std::pair<boost::counting_iterator<std::size_t>, boost::counting_iterator<std::size_t>>
51 edges(Graph<Vertex_handle, Filtration_value> const&g) {
52  typedef boost::counting_iterator<std::size_t> I;
53  return { I(0), I(g.elist.size()) };
54 }
55 template <class Vertex_handle, class Filtration_value>
56 Vertex_handle source(std::size_t e, Graph<Vertex_handle, Filtration_value> const&g) { return std::get<0>(g.elist[e]); }
57 template <class Vertex_handle, class Filtration_value>
58 Vertex_handle target(std::size_t e, Graph<Vertex_handle, Filtration_value> const&g) { return std::get<1>(g.elist[e]); }
59 template <class Vertex_handle, class Filtration_value>
60 Filtration_value get(vertex_filtration_t, Graph<Vertex_handle, Filtration_value> const&, Vertex_handle) { return 0; }
61 template <class Vertex_handle, class Filtration_value>
62 Filtration_value get(edge_filtration_t, Graph<Vertex_handle, Filtration_value> const&g, std::size_t e) { return std::get<2>(g.elist[e]); }
63 } // namespace rips_complex
64 } // namespace Gudhi
65 namespace boost {
66 template <class Vertex_handle, class Filtration_value>
67 struct graph_traits<Gudhi::rips_complex::Graph<Vertex_handle, Filtration_value>> {
68  typedef Gudhi::rips_complex::Graph<Vertex_handle, Filtration_value> G;
69  struct traversal_category : vertex_list_graph_tag, edge_list_graph_tag {};
70  typedef Vertex_handle vertex_descriptor;
71  typedef typename G::vertex_iterator vertex_iterator;
72  typedef std::size_t vertices_size_type;
73  typedef std::size_t edge_descriptor;
74  typedef typename G::edge_iterator edge_iterator;
75  typedef std::size_t edges_size_type;
76  typedef directed_tag directed_category;
77  typedef disallow_parallel_edge_tag edge_parallel_category;
78 };
79 // Etc, since we don't expose this graph to the world, we know we are not going to query property_traits for instance.
80 }
81 
82 namespace Gudhi {
83 
84 namespace rips_complex {
85 
86 // The whole interface is copied on Rips_complex. A redesign should be discussed with all complex creation classes in
87 // mind.
88 
103 template <typename Filtration_value>
105  private:
106  // TODO(MG): use a different graph where we know we can safely insert in parallel.
107  typedef int Vertex_handle;
108  typedef rips_complex::Graph<Vertex_handle, Filtration_value> Graph;
109 
110  public:
120  template <typename RandomAccessPointRange, typename Distance>
121  Sparse_rips_complex(const RandomAccessPointRange& points, Distance distance, double const epsilon, Filtration_value const mini=-std::numeric_limits<Filtration_value>::infinity(), Filtration_value const maxi=std::numeric_limits<Filtration_value>::infinity())
122  : epsilon_(epsilon) {
123  GUDHI_CHECK(epsilon > 0, "epsilon must be positive");
124  auto dist_fun = [&](Vertex_handle i, Vertex_handle j) { return distance(points[i], points[j]); };
125  // TODO: stop choose_n_farthest_points once it reaches mini or 0?
126  subsampling::choose_n_farthest_points_metric(dist_fun, boost::irange<Vertex_handle>(0, boost::size(points)), -1, -1,
127  std::back_inserter(sorted_points), std::back_inserter(params));
128  compute_sparse_graph(dist_fun, epsilon, mini, maxi);
129  }
130 
141  template <typename DistanceMatrix>
142  Sparse_rips_complex(const DistanceMatrix& distance_matrix, double const epsilon, Filtration_value const mini=-std::numeric_limits<Filtration_value>::infinity(), Filtration_value const maxi=std::numeric_limits<Filtration_value>::infinity())
143  : Sparse_rips_complex(boost::irange<Vertex_handle>(0, boost::size(distance_matrix)),
144  [&](Vertex_handle i, Vertex_handle j) { return (i==j) ? 0 : (i<j) ? distance_matrix[j][i] : distance_matrix[i][j]; },
145  epsilon, mini, maxi) {}
146 
157  template <typename SimplicialComplexForRips>
158  void create_complex(SimplicialComplexForRips& complex, int const dim_max) {
159  GUDHI_CHECK(complex.num_vertices() == 0,
160  std::invalid_argument("Sparse_rips_complex::create_complex - simplicial complex is not empty"));
161 
162  complex.insert_graph(graph_);
163  if(epsilon_ >= 1) {
164  complex.expansion(dim_max);
165  return;
166  }
167  const std::size_t n = num_vertices(graph_);
168  std::vector<Filtration_value> lambda(max_v + 1);
169  // lambda[original_order]=params[sorted_order]
170  for(std::size_t i=0;i<n;++i)
171  lambda[sorted_points[i]] = params[i];
172  double cst = epsilon_ * (1 - epsilon_) / 2;
173  auto block = [cst,&complex,&lambda](typename SimplicialComplexForRips::Simplex_handle sh){
174  auto filt = complex.filtration(sh);
175  auto min_f = filt * cst;
176  for(auto v : complex.simplex_vertex_range(sh)){
177  if(lambda[v] < min_f)
178  return true; // v died before this simplex could be born
179  }
180  return false;
181  };
182  complex.expansion_with_blockers(dim_max, block);
183  }
184 
185  private:
186  // PointRange must be random access.
187  template <typename Distance>
188  void compute_sparse_graph(Distance& dist, double const epsilon, Filtration_value const mini, Filtration_value const maxi) {
189  const auto& points = sorted_points; // convenience alias
190  std::size_t n = boost::size(points);
191  double cst = epsilon * (1 - epsilon) / 2;
192  max_v = -1; // Useful for the size of the map lambda.
193  for (std::size_t i = 0; i < n; ++i) {
194  if ((params[i] < mini || params[i] <= 0) && i != 0) break;
195  // The parameter of the first point is not very meaningful, it is supposed to be infinite,
196  // but if the type does not support it...
197  // It would be better to do this reduction of the number of points earlier, around choose_n_farthest_points.
198  add_vertex(points[i], graph_);
199  max_v = std::max(max_v, points[i]);
200  }
201  n = num_vertices(graph_);
202 
203  // TODO(MG):
204  // - make it parallel
205  // - only test near-enough neighbors
206  for (std::size_t i = 0; i < n; ++i) {
207  auto&& pi = points[i];
208  auto li = params[i];
209  // If we inserted all the points, points with multiplicity would get connected to their first representative,
210  // no need to handle the redundant ones in the outer loop.
211  // if (li <= 0 && i != 0) break;
212  for (std::size_t j = i + 1; j < n; ++j) {
213  auto&& pj = points[j];
214  auto d = dist(pi, pj);
215  auto lj = params[j];
216  GUDHI_CHECK(lj <= li, "Bad furthest point sorting");
217  Filtration_value alpha;
218 
219  // The paper has d/2 and d-lj/e to match the Cech, but we use doubles to match the Rips
220  if (d * epsilon <= 2 * lj)
221  alpha = d;
222  else if (d * epsilon > li + lj)
223  continue;
224  else {
225  alpha = (d - lj / epsilon) * 2;
226  // Keep the test exactly the same as in block to avoid inconsistencies
227  if (epsilon < 1 && alpha * cst > lj)
228  continue;
229  }
230 
231  if (alpha <= maxi)
232  add_edge(pi, pj, alpha, graph_);
233  }
234  }
235  }
236 
237  Graph graph_;
238  double epsilon_;
239  Vertex_handle max_v;
240  // Because of the arbitrary split between constructor and create_complex
241  // sorted_points[sorted_order]=original_order
242  std::vector<Vertex_handle> sorted_points;
243  // params[sorted_order]=distance to previous points
244  std::vector<Filtration_value> params;
245 };
246 
247 } // namespace rips_complex
248 
249 } // namespace Gudhi
250 
251 #endif // SPARSE_RIPS_COMPLEX_H_
Sparse Rips complex data structure.
Definition: Sparse_rips_complex.h:104
Sparse_rips_complex(const RandomAccessPointRange &points, Distance distance, double const epsilon, Filtration_value const mini=-std::numeric_limits< Filtration_value >::infinity(), Filtration_value const maxi=std::numeric_limits< Filtration_value >::infinity())
Sparse_rips_complex constructor from a list of points.
Definition: Sparse_rips_complex.h:121
Sparse_rips_complex(const DistanceMatrix &distance_matrix, double const epsilon, Filtration_value const mini=-std::numeric_limits< Filtration_value >::infinity(), Filtration_value const maxi=std::numeric_limits< Filtration_value >::infinity())
Sparse_rips_complex constructor from a distance matrix.
Definition: Sparse_rips_complex.h:142
void create_complex(SimplicialComplexForRips &complex, int const dim_max)
Fills the simplicial complex with the sparse Rips graph and expands it with all the cliques,...
Definition: Sparse_rips_complex.h:158
Graph simplicial complex methods.
void choose_n_farthest_points_metric(Distance dist_, Point_range const &input_pts, std::size_t final_size, std::size_t starting_point, PointOutputIterator output_it, DistanceOutputIterator dist_it={})
Subsample by an iterative, greedy strategy.
Definition: choose_n_farthest_points.h:208
Gudhi namespace.
Definition: SimplicialComplexForAlpha.h:14
Value type for a filtration function on a cell complex.
Definition: FiltrationValue.h:20
The concept SimplicialComplexForRips describes the requirements for a type to implement a simplicial ...
Definition: SimplicialComplexForRips.h:21
unspecified simplex_vertex_range(Simplex_handle sh)
Returns a range over the vertices of a simplex.
std::size_t num_vertices()
Returns the number of vertices in the simplicial complex.
unspecified Simplex_handle
Handle type to a simplex contained in the simplicial complex.
Definition: SimplicialComplexForRips.h:26
void expansion(int max_dim)
Expands the simplicial complex containing only its one skeleton until a given maximal dimension as ex...
void expansion_with_blockers(int max_dim, Blocker block_simplex)
Expands a simplicial complex containing only a graph. Simplices corresponding to cliques in the graph...
void insert_graph(const OneSkeletonGraph &skel_graph)
Inserts a given Gudhi::rips_complex::Rips_complex::OneSkeletonGraph in the simplicial complex.
Handle type for the vertices of a cell complex.
Definition: VertexHandle.h:15