Loading...
Searching...
No Matches
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
24namespace Gudhi {
25namespace rips_complex {
26// A custom graph class, because boost::adjacency_list does not conveniently allow to choose vertex descriptors
27template <class Vertex_handle, class Filtration_value>
28struct 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};
36template <class Vertex_handle, class Filtration_value>
37void add_vertex(Vertex_handle v, Graph<Vertex_handle, Filtration_value>&g) { g.vlist.push_back(v); }
38template <class Vertex_handle, class Filtration_value>
39void add_edge(Vertex_handle u, Vertex_handle v, Filtration_value f, Graph<Vertex_handle, Filtration_value>&g) { g.elist.emplace_back(u, v, f); }
40template <class Vertex_handle, class Filtration_value>
41std::size_t num_vertices(Graph<Vertex_handle, Filtration_value> const&g) { return g.vlist.size(); }
42template <class Vertex_handle, class Filtration_value>
43std::size_t num_edges(Graph<Vertex_handle, Filtration_value> const&g) { return g.elist.size(); }
44template <class Vertex_handle, class Filtration_value, class Iter = typename Graph<Vertex_handle, Filtration_value>::vertex_iterator>
45std::pair<Iter, Iter>
46vertices(Graph<Vertex_handle, Filtration_value> const&g) {
47 return { g.vlist.begin(), g.vlist.end() };
48}
49template <class Vertex_handle, class Filtration_value>
50std::pair<boost::counting_iterator<std::size_t>, boost::counting_iterator<std::size_t>>
51edges(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}
55template <class Vertex_handle, class Filtration_value>
56Vertex_handle source(std::size_t e, Graph<Vertex_handle, Filtration_value> const&g) { return std::get<0>(g.elist[e]); }
57template <class Vertex_handle, class Filtration_value>
58Vertex_handle target(std::size_t e, Graph<Vertex_handle, Filtration_value> const&g) { return std::get<1>(g.elist[e]); }
59template <class Vertex_handle, class Filtration_value>
60Filtration_value get(vertex_filtration_t, Graph<Vertex_handle, Filtration_value> const&, Vertex_handle) { return 0; }
61template <class Vertex_handle, class Filtration_value>
62Filtration_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
65namespace boost {
66template <class Vertex_handle, class Filtration_value>
67struct 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
82namespace Gudhi {
83
84namespace 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
103template <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
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