diff options
Diffstat (limited to 'src/python/include')
-rw-r--r-- | src/python/include/Alpha_complex_factory.h | 118 | ||||
-rw-r--r-- | src/python/include/Alpha_complex_interface.h | 62 | ||||
-rw-r--r-- | src/python/include/Persistent_cohomology_interface.h | 40 | ||||
-rw-r--r-- | src/python/include/Simplex_tree_interface.h | 72 | ||||
-rw-r--r-- | src/python/include/pybind11_diagram_utils.h | 25 |
5 files changed, 185 insertions, 132 deletions
diff --git a/src/python/include/Alpha_complex_factory.h b/src/python/include/Alpha_complex_factory.h index 3405fdd6..41eb72c1 100644 --- a/src/python/include/Alpha_complex_factory.h +++ b/src/python/include/Alpha_complex_factory.h @@ -31,15 +31,34 @@ namespace Gudhi { namespace alpha_complex { -template <typename CgalPointType> -std::vector<double> pt_cgal_to_cython(CgalPointType const& point) { - std::vector<double> vd; - vd.reserve(point.dimension()); - for (auto coord = point.cartesian_begin(); coord != point.cartesian_end(); coord++) - vd.push_back(CGAL::to_double(*coord)); - return vd; -} +// template Functor that transforms a CGAL point to a vector of double as expected by cython +template<typename CgalPointType, bool Weighted> +struct Point_cgal_to_cython; + +// Specialized Unweighted Functor +template<typename CgalPointType> +struct Point_cgal_to_cython<CgalPointType, false> { + std::vector<double> operator()(CgalPointType const& point) const + { + std::vector<double> vd; + vd.reserve(point.dimension()); + for (auto coord = point.cartesian_begin(); coord != point.cartesian_end(); coord++) + vd.push_back(CGAL::to_double(*coord)); + return vd; + } +}; +// Specialized Weighted Functor +template<typename CgalPointType> +struct Point_cgal_to_cython<CgalPointType, true> { + std::vector<double> operator()(CgalPointType const& weighted_point) const + { + const auto& point = weighted_point.point(); + return Point_cgal_to_cython<decltype(point), false>()(point); + } +}; + +// Function that transforms a cython point (aka. a vector of double) to a CGAL point template <typename CgalPointType> static CgalPointType pt_cython_to_cgal(std::vector<double> const& vec) { return CgalPointType(vec.size(), vec.begin(), vec.end()); @@ -51,24 +70,35 @@ class Abstract_alpha_complex { virtual bool create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, bool default_filtration_value) = 0; + + virtual std::size_t num_vertices() const = 0; virtual ~Abstract_alpha_complex() = default; }; -class Exact_Alphacomplex_dD final : public Abstract_alpha_complex { +template <bool Weighted = false> +class Exact_alpha_complex_dD final : public Abstract_alpha_complex { private: using Kernel = CGAL::Epeck_d<CGAL::Dynamic_dimension_tag>; - using Point = typename Kernel::Point_d; + using Bare_point = typename Kernel::Point_d; + using Point = std::conditional_t<Weighted, typename Kernel::Weighted_point_d, + typename Kernel::Point_d>; public: - Exact_Alphacomplex_dD(const std::vector<std::vector<double>>& points, bool exact_version) + Exact_alpha_complex_dD(const std::vector<std::vector<double>>& points, bool exact_version) + : exact_version_(exact_version), + alpha_complex_(boost::adaptors::transform(points, pt_cython_to_cgal<Bare_point>)) { + } + + Exact_alpha_complex_dD(const std::vector<std::vector<double>>& points, + const std::vector<double>& weights, bool exact_version) : exact_version_(exact_version), - alpha_complex_(boost::adaptors::transform(points, pt_cython_to_cgal<Point>)) { + alpha_complex_(boost::adaptors::transform(points, pt_cython_to_cgal<Bare_point>), weights) { } virtual std::vector<double> get_point(int vh) override { - Point const& point = alpha_complex_.get_point(vh); - return pt_cgal_to_cython(point); + // Can be a Weighted or a Bare point in function of Weighted + return Point_cgal_to_cython<Point, Weighted>()(alpha_complex_.get_point(vh)); } virtual bool create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, @@ -76,65 +106,49 @@ class Exact_Alphacomplex_dD final : public Abstract_alpha_complex { return alpha_complex_.create_complex(*simplex_tree, max_alpha_square, exact_version_, default_filtration_value); } + virtual std::size_t num_vertices() const override { + return alpha_complex_.num_vertices(); + } + private: bool exact_version_; - Alpha_complex<Kernel> alpha_complex_; + Alpha_complex<Kernel, Weighted> alpha_complex_; }; -class Inexact_Alphacomplex_dD final : public Abstract_alpha_complex { +template <bool Weighted = false> +class Inexact_alpha_complex_dD final : public Abstract_alpha_complex { private: using Kernel = CGAL::Epick_d<CGAL::Dynamic_dimension_tag>; - using Point = typename Kernel::Point_d; + using Bare_point = typename Kernel::Point_d; + using Point = std::conditional_t<Weighted, typename Kernel::Weighted_point_d, + typename Kernel::Point_d>; public: - Inexact_Alphacomplex_dD(const std::vector<std::vector<double>>& points, bool exact_version) - : exact_version_(exact_version), - alpha_complex_(boost::adaptors::transform(points, pt_cython_to_cgal<Point>)) { + Inexact_alpha_complex_dD(const std::vector<std::vector<double>>& points) + : alpha_complex_(boost::adaptors::transform(points, pt_cython_to_cgal<Bare_point>)) { + } + + Inexact_alpha_complex_dD(const std::vector<std::vector<double>>& points, const std::vector<double>& weights) + : alpha_complex_(boost::adaptors::transform(points, pt_cython_to_cgal<Bare_point>), weights) { } virtual std::vector<double> get_point(int vh) override { - Point const& point = alpha_complex_.get_point(vh); - return pt_cgal_to_cython(point); + // Can be a Weighted or a Bare point in function of Weighted + return Point_cgal_to_cython<Point, Weighted>()(alpha_complex_.get_point(vh)); } virtual bool create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, bool default_filtration_value) override { - return alpha_complex_.create_complex(*simplex_tree, max_alpha_square, exact_version_, default_filtration_value); + return alpha_complex_.create_complex(*simplex_tree, max_alpha_square, false, default_filtration_value); } - private: - bool exact_version_; - Alpha_complex<Kernel> alpha_complex_; -}; - -template <complexity Complexity> -class Alphacomplex_3D final : public Abstract_alpha_complex { - private: - using Point = typename Alpha_complex_3d<Complexity, false, false>::Bare_point_3; - - static Point pt_cython_to_cgal_3(std::vector<double> const& vec) { - return Point(vec[0], vec[1], vec[2]); - } - - public: - Alphacomplex_3D(const std::vector<std::vector<double>>& points) - : alpha_complex_(boost::adaptors::transform(points, pt_cython_to_cgal_3)) { - } - - virtual std::vector<double> get_point(int vh) override { - Point const& point = alpha_complex_.get_point(vh); - return pt_cgal_to_cython(point); - } - - virtual bool create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, - bool default_filtration_value) override { - return alpha_complex_.create_complex(*simplex_tree, max_alpha_square); + virtual std::size_t num_vertices() const override { + return alpha_complex_.num_vertices(); } private: - Alpha_complex_3d<Complexity, false, false> alpha_complex_; + Alpha_complex<Kernel, Weighted> alpha_complex_; }; - } // namespace alpha_complex } // namespace Gudhi diff --git a/src/python/include/Alpha_complex_interface.h b/src/python/include/Alpha_complex_interface.h index 23be194d..469b91ce 100644 --- a/src/python/include/Alpha_complex_interface.h +++ b/src/python/include/Alpha_complex_interface.h @@ -27,10 +27,23 @@ namespace alpha_complex { class Alpha_complex_interface { public: - Alpha_complex_interface(const std::vector<std::vector<double>>& points, bool fast_version, bool exact_version) - : points_(points), - fast_version_(fast_version), - exact_version_(exact_version) { + Alpha_complex_interface(const std::vector<std::vector<double>>& points, + const std::vector<double>& weights, + bool fast_version, bool exact_version) { + const bool weighted = (weights.size() > 0); + if (fast_version) { + if (weighted) { + alpha_ptr_ = std::make_unique<Inexact_alpha_complex_dD<true>>(points, weights); + } else { + alpha_ptr_ = std::make_unique<Inexact_alpha_complex_dD<false>>(points); + } + } else { + if (weighted) { + alpha_ptr_ = std::make_unique<Exact_alpha_complex_dD<true>>(points, weights, exact_version); + } else { + alpha_ptr_ = std::make_unique<Exact_alpha_complex_dD<false>>(points, exact_version); + } + } } std::vector<double> get_point(int vh) { @@ -39,38 +52,23 @@ class Alpha_complex_interface { void create_simplex_tree(Simplex_tree_interface<>* simplex_tree, double max_alpha_square, bool default_filtration_value) { - if (points_.size() > 0) { - std::size_t dimension = points_[0].size(); - if (dimension == 3 && !default_filtration_value) { - if (fast_version_) - alpha_ptr_ = std::make_unique<Alphacomplex_3D<Gudhi::alpha_complex::complexity::FAST>>(points_); - else if (exact_version_) - alpha_ptr_ = std::make_unique<Alphacomplex_3D<Gudhi::alpha_complex::complexity::EXACT>>(points_); - else - alpha_ptr_ = std::make_unique<Alphacomplex_3D<Gudhi::alpha_complex::complexity::SAFE>>(points_); - if (!alpha_ptr_->create_simplex_tree(simplex_tree, max_alpha_square, default_filtration_value)) { - // create_simplex_tree will fail if all points are on a plane - Retry with dD by setting dimension to 2 - dimension--; - alpha_ptr_.reset(); - } - } - // Not ** else ** because we have to take into account if 3d fails - if (dimension != 3 || default_filtration_value) { - if (fast_version_) { - alpha_ptr_ = std::make_unique<Inexact_Alphacomplex_dD>(points_, exact_version_); - } else { - alpha_ptr_ = std::make_unique<Exact_Alphacomplex_dD>(points_, exact_version_); - } - alpha_ptr_->create_simplex_tree(simplex_tree, max_alpha_square, default_filtration_value); - } - } + // Nothing to be done in case of an empty point set + if (alpha_ptr_->num_vertices() > 0) + alpha_ptr_->create_simplex_tree(simplex_tree, max_alpha_square, default_filtration_value); + } + + static void set_float_relative_precision(double precision) { + // cf. Exact_alpha_complex_dD kernel type in Alpha_complex_factory.h + CGAL::Epeck_d<CGAL::Dynamic_dimension_tag>::FT::set_relative_precision_of_to_double(precision); + } + + static double get_float_relative_precision() { + // cf. Exact_alpha_complex_dD kernel type in Alpha_complex_factory.h + return CGAL::Epeck_d<CGAL::Dynamic_dimension_tag>::FT::get_relative_precision_of_to_double(); } private: std::unique_ptr<Abstract_alpha_complex> alpha_ptr_; - std::vector<std::vector<double>> points_; - bool fast_version_; - bool exact_version_; }; } // namespace alpha_complex diff --git a/src/python/include/Persistent_cohomology_interface.h b/src/python/include/Persistent_cohomology_interface.h index e5a3dfba..945378a0 100644 --- a/src/python/include/Persistent_cohomology_interface.h +++ b/src/python/include/Persistent_cohomology_interface.h @@ -12,6 +12,8 @@ #define INCLUDE_PERSISTENT_COHOMOLOGY_INTERFACE_H_ #include <gudhi/Persistent_cohomology.h> +#include <gudhi/Simplex_tree.h> // for Extended_simplex_type + #include <cstdlib> #include <vector> @@ -223,6 +225,44 @@ persistent_cohomology::Persistent_cohomology<FilteredComplex, persistent_cohomol return out; } + using Filtration_value = typename FilteredComplex::Filtration_value; + using Birth_death = std::pair<Filtration_value, Filtration_value>; + using Persistence_subdiagrams = std::vector<std::vector<std::pair<int, Birth_death>>>; + + Persistence_subdiagrams compute_extended_persistence_subdiagrams(Filtration_value min_persistence){ + Persistence_subdiagrams pers_subs(4); + auto const& persistent_pairs = Base::get_persistent_pairs(); + for (auto pair : persistent_pairs) { + std::pair<Filtration_value, Extended_simplex_type> px = stptr_->decode_extended_filtration(stptr_->filtration(get<0>(pair)), + stptr_->efd); + std::pair<Filtration_value, Extended_simplex_type> py = stptr_->decode_extended_filtration(stptr_->filtration(get<1>(pair)), + stptr_->efd); + std::pair<int, Birth_death> pd_point = std::make_pair(stptr_->dimension(get<0>(pair)), + std::make_pair(px.first, py.first)); + if(std::abs(px.first - py.first) > min_persistence){ + //Ordinary + if (px.second == Extended_simplex_type::UP && py.second == Extended_simplex_type::UP){ + pers_subs[0].push_back(pd_point); + } + // Relative + else if (px.second == Extended_simplex_type::DOWN && py.second == Extended_simplex_type::DOWN){ + pers_subs[1].push_back(pd_point); + } + else{ + // Extended+ + if (px.first < py.first){ + pers_subs[2].push_back(pd_point); + } + //Extended- + else{ + pers_subs[3].push_back(pd_point); + } + } + } + } + return pers_subs; + } + private: // A copy FilteredComplex* stptr_; diff --git a/src/python/include/Simplex_tree_interface.h b/src/python/include/Simplex_tree_interface.h index 629f6083..0317ea39 100644 --- a/src/python/include/Simplex_tree_interface.h +++ b/src/python/include/Simplex_tree_interface.h @@ -15,9 +15,7 @@ #include <gudhi/distance_functions.h> #include <gudhi/Simplex_tree.h> #include <gudhi/Points_off_io.h> -#ifdef GUDHI_USE_EIGEN3 #include <gudhi/Flag_complex_edge_collapser.h> -#endif #include <iostream> #include <vector> @@ -42,6 +40,9 @@ class Simplex_tree_interface : public Simplex_tree<SimplexTreeOptions> { using Complex_simplex_iterator = typename Base::Complex_simplex_iterator; using Extended_filtration_data = typename Base::Extended_filtration_data; using Boundary_simplex_iterator = typename Base::Boundary_simplex_iterator; + using Siblings = typename Base::Siblings; + using Node = typename Base::Node; + typedef bool (*blocker_func_t)(Simplex simplex, void *user_data); public: @@ -63,6 +64,30 @@ class Simplex_tree_interface : public Simplex_tree<SimplexTreeOptions> { return (result.second); } + void insert_matrix(double* filtrations, int n, int stride0, int stride1, double max_filtration) { + // We could delegate to insert_graph, but wrapping the matrix in a graph interface is too much work, + // and this is a bit more efficient. + auto& rm = this->root()->members_; + for(int i=0; i<n; ++i) { + char* p = reinterpret_cast<char*>(filtrations) + i * stride0; + double fv = *reinterpret_cast<double*>(p + i * stride1); + if(fv > max_filtration) continue; + auto sh = rm.emplace_hint(rm.end(), i, Node(this->root(), fv)); + Siblings* children = nullptr; + // Should we make a first pass to count the number of edges so we can reserve the right space? + for(int j=i+1; j<n; ++j) { + double fe = *reinterpret_cast<double*>(p + j * stride1); + if(fe > max_filtration) continue; + if(!children) { + children = new Siblings(this->root(), i); + sh->second.assign_children(children); + } + children->members().emplace_hint(children->members().end(), j, Node(children, fe)); + } + } + + } + // Do not interface this function, only used in alpha complex interface for complex creation bool insert_simplex(const Simplex& simplex, Filtration_value filtration = 0) { Insertion_result result = Base::insert_simplex(simplex, filtration); @@ -133,38 +158,7 @@ class Simplex_tree_interface : public Simplex_tree<SimplexTreeOptions> { return; } - std::vector<std::vector<std::pair<int, std::pair<Filtration_value, Filtration_value>>>> compute_extended_persistence_subdiagrams(const std::vector<std::pair<int, std::pair<Filtration_value, Filtration_value>>>& dgm, Filtration_value min_persistence){ - std::vector<std::vector<std::pair<int, std::pair<Filtration_value, Filtration_value>>>> new_dgm(4); - for (unsigned int i = 0; i < dgm.size(); i++){ - std::pair<Filtration_value, Extended_simplex_type> px = this->decode_extended_filtration(dgm[i].second.first, this->efd); - std::pair<Filtration_value, Extended_simplex_type> py = this->decode_extended_filtration(dgm[i].second.second, this->efd); - std::pair<int, std::pair<Filtration_value, Filtration_value>> pd_point = std::make_pair(dgm[i].first, std::make_pair(px.first, py.first)); - if(std::abs(px.first - py.first) > min_persistence){ - //Ordinary - if (px.second == Extended_simplex_type::UP && py.second == Extended_simplex_type::UP){ - new_dgm[0].push_back(pd_point); - } - // Relative - else if (px.second == Extended_simplex_type::DOWN && py.second == Extended_simplex_type::DOWN){ - new_dgm[1].push_back(pd_point); - } - else{ - // Extended+ - if (px.first < py.first){ - new_dgm[2].push_back(pd_point); - } - //Extended- - else{ - new_dgm[3].push_back(pd_point); - } - } - } - } - return new_dgm; - } - Simplex_tree_interface* collapse_edges(int nb_collapse_iteration) { -#ifdef GUDHI_USE_EIGEN3 using Filtered_edge = std::tuple<Vertex_handle, Vertex_handle, Filtration_value>; std::vector<Filtered_edge> edges; for (Simplex_handle sh : Base::skeleton_simplex_range(1)) { @@ -178,7 +172,7 @@ class Simplex_tree_interface : public Simplex_tree<SimplexTreeOptions> { } for (int iteration = 0; iteration < nb_collapse_iteration; iteration++) { - edges = Gudhi::collapse::flag_complex_collapse_edges(edges); + edges = Gudhi::collapse::flag_complex_collapse_edges(std::move(edges)); } Simplex_tree_interface* collapsed_stree_ptr = new Simplex_tree_interface(); // Copy the original 0-skeleton @@ -190,9 +184,13 @@ class Simplex_tree_interface : public Simplex_tree<SimplexTreeOptions> { collapsed_stree_ptr->insert({std::get<0>(remaining_edge), std::get<1>(remaining_edge)}, std::get<2>(remaining_edge)); } return collapsed_stree_ptr; -#else - throw std::runtime_error("Unable to collapse edges as it requires Eigen3 >= 3.1.0."); -#endif + } + + void expansion_with_blockers_callback(int dimension, blocker_func_t user_func, void *user_data) { + Base::expansion_with_blockers(dimension, [&](Simplex_handle sh){ + Simplex simplex(Base::simplex_vertex_range(sh).begin(), Base::simplex_vertex_range(sh).end()); + return user_func(simplex, user_data); + }); } // Iterator over the simplex tree diff --git a/src/python/include/pybind11_diagram_utils.h b/src/python/include/pybind11_diagram_utils.h index 2d5194f4..5cb7c48b 100644 --- a/src/python/include/pybind11_diagram_utils.h +++ b/src/python/include/pybind11_diagram_utils.h @@ -17,16 +17,9 @@ namespace py = pybind11; typedef py::array_t<double> Dgm; -// Get m[i,0] and m[i,1] as a pair -static auto pairify(void* p, py::ssize_t h, py::ssize_t w) { - return [=](py::ssize_t i){ - char* birth = (char*)p + i * h; - char* death = birth + w; - return std::make_pair(*(double*)birth, *(double*)death); - }; -} - -inline auto numpy_to_range_of_pairs(py::array_t<double> dgm) { +// build_point(double birth, double death, ssize_t index) -> Point +template<class BuildPoint> +inline auto numpy_to_range_of_pairs(py::array_t<double> dgm, BuildPoint build_point) { py::buffer_info buf = dgm.request(); // shape (n,2) or (0) for empty if((buf.ndim!=2 || buf.shape[1]!=2) && (buf.ndim!=1 || buf.shape[0]!=0)) @@ -34,6 +27,16 @@ inline auto numpy_to_range_of_pairs(py::array_t<double> dgm) { // In the case of shape (0), avoid reading non-existing strides[1] even if we won't use it. py::ssize_t stride1 = buf.ndim == 2 ? buf.strides[1] : 0; auto cnt = boost::counting_range<py::ssize_t>(0, buf.shape[0]); - return boost::adaptors::transform(cnt, pairify(buf.ptr, buf.strides[0], stride1)); + + char* p = static_cast<char*>(buf.ptr); + auto h = buf.strides[0]; + auto w = stride1; + // Get m[i,0] and m[i,1] as a pair + auto pairify = [=](py::ssize_t i){ + char* birth = p + i * h; + char* death = birth + w; + return build_point(*(double*)birth, *(double*)death, i); + }; + return boost::adaptors::transform(cnt, pairify); // Be careful that the returned range cannot contain references to dead temporaries. } |