summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Rips_complex/include/gudhi/Sparse_rips_complex.h169
-rw-r--r--src/Rips_complex/utilities/sparse_rips_persistence.cpp145
2 files changed, 314 insertions, 0 deletions
diff --git a/src/Rips_complex/include/gudhi/Sparse_rips_complex.h b/src/Rips_complex/include/gudhi/Sparse_rips_complex.h
new file mode 100644
index 00000000..c5378b6e
--- /dev/null
+++ b/src/Rips_complex/include/gudhi/Sparse_rips_complex.h
@@ -0,0 +1,169 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Marc Glisse
+ *
+ * Copyright (C) 2018 INRIA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPARSE_RIPS_COMPLEX_H_
+#define SPARSE_RIPS_COMPLEX_H_
+
+#include <gudhi/Debug_utils.h>
+#include <gudhi/graph_simplicial_complex.h>
+#include <gudhi/choose_n_farthest_points.h>
+
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/range/metafunctions.hpp>
+
+#include <vector>
+
+
+namespace Gudhi {
+
+namespace rips_complex {
+
+// The whole interface is copied on Rips_complex. A redesign should be discussed with all complex creation classes in mind.
+
+/**
+ * \class Sparse_rips_complex
+ * \brief Sparse Rips complex data structure.
+ *
+ * \ingroup rips_complex
+ *
+ * \details
+ * This class is used to construct a sparse \f$(1+\epsilon)\f$-approximation of `Rips_complex`.
+ *
+ * \tparam Filtration_value is the type used to store the filtration values of the simplicial complex.
+ */
+template<typename Filtration_value>
+class Sparse_rips_complex {
+ private:
+ // TODO: use a different graph where we know we can safely insert in parallel.
+ typedef typename boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS
+ , boost::property<vertex_filtration_t, Filtration_value>
+ , boost::property<edge_filtration_t, Filtration_value>> Graph;
+
+ typedef int Vertex_handle;
+
+ public:
+ /** \brief Sparse_rips_complex constructor from a list of points.
+ *
+ * @param[in] points Range of points.
+ * @param[in] distance distance function that returns a `Filtration_value` from 2 given points.
+ *
+ */
+ template<typename RandomAccessPointRange, typename Distance >
+ Sparse_rips_complex(const RandomAccessPointRange& points, Distance distance, double epsilon) {
+ std::vector<Vertex_handle> sorted_points;
+ std::vector<Filtration_value> params;
+ auto dist_fun = [&](Vertex_handle i, Vertex_handle j){return distance(points[i], points[j]);};
+ Ker<decltype(dist_fun)> kernel(dist_fun);
+ subsampling::choose_n_farthest_points(kernel, boost::irange<Vertex_handle>(0, boost::size(points)), -1, -1, std::back_inserter(sorted_points), std::back_inserter(params));
+ compute_sparse_graph(sorted_points, params, dist_fun, epsilon);
+ }
+
+ /** \brief Rips_complex constructor from a distance matrix.
+ *
+ * @param[in] distance_matrix Range of range of distances.
+ * `distance_matrix[i][j]` returns the distance between points \f$i\f$ and
+ * \f$j\f$ as long as \f$ 0 \leqslant i < j \leqslant
+ * distance\_matrix.size().\f$
+ */
+ template<typename DistanceMatrix>
+ Sparse_rips_complex(const DistanceMatrix& distance_matrix, double epsilon)
+ : Sparse_rips_complex(
+ boost::irange<Vertex_handle>(0, boost::size(distance_matrix)),
+ [&](Vertex_handle i, Vertex_handle j){return distance_matrix[j][i];},
+ epsilon) {}
+
+ /** \brief Fills the simplicial complex with the sparse Rips graph and
+ * expands it with all the cliques, stopping at a given maximal dimension.
+ *
+ * \tparam SimplicialComplexForRips must meet `SimplicialComplexForRips` concept.
+ *
+ * @param[in] complex the complex to fill
+ * @param[in] dim_max maximal dimension of the simplicial complex.
+ * @exception std::invalid_argument In debug mode, if `complex.num_vertices()` does not return 0.
+ *
+ */
+ template <typename SimplicialComplexForRips>
+ void create_complex(SimplicialComplexForRips& complex, int dim_max) {
+ GUDHI_CHECK(complex.num_vertices() == 0,
+ std::invalid_argument("Sparse_rips_complex::create_complex - simplicial complex is not empty"));
+
+ complex.insert_graph(graph_);
+ complex.expansion(dim_max);
+ }
+
+ private:
+ // choose_n_farthest_points wants the distance function in this form...
+ template <class Distance>
+ struct Ker {
+ typedef std::size_t Point_d; // index into point range
+ Ker(Distance& d) : dist (d) {}
+ // Despite the name, this is not squared...
+ typedef Distance Squared_distance_d;
+ Squared_distance_d& squared_distance_d_object() const { return dist; }
+ Distance& dist;
+ };
+
+ // PointRange must be random access.
+ template<typename PointRange, typename ParamRange, typename Distance>
+ void compute_sparse_graph(const PointRange& points, const ParamRange& params, Distance& dist, double epsilon) {
+ const int n = boost::size(points);
+ graph_.~Graph();
+ new(&graph_) Graph(n);
+ //for(auto v : vertices(g)) // doesn't work :-(
+ typename boost::graph_traits<Graph>::vertex_iterator v_i, v_e;
+ for(std::tie(v_i, v_e) = vertices(graph_); v_i != v_e; ++v_i) {
+ auto v = *v_i;
+ // This whole loop might not be necessary, leave it until someone investigates if it is safe to remove.
+ put(vertex_filtration_t(), graph_, v, 0);
+ }
+
+ // TODO:
+ // - make it parallel
+ // - only test near-enough neighbors
+ for(int i = 0; i < n; ++i)
+ for(int j = i + 1; j < n; ++j){
+ auto&& pi = points[i];
+ auto&& pj = points[j];
+ auto d = dist(pi, pj);
+ auto li = params[i];
+ auto lj = params[j];
+ GUDHI_CHECK(lj <= li, "Bad furthest point sorting");
+ Filtration_value alpha;
+
+ if(d * epsilon <= 2 * lj)
+ alpha = d / 2;
+ else if(d * epsilon <= li + lj && (epsilon >= 1 || d * epsilon <= lj * (1 + 1 / (1 - epsilon))))
+ alpha = d - lj / epsilon;
+ else continue;
+
+ add_edge(pi, pj, alpha, graph_);
+ }
+ }
+
+ Graph graph_;
+};
+
+} // namespace rips_complex
+
+} // namespace Gudhi
+
+#endif // SPARSE_RIPS_COMPLEX_H_
diff --git a/src/Rips_complex/utilities/sparse_rips_persistence.cpp b/src/Rips_complex/utilities/sparse_rips_persistence.cpp
new file mode 100644
index 00000000..12b3b099
--- /dev/null
+++ b/src/Rips_complex/utilities/sparse_rips_persistence.cpp
@@ -0,0 +1,145 @@
+/* This file is part of the Gudhi Library. The Gudhi library
+ * (Geometric Understanding in Higher Dimensions) is a generic C++
+ * library for computational topology.
+ *
+ * Author(s): Marc Glisse, Clément Maria
+ *
+ * Copyright (C) 2014 INRIA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gudhi/Sparse_rips_complex.h>
+#include <gudhi/distance_functions.h>
+#include <gudhi/Simplex_tree.h>
+#include <gudhi/Persistent_cohomology.h>
+#include <gudhi/Points_off_io.h>
+
+#include <boost/program_options.hpp>
+
+#include <string>
+#include <vector>
+
+// Types definition
+using Simplex_tree = Gudhi::Simplex_tree<Gudhi::Simplex_tree_options_fast_persistence>;
+using Filtration_value = Simplex_tree::Filtration_value;
+using Sparse_rips = Gudhi::rips_complex::Sparse_rips_complex<Filtration_value>;
+using Field_Zp = Gudhi::persistent_cohomology::Field_Zp;
+using Persistent_cohomology = Gudhi::persistent_cohomology::Persistent_cohomology<Simplex_tree, Field_Zp >;
+using Point = std::vector<double>;
+using Points_off_reader = Gudhi::Points_off_reader<Point>;
+
+void program_options(int argc, char * argv[]
+ , std::string & off_file_points
+ , std::string & filediag
+ , double & epsilon
+ , int & dim_max
+ , int & p
+ , Filtration_value & min_persistence);
+
+int main(int argc, char * argv[]) {
+ std::string off_file_points;
+ std::string filediag;
+ double epsilon;
+ int dim_max;
+ int p;
+ Filtration_value min_persistence;
+
+ program_options(argc, argv, off_file_points, filediag, epsilon, dim_max, p, min_persistence);
+
+ Points_off_reader off_reader(off_file_points);
+ Sparse_rips sparse_rips(off_reader.get_point_cloud(), Gudhi::Euclidean_distance(), epsilon);
+
+ // Construct the Rips complex in a Simplex Tree
+ Simplex_tree simplex_tree;
+
+ sparse_rips.create_complex(simplex_tree, dim_max);
+ std::cout << "The complex contains " << simplex_tree.num_simplices() << " simplices \n";
+ std::cout << " and has dimension " << simplex_tree.dimension() << " \n";
+
+ // Sort the simplices in the order of the filtration
+ simplex_tree.initialize_filtration();
+
+ // Compute the persistence diagram of the complex
+ Persistent_cohomology pcoh(simplex_tree);
+ // initializes the coefficient field for homology
+ pcoh.init_coefficients(p);
+
+ pcoh.compute_persistent_cohomology(min_persistence);
+
+ // Output the diagram in filediag
+ if (filediag.empty()) {
+ pcoh.output_diagram();
+ } else {
+ std::ofstream out(filediag);
+ pcoh.output_diagram(out);
+ out.close();
+ }
+
+ return 0;
+}
+
+void program_options(int argc, char * argv[]
+ , std::string & off_file_points
+ , std::string & filediag
+ , double & epsilon
+ , int & dim_max
+ , int & p
+ , Filtration_value & min_persistence) {
+ namespace po = boost::program_options;
+ po::options_description hidden("Hidden options");
+ hidden.add_options()
+ ("input-file", po::value<std::string>(&off_file_points),
+ "Name of an OFF file containing a point set.\n");
+
+ po::options_description visible("Allowed options", 100);
+ visible.add_options()
+ ("help,h", "produce help message")
+ ("output-file,o", po::value<std::string>(&filediag)->default_value(std::string()),
+ "Name of file in which the persistence diagram is written. Default print in std::cout")
+ ("approximation,e", po::value<double>(&epsilon)->default_value(.5),
+ "Epsilon, where the sparse Rips complex is a (1+epsilon)-approximation of the Rips complex.")
+ ("cpx-dimension,d", po::value<int>(&dim_max)->default_value(1),
+ "Maximal dimension of the Rips complex we want to compute.")
+ ("field-charac,p", po::value<int>(&p)->default_value(11),
+ "Characteristic p of the coefficient field Z/pZ for computing homology.")
+ ("min-persistence,m", po::value<Filtration_value>(&min_persistence),
+ "Minimal lifetime of homology feature to be recorded. Default is 0. Enter a negative value to see zero length intervals");
+
+ po::positional_options_description pos;
+ pos.add("input-file", 1);
+
+ po::options_description all;
+ all.add(visible).add(hidden);
+
+ po::variables_map vm;
+ po::store(po::command_line_parser(argc, argv).
+ options(all).positional(pos).run(), vm);
+ po::notify(vm);
+
+ if (vm.count("help") || !vm.count("input-file")) {
+ std::cout << std::endl;
+ std::cout << "Compute the persistent homology with coefficient field Z/pZ \n";
+ std::cout << "of a sparse (1+epsilon)-approximation of the Rips complex \ndefined on a set of input points.\n \n";
+ std::cout << "The output diagram contains one bar per line, written with the convention: \n";
+ std::cout << " p dim b d \n";
+ std::cout << "where dim is the dimension of the homological feature,\n";
+ std::cout << "b and d are respectively the birth and death of the feature and \n";
+ std::cout << "p is the characteristic of the field Z/pZ used for homology coefficients." << std::endl << std::endl;
+
+ std::cout << "Usage: " << argv[0] << " [options] input-file" << std::endl << std::endl;
+ std::cout << visible << std::endl;
+ std::abort();
+ }
+}