From 4f06e5cf3367a79fbb14d145f0897e24eff82c65 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 28 Jul 2023 18:45:48 +0200 Subject: [PATCH 01/21] rips edge iterator --- src/Zigzag_persistence/example/CMakeLists.txt | 5 + .../example/comparison_for_tests.cpp | 109 +++ .../oscillating_rips_iterators.h | 718 ++++++++++++++++++ 3 files changed, 832 insertions(+) create mode 100644 src/Zigzag_persistence/example/comparison_for_tests.cpp create mode 100644 src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt index dd1f88e1d2..f0b424423d 100644 --- a/src/Zigzag_persistence/example/CMakeLists.txt +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -13,6 +13,11 @@ endif() file(COPY "zigzag_filtration_example.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_test(NAME Zigzag_persistence_example_zzfiltration_from_file COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/zigzag_filtration_example.txt") +add_executable ( comp comparison_for_tests.cpp ) +if(TARGET TBB::tbb) + target_link_libraries(comp TBB::tbb) +endif() + diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp new file mode 100644 index 0000000000..13db7163fe --- /dev/null +++ b/src/Zigzag_persistence/example/comparison_for_tests.cpp @@ -0,0 +1,109 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include +#include +#include // for pair +#include +#include + +#include + +#include +#include +#include + +using ST = Gudhi::Simplex_tree; +using Filtration_value = ST::Filtration_value; +using OR = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using ZE = Gudhi::zigzag_persistence::Zigzag_edge; +using Point = std::vector; + +void print_points(const std::vector& points){ + std::cout << "Number of points: " << points.size() << "\n"; + for (const Point& p : points){ + std::cout << "(" << p[0] << ", " << p[1] << ")\n"; + } + std::cout << "\n"; +} + +std::vector build_point_cloud(unsigned int numberOfPoints, int seed){ + std::vector finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0,10); + + for (unsigned int i = 0; i < numberOfPoints; ++i){ + auto res = points.insert({dist(rng), dist(rng)}); + while(!res.second){ + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + // print_points(finalPoints); + + return finalPoints; +} + +int main(int argc, char* const argv[]) { + if (argc != 4 && argc != 5) { + std::cout << "Usage: ./comp nu mu nomberOfPoints [seed]\n"; + return 0; + } + + double nu = std::stod(argv[1]); + double mu = std::stod(argv[2]); + unsigned int numberOfPoints = std::stoi(argv[3]); + int seed = -1; + + if (argc == 5) + seed = std::stoi(argv[4]); + + std::cout << "nu, mu: " << nu << ", " << mu << "\n"; + std::cout << "number of points: " << numberOfPoints << "\n"; + std::cout << "seed: " << seed << "\n"; + + std::vector points = build_point_cloud(numberOfPoints, seed); + + std::vector edges_v1 = OR::compute_oscillating_rips_edges(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING); + std::cout << edges_v1.size() << "\n"; + + // unsigned int i = 0; + // for (const auto& e : OR::compute_oscillating_rips_edges_as_iterator(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING)){ + // ++i; + // } + // std::cout << i << "\n"; + + // unsigned int i = 0; + // for (const auto& e : OR::compute_oscillating_rips_edges_as_iterator(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING)){ + // if (i < edges_v1.size()){ + // if (!(edges_v1[i] == e)){ + // std::cout << "[" << i << "] different:\n"; + // std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; + // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; + // }/* else { + // std::cout << "[" << i << "] same:\n"; + // std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; + // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; + // } */ + // } else { + // std::cout << "[" << i << "] too long:\n"; + // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; + // } + // ++i; + // } + + return 0; +} diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h new file mode 100644 index 0000000000..a84b7497dc --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -0,0 +1,718 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Clément Maria and Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ +#define ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ + +#include +#include +#include + +#include + +#ifdef GUDHI_USE_TBB +#include +#endif + +#include +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { + +template +class Zigzag_edge { + public: + Zigzag_edge(size_t u, size_t v, Filtration_value fil, bool direction) + : u_(u), v_(v), fil_(fil), direction_(direction) { + if (u > v) std::swap(u_, v_); + } + + Zigzag_edge() : u_(0), v_(0), fil_(0), direction_(true) {} + + /* Returns vertex with smaller label. */ + size_t get_smallest_vertex() const { return u_; } + /* Returns vertex with bigger label. */ + size_t get_biggest_vertex() const { return v_; } + /* Returns the filtration value of the edge. */ + Filtration_value get_filtration_value() const { return fil_; } + /* Returns true if insertion of the edge, false if removal. */ + bool get_direction() const { return direction_; } + + void set(size_t u, size_t v, Filtration_value fil, bool direction){ + u_ = u; + v_ = v; + fil_ = fil; + direction_ = direction; + } + + bool operator==(const Zigzag_edge& e) const { + return ((e.u_ == u_) && (e.v_ == v_) && (e.fil_ == fil_) && (e.direction_ == direction_)); + } + + void assign_filtration(Filtration_value fil) { fil_ = fil; } + + private: + size_t u_; + size_t v_; + Filtration_value fil_; + bool direction_; +}; + +template +class Identity_edge_modifier { + public: + static constexpr bool isActive_ = false; + + private: + Identity_edge_modifier() {} +}; + +template +class Square_root_edge_modifier { + public: + static Filtration_value apply_modifier(Filtration_value f) { return std::sqrt(f); } + static Filtration_value apply_inverse_modifier(Filtration_value f) { return f * f; } + + static constexpr bool isActive_ = true; + + private: + Square_root_edge_modifier() {} +}; + +template > +class Oscillating_rips_edge_range { + private: + class Oscillating_rips_edge_iterator; + + public: + enum Order_policy { ALREADY_ORDERED, FARTHEST_POINT_ORDERING, RANDOM_POINT_ORDERING }; + + template + static std::vector > compute_oscillating_rips_edges(Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + { + std::vector > edgeFiltration; + std::vector epsilonValues; + std::vector > > distanceMatrix; + auto n = points.size(); + + initialize_(nu, mu, epsilonValues, distanceMatrix, points, distance, orderPolicy); + + // edgesAdded[i] (resp. edgesRemoved[i]) == list of edges (i,j), with j > > edgesAdded, edgesRemoved; + + // auto it = std::upper_bound(distanceMatrix[1].begin(), distanceMatrix[1].end(), + // std::pair(distanceMatrix.size(), mu * epsilonValues[0]), + // Point_distance_comp()); + // std::cout << "start vect colind: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; + size_t number_of_arrows = compute_edges_(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); + + // Now, sort edges according to lengths, and put everything in edgeFiltration + edgeFiltration.clear(); + edgeFiltration.reserve(number_of_arrows + n); // count edges + vertices additions + + // initialise R({p_0}, +infinity) + edgeFiltration.emplace_back(0, 0, // add vertex p_0,+infty + std::numeric_limits::infinity(), true); + // epsilonValues[0], true); + + if constexpr (EdgeModifier::isActive_) { + for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i + edgeFiltration.emplace_back(i + 1, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); // add p_{i+1},eps_i + for (auto edg_it = edgesAdded[i].begin(); edg_it != edgesAdded[i].end(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + for (auto edg_it = edgesRemoved[i].rbegin(); // longest first + edg_it != edgesRemoved[i].rend(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + } + } else { + for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i + edgeFiltration.emplace_back(i + 1, i + 1, epsilonValues[i], true); // add p_{i+1},eps_i + for (auto edg_it = edgesAdded[i].begin(); edg_it != edgesAdded[i].end(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + for (auto edg_it = edgesRemoved[i].rbegin(); // longest first + edg_it != edgesRemoved[i].rend(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + } + } + + // what remains is removed in the zigzag iterator with -infinity values. If eps_n-1 + //== 0, which is the usual case, the remaining simplices in the filtration are + // the n vertices. + // cannot inforce this here. + + return edgeFiltration; + } + + template + static boost::iterator_range compute_oscillating_rips_edges_as_iterator( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + { + auto start = Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); + auto end = Oscillating_rips_edge_iterator(); + return boost::iterator_range( + start, end); + } + + private: + /* The two input types std::pair encode pairs + * (j, d(p_i,p_j)) and (k, d(p_i,p_k)) for some fixed point p_i. + * The operator() orders edges by length. By convention, if lengths are equal, + * it orders pairs by taking the smaller vertex label between j and k. + */ + struct Point_distance_comp { + bool operator()(const std::pair& p1, const std::pair& p2) const { + { + if (p1.second != p2.second) { + return p1.second < p2.second; // shorter first + } + return p1.first < p2.first; + } + } + }; + + class Oscillating_rips_edge_iterator + : public boost::iterator_facade&, + boost::forward_traversal_tag> { + public: + template + Oscillating_rips_edge_iterator(Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + : nu_(nu), mu_(mu), currentEdge_(0, 0, std::numeric_limits::infinity(), true), isEnd_(false), epsValIndex_(0), rowIndex_(1), inPosEd_(true), insertVertex_(true) + { + initialize_(nu_, mu_, epsilonValues_, distanceMatrix_, points, distance, orderPolicy); + auto it = std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), + Point_distance_comp()); + columnIndex_ = it - distanceMatrix_[1].begin(); + // std::cout << "start colind: " << columnIndex_ << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix_[1].begin()->first << ", " << distanceMatrix_[1].begin()->second << ")\n"; + } + + template + Oscillating_rips_edge_iterator(Filtration_value nu, Filtration_value mu, const PointRange& orderedPoints, + DistanceFunction&& distance, const std::vector& epsilonValues) + : epsilonValues_(epsilonValues), + nu_(nu), + mu_(mu), + currentEdge_(0, 0, std::numeric_limits::infinity(), true), + isEnd_(false), epsValIndex_(0), rowIndex_(1), inPosEd_(true), insertVertex_(true) { + GUDHI_CHECK(orderedPoints.size() == epsilonValues.size(), + "The number of points and the number of epsilon values should match."); + GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); + + if constexpr (EdgeModifier::isActive_) { + nu_ = EdgeModifier::apply_inverse_modifier(nu); + mu_ = EdgeModifier::apply_inverse_modifier(mu); + } + + // compute the distance matrix + distanceMatrix_ = compute_distance_matrix_(orderedPoints, distance); + + auto it = std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), + Point_distance_comp()); + columnIndex_ = it - distanceMatrix_[1].begin(); + } + + Oscillating_rips_edge_iterator() : nu_(0), mu_(0), currentEdge_(0, 0, 0, true), isEnd_(true), epsValIndex_(0), rowIndex_(1), columnIndex_(0), inPosEd_(true), insertVertex_(true) {} + + private: + friend class boost::iterator_core_access; + + std::vector epsilonValues_; + std::vector > > distanceMatrix_; + Filtration_value nu_; + Filtration_value mu_; + Zigzag_edge currentEdge_; + bool isEnd_; //to replace + size_t epsValIndex_, rowIndex_, columnIndex_; + bool inPosEd_, insertVertex_; //to replace + + bool equal(Oscillating_rips_edge_iterator const& other) const { return isEnd_ == other.isEnd_ && currentEdge_ == other.currentEdge_; } + + const Zigzag_edge& dereference() const { return currentEdge_; } + + void increment() { + if (epsValIndex_ < distanceMatrix_.size() - 1) { + if (insertVertex_) { + currentEdge_.set(epsValIndex_ + 1, epsValIndex_ + 1, epsilonValues_[epsValIndex_], true); + insertVertex_ = false; + return; + } + + while ((epsValIndex_ < distanceMatrix_.size() - 1 && inPosEd_ && col_index_is_not_valid_pos()) || + (epsValIndex_ < distanceMatrix_.size() - 1 && !inPosEd_ && col_index_is_not_valid_neg())) { + if (inPosEd_) { + ini_col_index_pos(); + } else { + ini_col_index_neg(); + if (epsValIndex_ == distanceMatrix_.size() - 1) { + set_end_(); + return; + } + if (insertVertex_) { + currentEdge_.set(epsValIndex_ + 1, epsValIndex_ + 1, epsilonValues_[epsValIndex_], true); + insertVertex_ = false; + return; + } + } + } + + if (inPosEd_) { + up_edge_pos(epsValIndex_); + if (rowIndex_ == epsValIndex_ + 2) { + inPosEd_ = false; + --rowIndex_; + ini_col_index_low(); + } + return; + } + + up_edge_neg(epsValIndex_); + if (rowIndex_ == 0) { + ++epsValIndex_; + if (epsValIndex_ == distanceMatrix_.size() - 1) return; + insertVertex_ = true; + inPosEd_ = true; + ++rowIndex_; + ini_col_index_up(); + } + return; + } + + set_end_(); + } + + void set_end_(){ + isEnd_ = true; + currentEdge_ = Zigzag_edge(); + } + + void ini_col_index_up() { + auto it = + std::upper_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), + Point_distance_comp()); + columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); + } + + void ini_col_index_low() { + auto it = + std::lower_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), + std::pair(0, nu_ * epsilonValues_[epsValIndex_ + 1]), + Point_distance_comp()); + while (it != distanceMatrix_[rowIndex_].end() && it->second == nu_ * epsilonValues_[epsValIndex_ + 1]) ++it; + columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); + } + + void ini_col_index_pos(){ + while (col_index_is_not_valid_pos() && rowIndex_ <= epsValIndex_) { + ++rowIndex_; + ini_col_index_up(); + } + if (col_index_is_not_valid_pos()){ + ini_col_index_low(); + inPosEd_ = false; + } + } + + void ini_col_index_neg(){ + while (col_index_is_not_valid_neg() && rowIndex_ > 1) { + --rowIndex_; + ini_col_index_low(); + } + if (col_index_is_not_valid_neg()){ + ini_col_index_up(); + ++epsValIndex_; + insertVertex_ = true; + inPosEd_ = true; + } + } + + bool col_index_is_not_valid_pos() { + return columnIndex_ == 0 || + (rowIndex_ != (epsValIndex_ + 1) && + distanceMatrix_[rowIndex_][columnIndex_ - 1].second <= nu_ * epsilonValues_[epsValIndex_]); + } + + bool col_index_is_not_valid_neg() { + return columnIndex_ == distanceMatrix_[rowIndex_].size() || + distanceMatrix_[rowIndex_][columnIndex_].second > mu_ * epsilonValues_[epsValIndex_]; + } + + void up_edge_pos(size_t i) { + --columnIndex_; + if constexpr (EdgeModifier::isActive_) + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, EdgeModifier::apply_modifier(epsilonValues_[i]), true); + else + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], true); + // std::cout << "pos: " << rowIndex_ << ", " << columnIndex_ << ", " << distanceMatrix_[rowIndex_][columnIndex_].first << "\n"; + while (col_index_is_not_valid_pos() && rowIndex_ <= i) { + ++rowIndex_; + ini_col_index_up(); + } + if (col_index_is_not_valid_pos()) { + ++rowIndex_; + } + } + + void up_edge_neg(size_t i) { + if constexpr (EdgeModifier::isActive_) + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, EdgeModifier::apply_modifier(epsilonValues_[i]), false); + else + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], false); + // std::cout << "neg: " << rowIndex_ << ", " << columnIndex_ << ", " << distanceMatrix_[rowIndex_][columnIndex_].first << "\n"; + ++columnIndex_; + while (col_index_is_not_valid_neg() && rowIndex_ > 1) { + --rowIndex_; + ini_col_index_low(); + } + if (col_index_is_not_valid_neg()) { + --rowIndex_; + } + } + }; + + template + static void initialize_(Filtration_value& nu, Filtration_value& mu, std::vector& epsilonValues, + std::vector > >& distanceMatrix, + const PointRange& points, DistanceFunction&& distance, Order_policy orderPolicy) { + GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); + + size_t n = points.size(); // number of points + PointRange sortedPoints; + sortedPoints.reserve(n); + + if constexpr (EdgeModifier::isActive_) { + nu = EdgeModifier::apply_inverse_modifier(nu); + mu = EdgeModifier::apply_inverse_modifier(mu); + } + + // compute epsilon values + if (orderPolicy == Order_policy::ALREADY_ORDERED) { + sortedPoints.assign(points.begin(), points.end()); + epsilonValues = compute_epsilon_values_(sortedPoints, distance); + } else if (orderPolicy == Order_policy::FARTHEST_POINT_ORDERING) { + epsilonValues.reserve(n); + Gudhi::subsampling::choose_n_farthest_points(distance, points, + n, // final size + 0, // starting point + std::back_inserter(sortedPoints), + std::back_inserter(epsilonValues)); + // need to shift values output by subsampling: + for (unsigned int i = 1; i < n; ++i) { + epsilonValues[i - 1] = epsilonValues[i]; + } + epsilonValues[n - 1] = 0; + } else { + Gudhi::subsampling::pick_n_random_points(points, n, std::back_inserter(sortedPoints)); + epsilonValues = compute_epsilon_values_(sortedPoints, distance); + } + + // compute the distance matrix + distanceMatrix = compute_distance_matrix_(sortedPoints, distance); + } + + /** \brief Compute the epsilon values for an ordered set of points, measuring the + * sparsity of the ordering. + * + * \details Let \f$P = \{p_0, \ldots, p_{n-1}\f$ be the ordered set of points. Then + * the method sets eps_range[i]<\CODE> with the value \$f\varepsilon_i\$f, + * defined as \f$\varpesilon_i = d_H(P_i,P)\f$, the Hausdorff between the points + * \f$P_i= \{p_0, \ldots, p_{i}\}\f$ and the entire point cloud + * \f$P = \{p_0, \ldots, p_{n-1}\}\f$. + * + * @param[in] points Range of points. + * @param[in] distance Distance function that can be called on two points. + * @param[in] eps_range Vector in which the epsilon values are written, + * eps_range[i]<\CODE> is \$f\varepsilon_i\$f. + * Satisfyies epsilonValues[i] >= + * epsilonValues[j] >= 0<\CODE> whenever i>=j<\CODE>. + * The range must be of same size as the number of points. + */ + template + static std::vector compute_epsilon_values_(const PointRange& points, DistanceFunction&& distance) { + size_t n = points.size(); + std::vector eps_range(n, std::numeric_limits::infinity()); + + // compute all \f$\varepsilon_i\f$ values, such that eps_range[i] == + // eps_i==d_H(P_i,P), for i=0 ... n-1: + for (size_t i = 0; i < n; ++i) { + // entering step i, maintain eps_range[j] = eps_j for j= i. +#ifdef GUDHI_USE_TBB + tbb::parallel_for(size_t(i + 1), n, [&](size_t k) { + // set eps_range[k] <- d(p_k, P_i) == + // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. + double dist_i_k = distance(points[i], points[k]); + if (dist_i_k < eps_range[k]) { + eps_range[k] = dist_i_k; + } + }); +#else + for (size_t k = i + 1; k < n; ++k) { + // set eps_range[k] <- d(p_k, P_i) == + // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. + double dist_i_k = distance(points[i], points[k]); + if (dist_i_k < eps_range[k]) { + eps_range[k] = dist_i_k; + } + } +#endif + // we have now eps_range[k] = d(p_k, P_i) for k > i. + // to do: implement parallel version by dividing the vector + // set eps_range[i] <- eps_i = d_H(P_i,P) = max_{k>i} d(p_k, P_i) + double eps_i = 0.; + for (size_t k = i + 1; k < n; ++k) { + if (eps_range[k] > eps_i) { + eps_i = eps_range[k]; + } + } + eps_range[i] = eps_i; + } + + return eps_range; + } + + template + static std::vector > > compute_distance_matrix_( + const PointRange& sortedPoints, DistanceFunction&& distance) { + std::vector > > distanceMatrix(sortedPoints.size()); +#ifdef GUDHI_USE_TBB + tbb::parallel_for(size_t(0), sortedPoints.size(), [&](size_t i) { + // distanceMatrix[i] = std::vector< std::pair >(); + distanceMatrix[i].resize(i); + for (size_t j = 0; j < i; ++j) { + distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); + } + // distanceMatrix[i] is sorted by (j, d(p_i,p_j)) < (k, d(p_i,p_k)) iff + // d(p_i,p_j) < d(p_i,p_k) or (j >(); + distanceMatrix[i].resize(i); + for (size_t j = 0; j < i; ++j) { + distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); + } + std::stable_sort(distanceMatrix[i].begin(), distanceMatrix[i].end(), Point_distance_comp()); + } +#endif + + return distanceMatrix; + } + + static size_t compute_edges_(Filtration_value nu, Filtration_value mu, + const std::vector& epsilonValues, + std::vector > >& distanceMatrix, + std::vector > >& edgesAdded, + std::vector > >& edgesRemoved) + { + size_t number_of_arrows = 0; + auto n = epsilonValues.size(); + edgesAdded.resize(n); + edgesRemoved.resize(n); + + // edgesAdded[i] must contain all new edges (k,j), 0 <= k < j <= i+1, + // inserted in inclusion: + // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i, p_i+1 }, mu * eps_i) + // and + // edgesRemoved[i] must contain all edges (k,j), 0 <= k < j <= i+1, + // removed in backward inclusion: + // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) +#ifdef GUDHI_USE_TBB + // no need to consider the case i=n-1 in an oRzz filtration + tbb::parallel_for(size_t(0), n - 1, [&](size_t i) { + typename std::vector >::iterator it; + //----edgesAdded[i]: + // consider first all edges added in inclusion: + // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), + // i.e., all (p_j,p_k) with 0 <= k < j <= i with + // nu eps_i < d(p_j,p_k) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + for (size_t j = 1; j <= i; ++j) { + // get very first edge (k,j), over all k(n, mu * epsilonValues[i]), Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // if edge already in R({p_0, ... , p_i}, nu * eps_i), stop + if (it->second <= nu * epsilonValues[i]) { + break; + } + if constexpr (EdgeModifier::isActive_){ + edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); + } else { + edgesAdded[i].emplace_back(it->first, j, epsilonValues[i], true); + } + // std::cout << j << ", " << (it - distanceMatrix[j].begin()) << ", " << it->first << "\n"; + ++number_of_arrows; + } + } + // now consider all edges added in inclusion: + // R({p_0, ... , p_i}, mu * eps_i) -> R({p_0, ... , p_i, p_i+1}, mu * eps_i) + // i.e., all (p_j,p_i+1) with 0 <= j <= i with d(p_j,p_i+1) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + // first striclty longer edge + it = std::upper_bound(distanceMatrix[i + 1].begin(), distanceMatrix[i + 1].end(), + std::pair(n, mu * epsilonValues[i]), Point_distance_comp()); + + while (it != distanceMatrix[i + 1].begin()) { + --it; + if constexpr (EdgeModifier::isActive_) { + edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); + } else { + edgesAdded[i].emplace_back(it->first, i + 1, epsilonValues[i], true); + } + // std::cout << (i + 1) << ", " << (it - distanceMatrix[i+1].begin()) << ", " << it->first << "\n"; + ++number_of_arrows; + } + + //----edgesRemoved[i]: + // consider all edges removed in + // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) + // i.e., all edges (p_k,p_j), 0<=k(n, mu * epsilonValues[i]), Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // when reading an edge in R({p_0, ... , p_{i+1}}, nu * eps_{i+1}), stop + if (it->second <= nu * epsilonValues[i + 1]) { + break; + } + // edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i+1], false); + if constexpr (EdgeModifier::isActive_){ + edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); + } else { + edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i], false); + } + // std::cout << j << ", " << (it - distanceMatrix[j].begin()) << ", " << it->first << "\n"; + ++number_of_arrows; + } + } + }); +#else // GUDHI_USE_TBB not defined + + typename std::vector >::iterator it; + + for (size_t i = 0; i < n - 1; ++i) { + //----edgesAdded[i]: + // consider first all edges added in inclusion: + // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), + // i.e., all (p_j,p_k) with 0 <= k < j <= i with + // nu eps_i < d(p_j,p_k) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + for (size_t j = 1; j <= i; ++j) { + // get very first edge (k,j), over all k(n, mu * epsilonValues[i]), Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // if edge already in R({p_0, ... , p_i}, nu * eps_i), stop + if (it->second <= nu * epsilonValues[i]) { + break; + } + if constexpr (EdgeModifier::isActive_){ + edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); + } else { + edgesAdded[i].emplace_back(it->first, j, epsilonValues[i], true); + } + ++number_of_arrows; + } + } + // now consider all edges added in inclusion: + // R({p_0, ... , p_i}, mu * eps_i) -> R({p_0, ... , p_i, p_i+1}, mu * eps_i) + // i.e., all (p_j,p_i+1) with 0 <= j <= i with d(p_j,p_i+1) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + // first striclty longer edge + it = std::upper_bound(distanceMatrix[i + 1].begin(), distanceMatrix[i + 1].end(), + std::pair(n, mu * epsilonValues[i]), Point_distance_comp()); +// if (i == 0){ +// std::cout << "start vect colind2: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; +// } + while (it != distanceMatrix[i + 1].begin()) { + --it; + if constexpr (EdgeModifier::isActive_) { + edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); + } else { + edgesAdded[i].emplace_back(it->first, i + 1, epsilonValues[i], true); + // if (i==0) std::cout << "added: " << it->first << ", " << (i + 1) << ", " << epsilonValues[i] << "\n"; + } + ++number_of_arrows; + } + + //----edgesRemoved[i]: + // consider all edges removed in + // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) + // i.e., all edges (p_k,p_j), 0<=k(n, mu * epsilonValues[i]), Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // when reading an edge in R({p_0, ... , p_{i+1}}, nu * eps_{i+1}), stop + if (it->second <= nu * epsilonValues[i + 1]) { + break; + } + // edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i+1], false); + if constexpr (EdgeModifier::isActive_){ + edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); + } else { + edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i], false); + } + ++number_of_arrows; + } + } + } +#endif + + return number_of_arrows; + } +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ From 5e530f782502ecac1e677c0eeafe12e7adc3613e Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 4 Aug 2023 18:59:37 +0200 Subject: [PATCH 02/21] oscillating rips range --- src/Simplex_tree/include/gudhi/Simplex_tree.h | 32 - .../gudhi/Simplex_tree/hooks_simplex_base.h | 1 + .../example/comparison_for_tests.cpp | 341 ++++++-- .../oscillating_rips_iterators.h | 744 ++++++++++++------ 4 files changed, 791 insertions(+), 327 deletions(-) diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree.h b/src/Simplex_tree/include/gudhi/Simplex_tree.h index 0594f7f479..c03ba65ea9 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree.h @@ -2510,38 +2510,6 @@ struct Simplex_tree_options_negative_indexation { static const bool simplex_handle_strong_validity = false; }; -/** Model of SimplexTreeOptions, same as `Simplex_tree_options_full_featured` - * but the possibility of much bigger keys and in particular of negative keys. - * - * Maximum number of simplices to compute persistence is std::numeric_limits::max() - * (way enough simplices). */ -struct Simplex_tree_options_wide_indexation { - typedef linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::int64_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = false; - static const bool link_nodes_by_label = false; -}; - -/** Model of SimplexTreeOptions, same as `Simplex_tree_options_full_featured` - * but with the possibility of negative keys. - * - * Maximum number of simplices to compute persistence is std::numeric_limits::max() - * (about 2 billions of simplices). */ -struct Simplex_tree_options_negative_indexation { - typedef linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::int32_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = false; - static const bool link_nodes_by_label = false; -}; - /** Model of SimplexTreeOptions, faster than `Simplex_tree_options_full_featured` but note the unsafe * `contiguous_vertices` option. * diff --git a/src/Simplex_tree/include/gudhi/Simplex_tree/hooks_simplex_base.h b/src/Simplex_tree/include/gudhi/Simplex_tree/hooks_simplex_base.h index 25cb794dfa..d000bcfc6e 100644 --- a/src/Simplex_tree/include/gudhi/Simplex_tree/hooks_simplex_base.h +++ b/src/Simplex_tree/include/gudhi/Simplex_tree/hooks_simplex_base.h @@ -12,6 +12,7 @@ #define SIMPLEX_TREE_HOOKS_SIMPLEX_BASE_H_ #include +#include namespace Gudhi { /** \brief No hook when SimplexTreeOptions::link_nodes_by_label is false. diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp index 13db7163fe..bca280febf 100644 --- a/src/Zigzag_persistence/example/comparison_for_tests.cpp +++ b/src/Zigzag_persistence/example/comparison_for_tests.cpp @@ -9,101 +9,290 @@ */ #include +#include #include #include #include #include // for pair #include -#include +#include #include #include #include #include +#include -using ST = Gudhi::Simplex_tree; +using ST = Gudhi::Simplex_tree; using Filtration_value = ST::Filtration_value; -using OR = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; using ZE = Gudhi::zigzag_persistence::Zigzag_edge; +using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using ORi = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; +using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::iterator>; using Point = std::vector; -void print_points(const std::vector& points){ - std::cout << "Number of points: " << points.size() << "\n"; - for (const Point& p : points){ - std::cout << "(" << p[0] << ", " << p[1] << ")\n"; - } - std::cout << "\n"; +void print_points(const std::vector& points) { + std::cout << "Number of points: " << points.size() << "\n"; + for (const Point& p : points) { + std::cout << "(" << p[0] << ", " << p[1] << ")\n"; + } + std::cout << "\n"; +} + +std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { + std::vector finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0, 10); + + for (unsigned int i = 0; i < numberOfPoints; ++i) { + auto res = points.insert({dist(rng), dist(rng)}); + while (!res.second) { + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + // print_points(finalPoints); + + return finalPoints; +} + +void canonical_sort_edge(std::vector& edges) { + // canonical sort of the edges: as much as possible, edges should be removed in + // the reverse order of their insertion. We decide to insert shorted edges first, + // with increasing lexicographical order, and remove larger edges first, with + // decreasing lexicographic order. + + // filtration then dimension, then lex order for insertion + auto edge_cmp = [](const ZE& e1, const ZE& e2) { + if (e1.get_filtration_value() != e2.get_filtration_value()) { + return e1.get_filtration_value() < e2.get_filtration_value(); + } // lower fil first + + if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first + if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + } //-> vertex of lower label + else { + return true; + } //-> always vertices before edges + } else { // e1 is an edge + if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { + return false; + } // e2 vertex, -> put it first + else { // both are edges, lexigraphic compare + if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + } // lex order + if (e1.get_biggest_vertex() != e2.get_biggest_vertex()) { + return e1.get_biggest_vertex() < e2.get_biggest_vertex(); + } + return false; // equality + } + } + }; + // the inverse ordering for deletions + auto inv_edge_cmp = [&](const ZE& e1, const ZE& e2) { + if (e1.get_smallest_vertex() == e2.get_smallest_vertex() && e1.get_biggest_vertex() == e2.get_biggest_vertex()) { + return false; + } //== => false + return !(edge_cmp(e1, e2)); // reverse order + }; + // sort sequences of inclusions of same filtration with edge_cmp + // sort sequences of removals of same filtration with inv_edge_cmp + auto beg = edges.begin(); + auto end = edges.begin(); + auto curr_fil = beg->get_filtration_value(); + auto curr_type = beg->get_direction(); + while (beg != edges.end()) { + while (end != edges.end() && end->get_filtration_value() == curr_fil && end->get_direction() == curr_type) { + ++end; + } + if (curr_type) { + sort(beg, end, edge_cmp); + } // sequence of insertions + else { + sort(beg, end, inv_edge_cmp); + } // sequence of removals + beg = end; + curr_fil = beg->get_filtration_value(); + curr_type = beg->get_direction(); + } +} + +void test_edges_comp(const std::vector& points, double nu, double mu, ORE::Order_policy p) +{ + std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + + unsigned int i = 0; + for (const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { + if (i < edges_v1.size()) { + if (!(edges_v1[i] == e)) { + std::cout << "[" << i << "] different:\n"; + std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " + << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; + std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() + << ", " << e.get_direction() << "\n"; + }/* else { + std::cout << "[" << i << "] same:\n"; + std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " + << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; + std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() + << ", " << e.get_direction() << "\n"; + } */ + } else { + std::cout << "[" << i << "] too long:\n"; + std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " + << e.get_direction() << "\n"; + } + ++i; + } +} + +void test_edges_canonical_sort(const std::vector& points, double nu, double mu, ORE::Order_policy p) { + std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + std::vector ordered_edges_v1(edges_v1); + canonical_sort_edge(ordered_edges_v1); + + for (unsigned int i = 0; i < edges_v1.size(); ++i) { + if (!(edges_v1[i] == ordered_edges_v1[i])) { + std::cout << "[" << i << "] different:\n"; + std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " + << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; + std::cout << ordered_edges_v1[i].get_smallest_vertex() << ", " << ordered_edges_v1[i].get_biggest_vertex() << ", " + << ordered_edges_v1[i].get_filtration_value() << ", " << ordered_edges_v1[i].get_direction() << "\n"; + } + } +} + +void test_edges_asymetry(const std::vector& points, double nu, double mu, ORE::Order_policy p) { + auto comp = [](const ZE& e1, const ZE& e2) { + if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + return e1.get_biggest_vertex() < e2.get_biggest_vertex(); + }; + std::set > curedges(comp); + for (const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { + if (e.get_direction()) + curedges.insert(e); + else + curedges.erase(e); + } + std::cout << "final state: " << curedges.size() << "\n"; + for (const auto& e : curedges) { + std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " + << e.get_direction() << "\n"; + } +} + +void test_edges_timings(const std::vector& points, double nu, double mu, ORE::Order_policy p) { +// { +// Gudhi::Clock time1("Vector version"); +// std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); +// std::cout << edges_v1.size() << "\n"; +// time1.end(); +// std::cout << time1; +// } + + { + Gudhi::Clock time2("Iterator version"); + unsigned int i = 0; + for ([[maybe_unused]] const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { + ++i; + } + std::cout << i << "\n"; + time2.end(); + std::cout << time2; + } + + { + Gudhi::Clock time1("Vector version"); + std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + std::cout << edges_v1.size() << "\n"; + time1.end(); + std::cout << time1; + } +} + +void test_edges(const std::vector& points, double nu, double mu) { + // ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; + // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; + ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; + + // test_edges_comp(points, nu, mu, p); + // test_edges_canonical_sort(points, nu, mu, p); + // test_edges_asymetry(points, nu, mu, p); + test_edges_timings(points, nu, mu, p); } -std::vector build_point_cloud(unsigned int numberOfPoints, int seed){ - std::vector finalPoints; - std::set points; - std::random_device dev; - std::mt19937 rng(dev()); - if (seed > -1) rng.seed(seed); - std::uniform_real_distribution dist(0,10); - - for (unsigned int i = 0; i < numberOfPoints; ++i){ - auto res = points.insert({dist(rng), dist(rng)}); - while(!res.second){ - res = points.insert({dist(rng), dist(rng)}); - } - finalPoints.push_back(*res.first); - } - - // print_points(finalPoints); - - return finalPoints; +void test_simplices_print(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { + ST st; + + auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto end = ORE::end(); + for (auto& t : ORi::get_iterator_range(start, end, st, maxDim)) { + for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; + std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; + } +} + +void test_simplices_comp(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p){ + ST st; + + auto startEIt = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto endEIt = ORE::end(); + auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto startEVec = vec.begin(); + auto endEVec = vec.end(); + + auto startIt = ORi::begin(startEIt, endEIt, st, maxDim); + auto endIt = ORi::end(); + auto rangeVec = ORv::get_iterator_range(startEVec, endEVec, st, maxDim); + auto startVec = rangeVec.begin(); + auto endVec = rangeVec.end(); + for (; startIt != endIt && startVec != endVec; ++startIt,++startVec) { + if () + for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; + std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; + } +} + +void test_simplices(const std::vector& points, double nu, double mu, int maxDim) { + ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; + // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; + // ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; + +// test_simplices_print(points, nu, mu, maxDim, p); + test_simplices_comp(points, nu, mu, maxDim, p); } int main(int argc, char* const argv[]) { - if (argc != 4 && argc != 5) { - std::cout << "Usage: ./comp nu mu nomberOfPoints [seed]\n"; - return 0; - } - - double nu = std::stod(argv[1]); - double mu = std::stod(argv[2]); - unsigned int numberOfPoints = std::stoi(argv[3]); - int seed = -1; - - if (argc == 5) - seed = std::stoi(argv[4]); - - std::cout << "nu, mu: " << nu << ", " << mu << "\n"; - std::cout << "number of points: " << numberOfPoints << "\n"; - std::cout << "seed: " << seed << "\n"; - - std::vector points = build_point_cloud(numberOfPoints, seed); - - std::vector edges_v1 = OR::compute_oscillating_rips_edges(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING); - std::cout << edges_v1.size() << "\n"; - - // unsigned int i = 0; - // for (const auto& e : OR::compute_oscillating_rips_edges_as_iterator(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING)){ - // ++i; - // } - // std::cout << i << "\n"; - - // unsigned int i = 0; - // for (const auto& e : OR::compute_oscillating_rips_edges_as_iterator(nu, mu, points, Gudhi::Euclidean_distance(), OR::Order_policy::FARTHEST_POINT_ORDERING)){ - // if (i < edges_v1.size()){ - // if (!(edges_v1[i] == e)){ - // std::cout << "[" << i << "] different:\n"; - // std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; - // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; - // }/* else { - // std::cout << "[" << i << "] same:\n"; - // std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; - // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; - // } */ - // } else { - // std::cout << "[" << i << "] too long:\n"; - // std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " << e.get_direction() << "\n"; - // } - // ++i; - // } - - return 0; + if (argc != 5 && argc != 6) { + std::cout << "Usage: ./comp nu mu max_dim nomberOfPoints [seed]\n"; + return 0; + } + + double nu = std::stod(argv[1]); + double mu = std::stod(argv[2]); + int maxDim = std::stoi(argv[3]); + unsigned int numberOfPoints = std::stoi(argv[4]); + int seed = -1; + + if (argc == 6) seed = std::stoi(argv[5]); + + std::cout << "nu, mu: " << nu << ", " << mu << "\n"; + std::cout << "number of points: " << numberOfPoints << "\n"; + std::cout << "seed: " << seed << "\n"; + + std::vector points = build_point_cloud(numberOfPoints, seed); + +// test_edges(points, nu, mu); + test_simplices(points, nu, mu, maxDim); + + return 0; } diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index a84b7497dc..446aeb54ca 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include @@ -31,7 +33,7 @@ namespace zigzag_persistence { template class Zigzag_edge { public: - Zigzag_edge(size_t u, size_t v, Filtration_value fil, bool direction) + Zigzag_edge(int u, int v, Filtration_value fil, bool direction) : u_(u), v_(v), fil_(fil), direction_(direction) { if (u > v) std::swap(u_, v_); } @@ -39,15 +41,15 @@ class Zigzag_edge { Zigzag_edge() : u_(0), v_(0), fil_(0), direction_(true) {} /* Returns vertex with smaller label. */ - size_t get_smallest_vertex() const { return u_; } + int get_smallest_vertex() const { return u_; } /* Returns vertex with bigger label. */ - size_t get_biggest_vertex() const { return v_; } + int get_biggest_vertex() const { return v_; } /* Returns the filtration value of the edge. */ Filtration_value get_filtration_value() const { return fil_; } /* Returns true if insertion of the edge, false if removal. */ bool get_direction() const { return direction_; } - void set(size_t u, size_t v, Filtration_value fil, bool direction){ + void set(int u, int v, Filtration_value fil, bool direction){ u_ = u; v_ = v; fil_ = fil; @@ -58,11 +60,16 @@ class Zigzag_edge { return ((e.u_ == u_) && (e.v_ == v_) && (e.fil_ == fil_) && (e.direction_ == direction_)); } - void assign_filtration(Filtration_value fil) { fil_ = fil; } +// bool operator<(const Zigzag_edge& e) const { +// if (e.fil_ != fil_) return fil_ < e.fil_; +// if (e.direction_ != direction_) return direction_; +// if (e.u_ != u_) return u_ < e.u_; +// return v_ < e.v_; +// } private: - size_t u_; - size_t v_; + int u_; + int v_; Filtration_value fil_; bool direction_; }; @@ -88,111 +95,12 @@ class Square_root_edge_modifier { Square_root_edge_modifier() {} }; +//assumes that eps_n-1 == 0 template > class Oscillating_rips_edge_range { - private: - class Oscillating_rips_edge_iterator; - public: enum Order_policy { ALREADY_ORDERED, FARTHEST_POINT_ORDERING, RANDOM_POINT_ORDERING }; - template - static std::vector > compute_oscillating_rips_edges(Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Order_policy orderPolicy) - { - std::vector > edgeFiltration; - std::vector epsilonValues; - std::vector > > distanceMatrix; - auto n = points.size(); - - initialize_(nu, mu, epsilonValues, distanceMatrix, points, distance, orderPolicy); - - // edgesAdded[i] (resp. edgesRemoved[i]) == list of edges (i,j), with j > > edgesAdded, edgesRemoved; - - // auto it = std::upper_bound(distanceMatrix[1].begin(), distanceMatrix[1].end(), - // std::pair(distanceMatrix.size(), mu * epsilonValues[0]), - // Point_distance_comp()); - // std::cout << "start vect colind: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; - size_t number_of_arrows = compute_edges_(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); - - // Now, sort edges according to lengths, and put everything in edgeFiltration - edgeFiltration.clear(); - edgeFiltration.reserve(number_of_arrows + n); // count edges + vertices additions - - // initialise R({p_0}, +infinity) - edgeFiltration.emplace_back(0, 0, // add vertex p_0,+infty - std::numeric_limits::infinity(), true); - // epsilonValues[0], true); - - if constexpr (EdgeModifier::isActive_) { - for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i - edgeFiltration.emplace_back(i + 1, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); // add p_{i+1},eps_i - for (auto edg_it = edgesAdded[i].begin(); edg_it != edgesAdded[i].end(); ++edg_it) { - edgeFiltration.push_back(*edg_it); - } - for (auto edg_it = edgesRemoved[i].rbegin(); // longest first - edg_it != edgesRemoved[i].rend(); ++edg_it) { - edgeFiltration.push_back(*edg_it); - } - } - } else { - for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i - edgeFiltration.emplace_back(i + 1, i + 1, epsilonValues[i], true); // add p_{i+1},eps_i - for (auto edg_it = edgesAdded[i].begin(); edg_it != edgesAdded[i].end(); ++edg_it) { - edgeFiltration.push_back(*edg_it); - } - for (auto edg_it = edgesRemoved[i].rbegin(); // longest first - edg_it != edgesRemoved[i].rend(); ++edg_it) { - edgeFiltration.push_back(*edg_it); - } - } - } - - // what remains is removed in the zigzag iterator with -infinity values. If eps_n-1 - //== 0, which is the usual case, the remaining simplices in the filtration are - // the n vertices. - // cannot inforce this here. - - return edgeFiltration; - } - - template - static boost::iterator_range compute_oscillating_rips_edges_as_iterator( - Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Order_policy orderPolicy) - { - auto start = Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); - auto end = Oscillating_rips_edge_iterator(); - return boost::iterator_range( - start, end); - } - - private: - /* The two input types std::pair encode pairs - * (j, d(p_i,p_j)) and (k, d(p_i,p_k)) for some fixed point p_i. - * The operator() orders edges by length. By convention, if lengths are equal, - * it orders pairs by taking the smaller vertex label between j and k. - */ - struct Point_distance_comp { - bool operator()(const std::pair& p1, const std::pair& p2) const { - { - if (p1.second != p2.second) { - return p1.second < p2.second; // shorter first - } - return p1.first < p2.first; - } - } - }; - class Oscillating_rips_edge_iterator : public boost::iterator_facade&, @@ -204,24 +112,37 @@ class Oscillating_rips_edge_range { const PointRange& points, DistanceFunction&& distance, Order_policy orderPolicy) - : nu_(nu), mu_(mu), currentEdge_(0, 0, std::numeric_limits::infinity(), true), isEnd_(false), epsValIndex_(0), rowIndex_(1), inPosEd_(true), insertVertex_(true) + : nu_(nu), + mu_(mu), + currentEdge_(0, 0, std::numeric_limits::infinity(), true), + epsilonIndex_(0), + rowIndex_(1), + inPositiveDirection_(true), + insertVertex_(true) { - initialize_(nu_, mu_, epsilonValues_, distanceMatrix_, points, distance, orderPolicy); - auto it = std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), - std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), - Point_distance_comp()); + _initialize(nu_, mu_, epsilonValues_, distanceMatrix_, points, distance, orderPolicy); + auto it = + std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsilonIndex_]), + Point_distance_comp()); columnIndex_ = it - distanceMatrix_[1].begin(); - // std::cout << "start colind: " << columnIndex_ << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix_[1].begin()->first << ", " << distanceMatrix_[1].begin()->second << ")\n"; } template - Oscillating_rips_edge_iterator(Filtration_value nu, Filtration_value mu, const PointRange& orderedPoints, - DistanceFunction&& distance, const std::vector& epsilonValues) + Oscillating_rips_edge_iterator(Filtration_value nu, + Filtration_value mu, + const PointRange& orderedPoints, + DistanceFunction&& distance, + const std::vector& epsilonValues) : epsilonValues_(epsilonValues), nu_(nu), mu_(mu), currentEdge_(0, 0, std::numeric_limits::infinity(), true), - isEnd_(false), epsValIndex_(0), rowIndex_(1), inPosEd_(true), insertVertex_(true) { + epsilonIndex_(0), + rowIndex_(1), + inPositiveDirection_(true), + insertVertex_(true) + { GUDHI_CHECK(orderedPoints.size() == epsilonValues.size(), "The number of points and the number of epsilon values should match."); GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); @@ -232,15 +153,24 @@ class Oscillating_rips_edge_range { } // compute the distance matrix - distanceMatrix_ = compute_distance_matrix_(orderedPoints, distance); + distanceMatrix_ = _compute_distance_matrix(orderedPoints, distance); - auto it = std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), - std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), - Point_distance_comp()); + auto it = + std::upper_bound(distanceMatrix_[1].begin(), distanceMatrix_[1].end(), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsilonIndex_]), + Point_distance_comp()); columnIndex_ = it - distanceMatrix_[1].begin(); } - Oscillating_rips_edge_iterator() : nu_(0), mu_(0), currentEdge_(0, 0, 0, true), isEnd_(true), epsValIndex_(0), rowIndex_(1), columnIndex_(0), inPosEd_(true), insertVertex_(true) {} + Oscillating_rips_edge_iterator() + : nu_(0), + mu_(0), + currentEdge_(0, 0, 0, true), + epsilonIndex_(0), + rowIndex_(0), + columnIndex_(0), + inPositiveDirection_(true), + insertVertex_(true) {} private: friend class boost::iterator_core_access; @@ -250,157 +180,265 @@ class Oscillating_rips_edge_range { Filtration_value nu_; Filtration_value mu_; Zigzag_edge currentEdge_; - bool isEnd_; //to replace - size_t epsValIndex_, rowIndex_, columnIndex_; - bool inPosEd_, insertVertex_; //to replace + size_t epsilonIndex_, rowIndex_, columnIndex_; + bool inPositiveDirection_, insertVertex_; - bool equal(Oscillating_rips_edge_iterator const& other) const { return isEnd_ == other.isEnd_ && currentEdge_ == other.currentEdge_; } + bool equal(Oscillating_rips_edge_iterator const& other) const { + return rowIndex_ == other.rowIndex_ && currentEdge_ == other.currentEdge_; + } const Zigzag_edge& dereference() const { return currentEdge_; } void increment() { - if (epsValIndex_ < distanceMatrix_.size() - 1) { + if (epsilonIndex_ < distanceMatrix_.size() - 1) { if (insertVertex_) { - currentEdge_.set(epsValIndex_ + 1, epsValIndex_ + 1, epsilonValues_[epsValIndex_], true); + _update_edge_as_positive_vertex(); insertVertex_ = false; return; } - while ((epsValIndex_ < distanceMatrix_.size() - 1 && inPosEd_ && col_index_is_not_valid_pos()) || - (epsValIndex_ < distanceMatrix_.size() - 1 && !inPosEd_ && col_index_is_not_valid_neg())) { - if (inPosEd_) { - ini_col_index_pos(); - } else { - ini_col_index_neg(); - if (epsValIndex_ == distanceMatrix_.size() - 1) { - set_end_(); - return; - } - if (insertVertex_) { - currentEdge_.set(epsValIndex_ + 1, epsValIndex_ + 1, epsilonValues_[epsValIndex_], true); - insertVertex_ = false; + if (inPositiveDirection_ && _positive_col_index_is_not_valid()) { + while (_positive_col_index_is_not_valid() && rowIndex_ <= epsilonIndex_) { + ++rowIndex_; + _initialize_positive_col_index(); + } + if (_positive_col_index_is_not_valid()) { + _initialize_negative_col_index(); + inPositiveDirection_ = false; + } + } + + if (!inPositiveDirection_ && _negative_col_index_is_not_valid()) { + while (_negative_col_index_is_not_valid() && rowIndex_ > 1) { + --rowIndex_; + _initialize_negative_col_index(); + } + if (_negative_col_index_is_not_valid()) { + _initialize_positive_col_index(); + ++epsilonIndex_; + inPositiveDirection_ = true; + if (epsilonIndex_ == distanceMatrix_.size() - 1) { + // _set_end(); + rowIndex_ = distanceMatrix_.size(); + _update_edge_as_negative_vertex(); return; } + _update_edge_as_positive_vertex(); + return; } } - if (inPosEd_) { - up_edge_pos(epsValIndex_); - if (rowIndex_ == epsValIndex_ + 2) { - inPosEd_ = false; + if (inPositiveDirection_) { + --columnIndex_; + _update_edge(epsilonIndex_, true); + while (_positive_col_index_is_not_valid() && rowIndex_ <= epsilonIndex_) { + ++rowIndex_; + _initialize_positive_col_index(); + } + if (_positive_col_index_is_not_valid()) { + ++rowIndex_; + } + if (rowIndex_ == epsilonIndex_ + 2) { + inPositiveDirection_ = false; --rowIndex_; - ini_col_index_low(); + _initialize_negative_col_index(); } return; } - up_edge_neg(epsValIndex_); + _update_edge(epsilonIndex_, false); + ++columnIndex_; + while (_negative_col_index_is_not_valid() && rowIndex_ > 1) { + --rowIndex_; + _initialize_negative_col_index(); + } + if (_negative_col_index_is_not_valid()) { + --rowIndex_; + } if (rowIndex_ == 0) { - ++epsValIndex_; - if (epsValIndex_ == distanceMatrix_.size() - 1) return; + ++epsilonIndex_; + if (epsilonIndex_ == distanceMatrix_.size() - 1) { + rowIndex_ = distanceMatrix_.size() + 1; + return; + } insertVertex_ = true; - inPosEd_ = true; + inPositiveDirection_ = true; ++rowIndex_; - ini_col_index_up(); + _initialize_positive_col_index(); } return; + } if (rowIndex_ > 1){ + --rowIndex_; + _update_edge_as_negative_vertex(); + return; } - set_end_(); + _set_end(); } - void set_end_(){ - isEnd_ = true; + void _set_end(){ + rowIndex_ = 0; currentEdge_ = Zigzag_edge(); } - void ini_col_index_up() { + void _initialize_positive_col_index() { auto it = std::upper_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), - std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsValIndex_]), + std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsilonIndex_]), Point_distance_comp()); columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); } - void ini_col_index_low() { + void _initialize_negative_col_index() { auto it = std::lower_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), - std::pair(0, nu_ * epsilonValues_[epsValIndex_ + 1]), + std::pair(0, nu_ * epsilonValues_[epsilonIndex_ + 1]), Point_distance_comp()); - while (it != distanceMatrix_[rowIndex_].end() && it->second == nu_ * epsilonValues_[epsValIndex_ + 1]) ++it; + while (it != distanceMatrix_[rowIndex_].end() && it->second == nu_ * epsilonValues_[epsilonIndex_ + 1]) ++it; columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); } - void ini_col_index_pos(){ - while (col_index_is_not_valid_pos() && rowIndex_ <= epsValIndex_) { - ++rowIndex_; - ini_col_index_up(); - } - if (col_index_is_not_valid_pos()){ - ini_col_index_low(); - inPosEd_ = false; - } - } - - void ini_col_index_neg(){ - while (col_index_is_not_valid_neg() && rowIndex_ > 1) { - --rowIndex_; - ini_col_index_low(); - } - if (col_index_is_not_valid_neg()){ - ini_col_index_up(); - ++epsValIndex_; - insertVertex_ = true; - inPosEd_ = true; - } - } - - bool col_index_is_not_valid_pos() { + bool _positive_col_index_is_not_valid() { return columnIndex_ == 0 || - (rowIndex_ != (epsValIndex_ + 1) && - distanceMatrix_[rowIndex_][columnIndex_ - 1].second <= nu_ * epsilonValues_[epsValIndex_]); + (rowIndex_ != (epsilonIndex_ + 1) && + distanceMatrix_[rowIndex_][columnIndex_ - 1].second <= nu_ * epsilonValues_[epsilonIndex_]); } - bool col_index_is_not_valid_neg() { + bool _negative_col_index_is_not_valid() { return columnIndex_ == distanceMatrix_[rowIndex_].size() || - distanceMatrix_[rowIndex_][columnIndex_].second > mu_ * epsilonValues_[epsValIndex_]; + distanceMatrix_[rowIndex_][columnIndex_].second > mu_ * epsilonValues_[epsilonIndex_]; } - void up_edge_pos(size_t i) { - --columnIndex_; + void _update_edge(size_t i, bool direction) { if constexpr (EdgeModifier::isActive_) - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, EdgeModifier::apply_modifier(epsilonValues_[i]), true); - else - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], true); - // std::cout << "pos: " << rowIndex_ << ", " << columnIndex_ << ", " << distanceMatrix_[rowIndex_][columnIndex_].first << "\n"; - while (col_index_is_not_valid_pos() && rowIndex_ <= i) { - ++rowIndex_; - ini_col_index_up(); - } - if (col_index_is_not_valid_pos()) { - ++rowIndex_; - } + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, + EdgeModifier::apply_modifier(epsilonValues_[i]), direction); + else + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], direction); } - void up_edge_neg(size_t i) { + void _update_edge_as_positive_vertex() { if constexpr (EdgeModifier::isActive_) - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, EdgeModifier::apply_modifier(epsilonValues_[i]), false); - else - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], false); - // std::cout << "neg: " << rowIndex_ << ", " << columnIndex_ << ", " << distanceMatrix_[rowIndex_][columnIndex_].first << "\n"; - ++columnIndex_; - while (col_index_is_not_valid_neg() && rowIndex_ > 1) { - --rowIndex_; - ini_col_index_low(); - } - if (col_index_is_not_valid_neg()) { - --rowIndex_; + currentEdge_.set(epsilonIndex_ + 1, epsilonIndex_ + 1, + EdgeModifier::apply_modifier(epsilonValues_[epsilonIndex_]), true); + else + currentEdge_.set(epsilonIndex_ + 1, epsilonIndex_ + 1, epsilonValues_[epsilonIndex_], true); + } + + void _update_edge_as_negative_vertex() { + currentEdge_.set(rowIndex_ - 1, rowIndex_ - 1, -std::numeric_limits::infinity(), false); + } + }; + + template + static std::vector > compute_vector_range(Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + { + std::vector > edgeFiltration; + std::vector epsilonValues; + std::vector > > distanceMatrix; + auto n = points.size(); + + _initialize(nu, mu, epsilonValues, distanceMatrix, points, distance, orderPolicy); + + // edgesAdded[i] (resp. edgesRemoved[i]) == list of edges (i,j), with j > > edgesAdded, edgesRemoved; + + // auto it = std::upper_bound(distanceMatrix[1].begin(), distanceMatrix[1].end(), + // std::pair(distanceMatrix.size(), mu * epsilonValues[0]), + // Point_distance_comp()); + // std::cout << "start vect colind: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; + size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); + + // Now, sort edges according to lengths, and put everything in edgeFiltration + edgeFiltration.clear(); + edgeFiltration.reserve(number_of_arrows + n); // count edges + vertices additions + + // initialise R({p_0}, +infinity) + edgeFiltration.emplace_back(0, 0, // add vertex p_0,+infty + std::numeric_limits::infinity(), true); + // epsilonValues[0], true); + + for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i + if constexpr (EdgeModifier::isActive_) { + edgeFiltration.emplace_back(i + 1, i + 1, + EdgeModifier::apply_modifier(epsilonValues[i]), + true); // add p_{i+1},eps_i + } else { + edgeFiltration.emplace_back(i + 1, i + 1, epsilonValues[i], true); // add p_{i+1},eps_i + } + for (auto edg_it = edgesAdded[i].begin(); edg_it != edgesAdded[i].end(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + for (auto edg_it = edgesRemoved[i].rbegin(); // longest first + edg_it != edgesRemoved[i].rend(); ++edg_it) { + edgeFiltration.push_back(*edg_it); + } + } + for (int i = n - 1; i >= 0; --i) { + edgeFiltration.emplace_back(i, i, -std::numeric_limits::infinity(), false); + } + + _canonically_sort_edges(edgeFiltration); + + return edgeFiltration; + } + + template + static boost::iterator_range get_iterator_range( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + { + return boost::iterator_range( + Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy), Oscillating_rips_edge_iterator()); + } + + //as Oscillating_rips_edge_iterator is a heavy iterator to copy, it should not be used as a usual iterator + template + static Oscillating_rips_edge_iterator begin( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy) + { + return Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); + } + + static Oscillating_rips_edge_iterator end() + { + return Oscillating_rips_edge_iterator(); + } + + private: + Oscillating_rips_edge_range(){}; + + /* The two input types std::pair encode pairs + * (j, d(p_i,p_j)) and (k, d(p_i,p_k)) for some fixed point p_i. + * The operator() orders edges by length. By convention, if lengths are equal, + * it orders pairs by taking the smaller vertex label between j and k. + */ + struct Point_distance_comp { + bool operator()(const std::pair& p1, const std::pair& p2) const { + { + if (p1.second != p2.second) { + return p1.second < p2.second; // shorter first } + return p1.first < p2.first; + } } }; template - static void initialize_(Filtration_value& nu, Filtration_value& mu, std::vector& epsilonValues, + static void _initialize(Filtration_value& nu, Filtration_value& mu, std::vector& epsilonValues, std::vector > >& distanceMatrix, const PointRange& points, DistanceFunction&& distance, Order_policy orderPolicy) { GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); @@ -417,7 +455,7 @@ class Oscillating_rips_edge_range { // compute epsilon values if (orderPolicy == Order_policy::ALREADY_ORDERED) { sortedPoints.assign(points.begin(), points.end()); - epsilonValues = compute_epsilon_values_(sortedPoints, distance); + epsilonValues = _compute_epsilon_values(sortedPoints, distance); } else if (orderPolicy == Order_policy::FARTHEST_POINT_ORDERING) { epsilonValues.reserve(n); Gudhi::subsampling::choose_n_farthest_points(distance, points, @@ -432,11 +470,11 @@ class Oscillating_rips_edge_range { epsilonValues[n - 1] = 0; } else { Gudhi::subsampling::pick_n_random_points(points, n, std::back_inserter(sortedPoints)); - epsilonValues = compute_epsilon_values_(sortedPoints, distance); + epsilonValues = _compute_epsilon_values(sortedPoints, distance); } // compute the distance matrix - distanceMatrix = compute_distance_matrix_(sortedPoints, distance); + distanceMatrix = _compute_distance_matrix(sortedPoints, distance); } /** \brief Compute the epsilon values for an ordered set of points, measuring the @@ -457,7 +495,7 @@ class Oscillating_rips_edge_range { * The range must be of same size as the number of points. */ template - static std::vector compute_epsilon_values_(const PointRange& points, DistanceFunction&& distance) { + static std::vector _compute_epsilon_values(const PointRange& points, DistanceFunction&& distance) { size_t n = points.size(); std::vector eps_range(n, std::numeric_limits::infinity()); @@ -501,7 +539,7 @@ class Oscillating_rips_edge_range { } template - static std::vector > > compute_distance_matrix_( + static std::vector > > _compute_distance_matrix( const PointRange& sortedPoints, DistanceFunction&& distance) { std::vector > > distanceMatrix(sortedPoints.size()); #ifdef GUDHI_USE_TBB @@ -529,7 +567,7 @@ class Oscillating_rips_edge_range { return distanceMatrix; } - static size_t compute_edges_(Filtration_value nu, Filtration_value mu, + static size_t _compute_edges(Filtration_value nu, Filtration_value mu, const std::vector& epsilonValues, std::vector > >& distanceMatrix, std::vector > >& edgesAdded, @@ -710,6 +748,274 @@ class Oscillating_rips_edge_range { return number_of_arrows; } + + static void _canonically_sort_edges(std::vector >& edges) { + // canonical sort of the edges: as much as possible, edges should be removed in + // the reverse order of their insertion. We decide to insert shorted edges first, + // with increasing lexicographical order, and remove larger edges first, with + // decreasing lexicographic order. + + // filtration then dimension, then lex order for insertion + auto edge_cmp = [](const Zigzag_edge& e1, const Zigzag_edge& e2) { + if (e1.get_filtration_value() != e2.get_filtration_value()) { + return e1.get_filtration_value() < e2.get_filtration_value(); + } // lower fil first + + if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first + if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + } //-> vertex of lower label + else { + return true; + } //-> always vertices before edges + } + // e1 is an edge + if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { + return false; + } // e2 vertex, -> put it first + // both are edges, lexigraphic compare + if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + } // lex order + if (e1.get_biggest_vertex() != e2.get_biggest_vertex()) { + return e1.get_biggest_vertex() < e2.get_biggest_vertex(); + } + return false; // equality + }; + // the inverse ordering for deletions + auto inv_edge_cmp = [&](const Zigzag_edge& e1, const Zigzag_edge& e2) { + if (e1.get_smallest_vertex() == e2.get_smallest_vertex() && e1.get_biggest_vertex() == e2.get_biggest_vertex()) { + return false; + } //== => false + return !(edge_cmp(e1, e2)); // reverse order + }; + // sort sequences of inclusions of same filtration with edge_cmp + // sort sequences of removals of same filtration with inv_edge_cmp + auto beg = edges.begin(); + auto end = edges.begin(); + auto curr_fil = beg->get_filtration_value(); + auto curr_type = beg->get_direction(); + while (beg != edges.end()) { + while (end != edges.end() && end->get_filtration_value() == curr_fil && end->get_direction() == curr_type) { + ++end; + } + if (curr_type) { +#ifdef GUDHI_USE_TBB + tbb::parallel_sort(beg, end, edge_cmp); +#else + std::sort(beg, end, edge_cmp); +#endif + } // sequence of insertions + else { +#ifdef GUDHI_USE_TBB + tbb::parallel_sort(beg, end, inv_edge_cmp); +#else + std::sort(beg, end, inv_edge_cmp); +#endif + } // sequence of removals + beg = end; + curr_fil = beg->get_filtration_value(); + curr_type = beg->get_direction(); + } + } +}; + +//needs Filtered_complex to have stable simplex handles +template +class Oscillating_rips_simplex_range { + public: + class Oscillating_rips_iterator + : public boost::iterator_facade&, + boost::forward_traversal_tag> { + public: + using Filtration_value = typename Filtered_complex::Filtration_value; + using Simplex_handle = typename Filtered_complex::Simplex_handle; + using Simplex_key = typename Filtered_complex::Simplex_key; + + // edges and complex are not copied, so do not modifiy outside as long as iterator != end. + // TODO: move constructor for iterator? + Oscillating_rips_iterator(EdgeRangeIterator& edgeStartIterator, EdgeRangeIterator& edgeEndIterator, + Filtered_complex& complex, int maxDimension = -1) + : complex_(&complex), + currentSimplexIndex_(0), + currentEdgeIt_(std::move(edgeStartIterator)), + endEdgeIt_(std::move(edgeEndIterator)), + currentDirection_(true), + maxDimension_(maxDimension), + currentArrowNumber_(0) + { + if (currentEdgeIt_ == endEdgeIt_) { + _set_end(); + return; + } + + // first simplex is the vertex (0,0) which is the only one with its filtration value. + complex_->insert_edge_as_flag(currentEdgeIt_->get_smallest_vertex(), currentEdgeIt_->get_biggest_vertex(), + currentEdgeIt_->get_filtration_value(), maxDimension_, currentSimplices_); + ++currentEdgeIt_; + + std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; + std::get<1>(currentArrow_) = complex_->filtration(currentSimplices_[currentSimplexIndex_]); + std::get<2>(currentArrow_) = currentDirection_; + } + + Oscillating_rips_iterator() : complex_(nullptr), currentSimplexIndex_(0), maxDimension_(0) {} + + private: + friend class boost::iterator_core_access; + + struct reverse_lexicographic_order { + explicit reverse_lexicographic_order(Filtered_complex* st) : st_(st) {} + + bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const { + auto rg1 = st_->simplex_vertex_range(sh1); + auto rg2 = st_->simplex_vertex_range(sh2); + auto it1 = rg1.begin(); + auto it2 = rg2.begin(); + while (it1 != rg1.end() && it2 != rg2.end()) { + if (*it1 == *it2) { + ++it1; + ++it2; + } else { + return *it1 < *it2; + } + } + return ((it1 == rg1.end()) && (it2 != rg2.end())); + } + Filtered_complex* st_; + }; + + std::vector currentSimplices_; + Filtered_complex* complex_; + size_t currentSimplexIndex_; + EdgeRangeIterator currentEdgeIt_; + EdgeRangeIterator endEdgeIt_; + bool currentDirection_; + const int maxDimension_; + // bool isEnd_; + std::tuple currentArrow_; + Simplex_key currentArrowNumber_; + + bool equal(Oscillating_rips_iterator const& other) const { + if (complex_ == nullptr) return other.complex_ == nullptr; + + return complex_ == other.complex_ && currentEdgeIt_ == other.currentEdgeIt_ && + currentSimplexIndex_ == other.currentSimplexIndex_; + } + + const std::tuple& dereference() const { + return currentArrow_; + } + + void increment() { + ++currentSimplexIndex_; + ++currentArrowNumber_; + + if (currentSimplexIndex_ == currentSimplices_.size()) { + if (currentEdgeIt_ == endEdgeIt_) { + _set_end(); + return; + } + + if (!currentDirection_) { + for (auto& sh : currentSimplices_) complex_->remove_maximal_simplex(sh); + } + currentSimplices_.clear(); + + auto fil = currentEdgeIt_->get_filtration_value(); + currentDirection_ = currentEdgeIt_->get_direction(); + + if (currentDirection_) { + _update_positive_current_simplices(fil); + } else { + _update_negative_current_simplices(fil); + } + currentSimplexIndex_ = 0; + } + + std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; + std::get<1>(currentArrow_) = complex_->filtration(currentSimplices_[currentSimplexIndex_]); + std::get<2>(currentArrow_) = currentDirection_; + if (currentDirection_) complex_->assign_key(currentSimplices_[currentSimplexIndex_], currentArrowNumber_); + } + + void _set_end() { complex_ = nullptr; } + + void _update_positive_current_simplices(Filtration_value fil) { + while (currentEdgeIt_ != endEdgeIt_ && currentEdgeIt_->get_direction() && + currentEdgeIt_->get_filtration_value() == fil) + { + complex_->insert_edge_as_flag(currentEdgeIt_->get_smallest_vertex(), currentEdgeIt_->get_biggest_vertex(), + currentEdgeIt_->get_filtration_value(), maxDimension_, currentSimplices_); + // std::cout << "add edge: " << currentEdgeIt_->get_smallest_vertex() << " " << currentEdgeIt_->get_biggest_vertex() << " - " << currentEdgeIt_->get_filtration_value() << "\n"; + // std::cout << "current:\n"; + // for (auto sh : currentSimplices_){ + // for (auto v : complex_->simplex_vertex_range(sh)) std::cout << v << " "; + // std::cout << "\n"; + // } + ++currentEdgeIt_; + } +#ifdef GUDHI_USE_TBB + tbb::parallel_sort(currentSimplices_.begin(), currentSimplices_.end(), + reverse_lexicographic_order(complex_)); +#else + std::sort(currentSimplices_.begin(), currentSimplices_.end(), + reverse_lexicographic_order(complex_)); +#endif + } + + void _update_negative_current_simplices(Filtration_value fil) { + unsigned int count = 0; + while (currentEdgeIt_ != endEdgeIt_ && !currentEdgeIt_->get_direction() && + currentEdgeIt_->get_filtration_value() == fil) + { + Simplex_handle sh = complex_->find({currentEdgeIt_->get_smallest_vertex()}); + if (currentEdgeIt_->get_smallest_vertex() != currentEdgeIt_->get_biggest_vertex()) { + sh = sh->second.children()->members().find(currentEdgeIt_->get_biggest_vertex()); + } + auto toRemove = complex_->star_simplex_range(sh); + currentSimplices_.insert(currentSimplices_.end(), toRemove.begin(), toRemove.end()); + ++currentEdgeIt_; + ++count; + } +#ifdef GUDHI_USE_TBB + tbb::parallel_sort( + currentSimplices_.begin(), currentSimplices_.end(), + [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { return complex_->key(sh1) > complex_->key(sh2); }); +#else + std::sort( + currentSimplices_.begin(), currentSimplices_.end(), + [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { return complex_->key(sh1) > complex_->key(sh2); }); +#endif + if (count > 1) { // more than 1 edge inserted: cofaces can be duplicated + auto last = std::unique(currentSimplices_.begin(), currentSimplices_.end(), + [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { + return complex_->key(sh1) == complex_->key(sh2); + }); // equal simplex handles means equal key + currentSimplices_.erase(last, currentSimplices_.end()); // remove duplicated cofaces + } + } + }; + + static boost::iterator_range get_iterator_range(EdgeRangeIterator& edgeStartIterator, + EdgeRangeIterator& edgeEndIterator, + Filtered_complex& complex, + int maxDimension = -1) { + return boost::iterator_range( + Oscillating_rips_iterator(edgeStartIterator, edgeEndIterator, complex, maxDimension), + Oscillating_rips_iterator()); + } + + static Oscillating_rips_iterator begin(EdgeRangeIterator& edgeStartIterator, EdgeRangeIterator& edgeEndIterator, + Filtered_complex& complex, int maxDimension = -1) { + return Oscillating_rips_iterator(edgeStartIterator, edgeEndIterator, complex, maxDimension); + } + + static Oscillating_rips_iterator end() { return Oscillating_rips_iterator(); } + + private: + Oscillating_rips_simplex_range(){}; }; } // namespace zigzag_persistence From 9b0a7ccf96184b8baf45d1cf0daefa088fc98d56 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 8 Aug 2023 17:26:07 +0200 Subject: [PATCH 03/21] oscillating rips persistence, example and benchmark --- .../benchmark/CMakeLists.txt | 19 + .../benchmark/Oscillating_rips_benchmark.cpp | 213 ++ .../Oscillating_rips_comp_benchmark.cpp | 250 ++ .../benchmark/ext_zz/dionysus/chain.h | 153 + .../benchmark/ext_zz/dionysus/chain.hpp | 188 ++ .../ext_zz/dionysus/clearing-reduction.h | 45 + .../ext_zz/dionysus/clearing-reduction.hpp | 60 + .../benchmark/ext_zz/dionysus/cnpy.h | 241 ++ .../ext_zz/dionysus/cohomology-persistence.h | 116 + .../dionysus/cohomology-persistence.hpp | 61 + .../benchmark/ext_zz/dionysus/common.h | 25 + .../benchmark/ext_zz/dionysus/diagram.h | 114 + .../benchmark/ext_zz/dionysus/distances.h | 93 + .../benchmark/ext_zz/dionysus/distances.hpp | 30 + .../benchmark/ext_zz/dionysus/dlog/progress.h | 57 + .../benchmark/ext_zz/dionysus/fields/q.h | 63 + .../benchmark/ext_zz/dionysus/fields/z2.h | 31 + .../benchmark/ext_zz/dionysus/fields/zp.h | 55 + .../benchmark/ext_zz/dionysus/filtration.h | 124 + .../benchmark/ext_zz/dionysus/format.h | 8 + .../ext_zz/dionysus/format/format.cc | 1156 ++++++++ .../benchmark/ext_zz/dionysus/format/format.h | 2546 +++++++++++++++++ .../benchmark/ext_zz/dionysus/grid/box.h | 136 + .../benchmark/ext_zz/dionysus/grid/box.hpp | 141 + .../benchmark/ext_zz/dionysus/grid/grid.h | 143 + .../benchmark/ext_zz/dionysus/grid/point.h | 132 + .../benchmark/ext_zz/dionysus/grid/vertices.h | 86 + .../ext_zz/dionysus/matrix-filtration.h | 120 + .../ext_zz/dionysus/omni-field-persistence.h | 145 + .../dionysus/omni-field-persistence.hpp | 250 ++ .../benchmark/ext_zz/dionysus/opts/opts.h | 499 ++++ .../ext_zz/dionysus/ordinary-persistence.h | 64 + .../benchmark/ext_zz/dionysus/pair-recorder.h | 78 + .../ext_zz/dionysus/reduced-matrix.h | 170 ++ .../ext_zz/dionysus/reduced-matrix.hpp | 78 + .../benchmark/ext_zz/dionysus/reduction.h | 109 + .../dionysus/relative-homology-zigzag.h | 84 + .../dionysus/relative-homology-zigzag.hpp | 122 + .../benchmark/ext_zz/dionysus/rips.h | 147 + .../benchmark/ext_zz/dionysus/rips.hpp | 162 ++ .../benchmark/ext_zz/dionysus/row-reduction.h | 54 + .../ext_zz/dionysus/row-reduction.hpp | 103 + .../benchmark/ext_zz/dionysus/simplex.h | 280 ++ .../ext_zz/dionysus/sparse-row-matrix.h | 184 ++ .../ext_zz/dionysus/sparse-row-matrix.hpp | 103 + .../ext_zz/dionysus/standard-reduction.h | 44 + .../ext_zz/dionysus/standard-reduction.hpp | 47 + .../benchmark/ext_zz/dionysus/trails-chains.h | 17 + .../ext_zz/dionysus/zigzag-persistence.h | 142 + .../ext_zz/dionysus/zigzag-persistence.hpp | 541 ++++ .../benchmark/ext_zz/fzz/fzz.cpp | 206 ++ .../benchmark/ext_zz/fzz/fzz.h | 87 + .../ext_zz/phat/algorithms/chunk_reduction.h | 223 ++ .../ext_zz/phat/algorithms/row_reduction.h | 56 + .../algorithms/spectral_sequence_reduction.h | 80 + .../phat/algorithms/standard_reduction.h | 47 + .../ext_zz/phat/algorithms/twist_reduction.h | 51 + .../benchmark/ext_zz/phat/boundary_matrix.h | 343 +++ .../ext_zz/phat/compute_persistence_pairs.h | 128 + .../benchmark/ext_zz/phat/helpers/dualize.h | 74 + .../benchmark/ext_zz/phat/helpers/misc.h | 75 + .../phat/helpers/thread_local_storage.h | 52 + .../benchmark/ext_zz/phat/persistence_pairs.h | 155 + .../representations/abstract_pivot_column.h | 102 + .../representations/bit_tree_pivot_column.h | 165 ++ .../phat/representations/full_pivot_column.h | 100 + .../phat/representations/heap_pivot_column.h | 126 + .../representations/sparse_pivot_column.h | 79 + .../ext_zz/phat/representations/vector_heap.h | 170 ++ .../ext_zz/phat/representations/vector_list.h | 101 + .../ext_zz/phat/representations/vector_set.h | 99 + .../phat/representations/vector_vector.h | 107 + src/Zigzag_persistence/example/CMakeLists.txt | 6 + .../example/comparison_for_tests.cpp | 130 +- .../example_oscillating_rips_persistence.cpp | 95 + .../gudhi/Oscillating_rips_persistence.h | 93 + .../include/gudhi/Zigzag_persistence.h | 69 +- .../oscillating_rips_iterators.h | 48 +- 78 files changed, 12854 insertions(+), 42 deletions(-) create mode 100644 src/Zigzag_persistence/benchmark/CMakeLists.txt create mode 100644 src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp create mode 100644 src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h create mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h create mode 100644 src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp create mode 100644 src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h diff --git a/src/Zigzag_persistence/benchmark/CMakeLists.txt b/src/Zigzag_persistence/benchmark/CMakeLists.txt new file mode 100644 index 0000000000..3d21cbe72a --- /dev/null +++ b/src/Zigzag_persistence/benchmark/CMakeLists.txt @@ -0,0 +1,19 @@ +project(Zigzag_benchmark) + +find_package(benchmark REQUIRED) + +add_executable(Oscillating_rips_benchmark Oscillating_rips_benchmark.cpp) +target_link_libraries(Oscillating_rips_benchmark benchmark::benchmark) +if(TARGET TBB::tbb) + target_link_libraries(Oscillating_rips_benchmark TBB::tbb) +endif() + +add_executable(Comp_benchmark Oscillating_rips_comp_benchmark.cpp ./ext_zz/fzz/fzz.cpp) +target_link_libraries(Comp_benchmark benchmark::benchmark) +target_include_directories(Comp_benchmark PUBLIC . ./ext_zz) +if(TARGET TBB::tbb) + target_link_libraries(Comp_benchmark TBB::tbb) +endif() +target_compile_options(Comp_benchmark PUBLIC "-fopenmp") +target_link_options(Comp_benchmark PUBLIC "-fopenmp") + diff --git a/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp b/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp new file mode 100644 index 0000000000..179eecaebb --- /dev/null +++ b/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp @@ -0,0 +1,213 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include +#include // for pair +#include + +#include +#include +#include +#include +#include +#include + +using ST = Gudhi::Simplex_tree; +using Filtration_value = ST::Filtration_value; +using Simplex_handle = ST::Simplex_handle; +using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; +using ZE = Gudhi::zigzag_persistence::Zigzag_edge; +using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; +using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::iterator>; +using Point = std::vector; + +std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { + std::vector finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0, 10); + + for (unsigned int i = 0; i < numberOfPoints; ++i) { + auto res = points.insert({dist(rng), dist(rng)}); + while (!res.second) { + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + return finalPoints; +} + +// static void ORP_with_custom_iterator(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::FARTHEST_POINT_ORDERING); +// } +// } +// BENCHMARK(ORP_with_custom_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(2, 5, 4), +// benchmark::CreateRange(5, 5, 4)}) +// ->Unit(benchmark::kMicrosecond); +// BENCHMARK(ORP_with_custom_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(20, 64, 4)}) +// ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_custom_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(256, 500, 4)}) +// ->Unit(benchmark::kMillisecond); + +// static void ORP_with_vector_iterator(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< +// std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::FARTHEST_POINT_ORDERING); +// } +// } +// BENCHMARK(ORP_with_vector_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(2, 5, 4), +// benchmark::CreateRange(5, 5, 4)}) +// ->Unit(benchmark::kMicrosecond); +// BENCHMARK(ORP_with_vector_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(20, 64, 4)}) +// ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_vector_iterator) +// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(256, 500, 4)}) +// ->Unit(benchmark::kMillisecond); + +// static void ORP_with_custom_iterator_random(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::RANDOM_POINT_ORDERING); +// } +// } +// BENCHMARK(ORP_with_custom_iterator_random) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateDenseRange(5, 15, 5)}) +// ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_custom_iterator_random) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateDenseRange(20, 50, 30)}) +// ->Unit(benchmark::kMillisecond); + +static void ORP_with_vector_iterator_random(benchmark::State& state) { + double nu = state.range(0); + double mu = state.range(1); + int maxDim = state.range(2); + unsigned int numberOfPoints = state.range(3); + int seed = 0; + + std::vector points = build_point_cloud(numberOfPoints, seed); + + for (auto _ : state) { + auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< + std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::RANDOM_POINT_ORDERING); + } +} +BENCHMARK(ORP_with_vector_iterator_random) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(4, 50, 4), + benchmark::CreateDenseRange(5, 15, 5)}) + ->Unit(benchmark::kMillisecond); +BENCHMARK(ORP_with_vector_iterator_random) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 4, 1), + benchmark::CreateRange(4, 50, 4), + benchmark::CreateDenseRange(20, 50, 30)}) + ->Unit(benchmark::kMillisecond); + +// static void ORP_with_custom_iterator_ordered(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::ALREADY_ORDERED); +// } +// } +// BENCHMARK(ORP_with_custom_iterator_ordered) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(5, 50, 4)}) +// ->Unit(benchmark::kMillisecond); + +// static void ORP_with_vector_iterator_ordered(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< +// std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::ALREADY_ORDERED); +// } +// } +// BENCHMARK(ORP_with_vector_iterator_ordered) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(5, 50, 4)}) +// ->Unit(benchmark::kMillisecond); + +BENCHMARK_MAIN(); + diff --git a/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp b/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp new file mode 100644 index 0000000000..5e8f512f61 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp @@ -0,0 +1,250 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include +#include +#include // for pair +#include + +#include +#include +#include +#include +#include +#include + +#include "ext_zz/fzz/fzz.h" +#include "ext_zz/dionysus/simplex.h" +#include "ext_zz/dionysus/filtration.h" +#include "ext_zz/dionysus/zigzag-persistence.h" + +using ST = Gudhi::Simplex_tree; +using Vertex_handle = ST::Vertex_handle; +using Filtration_value = ST::Filtration_value; +using Simplex_handle = ST::Simplex_handle; +using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; +using ZE = Gudhi::zigzag_persistence::Zigzag_edge; +using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; +using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::const_iterator>; +using Point = std::vector; + +using DField = dionysus::Z2Field; +using Simplex = dionysus::Simplex<>; +using DFiltration = dionysus::Filtration; +using DZZ = dionysus::ZigzagPersistence; +using DIndex = typename DZZ::Index; +using DChain = dionysus::ChainEntry; +using DIChain = dionysus::ChainEntry; + +std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { + std::vector finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0, 10); + + for (unsigned int i = 0; i < numberOfPoints; ++i) { + auto res = points.insert({dist(rng), dist(rng)}); + while (!res.second) { + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + return finalPoints; +} + +std::vector > compute_with_dionysus(const std::vector& edges, int maxDim) { + ST st; + DField k; + std::unordered_map indices; + DZZ persistence(k); + std::vector > res; + + std::set essentials; + + unsigned int op = 0; + unsigned int idx = 0; + + auto start = edges.begin(); + auto end = edges.end(); + for (const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { + auto r = st.simplex_vertex_range(std::get<0>(t)); + std::vector simplex(r.begin(), r.end()); + Simplex c(simplex); + DIndex pair; + if (std::get<2>(t)) { + indices.try_emplace(c, idx++); + // int dim = boost::distance(c.boundary(persistence.field())); + // dim = dim == 0 ? 0 : dim -1; + // fmt::print("[{}] Adding: {} : {}\n", op, c, dim); + pair = + persistence.add(c.boundary(persistence.field()) | boost::adaptors::transformed([&indices](const DChain& e) { + return DIChain(e.element(), indices.find(e.index())->second); + })); + } else { + // fmt::print("[{}] Removing: {} : {}\n", op, c, boost::distance(c.boundary(persistence.field())) - 1); + auto idxIt = indices.find(c); + pair = persistence.remove(idxIt->second); + indices.erase(idxIt); + } + + if (pair != DZZ::unpaired()) { + // fmt::print("{} - {}\n", pair, op); + res.emplace_back(pair, op); + essentials.erase(pair); + } else { + essentials.insert(essentials.end(), op); + } + op++; + } + + for (unsigned int v : essentials) { + // fmt::print("{} - inf\n", v); + res.emplace_back(v, op); + } + + return res; +} + +std::vector > compute_with_fzz(const std::vector& edges, + int maxDim) { + std::vector > persistence; + FZZ::FastZigzag fzz; + std::vector > simplices; + std::vector dirs; + ST st; + + auto start = edges.begin(); + auto end = edges.end(); + for (const auto& t : ORv::get_iterator_range(start, end, st, maxDim)){ + auto r = st.simplex_vertex_range(std::get<0>(t)); + simplices.emplace_back(r.begin(), r.end()); + dirs.push_back(std::get<2>(t)); + } + + fzz.compute(simplices, dirs, &persistence); + + std::sort(persistence.begin(), persistence.end(), + [](const std::tuple& p1, + const std::tuple& p2) { + if (std::get<1>(p1) == std::get<1>(p2)) { + return std::get<0>(p1) < std::get<0>(p2); + } + + return std::get<1>(p1) < std::get<1>(p2); + }); + + return persistence; +} + +static void ORP_with_gudhi(benchmark::State& state) { + double nu = state.range(0); + double mu = state.range(1); + int maxDim = state.range(2); + unsigned int numberOfPoints = state.range(3); + int seed = 0; + + std::vector points = build_point_cloud(numberOfPoints, seed); + + for (auto _ : state) { + auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); + } +} +BENCHMARK(ORP_with_gudhi) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(2, 5, 4), + benchmark::CreateRange(5, 5, 4)}) + ->Unit(benchmark::kMicrosecond); +BENCHMARK(ORP_with_gudhi) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(4, 50, 4), + benchmark::CreateRange(20, 64, 4)}) + ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_gudhi) +// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(256, 500, 4)}) +// ->Unit(benchmark::kMillisecond); + +static void ORP_with_dionysus(benchmark::State& state) { + double nu = state.range(0); + double mu = state.range(1); + int maxDim = state.range(2); + unsigned int numberOfPoints = state.range(3); + int seed = 0; + + std::vector points = build_point_cloud(numberOfPoints, seed); + + for (auto _ : state) { + auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance()); + auto res = compute_with_dionysus(vec, maxDim); + } +} +BENCHMARK(ORP_with_dionysus) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(2, 5, 4), + benchmark::CreateRange(5, 5, 4)}) + ->Unit(benchmark::kMicrosecond); +BENCHMARK(ORP_with_dionysus) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(4, 50, 4), + benchmark::CreateRange(20, 64, 4)}) + ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_dionysus) +// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(256, 500, 4)}) +// ->Unit(benchmark::kMillisecond); + +static void ORP_with_fzz(benchmark::State& state) { + double nu = state.range(0); + double mu = state.range(1); + int maxDim = state.range(2); + unsigned int numberOfPoints = state.range(3); + int seed = 0; + + std::vector points = build_point_cloud(numberOfPoints, seed); + + for (auto _ : state) { + auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance()); + auto res = compute_with_fzz(vec, maxDim); + } +} +BENCHMARK(ORP_with_fzz) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(2, 5, 4), + benchmark::CreateRange(5, 5, 4)}) + ->Unit(benchmark::kMicrosecond); +BENCHMARK(ORP_with_fzz) + ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), + benchmark::CreateDenseRange(3, 5, 1), + benchmark::CreateRange(4, 50, 4), + benchmark::CreateRange(20, 64, 4)}) + ->Unit(benchmark::kMillisecond); +// BENCHMARK(ORP_with_fzz) +// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// benchmark::CreateDenseRange(3, 4, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(256, 500, 4)}) +// ->Unit(benchmark::kMillisecond); + +BENCHMARK_MAIN(); + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h new file mode 100644 index 0000000000..00c983623a --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h @@ -0,0 +1,153 @@ +#ifndef DIONYSUS_CHAIN_H +#define DIONYSUS_CHAIN_H + +#include +#include +#include + +#include "fields/z2.h" + +namespace dionysus +{ + +template +struct FieldElement +{ + typedef typename Field::Element Element; + FieldElement(Element e_): + e(e_) {} + Element element() const { return e; } + void set_element(Element e_) { e = e_; } + Element e; +}; + +template<> +struct FieldElement +{ + typedef Z2Field::Element Element; + FieldElement(Element) {} + Element element() const { return Z2Field::id(); } + void set_element(Element) {} +}; + +template +struct ChainEntry: public FieldElement, public Extra... +{ + typedef Field_ Field; + typedef Index_ Index; + + typedef FieldElement Parent; + typedef typename Parent::Element Element; + + ChainEntry(): Parent(Element()), i(Index()) {} // need for serialization + + ChainEntry(ChainEntry&& other) = default; + ChainEntry(const ChainEntry& other) = default; + ChainEntry& operator=(ChainEntry&& other) = default; + + ChainEntry(Element e_, const Index& i_): + Parent(e_), i(i_) {} + + ChainEntry(Element e_, Index&& i_): + Parent(e_), i(std::move(i_)) {} + + const Index& index() const { return i; } + Index& index() { return i; } + + // debug + bool operator==(const ChainEntry& other) const { return i == other.i; } + + Index i; +}; + +template +struct Chain +{ + struct Visitor + { + template + void first(Iter it) const {} + + template + void second(Iter it) const {} + + template + void equal_keep(Iter it) const {} + + template + void equal_drop(Iter it) const {} + }; + + // x += a*y + template + static void addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); +}; + +template +struct Chain> +{ + struct Visitor + { + template + void first(Iter it) const {} + + template + void second(Iter it) const {} + + template + void equal_keep(Iter it) const {} + + template + void equal_drop(Iter it) const {} + }; + + // x += a*y + template + static void addto(std::list& x, typename Field::Element a, const C2& y, + const Field& field, const Cmp& cmp, const Visitor_& visitor = Visitor_()); +}; + + +template +struct Chain> +{ + struct Visitor + { + template + void first(Iter it) const {} + + template + void second(Iter it) const {} + + template + void equal_keep(Iter it) const {} + + template + void equal_drop(Iter it) const {} + }; + + // x += a*y + template + static void addto(std::set& x, typename Field::Element a, const C2& y, + const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); + + template + static void addto(std::set& x, typename Field::Element a, T&& y, + const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); +}; + +} + +//namespace std +//{ +// template +// void swap(::dionysus::ChainEntry& x, ::dionysus::ChainEntry& y) +// { +// std::swap(x.e, y.e); +// std::swap(x.i, y.i); +// } +//} + +#include "chain.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp new file mode 100644 index 0000000000..4da9f44615 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp @@ -0,0 +1,188 @@ +template +template +void +dionysus::Chain>:: +addto(std::list& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + auto cur_x = std::begin(x), + end_x = std::end(x); + auto cur_y = std::begin(y), + end_y = std::end(y); + + while (cur_x != end_x && cur_y != end_y) + { + if (cmp(cur_x->index(), cur_y->index())) + { + visitor.first(cur_x++); + } else if (cmp(cur_y->index(), cur_x->index())) + { + // multiply and add + Element ay = field.mul(a, cur_y->element()); + auto nw_x = x.insert(cur_x, *cur_y); + nw_x->set_element(ay); + ++cur_y; + visitor.second(nw_x); + } else + { + Element ay = field.mul(a, cur_y->element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + { + visitor.equal_drop(cur_x); + x.erase(cur_x++); + } + else + { + cur_x->set_element(r); + visitor.equal_keep(cur_x); + ++cur_x; + } + ++cur_y; + } + } + + for (auto it = cur_y; it != end_y; ++it) + { + Element ay = field.mul(a, it->element()); + x.push_back(*it); + x.back().set_element(ay); + visitor.second(--x.end()); + } +} + +template +template +void +dionysus::Chain>:: +addto(std::set& x, typename Field::Element a, const C2& y, const Field& field, const Cmp&, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + auto cur_y = std::begin(y), + end_y = std::end(y); + + while (cur_y != end_y) + { + auto cur_x = x.find(*cur_y); + if (cur_x == x.end()) + { + auto nw = x.insert(*cur_y).first; + Element ay = field.mul(a, nw->element()); + const_cast(*nw).set_element(ay); + visitor.second(nw); + } else + { + Element ay = field.mul(a, cur_y->element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + { + visitor.equal_drop(cur_x); + x.erase(cur_x); + } + else + { + const_cast(*cur_x).set_element(r); + visitor.equal_keep(cur_x); + } + } + ++cur_y; + } +} + +template +template +void +dionysus::Chain>:: +addto(std::set& x, typename Field::Element a, T&& y, const Field& field, const Cmp&, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + auto cur_x = x.find(y); + if (cur_x == x.end()) + { + auto nw = x.insert(std::move(y)).first; + Element ay = field.mul(a, nw->element()); + const_cast(*nw).set_element(ay); + visitor.second(nw); + } else + { + Element ay = field.mul(a, y.element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + { + visitor.equal_drop(cur_x); + x.erase(cur_x); + } + else + { + const_cast(*cur_x).set_element(r); + visitor.equal_keep(cur_x); + } + } +} + +template +template +void +dionysus::Chain:: +addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) +{ + typedef typename Field::Element Element; + + C1 res; + + auto cur_x = std::begin(x), + end_x = std::end(x); + auto cur_y = std::begin(y), + end_y = std::end(y); + + while (cur_x != end_x && cur_y != end_y) + { + if (cmp(*cur_x, *cur_y)) + { + res.emplace_back(std::move(*cur_x)); + visitor.first(--res.end()); + ++cur_x; + } else if (cmp(*cur_y, *cur_x)) + { + // multiply and add + Element ay = field.mul(a, cur_y->element()); + res.emplace_back(ay, cur_y->index()); + visitor.second(--res.end()); + ++cur_y; + } else + { + Element ay = field.mul(a, cur_y->element()); + Element r = field.add(cur_x->element(), ay); + if (field.is_zero(r)) + visitor.equal_drop(cur_x); + else + { + res.emplace_back(std::move(*cur_x)); + res.back().set_element(r); + visitor.equal_keep(--res.end()); + } + ++cur_x; + ++cur_y; + } + } + + while (cur_y != end_y) + { + Element ay = field.mul(a, cur_y->element()); + res.emplace_back(ay, cur_y->index()); + visitor.second(--res.end()); + ++cur_y; + } + + while (cur_x != end_x) + { + res.emplace_back(std::move(*cur_x)); + visitor.first(--res.end()); + ++cur_x; + } + + x.swap(res); +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h new file mode 100644 index 0000000000..8651e9a69a --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h @@ -0,0 +1,45 @@ +#ifndef DIONYSUS_CLEARING_REDUCTION_H +#define DIONYSUS_CLEARING_REDUCTION_H + +namespace dionysus +{ + +// Mid-level interface +template +class ClearingReduction +{ + public: + using Persistence = Persistence_; + using Field = typename Persistence::Field; + using Index = typename Persistence::Index; + + public: + ClearingReduction(Persistence& persistence): + persistence_(persistence) {} + + template + void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); + + template + void operator()(const Filtration& f, const ReportPair& report_pair); + + template + void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } + + static void no_report_pair(int, Index, Index) {} + static void no_progress() {} + + const Persistence& + persistence() const { return persistence_; } + Persistence& persistence() { return persistence_; } + + private: + Persistence& persistence_; +}; + +} + +#include "clearing-reduction.hpp" + +#endif + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp new file mode 100644 index 0000000000..ceac11879d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp @@ -0,0 +1,60 @@ +#include +#include + +#include +namespace ba = boost::adaptors; + +template +template +void +dionysus::ClearingReduction

:: +operator()(const Filtration& filtration, const ReportPair& report_pair) +{ + using Cell = typename Filtration::Cell; + (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); +} + +template +template +void +dionysus::ClearingReduction

:: +operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) +{ + persistence_.resize(filtration.size()); + + // sort indices by decreasing dimension + std::vector indices(filtration.size()); + std::iota(indices.begin(), indices.end(), 0); + std::stable_sort(indices.begin(), indices.end(), + [&filtration](size_t x, size_t y) + { return filtration[x].dimension() > filtration[y].dimension(); }); + + typedef typename Filtration::Cell Cell; + typedef ChainEntry CellChainEntry; + typedef ChainEntry ChainEntry; + + for(size_t i : indices) + { + progress(); + const auto& c = filtration[i]; + + if (relative(c)) + { + persistence_.set_skip(i); + continue; + } + + if (persistence_.pair(i) != persistence_.unpaired()) + continue; + + persistence_.set(i, c.boundary(persistence_.field()) | + ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | + ba::transformed([this,&filtration](const CellChainEntry& e) + { return ChainEntry(e.element(), filtration.index(e.index())); })); + + Index pair = persistence_.reduce(i); + if (pair != persistence_.unpaired()) + report_pair(c.dimension(), pair, i); + } +} + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h new file mode 100644 index 0000000000..b11013b9d7 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h @@ -0,0 +1,241 @@ +//Copyright (C) 2011 Carl Rogers +//Released under MIT License +//license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php + +#ifndef LIBCNPY_H_ +#define LIBCNPY_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cnpy { + + struct NpyArray { + char* data; + std::vector shape; + unsigned int word_size; + bool fortran_order; + void destruct() {delete[] data;} + }; + + struct npz_t : public std::map + { + void destruct() + { + npz_t::iterator it = this->begin(); + for(; it != this->end(); ++it) (*it).second.destruct(); + } + }; + + char BigEndianTest(); + char map_type(const std::type_info& t); + template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims); + void parse_npy_header(FILE* fp,unsigned int& word_size, unsigned int*& shape, unsigned int& ndims, bool& fortran_order); + void parse_zip_footer(FILE* fp, unsigned short& nrecs, unsigned int& global_header_size, unsigned int& global_header_offset); + npz_t npz_load(std::string fname); + NpyArray npz_load(std::string fname, std::string varname); + NpyArray npy_load(std::string fname); + + template std::vector& operator+=(std::vector& lhs, const T rhs) { + //write in little endian + for(char byte = 0; byte < sizeof(T); byte++) { + char val = *((char*)&rhs+byte); + lhs.push_back(val); + } + return lhs; + } + + template<> std::vector& operator+=(std::vector& lhs, const std::string rhs); + template<> std::vector& operator+=(std::vector& lhs, const char* rhs); + + + template std::string tostring(T i, int pad = 0, char padval = ' ') { + std::stringstream s; + s << i; + return s.str(); + } + + template void npy_save(std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") { + FILE* fp = NULL; + + if(mode == "a") fp = fopen(fname.c_str(),"r+b"); + + if(fp) { + //file exists. we need to append to it. read the header, modify the array size + unsigned int word_size, tmp_dims; + unsigned int* tmp_shape = 0; + bool fortran_order; + parse_npy_header(fp,word_size,tmp_shape,tmp_dims,fortran_order); + assert(!fortran_order); + + if(word_size != sizeof(T)) { + std::cout<<"libnpy error: "< header = create_npy_header(data,tmp_shape,ndims); + fwrite(&header[0],sizeof(char),header.size(),fp); + fseek(fp,0,SEEK_END); + + delete[] tmp_shape; + } + else { + fp = fopen(fname.c_str(),"wb"); + std::vector header = create_npy_header(data,shape,ndims); + fwrite(&header[0],sizeof(char),header.size(),fp); + } + + unsigned int nels = 1; + for(int i = 0;i < ndims;i++) nels *= shape[i]; + + fwrite(data,sizeof(T),nels,fp); + fclose(fp); + } + + template void npz_save(std::string zipname, std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") + { + //first, append a .npy to the fname + fname += ".npy"; + + //now, on with the show + FILE* fp = NULL; + unsigned short nrecs = 0; + unsigned int global_header_offset = 0; + std::vector global_header; + + if(mode == "a") fp = fopen(zipname.c_str(),"r+b"); + + if(fp) { + //zip file exists. we need to add a new npy file to it. + //first read the footer. this gives us the offset and size of the global header + //then read and store the global header. + //below, we will write the the new data at the start of the global header then append the global header and footer below it + unsigned int global_header_size; + parse_zip_footer(fp,nrecs,global_header_size,global_header_offset); + fseek(fp,global_header_offset,SEEK_SET); + global_header.resize(global_header_size); + size_t res = fread(&global_header[0],sizeof(char),global_header_size,fp); + if(res != global_header_size){ + throw std::runtime_error("npz_save: header read error while adding to existing zip"); + } + fseek(fp,global_header_offset,SEEK_SET); + } + else { + fp = fopen(zipname.c_str(),"wb"); + } + + std::vector npy_header = create_npy_header(data,shape,ndims); + + unsigned long nels = 1; + for (int m=0; m local_header; + local_header += "PK"; //first part of sig + local_header += (unsigned short) 0x0403; //second part of sig + local_header += (unsigned short) 20; //min version to extract + local_header += (unsigned short) 0; //general purpose bit flag + local_header += (unsigned short) 0; //compression method + local_header += (unsigned short) 0; //file last mod time + local_header += (unsigned short) 0; //file last mod date + local_header += (unsigned int) crc; //crc + local_header += (unsigned int) nbytes; //compressed size + local_header += (unsigned int) nbytes; //uncompressed size + local_header += (unsigned short) fname.size(); //fname length + local_header += (unsigned short) 0; //extra field length + local_header += fname; + + //build global header + global_header += "PK"; //first part of sig + global_header += (unsigned short) 0x0201; //second part of sig + global_header += (unsigned short) 20; //version made by + global_header.insert(global_header.end(),local_header.begin()+4,local_header.begin()+30); + global_header += (unsigned short) 0; //file comment length + global_header += (unsigned short) 0; //disk number where file starts + global_header += (unsigned short) 0; //internal file attributes + global_header += (unsigned int) 0; //external file attributes + global_header += (unsigned int) global_header_offset; //relative offset of local file header, since it begins where the global header used to begin + global_header += fname; + + //build footer + std::vector footer; + footer += "PK"; //first part of sig + footer += (unsigned short) 0x0605; //second part of sig + footer += (unsigned short) 0; //number of this disk + footer += (unsigned short) 0; //disk where footer starts + footer += (unsigned short) (nrecs+1); //number of records on this disk + footer += (unsigned short) (nrecs+1); //total number of records + footer += (unsigned int) global_header.size(); //nbytes of global headers + footer += (unsigned int) (global_header_offset + nbytes + local_header.size()); //offset of start of global headers, since global header now starts after newly written array + footer += (unsigned short) 0; //zip file comment length + + //write everything + fwrite(&local_header[0],sizeof(char),local_header.size(),fp); + fwrite(&npy_header[0],sizeof(char),npy_header.size(),fp); + fwrite(data,sizeof(T),nels,fp); + fwrite(&global_header[0],sizeof(char),global_header.size(),fp); + fwrite(&footer[0],sizeof(char),footer.size(),fp); + fclose(fp); + } + + template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims) { + + std::vector dict; + dict += "{'descr': '"; + dict += BigEndianTest(); + dict += map_type(typeid(T)); + dict += tostring(sizeof(T)); + dict += "', 'fortran_order': False, 'shape': ("; + dict += tostring(shape[0]); + for(int i = 1;i < ndims;i++) { + dict += ", "; + dict += tostring(shape[i]); + } + if(ndims == 1) dict += ","; + dict += "), }"; + //pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to end with \n + int remainder = 16 - (10 + dict.size()) % 16; + dict.insert(dict.end(),remainder,' '); + dict.back() = '\n'; + + std::vector header; + header += (char) 0x93; + header += "NUMPY"; + header += (char) 0x01; //major version of numpy format + header += (char) 0x00; //minor version of numpy format + header += (unsigned short) dict.size(); + header.insert(header.end(),dict.begin(),dict.end()); + + return header; + } + + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h new file mode 100644 index 0000000000..8d2019e89d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h @@ -0,0 +1,116 @@ +#ifndef DIONYSUS_COHOMOLOGY_PERSISTENCE_H +#define DIONYSUS_COHOMOLOGY_PERSISTENCE_H + +#include +#include + +#include +namespace bi = boost::intrusive; + +#include "reduction.h" +#include "chain.h" + +namespace dionysus +{ + +template> +class CohomologyPersistence +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef typename Field::Element FieldElement; + + typedef bi::list_base_hook> auto_unlink_hook; + struct Entry; + struct ColumnHead; + + typedef std::vector Column; + typedef bi::list> Row; + typedef std::list Columns; + typedef typename Columns::iterator ColumnsIterator; + typedef Column Chain; + + using IndexColumn = std::tuple; + + CohomologyPersistence(const Field& field, + const Comparison& cmp = Comparison()): + field_(field), cmp_(cmp) {} + + CohomologyPersistence(Field&& field, + const Comparison& cmp = Comparison()): + field_(std::move(field)), + cmp_(cmp) {} + + CohomologyPersistence(CohomologyPersistence&& other): + field_(std::move(other.field_)), + cmp_(std::move(other.cmp_)), + columns_(std::move(other.columns_)), + rows_(std::move(other.rows_)) {} + + template + Index add(const ChainRange& chain); + + template + IndexColumn add(const ChainRange& chain, bool keep_cocycle); + + // TODO: no skip support for now + bool skip(Index) const { return false; } + void add_skip() {} + void set_skip(Index, bool flag = true) {} + + const Field& field() const { return field_; } + const Columns& columns() const { return columns_; } + void reserve(size_t s) { rows_.reserve(s); } + + struct AddtoVisitor; + + static const Index unpaired() { return Reduction::unpaired; } + + private: + Field field_; + Comparison cmp_; + Columns columns_; + std::vector rows_; +}; + + +template +struct CohomologyPersistence::ColumnHead +{ + ColumnHead(Index i): index_(i) {} + + Index index() const { return index_; } + + Index index_; + Column chain; +}; + +template +struct CohomologyPersistence::Entry: + public ChainEntry +{ + typedef ChainEntry Parent; + + Entry(FieldElement e, const Index& i): // slightly dangerous + Parent(e,i) {} + + Entry(FieldElement e, const Index& i, ColumnsIterator it): + Parent(e,i), column(it) {} + + Entry(const Entry& other) = default; + Entry(Entry&& other) = default; + + void unlink() { auto_unlink_hook::unlink(); } + bool is_linked() const { return auto_unlink_hook::is_linked(); } + + ColumnsIterator column; // TODO: I really don't like this overhead +}; + +} + +#include "cohomology-persistence.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp new file mode 100644 index 0000000000..b2334f99e1 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp @@ -0,0 +1,61 @@ +template +template +typename dionysus::CohomologyPersistence::Index +dionysus::CohomologyPersistence:: +add(const ChainRange& chain) +{ + return std::get<0>(add(chain, false)); // return just the index +} + + +template +template +typename dionysus::CohomologyPersistence::IndexColumn +dionysus::CohomologyPersistence:: +add(const ChainRange& chain, bool keep_cocycle) +{ + auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; + std::set row_sum(entry_cmp); + for (auto it = std::begin(chain); it != std::end(chain); ++it) + for (auto& re : rows_[it->index()]) + dionysus::Chain::addto(row_sum, it->element(), Entry(re.element(), re.column->index(), re.column), field_, cmp_); + + if (row_sum.empty()) // Birth + { + columns_.emplace_back(rows_.size()); + auto before_end = columns_.end(); + --before_end; + columns_.back().chain.push_back(Entry(field_.id(), rows_.size(), before_end)); + rows_.emplace_back(); + rows_.back().push_back(columns_.back().chain.front()); + return std::make_tuple(unpaired(), Column()); + } else // Death + { + // Select front element in terms of comparison (rows are unsorted) + auto it = std::max_element(std::begin(row_sum), std::end(row_sum), entry_cmp); + + Entry first = std::move(*it); + row_sum.erase(it); + + for (auto& ce : row_sum) + { + FieldElement ay = field_.neg(field_.div(ce.element(), first.element())); + dionysus::Chain::addto(ce.column->chain, ay, first.column->chain, field_, + [this](const Entry& e1, const Entry& e2) + { return this->cmp_(e1.index(), e2.index()); }); + + for (auto& x : ce.column->chain) + { + x.column = ce.column; + rows_[x.index()].push_back(x); + } + } + Index pair = first.column->index(); + Column cocycle; + if (keep_cocycle) + cocycle = std::move(first.column->chain); + columns_.erase(first.column); + rows_.emplace_back(); // useless row; only present to make indices match + return std::make_tuple(pair, cocycle); + } +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h new file mode 100644 index 0000000000..e012b10539 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h @@ -0,0 +1,25 @@ +#ifndef DIONYSUS_EXAMPLES_COMMON_H +#define DIONYSUS_EXAMPLES_COMMON_H + +#include +#include + +template +void read_points(const std::string& infilename, PointContainer& points) +{ + typedef typename PointContainer::value_type Point; + + std::ifstream in(infilename.c_str()); + std::string line; + while(std::getline(in, line)) + { + if (line[0] == '#') continue; // comment line in the file + std::stringstream linestream(line); + double x; + points.push_back(Point()); + while (linestream >> x) + points.back().push_back(x); + } +} + +#endif // DIONYSUS_EXAMPLES_COMMON_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h new file mode 100644 index 0000000000..04eb29a927 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h @@ -0,0 +1,114 @@ +#ifndef DIONYSUS_DIAGRAM_H +#define DIONYSUS_DIAGRAM_H + +#include +#include +#include + +namespace dionysus +{ + +template +class Diagram +{ + public: + using Value = Value_; + using Data = Data_; + struct Point: public std::pair + { + using Parent = std::pair; + + Point(Value b, Value d, Data dd): + Parent(b,d), data(dd) {} + + Value birth() const { return Parent::first; } + Value death() const { return Parent::second; } + + // FIXME: temporary hack + Value operator[](size_t i) const { if (i == 0) return birth(); return death(); } + + Data data; + }; + + using Points = std::vector; + using iterator = typename Points::iterator; + using const_iterator = typename Points::const_iterator; + using value_type = Point; + + public: + const_iterator begin() const { return points.begin(); } + const_iterator end() const { return points.end(); } + iterator begin() { return points.begin(); } + iterator end() { return points.end(); } + + const Point& operator[](size_t i) const { return points[i]; } + + size_t size() const { return points.size(); } + void push_back(const Point& p) { points.push_back(p); } + template + void emplace_back(Args&&... args) { points.emplace_back(std::forward(args)...); } + + private: + std::vector points; +}; + +namespace detail +{ + template + struct Diagrams + { + using Value = decltype(std::declval()(std::declval())); + using Data = decltype(std::declval()(std::declval())); + using type = std::vector>; + }; +} + +template +typename detail::Diagrams::type +init_diagrams(const ReducedMatrix& m, const Filtration& f, const GetValue& get_value, const GetData& get_data) +{ + using Result = typename detail::Diagrams::type; + + Result diagrams; + for (typename ReducedMatrix::Index i = 0; i < m.size(); ++i) + { + if (m.skip(i)) + continue; + + auto s = f[i]; + auto d = s.dimension(); + + while (d + 1 > diagrams.size()) + diagrams.emplace_back(); + + auto pair = m.pair(i); + if (pair == m.unpaired()) + { + auto birth = get_value(s); + using Value = decltype(birth); + Value death = std::numeric_limits::infinity(); + diagrams[d].emplace_back(birth, death, get_data(i)); + } else if (pair > i) // positive + { + auto birth = get_value(s); + auto death = get_value(f[pair]); + + // hack to work with coboundaries + auto pd = f[pair].dimension(); + if (pd < d) + { + d = pd; + std::swap(birth, death); + } + + if (birth != death) // skip diagonal + diagrams[d].emplace_back(birth, death, get_data(i)); + } // else negative: do nothing + } + + return diagrams; +} + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h new file mode 100644 index 0000000000..29cac601af --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h @@ -0,0 +1,93 @@ +#ifndef DIONYSUS_DISTANCES_H +#define DIONYSUS_DISTANCES_H + +#include +#include + +namespace dionysus +{ + +/** + * Class: ExplicitDistances + * Stores the pairwise distances of Distances_ instance passed at construction. + * It's a protypical Distances template argument for the Rips complex. + */ +template +class ExplicitDistances +{ + public: + typedef Distances_ Distances; + typedef size_t IndexType; + typedef typename Distances::DistanceType DistanceType; + + ExplicitDistances(IndexType size): + size_(size), + distances_(size*(size + 1)/2 + size) {} + ExplicitDistances(const Distances& distances); + + DistanceType operator()(IndexType a, IndexType b) const; + DistanceType& operator()(IndexType a, IndexType b); + + size_t size() const { return size_; } + IndexType begin() const { return 0; } + IndexType end() const { return size(); } + + private: + std::vector distances_; + size_t size_; +}; + + +/** + * Class: PairwiseDistances + * Given a Container_ of points and a Distance_, it computes distances between elements + * in the container (given as instances of Index_ defaulted to unsigned) using the Distance_ functor. + * + * Container_ is assumed to be an std::vector. That simplifies a number of things. + */ +template +class PairwiseDistances +{ + public: + typedef Container_ Container; + typedef Distance_ Distance; + typedef Index_ IndexType; + typedef typename Distance::result_type DistanceType; + + + PairwiseDistances(const Container& container, + const Distance& distance = Distance()): + container_(container), distance_(distance) {} + + DistanceType operator()(IndexType a, IndexType b) const { return distance_(container_[a], container_[b]); } + + size_t size() const { return container_.size(); } + IndexType begin() const { return 0; } + IndexType end() const { return size(); } + + private: + const Container& container_; + Distance distance_; +}; + +template +struct L2Distance +{ + typedef Point_ Point; + typedef decltype(Point()[0] + 0) result_type; + + result_type operator()(const Point& p1, const Point& p2) const + { + result_type sum = 0; + for (size_t i = 0; i < p1.size(); ++i) + sum += (p1[i] - p2[i])*(p1[i] - p2[i]); + + return sqrt(sum); + } +}; + +} + +#include "distances.hpp" + +#endif // DIONYSUS_DISTANCES_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp new file mode 100644 index 0000000000..9b1f20aa2b --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp @@ -0,0 +1,30 @@ +template +dionysus::ExplicitDistances:: +ExplicitDistances(const Distances& distances): + size_(distances.size()), distances_((distances.size() * (distances.size() + 1))/2) +{ + IndexType i = 0; + for (typename Distances::IndexType a = distances.begin(); a != distances.end(); ++a) + for (typename Distances::IndexType b = a; b != distances.end(); ++b) + { + distances_[i++] = distances(a,b); + } +} + +template +typename dionysus::ExplicitDistances::DistanceType +dionysus::ExplicitDistances:: +operator()(IndexType a, IndexType b) const +{ + if (a > b) std::swap(a,b); + return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; +} + +template +typename dionysus::ExplicitDistances::DistanceType& +dionysus::ExplicitDistances:: +operator()(IndexType a, IndexType b) +{ + if (a > b) std::swap(a,b); + return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h new file mode 100644 index 0000000000..12bf86a4a2 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h @@ -0,0 +1,57 @@ +#ifndef DLOG_PROGRESS_H +#define DLOG_PROGRESS_H + +#include +#include +#include +#include + +namespace dlog +{ + +struct progress +{ + progress(size_t total): + current_(0), total_(total) { show_progress(); } + + progress& operator++() { current_++; if (current_ * 100 / total_ > (current_ - 1) * 100 / total_) show_progress(); check_done(); return *this; } + progress& operator=(size_t cur) { current_ = cur; show_progress(); check_done(); return *this; } + progress& operator()(const std::string& s) { message_ = s; show_progress(); check_done(); return *this; } + template + progress& operator()(const T& x) { std::ostringstream oss; oss << x; return (*this)(oss.str()); } + + inline void show_progress() const; + void check_done() const { if (current_ >= total_) std::cout << "\n" << std::flush; } + + private: + size_t current_, total_; + std::string message_; +}; + +} + +void +dlog::progress:: +show_progress() const +{ + int barWidth = 70; + + std::cout << "["; + int pos = barWidth * current_ / total_; + for (int i = 0; i < barWidth; ++i) + { + if (i < pos) + std::cout << "="; + else if (i == pos) + std::cout << ">"; + else + std::cout << " "; + } + std::cout << "] " << std::setw(3) << current_ * 100 / total_ << "%"; + if (!message_.empty()) + std::cout << " (" << message_ << ")"; + std::cout << "\r"; + std::cout.flush(); +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h new file mode 100644 index 0000000000..8972ae2b5a --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h @@ -0,0 +1,63 @@ +#ifndef DIONYSUS_Q_H +#define DIONYSUS_Q_H + +#include + +// TODO: eventually need to be able to adaptively switch to arbitrary precision arithmetic + +namespace dionysus +{ + +template +class Q +{ + public: + using BaseElement = Element_; + struct Element + { + BaseElement numerator, denominator; + + bool operator==(Element o) const { return numerator == o.numerator && denominator == o.denominator; } + bool operator!=(Element o) const { return !((*this) == o); } + + friend + std::ostream& operator<<(std::ostream& out, Element e) { out << e.numerator << '/' << e.denominator; return out; } + }; + + Element id() const { return { 1,1 }; } + Element zero() const { return { 0,1 }; } + Element init(BaseElement a) const { return { a,1 }; } + + Element neg(Element a) const { return { -a.numerator, a.denominator }; } + Element add(Element a, Element b) const { Element x { a.numerator*b.denominator + b.numerator*a.denominator, a.denominator*b.denominator }; normalize(x); return x; } + + Element inv(Element a) const { return { a.denominator, a.numerator }; } + Element mul(Element a, Element b) const { Element x { a.numerator*b.numerator, a.denominator*b.denominator }; normalize(x); return x; } + Element div(Element a, Element b) const { return mul(a, inv(b)); } + + bool is_zero(Element a) const { return a.numerator == 0; } + + BaseElement numerator(const Element& x) const { return x.numerator; } + BaseElement denominator(const Element& x) const { return x.denominator; } + + static void normalize(Element& x) + { + BaseElement q = gcd(abs(x.numerator), abs(x.denominator)); + x.numerator /= q; + x.denominator /= q; + if (x.denominator < 0) + { + x.numerator = -x.numerator; + x.denominator = -x.denominator; + } + } + + static BaseElement abs(BaseElement x) { if (x < 0) return -x; return x; } + static BaseElement gcd(BaseElement a, BaseElement b) { if (b < a) return gcd(b,a); while (a != 0) { b %= a; std::swap(a,b); } return b; } + + static bool is_prime(BaseElement x) { return false; } // Ok, since is_prime is only used as a shortcut +}; + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h new file mode 100644 index 0000000000..6317ace6df --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h @@ -0,0 +1,31 @@ +#ifndef DIONYSUS_Z2_H +#define DIONYSUS_Z2_H + +namespace dionysus +{ + +class Z2Field +{ + public: + typedef short Element; + + Z2Field() {} + + static Element id() { return 1; } + static Element zero() { return 0; } + static Element init(int a) { return (a % 2 + 2) % 2; } + + Element neg(Element a) const { return 2 - a; } + Element add(Element a, Element b) const { return (a+b) % 2; } + + Element inv(Element a) const { return a; } + Element mul(Element a, Element b) const { return a*b; } + Element div(Element a, Element b) const { return a; } + + bool is_zero(Element a) const { return a == 0; } +}; + +} + +#endif + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h new file mode 100644 index 0000000000..c70c61cc87 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h @@ -0,0 +1,55 @@ +#ifndef DIONYSUS_ZP_H +#define DIONYSUS_ZP_H + +#include + +namespace dionysus +{ + +template +class ZpField +{ + public: + typedef Element_ Element; + + ZpField(Element p); + ZpField(const ZpField& other) = default; + ZpField(ZpField&& other) = default; + + Element id() const { return 1; } + Element zero() const { return 0; } + Element init(int a) const { return (a % p_ + p_) % p_; } + + Element neg(Element a) const { return p_ - a; } + Element add(Element a, Element b) const { return (a+b) % p_; } + + Element inv(Element a) const { while (a < 0) a += p_; return inverses_[a]; } + Element mul(Element a, Element b) const { return (a*b) % p_; } + Element div(Element a, Element b) const { return mul(a, inv(b)); } + + bool is_zero(Element a) const { return (a % p_) == 0; } + + Element prime() const { return p_; } + + private: + Element p_; + std::vector inverses_; +}; + +template +ZpField:: +ZpField(Element p): + p_(p), inverses_(p_) +{ + for (Element i = 1; i < p_; ++i) + for (Element j = 1; j < p_; ++j) + if (mul(i,j) == 1) + { + inverses_[i] = j; + break; + } +} + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h new file mode 100644 index 0000000000..caf871cdfb --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h @@ -0,0 +1,124 @@ +#ifndef DIONYSUS_FILTRATION_H +#define DIONYSUS_FILTRATION_H + +#include +#include + +#include +#include +#include +#include + +namespace b = boost; +namespace bmi = boost::multi_index; + +namespace dionysus +{ + +// Filtration stores a filtered cell complex as boost::multi_index_container<...>. +// It allows for bidirectional translation between a cell and its index. +template>, + bool checked_index = false> +class Filtration +{ + public: + struct order {}; + + typedef Cell_ Cell; + typedef CellLookupIndex_ CellLookupIndex; + + typedef b::multi_index_container> + >> Container; + typedef typename Container::value_type value_type; + + typedef typename Container::template nth_index<0>::type Complex; + typedef typename Container::template nth_index<1>::type Order; + typedef typename Order::const_iterator OrderConstIterator; + typedef typename Order::iterator OrderIterator; + + + public: + Filtration() = default; + Filtration(Filtration&& other) = default; + Filtration& operator=(Filtration&& other) = default; + + Filtration(const std::initializer_list& cells): + Filtration(std::begin(cells), std::end(cells)) {} + + template + Filtration(Iterator bg, Iterator end): + cells_(bg, end) {} + + template + Filtration(const CellRange& cells): + Filtration(std::begin(cells), std::end(cells)) {} + + // Lookup + const Cell& operator[](size_t i) const { return cells_.template get()[i]; } + OrderConstIterator iterator(const Cell& s) const { return bmi::project(cells_, cells_.find(s)); } + size_t index(const Cell& s) const; + bool contains(const Cell& s) const { return cells_.find(s) != cells_.end(); } + + void push_back(const Cell& s) { cells_.template get().push_back(s); } + void push_back(Cell&& s) { cells_.template get().push_back(s); } + + void replace(size_t i, const Cell& s) { cells_.template get().replace(begin() + i, s); } + + // return index of the cell, adding it, if necessary + size_t add(const Cell& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(s); return i; } + size_t add(Cell&& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(std::move(s)); return i; } + + template + void emplace_back(Args&&... args) { cells_.template get().emplace_back(std::forward(args)...); } + + template> + void sort(const Cmp& cmp = Cmp()) { cells_.template get().sort(cmp); } + + void rearrange(const std::vector& indices); + + OrderConstIterator begin() const { return cells_.template get().begin(); } + OrderConstIterator end() const { return cells_.template get().end(); } + OrderIterator begin() { return cells_.template get().begin(); } + OrderIterator end() { return cells_.template get().end(); } + size_t size() const { return cells_.size(); } + void clear() { return Container().swap(cells_); } + + Cell& back() { return const_cast(cells_.template get().back()); } + const Cell& back() const { return cells_.template get().back(); } + + private: + Container cells_; +}; + +} + +template +size_t +dionysus::Filtration:: +index(const Cell& s) const +{ + auto it = iterator(s); + if (checked_index && it == end()) + { + std::ostringstream oss; + oss << "Trying to access non-existent cell: " << s; + throw std::runtime_error(oss.str()); + } + return it - begin(); +} + +template +void +dionysus::Filtration:: +rearrange(const std::vector& indices) +{ + std::vector> references; references.reserve(indices.size()); + for (size_t i : indices) + references.push_back(std::cref((*this)[i])); + cells_.template get().rearrange(references.begin()); +} + + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h new file mode 100644 index 0000000000..7f7ba83318 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h @@ -0,0 +1,8 @@ +#ifndef DIONYSUS_FORMAT_H +#define DIONYSUS_FORMAT_H + +#define FMT_HEADER_ONLY + +#include "format/format.h" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc new file mode 100644 index 0000000000..a01e272fd7 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc @@ -0,0 +1,1156 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2014, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "format.h" + +#include + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# ifdef __MINGW32__ +# include +# endif +# include +#endif + +using fmt::internal::Arg; + +// Check if exceptions are disabled. +#if __GNUC__ && !__EXCEPTIONS +# define FMT_EXCEPTIONS 0 +#endif +#if _MSC_VER && !_HAS_EXCEPTIONS +# define FMT_EXCEPTIONS 0 +#endif +#ifndef FMT_EXCEPTIONS +# define FMT_EXCEPTIONS 1 +#endif + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifndef FMT_THROW +# if FMT_EXCEPTIONS +# define FMT_THROW(x) throw x +# define FMT_RETURN_AFTER_THROW(x) +# else +# define FMT_THROW(x) assert(false) +# define FMT_RETURN_AFTER_THROW(x) return x +# endif +#endif + +#ifdef FMT_HEADER_ONLY +# define FMT_FUNC inline +#else +# define FMT_FUNC +#endif + +#if _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4702) // unreachable code +#endif + +namespace { + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +// Checks if a value fits in int - used to avoid warnings about comparing +// signed and unsigned integers. +template +struct IntChecker { + template + static bool fits_in_int(T value) { + unsigned max = INT_MAX; + return value <= max; + } +}; + +template <> +struct IntChecker { + template + static bool fits_in_int(T value) { + return value >= INT_MIN && value <= INT_MAX; + } +}; + +const char RESET_COLOR[] = "\x1b[0m"; + +typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef); + +// Portable thread-safe version of strerror. +// Sets buffer to point to a string describing the error code. +// This can be either a pointer to a string stored in buffer, +// or a pointer to some static immutable string. +// Returns one of the following values: +// 0 - success +// ERANGE - buffer is not large enough to store the error message +// other - failure +// Buffer should be at least of size 1. +int safe_strerror( + int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { + assert(buffer != 0 && buffer_size != 0); + int result = 0; +#if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) || __ANDROID__ + // XSI-compliant version of strerror_r. + result = strerror_r(error_code, buffer, buffer_size); + if (result != 0) + result = errno; +#elif _GNU_SOURCE + // GNU-specific version of strerror_r. + char *message = strerror_r(error_code, buffer, buffer_size); + // If the buffer is full then the message is probably truncated. + if (message == buffer && strlen(buffer) == buffer_size - 1) + result = ERANGE; + buffer = message; +#elif __MINGW32__ + errno = 0; + (void)buffer_size; + buffer = strerror(error_code); + result = errno; +#elif _WIN32 + result = strerror_s(buffer, buffer_size, error_code); + // If the buffer is full then the message is probably truncated. + if (result == 0 && std::strlen(buffer) == buffer_size - 1) + result = ERANGE; +#else + result = strerror_r(error_code, buffer, buffer_size); + if (result == -1) + result = errno; // glibc versions before 2.13 return result in errno. +#endif + return result; +} + +void format_error_code(fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT { + // Report error code making sure that the output fits into + // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential + // bad_alloc. + out.clear(); + static const char SEP[] = ": "; + static const char ERR[] = "error "; + fmt::internal::IntTraits::MainType ec_value = error_code; + // Subtract 2 to account for terminating null characters in SEP and ERR. + std::size_t error_code_size = + sizeof(SEP) + sizeof(ERR) + fmt::internal::count_digits(ec_value) - 2; + if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size) + out << message << SEP; + out << ERR << error_code; + assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE); +} + +void report_error(FormatFunc func, + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + fmt::MemoryWriter full_message; + func(full_message, error_code, message); + // Use Writer::data instead of Writer::c_str to avoid potential memory + // allocation. + std::fwrite(full_message.data(), full_message.size(), 1, stderr); + std::fputc('\n', stderr); +} + +// IsZeroInt::visit(arg) returns true iff arg is a zero integer. +class IsZeroInt : public fmt::internal::ArgVisitor { + public: + template + bool visit_any_int(T value) { return value == 0; } +}; + +// Parses an unsigned integer advancing s to the end of the parsed input. +// This function assumes that the first character of s is a digit. +template +int parse_nonnegative_int(const Char *&s) { + assert('0' <= *s && *s <= '9'); + unsigned value = 0; + do { + unsigned new_value = value * 10 + (*s++ - '0'); + // Check if value wrapped around. + if (new_value < value) { + value = UINT_MAX; + break; + } + value = new_value; + } while ('0' <= *s && *s <= '9'); + if (value > INT_MAX) + FMT_THROW(fmt::FormatError("number is too big")); + return value; +} + +inline void require_numeric_argument(const Arg &arg, char spec) { + if (arg.type > Arg::LAST_NUMERIC_TYPE) { + std::string message = + fmt::format("format specifier '{}' requires numeric argument", spec); + FMT_THROW(fmt::FormatError(message)); + } +} + +template +void check_sign(const Char *&s, const Arg &arg) { + char sign = static_cast(*s); + require_numeric_argument(arg, sign); + if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { + FMT_THROW(fmt::FormatError(fmt::format( + "format specifier '{}' requires signed argument", sign))); + } + ++s; +} + +// Checks if an argument is a valid printf width specifier and sets +// left alignment if it is negative. +class WidthHandler : public fmt::internal::ArgVisitor { + private: + fmt::FormatSpec &spec_; + + FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); + + public: + explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {} + + unsigned visit_unhandled_arg() { + FMT_THROW(fmt::FormatError("width is not integer")); + FMT_RETURN_AFTER_THROW(0); + } + + template + unsigned visit_any_int(T value) { + typedef typename fmt::internal::IntTraits::MainType UnsignedType; + UnsignedType width = value; + if (fmt::internal::is_negative(value)) { + spec_.align_ = fmt::ALIGN_LEFT; + width = 0 - width; + } + if (width > INT_MAX) + FMT_THROW(fmt::FormatError("number is too big")); + return static_cast(width); + } +}; + +class PrecisionHandler : + public fmt::internal::ArgVisitor { + public: + unsigned visit_unhandled_arg() { + FMT_THROW(fmt::FormatError("precision is not integer")); + FMT_RETURN_AFTER_THROW(0); + } + + template + int visit_any_int(T value) { + if (!IntChecker::is_signed>::fits_in_int(value)) + FMT_THROW(fmt::FormatError("number is too big")); + return static_cast(value); + } +}; + +// Converts an integer argument to an integral type T for printf. +template +class ArgConverter : public fmt::internal::ArgVisitor, void> { + private: + fmt::internal::Arg &arg_; + wchar_t type_; + + FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); + + public: + ArgConverter(fmt::internal::Arg &arg, wchar_t type) + : arg_(arg), type_(type) {} + + template + void visit_any_int(U value) { + bool is_signed = type_ == 'd' || type_ == 'i'; + using fmt::internal::Arg; + if (sizeof(T) <= sizeof(int)) { + // Extra casts are used to silence warnings. + if (is_signed) { + arg_.type = Arg::INT; + arg_.int_value = static_cast(static_cast(value)); + } else { + arg_.type = Arg::UINT; + arg_.uint_value = static_cast( + static_cast::Type>(value)); + } + } else { + if (is_signed) { + arg_.type = Arg::LONG_LONG; + arg_.long_long_value = + static_cast::Type>(value); + } else { + arg_.type = Arg::ULONG_LONG; + arg_.ulong_long_value = + static_cast::Type>(value); + } + } + } +}; + +// Converts an integer argument to char for printf. +class CharConverter : public fmt::internal::ArgVisitor { + private: + fmt::internal::Arg &arg_; + + FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); + + public: + explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {} + + template + void visit_any_int(T value) { + arg_.type = Arg::CHAR; + arg_.int_value = static_cast(value); + } +}; + +// This function template is used to prevent compile errors when handling +// incompatible string arguments, e.g. handling a wide string in a narrow +// string formatter. +template +Arg::StringValue ignore_incompatible_str(Arg::StringValue); + +template <> +inline Arg::StringValue ignore_incompatible_str( + Arg::StringValue) { return Arg::StringValue(); } + +template <> +inline Arg::StringValue ignore_incompatible_str( + Arg::StringValue s) { return s; } +} // namespace + +FMT_FUNC void fmt::SystemError::init( + int err_code, StringRef format_str, ArgList args) { + error_code_ = err_code; + MemoryWriter w; + internal::format_system_error(w, err_code, format(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(w.str()); +} + +template +int fmt::internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, value) : + FMT_SNPRINTF(buffer, size, format, precision, value); + } + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, width, value) : + FMT_SNPRINTF(buffer, size, format, width, precision, value); +} + +template +int fmt::internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + swprintf(buffer, size, format, value) : + swprintf(buffer, size, format, precision, value); + } + return precision < 0 ? + swprintf(buffer, size, format, width, value) : + swprintf(buffer, size, format, width, precision, value); +} + +template +const char fmt::internal::BasicData::DIGITS[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, \ + factor * 100, \ + factor * 1000, \ + factor * 10000, \ + factor * 100000, \ + factor * 1000000, \ + factor * 10000000, \ + factor * 100000000, \ + factor * 1000000000 + +template +const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = { + 0, FMT_POWERS_OF_10(1) +}; + +template +const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = { + 0, + FMT_POWERS_OF_10(1), + FMT_POWERS_OF_10(fmt::ULongLong(1000000000)), + // Multiply several constants instead of using a single long long constant + // to avoid warnings about C++98 not supporting long long. + fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10 +}; + +FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) { + if (std::isprint(static_cast(code))) { + FMT_THROW(fmt::FormatError( + fmt::format("unknown format code '{}' for {}", code, type))); + } + FMT_THROW(fmt::FormatError( + fmt::format("unknown format code '\\x{:02x}' for {}", + static_cast(code), type))); +} + +#ifdef _WIN32 + +FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) { + int length = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0); + static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; + if (length == 0) + FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); + buffer_.resize(length); + length = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length); + if (length == 0) + FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); +} + +FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) { + if (int error_code = convert(s)) { + FMT_THROW(WindowsError(error_code, + "cannot convert string from UTF-16 to UTF-8")); + } +} + +FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { + int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0); + if (length == 0) + return GetLastError(); + buffer_.resize(length); + length = WideCharToMultiByte( + CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0); + if (length == 0) + return GetLastError(); + return 0; +} + +FMT_FUNC void fmt::WindowsError::init( + int err_code, StringRef format_str, ArgList args) { + error_code_ = err_code; + MemoryWriter w; + internal::format_windows_error(w, err_code, format(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(w.str()); +} + +#endif + +FMT_FUNC void fmt::internal::format_system_error( + fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT { + FMT_TRY { + MemoryBuffer buffer; + buffer.resize(INLINE_BUFFER_SIZE); + for (;;) { + char *system_message = &buffer[0]; + int result = safe_strerror(error_code, system_message, buffer.size()); + if (result == 0) { + out << message << ": " << system_message; + return; + } + if (result != ERANGE) + break; // Can't get error message, report error code instead. + buffer.resize(buffer.size() * 2); + } + } FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} + +#ifdef _WIN32 +FMT_FUNC void fmt::internal::format_windows_error( + fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT { + class String { + private: + LPWSTR str_; + + public: + String() : str_() {} + ~String() { LocalFree(str_); } + LPWSTR *ptr() { return &str_; } + LPCWSTR c_str() const { return str_; } + }; + FMT_TRY { + String system_message; + if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, + error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(system_message.ptr()), 0, 0)) { + UTF16ToUTF8 utf8_message; + if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) { + out << message << ": " << utf8_message; + return; + } + } + } FMT_CATCH(...) {} + format_error_code(out, error_code, message); +} +#endif + +// An argument formatter. +template +class fmt::internal::ArgFormatter : + public fmt::internal::ArgVisitor, void> { + private: + fmt::BasicFormatter &formatter_; + fmt::BasicWriter &writer_; + fmt::FormatSpec &spec_; + const Char *format_; + + FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatter); + + public: + ArgFormatter( + fmt::BasicFormatter &f,fmt::FormatSpec &s, const Char *fmt) + : formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {} + + template + void visit_any_int(T value) { writer_.write_int(value, spec_); } + + template + void visit_any_double(T value) { writer_.write_double(value, spec_); } + + void visit_char(int value) { + if (spec_.type_ && spec_.type_ != 'c') { + spec_.flags_ |= CHAR_FLAG; + writer_.write_int(value, spec_); + return; + } + if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) + FMT_THROW(FormatError("invalid format specifier for char")); + typedef typename fmt::BasicWriter::CharPtr CharPtr; + Char fill = static_cast(spec_.fill()); + if (spec_.precision_ == 0) { + std::fill_n(writer_.grow_buffer(spec_.width_), spec_.width_, fill); + return; + } + CharPtr out = CharPtr(); + if (spec_.width_ > 1) { + out = writer_.grow_buffer(spec_.width_); + if (spec_.align_ == fmt::ALIGN_RIGHT) { + std::fill_n(out, spec_.width_ - 1, fill); + out += spec_.width_ - 1; + } else if (spec_.align_ == fmt::ALIGN_CENTER) { + out = writer_.fill_padding(out, spec_.width_, 1, fill); + } else { + std::fill_n(out + 1, spec_.width_ - 1, fill); + } + } else { + out = writer_.grow_buffer(1); + } + *out = static_cast(value); + } + + void visit_string(Arg::StringValue value) { + writer_.write_str(value, spec_); + } + void visit_wstring(Arg::StringValue value) { + writer_.write_str(ignore_incompatible_str(value), spec_); + } + + void visit_pointer(const void *value) { + if (spec_.type_ && spec_.type_ != 'p') + fmt::internal::report_unknown_type(spec_.type_, "pointer"); + spec_.flags_ = fmt::HASH_FLAG; + spec_.type_ = 'x'; + writer_.write_int(reinterpret_cast(value), spec_); + } + + void visit_custom(Arg::CustomValue c) { + c.format(&formatter_, c.value, &format_); + } +}; + +template +template +void fmt::BasicWriter::write_str( + const Arg::StringValue &s, const FormatSpec &spec) { + // Check if StrChar is convertible to Char. + internal::CharTraits::convert(StrChar()); + if (spec.type_ && spec.type_ != 's') + internal::report_unknown_type(spec.type_, "string"); + const StrChar *str_value = s.value; + std::size_t str_size = s.size; + if (str_size == 0) { + if (!str_value) + FMT_THROW(FormatError("string pointer is null")); + if (*str_value) + str_size = std::char_traits::length(str_value); + } + std::size_t precision = spec.precision_; + if (spec.precision_ >= 0 && precision < str_size) + str_size = spec.precision_; + write_str(str_value, str_size, spec); +} + +template +inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) { + const char *error = 0; + Arg arg = *s < '0' || *s > '9' ? + next_arg(error) : get_arg(parse_nonnegative_int(s), error); + if (error) { + FMT_THROW(FormatError( + *s != '}' && *s != ':' ? "invalid format string" : error)); + } + return arg; +} + +FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( + unsigned arg_index, const char *&error) { + Arg arg = args_[arg_index]; + if (arg.type == Arg::NONE) + error = "argument index out of range"; + return arg; +} + +inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) { + if (next_arg_index_ >= 0) + return do_get_arg(next_arg_index_++, error); + error = "cannot switch from manual to automatic argument indexing"; + return Arg(); +} + +inline Arg fmt::internal::FormatterBase::get_arg( + unsigned arg_index, const char *&error) { + if (next_arg_index_ <= 0) { + next_arg_index_ = -1; + return do_get_arg(arg_index, error); + } + error = "cannot switch from automatic to manual argument indexing"; + return Arg(); +} + +template +void fmt::internal::PrintfFormatter::parse_flags( + FormatSpec &spec, const Char *&s) { + for (;;) { + switch (*s++) { + case '-': + spec.align_ = ALIGN_LEFT; + break; + case '+': + spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + break; + case '0': + spec.fill_ = '0'; + break; + case ' ': + spec.flags_ |= SIGN_FLAG; + break; + case '#': + spec.flags_ |= HASH_FLAG; + break; + default: + --s; + return; + } + } +} + +template +Arg fmt::internal::PrintfFormatter::get_arg( + const Char *s, unsigned arg_index) { + const char *error = 0; + Arg arg = arg_index == UINT_MAX ? + next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); + if (error) + FMT_THROW(FormatError(!*s ? "invalid format string" : error)); + return arg; +} + +template +unsigned fmt::internal::PrintfFormatter::parse_header( + const Char *&s, FormatSpec &spec) { + unsigned arg_index = UINT_MAX; + Char c = *s; + if (c >= '0' && c <= '9') { + // Parse an argument index (if followed by '$') or a width possibly + // preceded with '0' flag(s). + unsigned value = parse_nonnegative_int(s); + if (*s == '$') { // value is an argument index + ++s; + arg_index = value; + } else { + if (c == '0') + spec.fill_ = '0'; + if (value != 0) { + // Nonzero value means that we parsed width and don't need to + // parse it or flags again, so return now. + spec.width_ = value; + return arg_index; + } + } + } + parse_flags(spec, s); + // Parse width. + if (*s >= '0' && *s <= '9') { + spec.width_ = parse_nonnegative_int(s); + } else if (*s == '*') { + ++s; + spec.width_ = WidthHandler(spec).visit(get_arg(s)); + } + return arg_index; +} + +template +void fmt::internal::PrintfFormatter::format( + BasicWriter &writer, BasicStringRef format_str, + const ArgList &args) { + const Char *start = format_str.c_str(); + set_args(args); + const Char *s = start; + while (*s) { + Char c = *s++; + if (c != '%') continue; + if (*s == c) { + write(writer, start, s); + start = ++s; + continue; + } + write(writer, start, s - 1); + + FormatSpec spec; + spec.align_ = ALIGN_RIGHT; + + // Parse argument index, flags and width. + unsigned arg_index = parse_header(s, spec); + + // Parse precision. + if (*s == '.') { + ++s; + if ('0' <= *s && *s <= '9') { + spec.precision_ = parse_nonnegative_int(s); + } else if (*s == '*') { + ++s; + spec.precision_ = PrecisionHandler().visit(get_arg(s)); + } + } + + Arg arg = get_arg(s, arg_index); + if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) + spec.flags_ &= ~HASH_FLAG; + if (spec.fill_ == '0') { + if (arg.type <= Arg::LAST_NUMERIC_TYPE) + spec.align_ = ALIGN_NUMERIC; + else + spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. + } + + // Parse length and convert the argument to the required type. + switch (*s++) { + case 'h': + if (*s == 'h') + ArgConverter(arg, *++s).visit(arg); + else + ArgConverter(arg, *s).visit(arg); + break; + case 'l': + if (*s == 'l') + ArgConverter(arg, *++s).visit(arg); + else + ArgConverter(arg, *s).visit(arg); + break; + case 'j': + ArgConverter(arg, *s).visit(arg); + break; + case 'z': + ArgConverter(arg, *s).visit(arg); + break; + case 't': + ArgConverter(arg, *s).visit(arg); + break; + case 'L': + // printf produces garbage when 'L' is omitted for long double, no + // need to do the same. + break; + default: + --s; + ArgConverter(arg, *s).visit(arg); + } + + // Parse type. + if (!*s) + FMT_THROW(FormatError("invalid format string")); + spec.type_ = static_cast(*s++); + if (arg.type <= Arg::LAST_INTEGER_TYPE) { + // Normalize type. + switch (spec.type_) { + case 'i': case 'u': + spec.type_ = 'd'; + break; + case 'c': + // TODO: handle wchar_t + CharConverter(arg).visit(arg); + break; + } + } + + start = s; + + // Format argument. + switch (arg.type) { + case Arg::INT: + writer.write_int(arg.int_value, spec); + break; + case Arg::UINT: + writer.write_int(arg.uint_value, spec); + break; + case Arg::LONG_LONG: + writer.write_int(arg.long_long_value, spec); + break; + case Arg::ULONG_LONG: + writer.write_int(arg.ulong_long_value, spec); + break; + case Arg::CHAR: { + if (spec.type_ && spec.type_ != 'c') + writer.write_int(arg.int_value, spec); + typedef typename BasicWriter::CharPtr CharPtr; + CharPtr out = CharPtr(); + if (spec.width_ > 1) { + Char fill = ' '; + out = writer.grow_buffer(spec.width_); + if (spec.align_ != ALIGN_LEFT) { + std::fill_n(out, spec.width_ - 1, fill); + out += spec.width_ - 1; + } else { + std::fill_n(out + 1, spec.width_ - 1, fill); + } + } else { + out = writer.grow_buffer(1); + } + *out = static_cast(arg.int_value); + break; + } + case Arg::DOUBLE: + writer.write_double(arg.double_value, spec); + break; + case Arg::LONG_DOUBLE: + writer.write_double(arg.long_double_value, spec); + break; + case Arg::CSTRING: + arg.string.size = 0; + writer.write_str(arg.string, spec); + break; + case Arg::STRING: + writer.write_str(arg.string, spec); + break; + case Arg::WSTRING: + writer.write_str(ignore_incompatible_str(arg.wstring), spec); + break; + case Arg::POINTER: + if (spec.type_ && spec.type_ != 'p') + internal::report_unknown_type(spec.type_, "pointer"); + spec.flags_= HASH_FLAG; + spec.type_ = 'x'; + writer.write_int(reinterpret_cast(arg.pointer), spec); + break; + case Arg::CUSTOM: { + if (spec.type_) + internal::report_unknown_type(spec.type_, "object"); + const void *str_format = "s"; + arg.custom.format(&writer, arg.custom.value, &str_format); + break; + } + default: + assert(false); + break; + } + } + write(writer, start, s); +} + +template +const Char *fmt::BasicFormatter::format( + const Char *&format_str, const Arg &arg) { + const Char *s = format_str; + FormatSpec spec; + if (*s == ':') { + if (arg.type == Arg::CUSTOM) { + arg.custom.format(this, arg.custom.value, &s); + return s; + } + ++s; + // Parse fill and alignment. + if (Char c = *s) { + const Char *p = s + 1; + spec.align_ = ALIGN_DEFAULT; + do { + switch (*p) { + case '<': + spec.align_ = ALIGN_LEFT; + break; + case '>': + spec.align_ = ALIGN_RIGHT; + break; + case '=': + spec.align_ = ALIGN_NUMERIC; + break; + case '^': + spec.align_ = ALIGN_CENTER; + break; + } + if (spec.align_ != ALIGN_DEFAULT) { + if (p != s) { + if (c == '}') break; + if (c == '{') + FMT_THROW(FormatError("invalid fill character '{'")); + s += 2; + spec.fill_ = c; + } else ++s; + if (spec.align_ == ALIGN_NUMERIC) + require_numeric_argument(arg, '='); + break; + } + } while (--p >= s); + } + + // Parse sign. + switch (*s) { + case '+': + check_sign(s, arg); + spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + break; + case '-': + check_sign(s, arg); + spec.flags_ |= MINUS_FLAG; + break; + case ' ': + check_sign(s, arg); + spec.flags_ |= SIGN_FLAG; + break; + } + + if (*s == '#') { + require_numeric_argument(arg, '#'); + spec.flags_ |= HASH_FLAG; + ++s; + } + + // Parse width and zero flag. + if ('0' <= *s && *s <= '9') { + if (*s == '0') { + require_numeric_argument(arg, '0'); + spec.align_ = ALIGN_NUMERIC; + spec.fill_ = '0'; + } + // Zero may be parsed again as a part of the width, but it is simpler + // and more efficient than checking if the next char is a digit. + spec.width_ = parse_nonnegative_int(s); + } + + // Parse precision. + if (*s == '.') { + ++s; + spec.precision_ = 0; + if ('0' <= *s && *s <= '9') { + spec.precision_ = parse_nonnegative_int(s); + } else if (*s == '{') { + ++s; + const Arg &precision_arg = parse_arg_index(s); + if (*s++ != '}') + FMT_THROW(FormatError("invalid format string")); + ULongLong value = 0; + switch (precision_arg.type) { + case Arg::INT: + if (precision_arg.int_value < 0) + FMT_THROW(FormatError("negative precision")); + value = precision_arg.int_value; + break; + case Arg::UINT: + value = precision_arg.uint_value; + break; + case Arg::LONG_LONG: + if (precision_arg.long_long_value < 0) + FMT_THROW(FormatError("negative precision")); + value = precision_arg.long_long_value; + break; + case Arg::ULONG_LONG: + value = precision_arg.ulong_long_value; + break; + default: + FMT_THROW(FormatError("precision is not integer")); + } + if (value > INT_MAX) + FMT_THROW(FormatError("number is too big")); + spec.precision_ = static_cast(value); + } else { + FMT_THROW(FormatError("missing precision specifier")); + } + if (arg.type < Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) { + FMT_THROW(FormatError( + fmt::format("precision not allowed in {} format specifier", + arg.type == Arg::POINTER ? "pointer" : "integer"))); + } + } + + // Parse type. + if (*s != '}' && *s) + spec.type_ = static_cast(*s++); + } + + if (*s++ != '}') + FMT_THROW(FormatError("missing '}' in format string")); + start_ = s; + + // Format argument. + internal::ArgFormatter(*this, spec, s - 1).visit(arg); + return s; +} + +template +void fmt::BasicFormatter::format( + BasicStringRef format_str, const ArgList &args) { + const Char *s = start_ = format_str.c_str(); + set_args(args); + while (*s) { + Char c = *s++; + if (c != '{' && c != '}') continue; + if (*s == c) { + write(writer_, start_, s); + start_ = ++s; + continue; + } + if (c == '}') + FMT_THROW(FormatError("unmatched '}' in format string")); + write(writer_, start_, s - 1); + Arg arg = parse_arg_index(s); + s = format(s, arg); + } + write(writer_, start_, s); +} + +FMT_FUNC void fmt::report_system_error( + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + report_error(internal::format_system_error, error_code, message); +} + +#ifdef _WIN32 +FMT_FUNC void fmt::report_windows_error( + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + report_error(internal::format_windows_error, error_code, message); +} +#endif + +FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + std::fwrite(w.data(), 1, w.size(), f); +} + +FMT_FUNC void fmt::print(StringRef format_str, ArgList args) { + print(stdout, format_str, args); +} + +FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + os.write(w.data(), w.size()); +} + +FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) { + char escape[] = "\x1b[30m"; + escape[3] = '0' + static_cast(c); + std::fputs(escape, stdout); + print(format, args); + std::fputs(RESET_COLOR, stdout); +} + +FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) { + MemoryWriter w; + printf(w, format, args); + std::size_t size = w.size(); + return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); +} + +// Explicit instantiations for char. + +template const char *fmt::BasicFormatter::format( + const char *&format_str, const fmt::internal::Arg &arg); + +template void fmt::BasicFormatter::format( + BasicStringRef format, const ArgList &args); + +template void fmt::internal::PrintfFormatter::format( + BasicWriter &writer, BasicStringRef format, const ArgList &args); + +template int fmt::internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, double value); + +template int fmt::internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, long double value); + +// Explicit instantiations for wchar_t. + +template const wchar_t *fmt::BasicFormatter::format( + const wchar_t *&format_str, const fmt::internal::Arg &arg); + +template void fmt::BasicFormatter::format( + BasicStringRef format, const ArgList &args); + +template void fmt::internal::PrintfFormatter::format( + BasicWriter &writer, BasicStringRef format, + const ArgList &args); + +template int fmt::internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, double value); + +template int fmt::internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, long double value); + +#if _MSC_VER +# pragma warning(pop) +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h new file mode 100644 index 0000000000..03ed685383 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h @@ -0,0 +1,2546 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2014, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FMT_FORMAT_H_ +#define FMT_FORMAT_H_ + +#include + +#include +#include +#include // for std::ptrdiff_t +#include +#include +#include +#include +#include +#include + +#if _SECURE_SCL +# include +#endif + +#ifdef _MSC_VER +# include // _BitScanReverse, _BitScanReverse64 + +namespace fmt { +namespace internal { +# pragma intrinsic(_BitScanReverse) +inline uint32_t clz(uint32_t x) { + unsigned long r = 0; + _BitScanReverse(&r, x); + return 31 - r; +} +# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) +inline uint32_t clzll(uint64_t x) { + unsigned long r = 0; +# ifdef _WIN64 +# pragma intrinsic(_BitScanReverse64) + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + return 63 - r; +} +# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) +} +} +#endif + +#ifdef __GNUC__ +# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) +# define FMT_GCC_EXTENSION __extension__ +# if FMT_GCC_VERSION >= 406 +# pragma GCC diagnostic push +// Disable the warning about "long long" which is sometimes reported even +// when using __extension__. +# pragma GCC diagnostic ignored "-Wlong-long" +// Disable the warning about declaration shadowing because it affects too +// many valid cases. +# pragma GCC diagnostic ignored "-Wshadow" +# endif +# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ +# define FMT_HAS_GXX_CXX11 1 +# endif +#else +# define FMT_GCC_EXTENSION +#endif + +#ifdef __clang__ +# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +#ifdef __GNUC_LIBSTD__ +# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) +#endif + +#ifdef __has_feature +# define FMT_HAS_FEATURE(x) __has_feature(x) +#else +# define FMT_HAS_FEATURE(x) 0 +#endif + +#ifdef __has_builtin +# define FMT_HAS_BUILTIN(x) __has_builtin(x) +#else +# define FMT_HAS_BUILTIN(x) 0 +#endif + +#ifdef __has_cpp_attribute +# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) +#else +# define FMT_HAS_CPP_ATTRIBUTE(x) 0 +#endif + +#ifndef FMT_USE_VARIADIC_TEMPLATES +// Variadic templates are available in GCC since version 4.4 +// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ +// since version 2013. +# define FMT_USE_VARIADIC_TEMPLATES \ + (FMT_HAS_FEATURE(cxx_variadic_templates) || \ + (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800) +#endif + +#ifndef FMT_USE_RVALUE_REFERENCES +// Don't use rvalue references when compiling with clang and an old libstdc++ +// as the latter doesn't provide std::move. +# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 +# define FMT_USE_RVALUE_REFERENCES 0 +# else +# define FMT_USE_RVALUE_REFERENCES \ + (FMT_HAS_FEATURE(cxx_rvalue_references) || \ + (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600) +# endif +#endif + +#if FMT_USE_RVALUE_REFERENCES +# include // for std::move +#endif + +// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature). +#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) +# define FMT_NOEXCEPT noexcept +#else +# define FMT_NOEXCEPT throw() +#endif + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ + (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800 +# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&) = delete; \ + TypeName& operator=(const TypeName&) = delete +#else +# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + TypeName& operator=(const TypeName&) +#endif + +namespace fmt { + +// Fix the warning about long long on older versions of GCC +// that don't support the diagnostic pragma. +FMT_GCC_EXTENSION typedef long long LongLong; +FMT_GCC_EXTENSION typedef unsigned long long ULongLong; + +#if FMT_USE_RVALUE_REFERENCES +using std::move; +#endif + +template +class BasicWriter; + +typedef BasicWriter Writer; +typedef BasicWriter WWriter; + +template +class BasicFormatter; + +template +void format(BasicFormatter &f, const Char *&format_str, const T &value); + +/** + \rst + A string reference. It can be constructed from a C string or + ``std::string``. + + You can use one of the following typedefs for common character types: + + +------------+-------------------------+ + | Type | Definition | + +============+=========================+ + | StringRef | BasicStringRef | + +------------+-------------------------+ + | WStringRef | BasicStringRef | + +------------+-------------------------+ + + This class is most useful as a parameter type to allow passing + different types of strings to a function, for example:: + + template + std::string format(StringRef format_str, const Args & ... args); + + format("{}", 42); + format(std::string("{}"), 42); + \endrst + */ +template +class BasicStringRef { + private: + const Char *data_; + std::size_t size_; + + public: + /** + Constructs a string reference object from a C string and a size. + */ + BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} + + /** + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + */ + BasicStringRef(const Char *s) + : data_(s), size_(std::char_traits::length(s)) {} + + /** + Constructs a string reference from an `std::string` object. + */ + BasicStringRef(const std::basic_string &s) + : data_(s.c_str()), size_(s.size()) {} + + /** + Converts a string reference to an `std::string` object. + */ + operator std::basic_string() const { + return std::basic_string(data_, size()); + } + + /** + Returns the pointer to a C string. + */ + const Char *c_str() const { return data_; } + + /** + Returns the string size. + */ + std::size_t size() const { return size_; } + + friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { + return lhs.data_ == rhs.data_; + } + friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { + return lhs.data_ != rhs.data_; + } +}; + +typedef BasicStringRef StringRef; +typedef BasicStringRef WStringRef; + +/** + A formatting error such as invalid format string. +*/ +class FormatError : public std::runtime_error { +public: + explicit FormatError(StringRef message) + : std::runtime_error(message.c_str()) {} +}; + +namespace internal { + +// The number of characters to store in the MemoryBuffer object itself +// to avoid dynamic memory allocation. +enum { INLINE_BUFFER_SIZE = 500 }; + +#if _SECURE_SCL +// Use checked iterator to avoid warnings on MSVC. +template +inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { + return stdext::checked_array_iterator(ptr, size); +} +#else +template +inline T *make_ptr(T *ptr, std::size_t) { return ptr; } +#endif + +// A buffer for POD types. It supports a subset of std::vector's operations. +template +class Buffer { + private: + FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); + + protected: + T *ptr_; + std::size_t size_; + std::size_t capacity_; + + Buffer(T *ptr = 0, std::size_t capacity = 0) + : ptr_(ptr), size_(0), capacity_(capacity) {} + + virtual void grow(std::size_t size) = 0; + + public: + virtual ~Buffer() {} + + // Returns the size of this buffer. + std::size_t size() const { return size_; } + + // Returns the capacity of this buffer. + std::size_t capacity() const { return capacity_; } + + // Resizes the buffer. If T is a POD type new elements are not initialized. + void resize(std::size_t new_size) { + if (new_size > capacity_) + grow(new_size); + size_ = new_size; + } + + // Reserves space to store at least capacity elements. + void reserve(std::size_t capacity) { + if (capacity > capacity_) + grow(capacity); + } + + void clear() FMT_NOEXCEPT { size_ = 0; } + + void push_back(const T &value) { + if (size_ == capacity_) + grow(size_ + 1); + ptr_[size_++] = value; + } + + // Appends data to the end of the buffer. + void append(const T *begin, const T *end); + + T &operator[](std::size_t index) { return ptr_[index]; } + const T &operator[](std::size_t index) const { return ptr_[index]; } +}; + +template +void Buffer::append(const T *begin, const T *end) { + std::ptrdiff_t num_elements = end - begin; + if (size_ + num_elements > capacity_) + grow(size_ + num_elements); + std::copy(begin, end, make_ptr(ptr_, capacity_) + size_); + size_ += num_elements; +} + +// A memory buffer for POD types with the first SIZE elements stored in +// the object itself. +template > +class MemoryBuffer : private Allocator, public Buffer { + private: + T data_[SIZE]; + + // Free memory allocated by the buffer. + void free() { + if (this->ptr_ != data_) this->deallocate(this->ptr_, this->capacity_); + } + + protected: + void grow(std::size_t size); + + public: + explicit MemoryBuffer(const Allocator &alloc = Allocator()) + : Allocator(alloc), Buffer(data_, SIZE) {} + ~MemoryBuffer() { free(); } + +#if FMT_USE_RVALUE_REFERENCES + private: + // Move data from other to this buffer. + void move(MemoryBuffer &other) { + Allocator &this_alloc = *this, &other_alloc = other; + this_alloc = std::move(other_alloc); + this->size_ = other.size_; + this->capacity_ = other.capacity_; + if (other.ptr_ == other.data_) { + this->ptr_ = data_; + std::copy(other.data_, + other.data_ + this->size_, make_ptr(data_, this->capacity_)); + } else { + this->ptr_ = other.ptr_; + // Set pointer to the inline array so that delete is not called + // when freeing. + other.ptr_ = other.data_; + } + } + + public: + MemoryBuffer(MemoryBuffer &&other) { + move(other); + } + + MemoryBuffer &operator=(MemoryBuffer &&other) { + assert(this != &other); + free(); + move(other); + return *this; + } +#endif + + // Returns a copy of the allocator associated with this buffer. + Allocator get_allocator() const { return *this; } +}; + +template +void MemoryBuffer::grow(std::size_t size) { + std::size_t new_capacity = + (std::max)(size, this->capacity_ + this->capacity_ / 2); + T *new_ptr = this->allocate(new_capacity); + // The following code doesn't throw, so the raw pointer above doesn't leak. + std::copy(this->ptr_, + this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); + std::size_t old_capacity = this->capacity_; + T *old_ptr = this->ptr_; + this->capacity_ = new_capacity; + this->ptr_ = new_ptr; + // deallocate may throw (at least in principle), but it doesn't matter since + // the buffer already uses the new storage and will deallocate it in case + // of exception. + if (old_ptr != data_) + this->deallocate(old_ptr, old_capacity); +} + +#ifndef _MSC_VER +// Portable version of signbit. +inline int getsign(double x) { + // When compiled in C++11 mode signbit is no longer a macro but a function + // defined in namespace std and the macro is undefined. +# ifdef signbit + return signbit(x); +# else + return std::signbit(x); +# endif +} + +// Portable version of isinf. +# ifdef isinf +inline int isinfinity(double x) { return isinf(x); } +inline int isinfinity(long double x) { return isinf(x); } +# else +inline int isinfinity(double x) { return std::isinf(x); } +inline int isinfinity(long double x) { return std::isinf(x); } +# endif +#else +inline int getsign(double value) { + if (value < 0) return 1; + if (value == value) return 0; + int dec = 0, sign = 0; + char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. + _ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign); + return sign; +} +inline int isinfinity(double x) { return !_finite(x); } +inline int isinfinity(long double x) { return !_finite(static_cast(x)); } +#endif + +template +class BasicCharTraits { + public: +#if _SECURE_SCL + typedef stdext::checked_array_iterator CharPtr; +#else + typedef Char *CharPtr; +#endif +}; + +template +class CharTraits; + +template <> +class CharTraits : public BasicCharTraits { + private: + // Conversion from wchar_t to char is not allowed. + static char convert(wchar_t); + +public: + typedef const wchar_t *UnsupportedStrType; + + static char convert(char value) { return value; } + + // Formats a floating-point number. + template + static int format_float(char *buffer, std::size_t size, + const char *format, unsigned width, int precision, T value); +}; + +template <> +class CharTraits : public BasicCharTraits { + public: + typedef const char *UnsupportedStrType; + + static wchar_t convert(char value) { return value; } + static wchar_t convert(wchar_t value) { return value; } + + template + static int format_float(wchar_t *buffer, std::size_t size, + const wchar_t *format, unsigned width, int precision, T value); +}; + +// Checks if a number is negative - used to avoid warnings. +template +struct SignChecker { + template + static bool is_negative(T value) { return value < 0; } +}; + +template <> +struct SignChecker { + template + static bool is_negative(T) { return false; } +}; + +// Returns true if value is negative, false otherwise. +// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. +template +inline bool is_negative(T value) { + return SignChecker::is_signed>::is_negative(value); +} + +// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. +template +struct TypeSelector { typedef uint32_t Type; }; + +template <> +struct TypeSelector { typedef uint64_t Type; }; + +template +struct IntTraits { + // Smallest of uint32_t and uint64_t that is large enough to represent + // all values of T. + typedef typename + TypeSelector::digits <= 32>::Type MainType; +}; + +// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. +template +struct MakeUnsigned { typedef T Type; }; + +#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ + template <> \ + struct MakeUnsigned { typedef U Type; } + +FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); +FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); +FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); +FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); + +void report_unknown_type(char code, const char *type); + +// Static data is placed in this class template to allow header-only +// configuration. +template +struct BasicData { + static const uint32_t POWERS_OF_10_32[]; + static const uint64_t POWERS_OF_10_64[]; + static const char DIGITS[]; +}; + +typedef BasicData<> Data; + +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#endif + +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#endif + +#ifdef FMT_BUILTIN_CLZLL +// Returns the number of decimal digits in n. Leading zeros are not counted +// except for n == 0 in which case count_digits returns 1. +inline unsigned count_digits(uint64_t n) { + // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. + unsigned t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; + return t - (n < Data::POWERS_OF_10_64[t]) + 1; +} +#else +// Fallback version of count_digits used when __builtin_clz is not available. +inline unsigned count_digits(uint64_t n) { + unsigned count = 1; + for (;;) { + // Integer division is slow so do it for a group of four digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + if (n < 10) return count; + if (n < 100) return count + 1; + if (n < 1000) return count + 2; + if (n < 10000) return count + 3; + n /= 10000u; + count += 4; + } +} +#endif + +#ifdef FMT_BUILTIN_CLZ +// Optional version of count_digits for better performance on 32-bit platforms. +inline unsigned count_digits(uint32_t n) { + uint32_t t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; + return t - (n < Data::POWERS_OF_10_32[t]) + 1; +} +#endif + +// Formats a decimal unsigned integer value writing into buffer. +template +inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { + --num_digits; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + unsigned index = (value % 100) * 2; + value /= 100; + buffer[num_digits] = Data::DIGITS[index + 1]; + buffer[num_digits - 1] = Data::DIGITS[index]; + num_digits -= 2; + } + if (value < 10) { + *buffer = static_cast('0' + value); + return; + } + unsigned index = static_cast(value * 2); + buffer[1] = Data::DIGITS[index + 1]; + buffer[0] = Data::DIGITS[index]; +} + +#ifdef _WIN32 +// A converter from UTF-8 to UTF-16. +// It is only provided for Windows since other systems support UTF-8 natively. +class UTF8ToUTF16 { + private: + MemoryBuffer buffer_; + + public: + explicit UTF8ToUTF16(StringRef s); + operator WStringRef() const { return WStringRef(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const wchar_t *c_str() const { return &buffer_[0]; } + std::wstring str() const { return std::wstring(&buffer_[0], size()); } +}; + +// A converter from UTF-16 to UTF-8. +// It is only provided for Windows since other systems support UTF-8 natively. +class UTF16ToUTF8 { + private: + MemoryBuffer buffer_; + + public: + UTF16ToUTF8() {} + explicit UTF16ToUTF8(WStringRef s); + operator StringRef() const { return StringRef(&buffer_[0], size()); } + size_t size() const { return buffer_.size() - 1; } + const char *c_str() const { return &buffer_[0]; } + std::string str() const { return std::string(&buffer_[0], size()); } + + // Performs conversion returning a system error code instead of + // throwing exception on conversion error. This method may still throw + // in case of memory allocation error. + int convert(WStringRef s); +}; +#endif + +void format_system_error(fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT; + +#ifdef _WIN32 +void format_windows_error(fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT; +#endif + +// Computes max(Arg, 1) at compile time. It is used to avoid errors about +// allocating an array of 0 size. +template +struct NonZero { + enum { VALUE = Arg }; +}; + +template <> +struct NonZero<0> { + enum { VALUE = 1 }; +}; + +// The value of a formatting argument. It is a POD type to allow storage in +// internal::MemoryBuffer. +struct Value { + template + struct StringValue { + const Char *value; + std::size_t size; + }; + + typedef void (*FormatFunc)( + void *formatter, const void *arg, void *format_str_ptr); + + struct CustomValue { + const void *value; + FormatFunc format; + }; + + union { + int int_value; + unsigned uint_value; + LongLong long_long_value; + ULongLong ulong_long_value; + double double_value; + long double long_double_value; + const void *pointer; + StringValue string; + StringValue sstring; + StringValue ustring; + StringValue wstring; + CustomValue custom; + }; +}; + +struct Arg : Value { + enum Type { + NONE, + // Integer types should go first, + INT, UINT, LONG_LONG, ULONG_LONG, CHAR, LAST_INTEGER_TYPE = CHAR, + // followed by floating-point types. + DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, + CSTRING, STRING, WSTRING, POINTER, CUSTOM + }; + Type type; +}; + +// Makes a Value object from any type. +template +class MakeValue : public Value { + private: + // The following two methods are private to disallow formatting of + // arbitrary pointers. If you want to output a pointer cast it to + // "void *" or "const void *". In particular, this forbids formatting + // of "[const] volatile char *" which is printed as bool by iostreams. + // Do not implement! + template + MakeValue(const T *value); + template + MakeValue(T *value); + + void set_string(StringRef str) { + string.value = str.c_str(); + string.size = str.size(); + } + + void set_string(WStringRef str) { + CharTraits::convert(wchar_t()); + wstring.value = str.c_str(); + wstring.size = str.size(); + } + + // Formats an argument of a custom type, such as a user-defined class. + template + static void format_custom_arg( + void *formatter, const void *arg, void *format_str_ptr) { + format(*static_cast*>(formatter), + *static_cast(format_str_ptr), + *static_cast(arg)); + } + +public: + MakeValue() {} + +#define FMT_MAKE_VALUE(Type, field, TYPE) \ + MakeValue(Type value) { field = value; } \ + static uint64_t type(Type) { return Arg::TYPE; } + + FMT_MAKE_VALUE(bool, int_value, INT) + FMT_MAKE_VALUE(short, int_value, INT) + FMT_MAKE_VALUE(unsigned short, uint_value, UINT) + FMT_MAKE_VALUE(int, int_value, INT) + FMT_MAKE_VALUE(unsigned, uint_value, UINT) + + MakeValue(long value) { + // To minimize the number of types we need to deal with, long is + // translated either to int or to long long depending on its size. + if (sizeof(long) == sizeof(int)) + int_value = static_cast(value); + else + long_long_value = value; + } + static uint64_t type(long) { + return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; + } + + MakeValue(unsigned long value) { + if (sizeof(unsigned long) == sizeof(unsigned)) + uint_value = static_cast(value); + else + ulong_long_value = value; + } + static uint64_t type(unsigned long) { + return sizeof(unsigned long) == sizeof(unsigned) ? + Arg::UINT : Arg::ULONG_LONG; + } + + FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) + FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) + FMT_MAKE_VALUE(float, double_value, DOUBLE) + FMT_MAKE_VALUE(double, double_value, DOUBLE) + FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) + FMT_MAKE_VALUE(signed char, int_value, CHAR) + FMT_MAKE_VALUE(unsigned char, int_value, CHAR) + FMT_MAKE_VALUE(char, int_value, CHAR) + + MakeValue(wchar_t value) { + int_value = internal::CharTraits::convert(value); + } + static uint64_t type(wchar_t) { return Arg::CHAR; } + +#define FMT_MAKE_STR_VALUE(Type, TYPE) \ + MakeValue(Type value) { set_string(value); } \ + static uint64_t type(Type) { return Arg::TYPE; } + + FMT_MAKE_VALUE(char *, string.value, CSTRING) + FMT_MAKE_VALUE(const char *, string.value, CSTRING) + FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) + FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) + FMT_MAKE_STR_VALUE(const std::string &, STRING) + FMT_MAKE_STR_VALUE(StringRef, STRING) + + FMT_MAKE_STR_VALUE(wchar_t *, WSTRING) + FMT_MAKE_STR_VALUE(const wchar_t *, WSTRING) + FMT_MAKE_STR_VALUE(const std::wstring &, WSTRING) + FMT_MAKE_STR_VALUE(WStringRef, WSTRING) + + FMT_MAKE_VALUE(void *, pointer, POINTER) + FMT_MAKE_VALUE(const void *, pointer, POINTER) + + template + MakeValue(const T &value) { + custom.value = &value; + custom.format = &format_custom_arg; + } + template + static uint64_t type(const T &) { return Arg::CUSTOM; } +}; + +#define FMT_DISPATCH(call) static_cast(this)->call + +// An argument visitor. +// To use ArgVisitor define a subclass that implements some or all of the +// visit methods with the same signatures as the methods in ArgVisitor, +// for example, visit_int(int). +// Specify the subclass name as the Impl template parameter. Then calling +// ArgVisitor::visit for some argument will dispatch to a visit method +// specific to the argument type. For example, if the argument type is +// double then visit_double(double) method of a subclass will be called. +// If the subclass doesn't contain a method with this signature, then +// a corresponding method of ArgVisitor will be called. +// +// Example: +// class MyArgVisitor : public ArgVisitor { +// public: +// void visit_int(int value) { print("{}", value); } +// void visit_double(double value) { print("{}", value ); } +// }; +// +// ArgVisitor uses the curiously recurring template pattern: +// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern +template +class ArgVisitor { + public: + Result visit_unhandled_arg() { return Result(); } + + Result visit_int(int value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_long_long(LongLong value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_uint(unsigned value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_ulong_long(ULongLong value) { + return FMT_DISPATCH(visit_any_int(value)); + } + Result visit_char(int value) { + return FMT_DISPATCH(visit_any_int(value)); + } + template + Result visit_any_int(T) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + Result visit_double(double value) { + return FMT_DISPATCH(visit_any_double(value)); + } + Result visit_long_double(long double value) { + return FMT_DISPATCH(visit_any_double(value)); + } + template + Result visit_any_double(T) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + Result visit_string(Arg::StringValue) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + Result visit_wstring(Arg::StringValue) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + Result visit_pointer(const void *) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + Result visit_custom(Arg::CustomValue) { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + Result visit(const Arg &arg) { + switch (arg.type) { + default: + assert(false); + return Result(); + case Arg::INT: + return FMT_DISPATCH(visit_int(arg.int_value)); + case Arg::UINT: + return FMT_DISPATCH(visit_uint(arg.uint_value)); + case Arg::LONG_LONG: + return FMT_DISPATCH(visit_long_long(arg.long_long_value)); + case Arg::ULONG_LONG: + return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); + case Arg::DOUBLE: + return FMT_DISPATCH(visit_double(arg.double_value)); + case Arg::LONG_DOUBLE: + return FMT_DISPATCH(visit_long_double(arg.long_double_value)); + case Arg::CHAR: + return FMT_DISPATCH(visit_char(arg.int_value)); + case Arg::CSTRING: { + Value::StringValue str = arg.string; + str.size = 0; + return FMT_DISPATCH(visit_string(str)); + } + case Arg::STRING: + return FMT_DISPATCH(visit_string(arg.string)); + case Arg::WSTRING: + return FMT_DISPATCH(visit_wstring(arg.wstring)); + case Arg::POINTER: + return FMT_DISPATCH(visit_pointer(arg.pointer)); + case Arg::CUSTOM: + return FMT_DISPATCH(visit_custom(arg.custom)); + } + } +}; + +class RuntimeError : public std::runtime_error { + protected: + RuntimeError() : std::runtime_error("") {} +}; + +template +class ArgFormatter; +} // namespace internal + +/** + An argument list. + */ +class ArgList { + private: + uint64_t types_; + const internal::Value *values_; + + public: + // Maximum number of arguments that can be passed in ArgList. + enum { MAX_ARGS = 16 }; + + ArgList() : types_(0) {} + ArgList(ULongLong types, const internal::Value *values) + : types_(types), values_(values) {} + + /** + Returns the argument at specified index. + */ + internal::Arg operator[](unsigned index) const { + using internal::Arg; + Arg arg; + if (index >= MAX_ARGS) { + arg.type = Arg::NONE; + return arg; + } + unsigned shift = index * 4; + uint64_t mask = 0xf; + Arg::Type type = + static_cast((types_ & (mask << shift)) >> shift); + arg.type = type; + if (type != Arg::NONE) { + internal::Value &value = arg; + value = values_[index]; + } + return arg; + } +}; + +struct FormatSpec; + +namespace internal { + +class FormatterBase { + private: + ArgList args_; + int next_arg_index_; + + // Returns the argument with specified index. + Arg do_get_arg(unsigned arg_index, const char *&error); + + protected: + void set_args(const ArgList &args) { + args_ = args; + next_arg_index_ = 0; + } + + // Returns the next argument. + Arg next_arg(const char *&error); + + // Checks if manual indexing is used and returns the argument with + // specified index. + Arg get_arg(unsigned arg_index, const char *&error); + + template + void write(BasicWriter &w, const Char *start, const Char *end) { + if (start != end) + w << BasicStringRef(start, end - start); + } +}; + +// A printf formatter. +template +class PrintfFormatter : private FormatterBase { + private: + void parse_flags(FormatSpec &spec, const Char *&s); + + // Returns the argument with specified index or, if arg_index is equal + // to the maximum unsigned value, the next argument. + Arg get_arg(const Char *s, + unsigned arg_index = (std::numeric_limits::max)()); + + // Parses argument index, flags and width and returns the argument index. + unsigned parse_header(const Char *&s, FormatSpec &spec); + + public: + void format(BasicWriter &writer, + BasicStringRef format_str, const ArgList &args); +}; +} // namespace internal + +// A formatter. +template +class BasicFormatter : private internal::FormatterBase { + private: + BasicWriter &writer_; + const Char *start_; + + FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); + + // Parses argument index and returns corresponding argument. + internal::Arg parse_arg_index(const Char *&s); + + public: + explicit BasicFormatter(BasicWriter &w) : writer_(w) {} + + BasicWriter &writer() { return writer_; } + + void format(BasicStringRef format_str, const ArgList &args); + + const Char *format(const Char *&format_str, const internal::Arg &arg); +}; + +enum Alignment { + ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC +}; + +// Flags. +enum { + SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, + CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. +}; + +// An empty format specifier. +struct EmptySpec {}; + +// A type specifier. +template +struct TypeSpec : EmptySpec { + Alignment align() const { return ALIGN_DEFAULT; } + unsigned width() const { return 0; } + int precision() const { return -1; } + bool flag(unsigned) const { return false; } + char type() const { return TYPE; } + char fill() const { return ' '; } +}; + +// A width specifier. +struct WidthSpec { + unsigned width_; + // Fill is always wchar_t and cast to char if necessary to avoid having + // two specialization of WidthSpec and its subclasses. + wchar_t fill_; + + WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} + + unsigned width() const { return width_; } + wchar_t fill() const { return fill_; } +}; + +// An alignment specifier. +struct AlignSpec : WidthSpec { + Alignment align_; + + AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) + : WidthSpec(width, fill), align_(align) {} + + Alignment align() const { return align_; } + + int precision() const { return -1; } +}; + +// An alignment and type specifier. +template +struct AlignTypeSpec : AlignSpec { + AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} + + bool flag(unsigned) const { return false; } + char type() const { return TYPE; } +}; + +// A full format specifier. +struct FormatSpec : AlignSpec { + unsigned flags_; + int precision_; + char type_; + + FormatSpec( + unsigned width = 0, char type = 0, wchar_t fill = ' ') + : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} + + bool flag(unsigned f) const { return (flags_ & f) != 0; } + int precision() const { return precision_; } + char type() const { return type_; } +}; + +// An integer format specifier. +template , typename Char = char> +class IntFormatSpec : public SpecT { + private: + T value_; + + public: + IntFormatSpec(T val, const SpecT &spec = SpecT()) + : SpecT(spec), value_(val) {} + + T value() const { return value_; } +}; + +// A string format specifier. +template +class StrFormatSpec : public AlignSpec { + private: + const T *str_; + + public: + StrFormatSpec(const T *str, unsigned width, wchar_t fill) + : AlignSpec(width, fill), str_(str) {} + + const T *str() const { return str_; } +}; + +/** + Returns an integer format specifier to format the value in base 2. + */ +IntFormatSpec > bin(int value); + +/** + Returns an integer format specifier to format the value in base 8. + */ +IntFormatSpec > oct(int value); + +/** + Returns an integer format specifier to format the value in base 16 using + lower-case letters for the digits above 9. + */ +IntFormatSpec > hex(int value); + +/** + Returns an integer formatter format specifier to format in base 16 using + upper-case letters for the digits above 9. + */ +IntFormatSpec > hexu(int value); + +/** + \rst + Returns an integer format specifier to pad the formatted argument with the + fill character to the specified width using the default (right) numeric + alignment. + + **Example**:: + + MemoryWriter out; + out << pad(hex(0xcafe), 8, '0'); + // out.str() == "0000cafe" + + \endrst + */ +template +IntFormatSpec, Char> pad( + int value, unsigned width, Char fill = ' '); + +#define FMT_DEFINE_INT_FORMATTERS(TYPE) \ +inline IntFormatSpec > bin(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'b'>()); \ +} \ + \ +inline IntFormatSpec > oct(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'o'>()); \ +} \ + \ +inline IntFormatSpec > hex(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'x'>()); \ +} \ + \ +inline IntFormatSpec > hexu(TYPE value) { \ + return IntFormatSpec >(value, TypeSpec<'X'>()); \ +} \ + \ +template \ +inline IntFormatSpec > pad( \ + IntFormatSpec > f, unsigned width) { \ + return IntFormatSpec >( \ + f.value(), AlignTypeSpec(width, ' ')); \ +} \ + \ +/* For compatibility with older compilers we provide two overloads for pad, */ \ +/* one that takes a fill character and one that doesn't. In the future this */ \ +/* can be replaced with one overload making the template argument Char */ \ +/* default to char (C++11). */ \ +template \ +inline IntFormatSpec, Char> pad( \ + IntFormatSpec, Char> f, \ + unsigned width, Char fill) { \ + return IntFormatSpec, Char>( \ + f.value(), AlignTypeSpec(width, fill)); \ +} \ + \ +inline IntFormatSpec > pad( \ + TYPE value, unsigned width) { \ + return IntFormatSpec >( \ + value, AlignTypeSpec<0>(width, ' ')); \ +} \ + \ +template \ +inline IntFormatSpec, Char> pad( \ + TYPE value, unsigned width, Char fill) { \ + return IntFormatSpec, Char>( \ + value, AlignTypeSpec<0>(width, fill)); \ +} + +FMT_DEFINE_INT_FORMATTERS(int) +FMT_DEFINE_INT_FORMATTERS(long) +FMT_DEFINE_INT_FORMATTERS(unsigned) +FMT_DEFINE_INT_FORMATTERS(unsigned long) +FMT_DEFINE_INT_FORMATTERS(LongLong) +FMT_DEFINE_INT_FORMATTERS(ULongLong) + +/** + \rst + Returns a string formatter that pads the formatted argument with the fill + character to the specified width using the default (left) string alignment. + + **Example**:: + + std::string s = str(MemoryWriter() << pad("abc", 8)); + // s == "abc " + + \endrst + */ +template +inline StrFormatSpec pad( + const Char *str, unsigned width, Char fill = ' ') { + return StrFormatSpec(str, width, fill); +} + +inline StrFormatSpec pad( + const wchar_t *str, unsigned width, char fill = ' ') { + return StrFormatSpec(str, width, fill); +} + +// Generates a comma-separated list with results of applying f to +// numbers 0..n-1. +# define FMT_GEN(n, f) FMT_GEN##n(f) +# define FMT_GEN1(f) f(0) +# define FMT_GEN2(f) FMT_GEN1(f), f(1) +# define FMT_GEN3(f) FMT_GEN2(f), f(2) +# define FMT_GEN4(f) FMT_GEN3(f), f(3) +# define FMT_GEN5(f) FMT_GEN4(f), f(4) +# define FMT_GEN6(f) FMT_GEN5(f), f(5) +# define FMT_GEN7(f) FMT_GEN6(f), f(6) +# define FMT_GEN8(f) FMT_GEN7(f), f(7) +# define FMT_GEN9(f) FMT_GEN8(f), f(8) +# define FMT_GEN10(f) FMT_GEN9(f), f(9) +# define FMT_GEN11(f) FMT_GEN10(f), f(10) +# define FMT_GEN12(f) FMT_GEN11(f), f(11) +# define FMT_GEN13(f) FMT_GEN12(f), f(12) +# define FMT_GEN14(f) FMT_GEN13(f), f(13) +# define FMT_GEN15(f) FMT_GEN14(f), f(14) + +namespace internal { +inline uint64_t make_type() { return 0; } + +template +inline uint64_t make_type(const T &arg) { return MakeValue::type(arg); } + +#if FMT_USE_VARIADIC_TEMPLATES +template +inline uint64_t make_type(const Arg &first, const Args & ... tail) { + return make_type(first) | (make_type(tail...) << 4); +} +#else + +struct ArgType { + uint64_t type; + + ArgType() : type(0) {} + + template + ArgType(const T &arg) : type(make_type(arg)) {} +}; + +# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() + +inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { + return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | + (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | + (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | + (t12.type << 48) | (t13.type << 52) | (t14.type << 56); +} +#endif +} // namespace internal + +# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n +# define FMT_MAKE_ARG_TYPE(n) T##n +# define FMT_MAKE_ARG(n) const T##n &v##n +# define FMT_MAKE_REF_char(n) fmt::internal::MakeValue(v##n) +# define FMT_MAKE_REF_wchar_t(n) fmt::internal::MakeValue(v##n) + +#if FMT_USE_VARIADIC_TEMPLATES +// Defines a variadic function returning void. +# define FMT_VARIADIC_VOID(func, arg_type) \ + template \ + void func(arg_type arg1, const Args & ... args) { \ + const fmt::internal::Value values[ \ + fmt::internal::NonZero::VALUE] = { \ + fmt::internal::MakeValue(args)... \ + }; \ + func(arg1, ArgList(fmt::internal::make_type(args...), values)); \ + } + +// Defines a variadic constructor. +# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ + template \ + ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ + using fmt::internal::MakeValue; \ + const fmt::internal::Value values[ \ + fmt::internal::NonZero::VALUE] = { \ + MakeValue(args)... \ + }; \ + func(arg0, arg1, ArgList(fmt::internal::make_type(args...), values)); \ + } + +#else + +# define FMT_MAKE_REF(n) fmt::internal::MakeValue(v##n) +# define FMT_MAKE_REF2(n) v##n + +// Defines a wrapper for a function taking one argument of type arg_type +// and n additional arguments of arbitrary types. +# define FMT_WRAP1(func, arg_type, n) \ + template \ + inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ + const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ + func(arg1, fmt::ArgList( \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ + } + +// Emulates a variadic function returning void on a pre-C++11 compiler. +# define FMT_VARIADIC_VOID(func, arg_type) \ + inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ + FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ + FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ + FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ + FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ + FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) + +# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ + template \ + ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ + const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ + func(arg0, arg1, fmt::ArgList( \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ + } + +// Emulates a variadic constructor on a pre-C++11 compiler. +# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) +#endif + +// Generates a comma-separated list with results of applying f to pairs +// (argument, index). +#define FMT_FOR_EACH1(f, x0) f(x0, 0) +#define FMT_FOR_EACH2(f, x0, x1) \ + FMT_FOR_EACH1(f, x0), f(x1, 1) +#define FMT_FOR_EACH3(f, x0, x1, x2) \ + FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) +#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ + FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) +#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ + FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) +#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ + FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) +#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ + FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) +#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ + FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) +#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ + FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) +#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ + FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) + +/** + An error returned by an operating system or a language runtime, + for example a file opening error. +*/ +class SystemError : public internal::RuntimeError { + private: + void init(int err_code, StringRef format_str, ArgList args); + + protected: + int error_code_; + + typedef char Char; // For FMT_VARIADIC_CTOR. + + SystemError() {} + + public: + /** + \rst + Constructs a :class:`fmt::SystemError` object with the description + of the form + + .. parsed-literal:: + **: ** + + where ** is the formatted message and ** is + the system message corresponding to the error code. + *error_code* is a system error code as given by ``errno``. + If *error_code* is not a valid error code such as -1, the system message + may look like "Unknown error -1" and is platform-dependent. + + **Example**:: + + // This throws a SystemError with the description + // cannot open file 'madeup': No such file or directory + // or similar (system message may vary). + const char *filename = "madeup"; + std::FILE *file = std::fopen(filename, "r"); + if (!file) + throw fmt::SystemError(errno, "cannot open file '{}'", filename); + \endrst + */ + SystemError(int error_code, StringRef message) { + init(error_code, message, ArgList()); + } + FMT_VARIADIC_CTOR(SystemError, init, int, StringRef) + + int error_code() const { return error_code_; } +}; + +/** + \rst + This template provides operations for formatting and writing data into + a character stream. The output is stored in a buffer provided by a subclass + such as :class:`fmt::BasicMemoryWriter`. + + You can use one of the following typedefs for common character types: + + +---------+----------------------+ + | Type | Definition | + +=========+======================+ + | Writer | BasicWriter | + +---------+----------------------+ + | WWriter | BasicWriter | + +---------+----------------------+ + + \endrst + */ +template +class BasicWriter { + private: + // Output buffer. + internal::Buffer &buffer_; + + FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); + + typedef typename internal::CharTraits::CharPtr CharPtr; + +#if _SECURE_SCL + // Returns pointer value. + static Char *get(CharPtr p) { return p.base(); } +#else + static Char *get(Char *p) { return p; } +#endif + + // Fills the padding around the content and returns the pointer to the + // content area. + static CharPtr fill_padding(CharPtr buffer, + unsigned total_size, std::size_t content_size, wchar_t fill); + + // Grows the buffer by n characters and returns a pointer to the newly + // allocated area. + CharPtr grow_buffer(std::size_t n) { + std::size_t size = buffer_.size(); + buffer_.resize(size + n); + return internal::make_ptr(&buffer_[size], n); + } + + // Prepare a buffer for integer formatting. + CharPtr prepare_int_buffer(unsigned num_digits, + const EmptySpec &, const char *prefix, unsigned prefix_size) { + unsigned size = prefix_size + num_digits; + CharPtr p = grow_buffer(size); + std::copy(prefix, prefix + prefix_size, p); + return p + size - 1; + } + + template + CharPtr prepare_int_buffer(unsigned num_digits, + const Spec &spec, const char *prefix, unsigned prefix_size); + + // Formats an integer. + template + void write_int(T value, Spec spec); + + // Formats a floating-point number (double or long double). + template + void write_double(T value, const FormatSpec &spec); + + // Writes a formatted string. + template + CharPtr write_str( + const StrChar *s, std::size_t size, const AlignSpec &spec); + + template + void write_str( + const internal::Arg::StringValue &str, const FormatSpec &spec); + + // This method is private to disallow writing a wide string to a + // char stream and vice versa. If you want to print a wide string + // as a pointer as std::ostream does, cast it to const void*. + // Do not implement! + void operator<<(typename internal::CharTraits::UnsupportedStrType); + + // Appends floating-point length specifier to the format string. + // The second argument is only used for overload resolution. + void append_float_length(Char *&format_ptr, long double) { + *format_ptr++ = 'L'; + } + + template + void append_float_length(Char *&, T) {} + + friend class internal::ArgFormatter; + friend class internal::PrintfFormatter; + + protected: + /** + Constructs a ``BasicWriter`` object. + */ + explicit BasicWriter(internal::Buffer &b) : buffer_(b) {} + + public: + /** + Destroys a ``BasicWriter`` object. + */ + virtual ~BasicWriter() {} + + /** + Returns the total number of characters written. + */ + std::size_t size() const { return buffer_.size(); } + + /** + Returns a pointer to the output buffer content. No terminating null + character is appended. + */ + const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; } + + /** + Returns a pointer to the output buffer content with terminating null + character appended. + */ + const Char *c_str() const { + std::size_t size = buffer_.size(); + buffer_.reserve(size + 1); + buffer_[size] = '\0'; + return &buffer_[0]; + } + + /** + Returns the content of the output buffer as an `std::string`. + */ + std::basic_string str() const { + return std::basic_string(&buffer_[0], buffer_.size()); + } + + /** + \rst + Writes formatted data. + + *args* is an argument list representing arbitrary arguments. + + **Example**:: + + MemoryWriter out; + out.write("Current point:\n"); + out.write("({:+f}, {:+f})", -3.14, 3.14); + + This will write the following output to the ``out`` object: + + .. code-block:: none + + Current point: + (-3.140000, +3.140000) + + The output can be accessed using :func:`data()`, :func:`c_str` or + :func:`str` methods. + + See also :ref:`syntax`. + \endrst + */ + void write(BasicStringRef format, ArgList args) { + BasicFormatter(*this).format(format, args); + } + FMT_VARIADIC_VOID(write, BasicStringRef) + + BasicWriter &operator<<(int value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(unsigned value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(long value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(unsigned long value) { + return *this << IntFormatSpec(value); + } + BasicWriter &operator<<(LongLong value) { + return *this << IntFormatSpec(value); + } + + /** + Formats *value* and writes it to the stream. + */ + BasicWriter &operator<<(ULongLong value) { + return *this << IntFormatSpec(value); + } + + BasicWriter &operator<<(double value) { + write_double(value, FormatSpec()); + return *this; + } + + /** + Formats *value* using the general format for floating-point numbers + (``'g'``) and writes it to the stream. + */ + BasicWriter &operator<<(long double value) { + write_double(value, FormatSpec()); + return *this; + } + + /** + Writes a character to the stream. + */ + BasicWriter &operator<<(char value) { + buffer_.push_back(value); + return *this; + } + + BasicWriter &operator<<(wchar_t value) { + buffer_.push_back(internal::CharTraits::convert(value)); + return *this; + } + + /** + Writes *value* to the stream. + */ + BasicWriter &operator<<(fmt::BasicStringRef value) { + const Char *str = value.c_str(); + buffer_.append(str, str + value.size()); + return *this; + } + + template + BasicWriter &operator<<(IntFormatSpec spec) { + internal::CharTraits::convert(FillChar()); + write_int(spec.value(), spec); + return *this; + } + + template + BasicWriter &operator<<(const StrFormatSpec &spec) { + const StrChar *s = spec.str(); + // TODO: error if fill is not convertible to Char + write_str(s, std::char_traits::length(s), spec); + return *this; + } + + void clear() FMT_NOEXCEPT { buffer_.clear(); } +}; + +template +template +typename BasicWriter::CharPtr BasicWriter::write_str( + const StrChar *s, std::size_t size, const AlignSpec &spec) { + CharPtr out = CharPtr(); + if (spec.width() > size) { + out = grow_buffer(spec.width()); + Char fill = static_cast(spec.fill()); + if (spec.align() == ALIGN_RIGHT) { + std::fill_n(out, spec.width() - size, fill); + out += spec.width() - size; + } else if (spec.align() == ALIGN_CENTER) { + out = fill_padding(out, spec.width(), size, fill); + } else { + std::fill_n(out + size, spec.width() - size, fill); + } + } else { + out = grow_buffer(size); + } + std::copy(s, s + size, out); + return out; +} + +template +typename BasicWriter::CharPtr + BasicWriter::fill_padding( + CharPtr buffer, unsigned total_size, + std::size_t content_size, wchar_t fill) { + std::size_t padding = total_size - content_size; + std::size_t left_padding = padding / 2; + Char fill_char = static_cast(fill); + std::fill_n(buffer, left_padding, fill_char); + buffer += left_padding; + CharPtr content = buffer; + std::fill_n(buffer + content_size, padding - left_padding, fill_char); + return content; +} + +template +template +typename BasicWriter::CharPtr + BasicWriter::prepare_int_buffer( + unsigned num_digits, const Spec &spec, + const char *prefix, unsigned prefix_size) { + unsigned width = spec.width(); + Alignment align = spec.align(); + Char fill = static_cast(spec.fill()); + if (spec.precision() > static_cast(num_digits)) { + // Octal prefix '0' is counted as a digit, so ignore it if precision + // is specified. + if (prefix_size > 0 && prefix[prefix_size - 1] == '0') + --prefix_size; + unsigned number_size = prefix_size + spec.precision(); + AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); + if (number_size >= width) + return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); + buffer_.reserve(width); + unsigned fill_size = width - number_size; + if (align != ALIGN_LEFT) { + CharPtr p = grow_buffer(fill_size); + std::fill(p, p + fill_size, fill); + } + CharPtr result = prepare_int_buffer( + num_digits, subspec, prefix, prefix_size); + if (align == ALIGN_LEFT) { + CharPtr p = grow_buffer(fill_size); + std::fill(p, p + fill_size, fill); + } + return result; + } + unsigned size = prefix_size + num_digits; + if (width <= size) { + CharPtr p = grow_buffer(size); + std::copy(prefix, prefix + prefix_size, p); + return p + size - 1; + } + CharPtr p = grow_buffer(width); + CharPtr end = p + width; + if (align == ALIGN_LEFT) { + std::copy(prefix, prefix + prefix_size, p); + p += size; + std::fill(p, end, fill); + } else if (align == ALIGN_CENTER) { + p = fill_padding(p, width, size, fill); + std::copy(prefix, prefix + prefix_size, p); + p += size; + } else { + if (align == ALIGN_NUMERIC) { + if (prefix_size != 0) { + p = std::copy(prefix, prefix + prefix_size, p); + size -= prefix_size; + } + } else { + std::copy(prefix, prefix + prefix_size, end - size); + } + std::fill(p, end - size, fill); + p = end; + } + return p - 1; +} + +template +template +void BasicWriter::write_int(T value, Spec spec) { + unsigned prefix_size = 0; + typedef typename internal::IntTraits::MainType UnsignedType; + UnsignedType abs_value = value; + char prefix[4] = ""; + if (internal::is_negative(value)) { + prefix[0] = '-'; + ++prefix_size; + abs_value = 0 - abs_value; + } else if (spec.flag(SIGN_FLAG)) { + prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; + ++prefix_size; + } + switch (spec.type()) { + case 0: case 'd': { + unsigned num_digits = internal::count_digits(abs_value); + CharPtr p = prepare_int_buffer( + num_digits, spec, prefix, prefix_size) + 1 - num_digits; + internal::format_decimal(get(p), abs_value, num_digits); + break; + } + case 'x': case 'X': { + UnsignedType n = abs_value; + if (spec.flag(HASH_FLAG)) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = spec.type(); + } + unsigned num_digits = 0; + do { + ++num_digits; + } while ((n >>= 4) != 0); + Char *p = get(prepare_int_buffer( + num_digits, spec, prefix, prefix_size)); + n = abs_value; + const char *digits = spec.type() == 'x' ? + "0123456789abcdef" : "0123456789ABCDEF"; + do { + *p-- = digits[n & 0xf]; + } while ((n >>= 4) != 0); + break; + } + case 'b': case 'B': { + UnsignedType n = abs_value; + if (spec.flag(HASH_FLAG)) { + prefix[prefix_size++] = '0'; + prefix[prefix_size++] = spec.type(); + } + unsigned num_digits = 0; + do { + ++num_digits; + } while ((n >>= 1) != 0); + Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); + n = abs_value; + do { + *p-- = '0' + (n & 1); + } while ((n >>= 1) != 0); + break; + } + case 'o': { + UnsignedType n = abs_value; + if (spec.flag(HASH_FLAG)) + prefix[prefix_size++] = '0'; + unsigned num_digits = 0; + do { + ++num_digits; + } while ((n >>= 3) != 0); + Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); + n = abs_value; + do { + *p-- = '0' + (n & 7); + } while ((n >>= 3) != 0); + break; + } + default: + internal::report_unknown_type( + spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); + break; + } +} + +template +template +void BasicWriter::write_double( + T value, const FormatSpec &spec) { + // Check type. + char type = spec.type(); + bool upper = false; + switch (type) { + case 0: + type = 'g'; + break; + case 'e': case 'f': case 'g': case 'a': + break; + case 'F': +#ifdef _MSC_VER + // MSVC's printf doesn't support 'F'. + type = 'f'; +#endif + // Fall through. + case 'E': case 'G': case 'A': + upper = true; + break; + default: + internal::report_unknown_type(type, "double"); + break; + } + + char sign = 0; + // Use getsign instead of value < 0 because the latter is always + // false for NaN. + if (internal::getsign(static_cast(value))) { + sign = '-'; + value = -value; + } else if (spec.flag(SIGN_FLAG)) { + sign = spec.flag(PLUS_FLAG) ? '+' : ' '; + } + + if (value != value) { + // Format NaN ourselves because sprintf's output is not consistent + // across platforms. + std::size_t nan_size = 4; + const char *nan = upper ? " NAN" : " nan"; + if (!sign) { + --nan_size; + ++nan; + } + CharPtr out = write_str(nan, nan_size, spec); + if (sign) + *out = sign; + return; + } + + if (internal::isinfinity(value)) { + // Format infinity ourselves because sprintf's output is not consistent + // across platforms. + std::size_t inf_size = 4; + const char *inf = upper ? " INF" : " inf"; + if (!sign) { + --inf_size; + ++inf; + } + CharPtr out = write_str(inf, inf_size, spec); + if (sign) + *out = sign; + return; + } + + std::size_t offset = buffer_.size(); + unsigned width = spec.width(); + if (sign) { + buffer_.reserve(buffer_.size() + (std::max)(width, 1u)); + if (width > 0) + --width; + ++offset; + } + + // Build format string. + enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg + Char format[MAX_FORMAT_SIZE]; + Char *format_ptr = format; + *format_ptr++ = '%'; + unsigned width_for_sprintf = width; + if (spec.flag(HASH_FLAG)) + *format_ptr++ = '#'; + if (spec.align() == ALIGN_CENTER) { + width_for_sprintf = 0; + } else { + if (spec.align() == ALIGN_LEFT) + *format_ptr++ = '-'; + if (width != 0) + *format_ptr++ = '*'; + } + if (spec.precision() >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + + append_float_length(format_ptr, value); + *format_ptr++ = type; + *format_ptr = '\0'; + + // Format using snprintf. + Char fill = static_cast(spec.fill()); + for (;;) { + std::size_t buffer_size = buffer_.capacity() - offset; +#if _MSC_VER + // MSVC's vsnprintf_s doesn't work with zero size, so reserve + // space for at least one extra character to make the size non-zero. + // Note that the buffer's capacity will increase by more than 1. + if (buffer_size == 0) { + buffer_.reserve(offset + 1); + buffer_size = buffer_.capacity() - offset; + } +#endif + Char *start = &buffer_[offset]; + int n = internal::CharTraits::format_float( + start, buffer_size, format, width_for_sprintf, spec.precision(), value); + if (n >= 0 && offset + n < buffer_.capacity()) { + if (sign) { + if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || + *start != ' ') { + *(start - 1) = sign; + sign = 0; + } else { + *(start - 1) = fill; + } + ++n; + } + if (spec.align() == ALIGN_CENTER && + spec.width() > static_cast(n)) { + width = spec.width(); + CharPtr p = grow_buffer(width); + std::copy(p, p + n, p + (width - n) / 2); + fill_padding(p, spec.width(), n, fill); + return; + } + if (spec.fill() != ' ' || sign) { + while (*start == ' ') + *start++ = fill; + if (sign) + *(start - 1) = sign; + } + grow_buffer(n); + return; + } + // If n is negative we ask to increase the capacity by at least 1, + // but as std::vector, the buffer grows exponentially. + buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1); + } +} + +/** + \rst + This template provides operations for formatting and writing data into + a character stream. The output is stored in a memory buffer that grows + dynamically. + + You can use one of the following typedefs for common character types + and the standard allocator: + + +---------------+-----------------------------------------------+ + | Type | Definition | + +===============+===============================================+ + | MemoryWriter | BasicWriter> | + +---------------+-----------------------------------------------+ + | WMemoryWriter | BasicWriter> | + +---------------+-----------------------------------------------+ + + **Example**:: + + MemoryWriter out; + out << "The answer is " << 42 << "\n"; + out.write("({:+f}, {:+f})", -3.14, 3.14); + + This will write the following output to the ``out`` object: + + .. code-block:: none + + The answer is 42 + (-3.140000, +3.140000) + + The output can be converted to an ``std::string`` with ``out.str()`` or + accessed as a C string with ``out.c_str()``. + \endrst + */ +template > +class BasicMemoryWriter : public BasicWriter { + private: + internal::MemoryBuffer buffer_; + + public: + explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) + : BasicWriter(buffer_), buffer_(alloc) {} + +#if FMT_USE_RVALUE_REFERENCES + /** + Constructs a :class:`fmt::BasicMemoryWriter` object moving the content + of the other object to it. + */ + BasicMemoryWriter(BasicMemoryWriter &&other) + : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) { + } + + /** + Moves the content of the other ``BasicMemoryWriter`` object to this one. + */ + BasicMemoryWriter &operator=(BasicMemoryWriter &&other) { + buffer_ = std::move(other.buffer_); + return *this; + } +#endif +}; + +typedef BasicMemoryWriter MemoryWriter; +typedef BasicMemoryWriter WMemoryWriter; + +// Formats a value. +template +void format(BasicFormatter &f, const Char *&format_str, const T &value) { + std::basic_ostringstream os; + os << value; + internal::Arg arg; + internal::Value &arg_value = arg; + std::basic_string str = os.str(); + arg_value = internal::MakeValue(str); + arg.type = static_cast(internal::MakeValue::type(str)); + format_str = f.format(format_str, arg); +} + +// Reports a system error without throwing an exception. +// Can be used to report errors from destructors. +void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; + +#ifdef _WIN32 + +/** A Windows error. */ +class WindowsError : public SystemError { + private: + void init(int error_code, StringRef format_str, ArgList args); + + public: + /** + \rst + Constructs a :class:`fmt::WindowsError` object with the description + of the form + + .. parsed-literal:: + **: ** + + where ** is the formatted message and ** is the system + message corresponding to the error code. + *error_code* is a Windows error code as given by ``GetLastError``. + If *error_code* is not a valid error code such as -1, the system message + will look like "error -1". + + **Example**:: + + // This throws a WindowsError with the description + // cannot open file 'madeup': The system cannot find the file specified. + // or similar (system message may vary). + const char *filename = "madeup"; + LPOFSTRUCT of = LPOFSTRUCT(); + HFILE file = OpenFile(filename, &of, OF_READ); + if (file == HFILE_ERROR) + throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename); + \endrst + */ + WindowsError(int error_code, StringRef message) { + init(error_code, message, ArgList()); + } + FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef) +}; + +// Reports a Windows error without throwing an exception. +// Can be used to report errors from destructors. +void report_windows_error(int error_code, StringRef message) FMT_NOEXCEPT; + +#endif + +enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; + +/** + Formats a string and prints it to stdout using ANSI escape sequences + to specify color (experimental). + Example: + PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; + */ +void print_colored(Color c, StringRef format, ArgList args); + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + std::string message = format("The answer is {}", 42); + \endrst +*/ +inline std::string format(StringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + return w.str(); +} + +inline std::wstring format(WStringRef format_str, ArgList args) { + WMemoryWriter w; + w.write(format_str, args); + return w.str(); +} + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + print(stderr, "Don't {}!", "panic"); + \endrst + */ +void print(std::FILE *f, StringRef format_str, ArgList args); + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ +void print(StringRef format_str, ArgList args); + +/** + \rst + Prints formatted data to the stream *os*. + + **Example**:: + + print(cerr, "Don't {}!", "panic"); + \endrst + */ +void print(std::ostream &os, StringRef format_str, ArgList args); + +template +void printf(BasicWriter &w, BasicStringRef format, ArgList args) { + internal::PrintfFormatter().format(w, format, args); +} + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + std::string message = fmt::sprintf("The answer is %d", 42); + \endrst +*/ +inline std::string sprintf(StringRef format, ArgList args) { + MemoryWriter w; + printf(w, format, args); + return w.str(); +} + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + fmt::fprintf(stderr, "Don't %s!", "panic"); + \endrst + */ +int fprintf(std::FILE *f, StringRef format, ArgList args); + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + fmt::printf("Elapsed time: %.2f seconds", 1.23); + \endrst + */ +inline int printf(StringRef format, ArgList args) { + return fprintf(stdout, format, args); +} + +/** + Fast integer formatter. + */ +class FormatInt { + private: + // Buffer should be large enough to hold all digits (digits10 + 1), + // a sign and a null character. + enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; + mutable char buffer_[BUFFER_SIZE]; + char *str_; + + // Formats value in reverse and returns the number of digits. + char *format_decimal(ULongLong value) { + char *buffer_end = buffer_ + BUFFER_SIZE - 1; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + unsigned index = (value % 100) * 2; + value /= 100; + *--buffer_end = internal::Data::DIGITS[index + 1]; + *--buffer_end = internal::Data::DIGITS[index]; + } + if (value < 10) { + *--buffer_end = static_cast('0' + value); + return buffer_end; + } + unsigned index = static_cast(value * 2); + *--buffer_end = internal::Data::DIGITS[index + 1]; + *--buffer_end = internal::Data::DIGITS[index]; + return buffer_end; + } + + void FormatSigned(LongLong value) { + ULongLong abs_value = static_cast(value); + bool negative = value < 0; + if (negative) + abs_value = 0 - abs_value; + str_ = format_decimal(abs_value); + if (negative) + *--str_ = '-'; + } + + public: + explicit FormatInt(int value) { FormatSigned(value); } + explicit FormatInt(long value) { FormatSigned(value); } + explicit FormatInt(LongLong value) { FormatSigned(value); } + explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} + explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} + explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} + + /** + Returns the number of characters written to the output buffer. + */ + std::size_t size() const { return buffer_ - str_ + BUFFER_SIZE - 1; } + + /** + Returns a pointer to the output buffer content. No terminating null + character is appended. + */ + const char *data() const { return str_; } + + /** + Returns a pointer to the output buffer content with terminating null + character appended. + */ + const char *c_str() const { + buffer_[BUFFER_SIZE - 1] = '\0'; + return str_; + } + + /** + Returns the content of the output buffer as an `std::string`. + */ + std::string str() const { return std::string(str_, size()); } +}; + +// Formats a decimal integer value writing into buffer and returns +// a pointer to the end of the formatted string. This function doesn't +// write a terminating null character. +template +inline void format_decimal(char *&buffer, T value) { + typename internal::IntTraits::MainType abs_value = value; + if (internal::is_negative(value)) { + *buffer++ = '-'; + abs_value = 0 - abs_value; + } + if (abs_value < 100) { + if (abs_value < 10) { + *buffer++ = static_cast('0' + abs_value); + return; + } + unsigned index = static_cast(abs_value * 2); + *buffer++ = internal::Data::DIGITS[index]; + *buffer++ = internal::Data::DIGITS[index + 1]; + return; + } + unsigned num_digits = internal::count_digits(abs_value); + internal::format_decimal(buffer, abs_value, num_digits); + buffer += num_digits; +} +} + +#if FMT_GCC_VERSION +// Use the system_header pragma to suppress warnings about variadic macros +// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't +// work. It is used at the end because we want to suppress as little warnings +// as possible. +# pragma GCC system_header +#endif + +// This is used to work around VC++ bugs in handling variadic macros. +#define FMT_EXPAND(args) args + +// Returns the number of arguments. +// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. +#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) +#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) +#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 + +#define FMT_CONCAT(a, b) a##b +#define FMT_FOR_EACH_(N, f, ...) \ + FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) +#define FMT_FOR_EACH(f, ...) \ + FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) + +#define FMT_ADD_ARG_NAME(type, index) type arg##index +#define FMT_GET_ARG_NAME(type, index) arg##index + +#if FMT_USE_VARIADIC_TEMPLATES +# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ + template \ + ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ + const Args & ... args) { \ + using fmt::internal::Value; \ + const Value values[fmt::internal::NonZero::VALUE] = { \ + fmt::internal::MakeValue(args)... \ + }; \ + call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ + fmt::internal::make_type(args...), values)); \ + } +#else +// Defines a wrapper for a function taking __VA_ARGS__ arguments +// and n additional arguments of arbitrary types. +# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ + template \ + inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ + FMT_GEN(n, FMT_MAKE_ARG)) { \ + const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \ + call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ + fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ + } + +# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ + inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ + call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ + } \ + FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ + FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) +#endif // FMT_USE_VARIADIC_TEMPLATES + +/** + \rst + Defines a variadic function with the specified return type, function name + and argument types passed as variable arguments to this macro. + + **Example**:: + + void print_error(const char *file, int line, const char *format, + fmt::ArgList args) { + fmt::print("{}: {}: ", file, line); + fmt::print(format, args); + } + FMT_VARIADIC(void, print_error, const char *, int, const char *) + + ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that + don't implement variadic templates. You don't have to use this macro if + you don't need legacy compiler support and can use variadic templates + directly:: + + template + void print_error(const char *file, int line, const char *format, + const Args & ... args) { + fmt::print("{}: {}: ", file, line); + fmt::print(format, args...); + } + \endrst + */ +#define FMT_VARIADIC(ReturnType, func, ...) \ + FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_VARIADIC_W(ReturnType, func, ...) \ + FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) + +namespace fmt { +FMT_VARIADIC(std::string, format, StringRef) +FMT_VARIADIC_W(std::wstring, format, WStringRef) +FMT_VARIADIC(void, print, StringRef) +FMT_VARIADIC(void, print, std::FILE *, StringRef) +FMT_VARIADIC(void, print, std::ostream &, StringRef) +FMT_VARIADIC(void, print_colored, Color, StringRef) +FMT_VARIADIC(std::string, sprintf, StringRef) +FMT_VARIADIC(int, printf, StringRef) +FMT_VARIADIC(int, fprintf, std::FILE *, StringRef) +} + +// Restore warnings. +#if FMT_GCC_VERSION >= 406 +# pragma GCC diagnostic pop +#endif + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#ifdef FMT_HEADER_ONLY +# include "format.cc" +#endif + +#endif // FMT_FORMAT_H_ diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h new file mode 100644 index 0000000000..5602141cb8 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h @@ -0,0 +1,136 @@ +#ifndef BOX_H +#define BOX_H + +#include +#include + +#include "grid.h" +#include "vertices.h" + +namespace grid +{ + +namespace ba = boost::adaptors; + +template +class Box +{ + public: + typedef GridRef GridProxy; + typedef typename GridProxy::Vertex Position; + + struct InternalTest; + struct BoundaryTest; + struct BoundsTest; + struct PositionToVertex; + + class FreudenthalLinkIterator; + typedef boost::iterator_range FreudenthalLinkRange; + + typedef VerticesIterator VI; + typedef boost::transformed_range + > VertexRange; + + // Topology interface + typedef typename GridProxy::Index Vertex; + typedef boost::transformed_range + > Link; + + + Box(): g_(0, Position()) {} + Box(const Position& shape): + g_(0, shape), to_(shape - Position::one()) {} + Box(const Position& shape, + const Position& from, + const Position& to): + g_(0, shape), from_(from), to_(to) {} + + + const Position& from() const { return from_; } + const Position& to() const { return to_; } + Position& from() { return from_; } + Position& to() { return to_; } + Position shape() const { return to_ - from_ + Position::one(); } + const Position& grid_shape() const { return g_.shape(); } + static unsigned dimension() { return D; } + + size_t size() const { size_t c = 1; for (unsigned i = 0; i < D; ++i) c *= (to_[i] - from_[i] + 1); return c; } + + VertexRange vertices() const { return boost::iterator_range(VI::begin(from_, to_), VI::end(from_, to_)) + | ba::transformed(position_to_vertex()); } + Link link(const Position& p) const { return FreudenthalLinkRange(FreudenthalLinkIterator::begin(p), FreudenthalLinkIterator::end(p)) + | ba::filtered(bounds_test()) + | ba::transformed(position_to_vertex()); } + Link link(const Vertex& v) const { return link(position(v)); } + + Box intersect(const Box& other) const; + bool intersects(const Box& other) const; + void merge(const Box& other); + + bool contains(const Position& p) const; + bool contains(const Vertex& v) const { return contains(position(v)); } + + bool boundary(const Position& p, bool degenerate = false) const; + bool boundary(const Vertex& v, bool deg = false) const { return boundary(position(v), deg); } + Box side(unsigned axis, bool upper) const; + + InternalTest internal_test() const { return InternalTest(*this); } + BoundaryTest boundary_test() const { return BoundaryTest(*this); } + BoundsTest bounds_test() const { return BoundsTest(*this); } + PositionToVertex position_to_vertex() const { return PositionToVertex(*this); } + + + void swap(Box& other) { g_.swap(other.g_); std::swap(from_, other.from_); std::swap(to_, other.to_); } + + bool operator==(const Box& other) const { return from_ == other.from_ && to_ == other.to_; } + + template + friend std::basic_ostream& + operator<<(std::basic_ostream& out, const Box& b) { out << "Box: " << b.from_ << " - " << b.to_ << " inside " << b.g_.shape(); return out; } + + struct InternalTest + { + InternalTest(const Box& box): box_(box) {} + bool operator()(const Vertex& v) const { return !box_.boundary(v); } + const Box& box_; + }; + + struct BoundaryTest + { + BoundaryTest(const Box& box): box_(box) {} + bool operator()(const Vertex& v) const { return box_.boundary(v); } + const Box& box_; + }; + + struct BoundsTest + { + BoundsTest(const Box& box): box_(box) {} + bool operator()(const Position& p) const { return box_.contains(p); } + bool operator()(const Vertex& v) const { return box_.contains(v); } + const Box& box_; + }; + + struct PositionToVertex + { + typedef Vertex result_type; + PositionToVertex(const Box& box): box_(box) {} + Vertex operator()(Position p) const { for (unsigned i = 0; i < D; ++i) p[i] %= box_.grid_shape()[i]; return box_.g_.index(p); } + const Box& box_; + }; + + // computes position inside the box (adjusted for the wrap-around, if need be) + Position position(const Vertex& v) const { Position p = g_.vertex(v); for (unsigned i = 0; i < D; ++i) if (p[i] < from()[i]) p[i] += grid_shape()[i]; return p; } + + private: + GridProxy g_; + Position from_, to_; +}; + +} + +#include "box.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp new file mode 100644 index 0000000000..f3af50c047 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp @@ -0,0 +1,141 @@ +template +grid::Box +grid::Box:: +intersect(const Box& other) const +{ + Position from, to; + for (unsigned i = 0; i < D; ++i) + { + from[i] = std::max(from_[i], other.from_[i]); + to[i] = std::min(to_[i], other.to_[i]); + } + + return Box(g_, from, to); +} + +template +bool +grid::Box:: +intersects(const Box& other) const +{ + for (unsigned i = 0; i < D; ++i) + if (std::max(from_[i], other.from_[i]) > std::min(to_[i], other.to_[i])) + return false; + + return true; +} + +template +bool +grid::Box:: +contains(const Position& p) const +{ + for (unsigned i = 0; i < D; ++i) + if (p[i] > to_[i] || p[i] < from_[i]) + return false; + return true; +} + +template +bool +grid::Box:: +boundary(const Position& p, bool degenerate) const +{ + for (unsigned i = 0; i < D; ++i) + { + if (degenerate && from_[i] == to_[i]) continue; + if (p[i] == from_[i] || p[i] == to_[i]) + return true; + } + + return false; +} + +template +grid::Box +grid::Box:: +side(unsigned axis, bool upper) const +{ + Box res(*this); + + if (upper) + res.from()[axis] = res.to()[axis]; + else + res.to()[axis] = res.from()[axis]; + + return res; +} + +template +void +grid::Box:: +merge(const Box& other) +{ + for (unsigned i = 0; i < D; ++i) + { + from_[i] = std::min(from_[i], other.from_[i]); + to_[i] = std::max(to_[i], other.to_[i]); + } +} + +/* Box::FreudenthalLinkIterator */ +template +class grid::Box::FreudenthalLinkIterator: + public boost::iterator_facade +{ + typedef boost::iterator_facade Parent; + + + public: + typedef typename Parent::value_type value_type; + typedef typename Parent::difference_type difference_type; + typedef typename Parent::reference reference; + + FreudenthalLinkIterator(): loc_(0), dir_(0) {} + FreudenthalLinkIterator(const Position& p, int loc = 0, int dir = 1): + p_(p), v_(p), loc_(loc), dir_(dir) {} + + static FreudenthalLinkIterator + begin(const Position& p) { FreudenthalLinkIterator it(p); ++it; return it; } + static FreudenthalLinkIterator + end(const Position& p) { return FreudenthalLinkIterator(p, 0, -1); } + + private: + void increment(); + bool equal(const FreudenthalLinkIterator& other) const { return v_ == other.v_; } + reference dereference() const { return v_; } + + friend class ::boost::iterator_core_access; + + private: + Position p_, v_; + int loc_; + int dir_; +}; + +template +void +grid::Box::FreudenthalLinkIterator:: +increment() +{ + loc_ += dir_; + if (loc_ == (1 << D)) + { + dir_ = -1; + loc_ += dir_; + } + + for (unsigned i = 0; i < D; ++i) + if (loc_ & (1 << i)) + v_[i] = p_[i] + dir_; + else + v_[i] = p_[i]; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h new file mode 100644 index 0000000000..c63fc7c598 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h @@ -0,0 +1,143 @@ +#ifndef GRID_H +#define GRID_H + +#include "point.h" + +namespace grid +{ + +template +struct Grid; + +template +struct GridRef +{ + public: + typedef C Value; + + typedef Point Vertex; + typedef size_t Index; + + public: + template + GridRef(C* data, const Point& shape, bool c_order = true): + data_(data), shape_(shape), c_order_(c_order) { set_stride(); } + + GridRef(Grid& g): + data_(g.data()), shape_(g.shape()), + c_order_(g.c_order()) { set_stride(); } + + template + C operator()(const Point& v) const { return data_[v*stride_]; } + + template + C& operator()(const Point& v) { return data_[v*stride_]; } + + C operator()(Index i) const { return data_[i]; } + C& operator()(Index i) { return data_[i]; } + + const Vertex& + shape() const { return shape_; } + + const C* + data() const { return data_; } + C* data() { return data_; } + + // Set every element to the given value + GridRef& operator=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] = value; return *this; } + GridRef& operator/=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] /= value; return *this; } + + Vertex vertex(Index idx) const { Vertex v; for (unsigned i = 0; i < D; ++i) { v[i] = idx / stride_[i]; idx %= stride_[i]; } return v; } + Index index(const Vertex& v) const { return v*stride_; } + + Index size() const { return size(shape()); } + void swap(GridRef& other) { std::swap(data_, other.data_); std::swap(shape_, other.shape_); std::swap(stride_, other.stride_); } + + bool c_order() const { return c_order_; } + + protected: + static Index + size(const Vertex& v) { Index res = 1; for (unsigned i = 0; i < D; ++i) res *= v[i]; return res; } + + void set_stride() + { + Index cur = 1; + if (c_order_) + for (unsigned i = D; i > 0; --i) { stride_[i-1] = cur; cur *= shape_[i-1]; } + else + for (unsigned i = 0; i < D; ++i) { stride_[i] = cur; cur *= shape_[i]; } + + } + void set_shape(const Vertex& v) { shape_ = v; set_stride(); } + void set_data(C* data) { data_ = data; } + void set_c_order(bool order) { c_order_ = order; } + + private: + C* data_; + Vertex shape_; + Vertex stride_; + bool c_order_; +}; + + +template +struct Grid: public GridRef +{ + public: + typedef GridRef Parent; + typedef typename Parent::Value Value; + typedef typename Parent::Index Index; + typedef Parent Reference; + + template + struct rebind { typedef Grid type; }; + + public: + template + Grid(const Point& shape, bool c_order = true): + Parent(new C[size(shape)], shape, c_order) + {} + + Grid(const Parent& g): + Parent(new C[size(g.shape())], g.shape(), + g.c_order()) { copy_data(g.data()); } + + template + Grid(const OtherGrid& g): + Parent(new C[size(g.shape())], + g.shape(), + g.c_order()) { copy_data(g.data()); } + + ~Grid() { delete[] Parent::data(); } + + template + Grid& operator=(const GridRef& other) + { + delete[] Parent::data(); + Parent::set_c_order(other.c_order()); // NB: order needs to be set before the shape, to set the stride correctly + Parent::set_shape(other.shape()); + Index s = size(shape()); + Parent::set_data(new C[s]); + copy_data(other.data()); + return *this; + } + + using Parent::data; + using Parent::shape; + using Parent::operator(); + using Parent::operator=; + using Parent::size; + + private: + template + void copy_data(const OC* data) + { + Index s = size(shape()); + for (Index i = 0; i < s; ++i) + Parent::data()[i] = data[i]; + } +}; + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h new file mode 100644 index 0000000000..0e867c34aa --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h @@ -0,0 +1,132 @@ +#ifndef POINT_H +#define POINT_H + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace grid +{ + +template +class Point: public boost::array, + private boost::addable< Point // Point + Point + , boost::subtractable< Point // Point - Point + , boost::dividable2< Point, Coordinate_ // Point / Coordinate + , boost::multipliable2< Point, Coordinate_ // Point * Coordinate, Coordinate * Point + > > > > +{ + public: + typedef Coordinate_ Coordinate; + typedef boost::array ArrayParent; + + typedef Point LPoint; + typedef Point UPoint; + + template + struct rebind { typedef Point type; }; + + public: + Point() { for (unsigned i = 0; i < D; ++i) (*this)[i] = 0; } + Point(const ArrayParent& a): + ArrayParent(a) {} + template Point(const Point& p) { for (size_t i = 0; i < D; ++i) (*this)[i] = p[i]; } + template Point(const T* a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } + template Point(const std::vector& a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } + + static + unsigned dimension() { return D; } + + static Point zero() { return Point(); } + static Point one() { Point p; for (unsigned i = 0; i < D; ++i) p[i] = 1; return p; } + + LPoint drop(int dim) const { LPoint p; unsigned c = 0; for (unsigned i = 0; i < D; ++i) { if (i == dim) continue; p[c++] = (*this)[i]; } return p; } + UPoint lift(int dim, Coordinate x) const { UPoint p; for (unsigned i = 0; i < D+1; ++i) { if (i < dim) p[i] = (*this)[i]; else if (i == dim) p[i] = x; else if (i > dim) p[i] = (*this)[i-1]; } return p; } + + using ArrayParent::operator[]; + + Point& operator+=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] += y[i]; return *this; } + Point& operator-=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] -= y[i]; return *this; } + Point& operator*=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] *= a; return *this; } + Point& operator/=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] /= a; return *this; } + + Point operator-() const { Point res; for (unsigned i = 0; i < D; ++i) res[i] = -(*this)[i]; return res; } + + Coordinate norm() const { return (*this)*(*this); } + + std::ostream& operator<<(std::ostream& out) const { out << (*this)[0]; for (unsigned i = 1; i < D; ++i) out << " " << (*this)[i]; return out; } + std::istream& operator>>(std::istream& in); + + friend + Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } + + template + friend + Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } + + private: + friend class boost::serialization::access; + + template + void serialize(Archive& ar, const unsigned int version) { ar & boost::serialization::base_object(*this); } +}; + +template +std::istream& +Point:: +operator>>(std::istream& in) +{ + std::string point_str; + in >> point_str; // read until ' ' + std::stringstream ps(point_str); + + char x; + for (unsigned i = 0; i < dimension(); ++i) + { + ps >> (*this)[i]; + ps >> x; + } + + return in; +} + + +template +Coordinate norm2(const Point& p) +{ Coordinate res = 0; for (unsigned i = 0; i < D; ++i) res += p[i]*p[i]; return res; } + +template +std::ostream& +operator<<(std::ostream& out, const Point& p) +{ return p.operator<<(out); } + +template +std::istream& +operator>>(std::istream& in, Point& p) +{ return p.operator>>(in); } + +} + +namespace opts +{ + template + struct Traits; + + template + struct Traits< grid::Point > + { + static + std::string type_string() { return "POINT"; } + }; +} + + +#endif // POINT_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h new file mode 100644 index 0000000000..339782e8c4 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h @@ -0,0 +1,86 @@ +#ifndef VERTICES_H +#define VERTICES_H + +#include + +namespace grid +{ + +template +class VerticesIterator: + public boost::iterator_facade, + Vertex_, + boost::forward_traversal_tag, + Vertex_, + std::ptrdiff_t> +{ + typedef boost::iterator_facade Parent; + + + public: + typedef typename Parent::value_type value_type; + typedef typename Parent::difference_type difference_type; + typedef typename Parent::reference reference; + + typedef value_type Vertex; + typedef typename Vertex::Coordinate Coordinate; + + // upper bounds are non-inclusive + VerticesIterator(const Vertex& bounds): + to_(bounds - Vertex::one()) {} + + VerticesIterator(const Vertex& pos, + const Vertex& bounds): + pos_(pos), to_(bounds - Vertex::one()) {} + + VerticesIterator(const Vertex& pos, + const Vertex& from, + const Vertex& to): + pos_(pos), from_(from), + to_(to) {} + + + static VerticesIterator + begin(const Vertex& bounds) { return VerticesIterator(bounds); } + static VerticesIterator + end(const Vertex& bounds) { Vertex e; e[0] = bounds[0]; return VerticesIterator(e, bounds); } + + static VerticesIterator + begin(const Vertex& from, const Vertex& to) { return VerticesIterator(from, from, to); } + static VerticesIterator + end(const Vertex& from, const Vertex& to) { Vertex e = from; e[0] = to[0] + 1; return VerticesIterator(e, from, to); } + + private: + void increment(); + bool equal(const VerticesIterator& other) const { return pos_ == other.pos_; } + reference dereference() const { return pos_; } + + friend class ::boost::iterator_core_access; + + private: + Vertex pos_; + Vertex from_; + Vertex to_; +}; + +} + +template +void +grid::VerticesIterator:: +increment() +{ + unsigned j = Vertex::dimension() - 1; + while (j > 0 && pos_[j] == to_[j]) + { + pos_[j] = from_[j]; + --j; + } + ++pos_[j]; +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h new file mode 100644 index 0000000000..516f27b2d1 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include + +namespace dionysus +{ + + +template +class MatrixFiltrationCell; + + +// adapt Matrix as a Filtration to make it possible to feed into reduction algorithms +template +class MatrixFiltration +{ + public: + using Matrix = Matrix_; + using CellValue = CellValue_; + using Dimensions = std::vector; + using Values = std::vector; + using Cell = MatrixFiltrationCell; + + + public: + MatrixFiltration(Matrix m, Dimensions dimensions, Values values): + m_(std::move(m)), + dimensions_(dimensions), + values_(values) { assert(m_->size() == dimensions_.size()); assert(m_->size() == values_.size()); } + + Cell operator[](size_t i) const { return Cell(this, i); } + size_t size() const { return m_.size(); } + + size_t index(const Cell& c) const; + + Cell begin() const { return Cell(this, 0); } + Cell end() const { return Cell(this, size()); } + + const Dimensions& dimensions() const { return dimensions_; } + const Values& values() const { return values_; } + + private: + Matrix m_; + Dimensions dimensions_; + Values values_; + + friend class MatrixFiltrationCell; +}; + + +template +class MatrixFiltrationCell +{ + public: + using MatrixFiltration = MatrixFiltration_; + using Matrix = typename MatrixFiltration::Matrix; + using Data = typename MatrixFiltration::CellValue; + using Field = typename Matrix::Field; + + template + using Entry = ChainEntry; + + template + using BoundaryChain = std::vector>; + + public: + MatrixFiltrationCell(const MatrixFiltration* mf, size_t i): + mf_(mf), i_(i) {} + + short unsigned dimension() const { return mf_->dimensions_[i_]; } + const Data& data() const { return mf_->values_[i_]; } + + bool operator==(const MatrixFiltrationCell& other) const { return i_ == other.i_; } + bool operator!=(const MatrixFiltrationCell& other) const { return i_ != other.i_; } + + BoundaryChain<> boundary() const + { + BoundaryChain<> bdry; + for (auto& entry : (mf_->m_)[i_]) + bdry.emplace_back(Entry<> { entry.e, MatrixFiltrationCell(mf_, entry.i) }); + return bdry; + } + + template + BoundaryChain boundary(const Field_& field) const + { + BoundaryChain bdry; + for (auto& entry : (mf_->m_)[i_]) + bdry.emplace_back(Entry { field.init(entry.e), MatrixFiltrationCell(mf_, entry.i) }); + return bdry; + } + + // iterator interface + MatrixFiltrationCell operator++(int) { MatrixFiltrationCell copy = *this; i_++; return copy; } + MatrixFiltrationCell& operator++() { ++i_; return *this; } + + const MatrixFiltrationCell& operator*() const { return *this; } + MatrixFiltrationCell& operator*() { return *this; } + + size_t i() const { return i_; } + + friend + std::ostream& operator<<(std::ostream& out, const MatrixFiltrationCell& c) + { out << c.i_; return out; } + + private: + const MatrixFiltration* mf_ = nullptr; + size_t i_; +}; + +template +size_t +MatrixFiltration::index(const Cell& c) const +{ + return c.i(); +} + +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h new file mode 100644 index 0000000000..3d83d4ae44 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h @@ -0,0 +1,145 @@ +#ifndef DIONYSUS_OMNI_FIELD_REDUCTION_H +#define DIONYSUS_OMNI_FIELD_REDUCTION_H + +#include +#include + +#include "reduction.h" // for unpaired +#include "fields/q.h" +#include "fields/zp.h" +#include "chain.h" + +namespace dionysus +{ + +template, class Q_ = ::dionysus::Q<>, class Zp_ = ::dionysus::ZpField> +class OmniFieldPersistence +{ + public: + using Index = Index_; + using Q = Q_; + using Field = Q; + using Comparison = Comparison_; + + using BaseElement = typename Q::BaseElement; + using Zp = Zp_; + using Zps = std::unordered_map; + + using QElement = typename Q::Element; + using QEntry = ChainEntry; + using QChain = std::vector; + + using ZpElement = typename Zp::Element; + using ZpEntry = ChainEntry; + using ZpChain = std::vector; + + using QChains = std::vector; + using ZpChains = std::unordered_map>; + + using QLows = std::unordered_map; + using ZpLows = std::unordered_map>; + + using QPairs = std::vector; + using ZpPairs = std::unordered_map>; + + using Factors = std::vector; + + using Specials = std::unordered_map>; + + const Field& field() const { return q_; } + + void sort(QChain& c) { std::sort(c.begin(), c.end(), + [this](const QEntry& e1, const QEntry& e2) + { return this->cmp_(e1.index(), e2.index()); }); } + + template + void add(const ChainRange& chain) { return add(QChain(std::begin(chain), std::end(chain))); } + void add(QChain&& chain); + + void reserve(size_t s) { q_chains_.reserve(s); q_pairs_.reserve(s); } + size_t size() const { return q_pairs_.size(); } + + void reduce(ZpChain& zp_chain, BaseElement p); + ZpChain convert(const QChain& c, const Zp& field) const; + bool special(Index i, BaseElement p) const { auto it = zp_chains_.find(i); if (it == zp_chains_.end()) return false; if (it->second.find(p) == it->second.end()) return false; return true; } + Specials specials() const + { + Specials specials; + for (auto& x : zp_chains_) + for (auto& y : x.second) + specials[x.first].push_back(y.first); + return specials; + } + + const Zp& zp(BaseElement p) const { auto it = zps_.find(p); if (it != zps_.end()) return it->second; return zps_.emplace(p, Zp(p)).first->second; } + + static Factors factor(BaseElement x); + + const QChains& q_chains() const { return q_chains_; } + const ZpChains& zp_chains() const { return zp_chains_; } + + // This is a bit of a hack; it takes advantage of the fact that zp(p) + // generates field on-demand and memoizes them. So there is an entry in + // zps_ only if something special happened over the prime. + Factors primes() const { Factors result; result.reserve(zps_.size()); for (auto& x : zps_) result.push_back(x.first); return result; } + + // TODO: no skip support for now + bool skip(Index) const { return false; } + void add_skip() {} + void set_skip(Index, bool flag = true) {} + + Index pair(Index i, BaseElement p) const; + void set_pair(Index i, Index j); + void set_pair(Index i, Index j, BaseElement p); + static const Index unpaired() { return Reduction::unpaired; } + + private: + QChains q_chains_; + ZpChains zp_chains_; + + QLows q_lows_; + ZpLows zp_lows_; + + QPairs q_pairs_; + ZpPairs zp_pairs_; + + Q q_; + mutable Zps zps_; + + Comparison cmp_; +}; + +// Make OmniFieldPersistence act like a ReducedMatrix (e.g., for the purpose of constructing a persistence diagram) +template +struct PrimeAdapter +{ + using Persistence = OmniFieldPersistence; + using Prime = typename Persistence::BaseElement; + using Index = typename Persistence::Index; + + PrimeAdapter(const Persistence& persistence, Prime p): + persistence_(persistence), p_(p) {} + + bool skip(Index i) const { return persistence_.skip(i); } + + size_t size() const { return persistence_.size(); } + Index pair(Index i) const { return persistence_.pair(i, p_); } + static const Index unpaired() { return Persistence::unpaired(); } + + const Persistence& persistence_; + Prime p_; +}; + +template +PrimeAdapter +prime_adapter(const OmniFieldPersistence& persistence, + typename PrimeAdapter::Prime p) +{ + return PrimeAdapter(persistence, p); +} + +} // dionysus + +#include "omni-field-persistence.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp new file mode 100644 index 0000000000..68d5fbede7 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp @@ -0,0 +1,250 @@ +template +void +dionysus::OmniFieldPersistence:: +add(QChain&& chain) +{ + sort(chain); + + q_chains_.emplace_back(std::move(chain)); + q_pairs_.emplace_back(unpaired()); + Index i = q_chains_.size() - 1; + + QChain& c = q_chains_.back(); + + auto reduce = [this,&c,i](BaseElement p) + { + auto zp_chain = convert(c, zp(p)); + + this->reduce(zp_chain, p); + + if (!zp_chain.empty()) + { + auto l = zp_chain.back().index(); + zp_lows_[l].emplace(p,i); + set_pair(l,i,p); + } + + zp_chains_[i].emplace(p, std::move(zp_chain)); // empty chain is still a valid indicator that we don't need to bother with this field + }; + + // reduce + auto entry_cmp = [this](const QEntry& e1, const QEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; + while (!c.empty()) + { + auto& low = c.back(); + + auto e = low.element(); + auto l = low.index(); + assert(!q_.is_zero(e)); + if (e != q_.id()) + { + auto factors = factor(q_.numerator(e)); + for (auto p : factors) + { + if (!special(i, p)) // there is already a dedicated column over p + reduce(p); + } + } + + auto it_zp = zp_lows_.find(l); + if (it_zp != zp_lows_.end()) + for (auto& x : it_zp->second) + { + auto p = x.first; + if (!special(i,p)) + reduce(p); + } + + auto it_q = q_lows_.find(l); + if (it_q != q_lows_.end()) + { + Index j = it_q->second; + + // add the primes from j to i + auto it_zp = zp_chains_.find(j); + if (it_zp != zp_chains_.end()) + for (auto& x : it_zp->second) + { + auto p = x.first; + if (!special(i,p)) + reduce(p); + } + + // reduce over Q + auto j_chain = q_chains_[j]; + auto j_e = j_chain.back().element(); + + auto m = q_.neg(q_.div(e,j_e)); + Chain::addto(c, m, j_chain, q_, entry_cmp); + assert(c.empty() || !q_.is_zero(c.back().element())); + } else + { + q_lows_.emplace(l,i); + set_pair(l,i); + break; + } + } +} + +template +void +dionysus::OmniFieldPersistence:: +reduce(ZpChain& zp_chain, BaseElement p) +{ + auto& field = zp(p); + + auto entry_cmp = [this](const ZpEntry& e1, const ZpEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; + + while (!zp_chain.empty()) + { + auto& low = zp_chain.back(); + auto j = low.index(); + + auto it = zp_lows_.find(j); + if (it != zp_lows_.end()) + { + auto it2 = it->second.find(p); + if (it2 != it->second.end()) + { + const ZpChain& co = zp_chains_[it2->second][p]; + + auto m = field.neg(field.div(low.element(), co.back().element())); + assert(m < p); + Chain::addto(zp_chain, m, co, field, entry_cmp); + continue; + } + } + + auto qit = q_lows_.find(j); + if (qit == q_lows_.end() || special(qit->second, p)) // no valid pivot over Q + return; + + // TODO: this could be optimized (add and convert on the fly) + auto& q_chain = q_chains_[qit->second]; + assert(q_chain.empty() || !q_.is_zero(q_chain.back().element())); + + auto co = convert(q_chain, field); + auto m = field.neg(field.div(low.element(), co.back().element())); + Chain::addto(zp_chain, m, co, field, entry_cmp); + + assert(!zp_chain.empty() || zp_chain.back().index() != j); + } +} + +template +typename dionysus::OmniFieldPersistence::ZpChain +dionysus::OmniFieldPersistence:: +convert(const QChain& c, const Zp& field) const +{ + ZpChain result; + result.reserve(c.size()); + auto p = field.prime(); + for (auto& x : c) + { + auto num = q_.numerator(x.element()) % p; + if (num != 0) + { + while (num < 0) num += p; + auto denom = q_.denominator(x.element()) % p; + while (denom < 0) denom += p; + assert(denom % p != 0); + result.emplace_back(field.div(num, denom), x.index()); + } + } + return result; +} + + +template +typename dionysus::OmniFieldPersistence::Factors +dionysus::OmniFieldPersistence:: +factor(BaseElement x) +{ + if (x < 0) + x = -x; + Factors result; + + if (Q::is_prime(x)) + { + result.push_back(x); + return result; + } + + BaseElement p { 2 }; + while (p*p <= x) + { + if (x % p == 0) + { + result.push_back(p); + do { x /= p; } while (x % p == 0); + if (Q::is_prime(x)) + { + result.push_back(x); + break; + } + } + ++p; + } + if (x > 1) + result.push_back(x); + + return result; +} + +template +typename dionysus::OmniFieldPersistence::Index +dionysus::OmniFieldPersistence:: +pair(Index i, BaseElement p) const +{ + if (p == 1) + return q_pairs_[i]; + else + { + auto it = zp_pairs_.find(p); + if (it == zp_pairs_.end()) + return q_pairs_[i]; + else + { + auto pit = it->second.find(i); + if (pit == it->second.end()) + return q_pairs_[i]; + else + return pit->second; + } + } +} + +template +void +dionysus::OmniFieldPersistence:: +set_pair(Index i, Index j, BaseElement p) +{ + auto& pairs = zp_pairs_[p]; + pairs[i] = j; + pairs[j] = i; +} + +template +void +dionysus::OmniFieldPersistence:: +set_pair(Index i, Index j) +{ + q_pairs_[i] = j; + q_pairs_[j] = i; + + auto it = zp_chains_.find(j); + if (it == zp_chains_.end()) + return; + + auto& chains = it->second; + for (auto& x : chains) + { + auto p = x.first; + auto& chain = x.second; + if (chain.empty()) + { + zp_pairs_[p][j] = unpaired(); + zp_pairs_[p][i] = unpaired(); + } + } +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h new file mode 100644 index 0000000000..1a9bbf71bf --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h @@ -0,0 +1,499 @@ +/** + * Author: Dmitriy Morozov + * The interface is heavily influenced by GetOptPP (https://code.google.com/p/getoptpp/). + * The parsing logic is from ProgramOptions.hxx (https://github.com/Fytch/ProgramOptions.hxx). + * + * History: + * - 2015-06-01: added Traits<...>::type_string() for long, unsigned long + * - ... + * - 2018-04-27: replace parsing logic with the one from ProgramOptions.hxx to + * make the parser compliant with [GNU Program Argument Syntax + * Conventions](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html) + * - 2018-05-11: add dashed_non_option(), to accept arguments that are negative numbers + */ + +#ifndef OPTS_OPTS_H +#define OPTS_OPTS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace opts { + +// Converters +template +struct Converter +{ + Converter() {} + static + bool convert(const std::string& val, T& res) + { + std::istringstream iss(val); + iss >> res; + return !iss.fail() && iss.eof(); + } +}; + +// Type +template +struct Traits +{ + static std::string type_string() { return "UNKNOWN TYPE"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "SHORT INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "LONG"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "UNSIGNED INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "SHORT UNSIGNED INT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "UNSIGNED LONG"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "FLOAT"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "DOUBLE"; } +}; + +template<> +struct Traits +{ + static std::string type_string() { return "STRING"; } +}; + + +struct BasicOption +{ + using IsShort = std::function; + + BasicOption(char s_, + std::string l_, + std::string default_, + std::string type_, + std::string help_): + s(s_), l(l_), d(default_), t(type_), help(help_) {} + virtual ~BasicOption() {} + + int long_size() const { return l.size() + 1 + t.size(); } + + void output(std::ostream& out, int max_long) const + { + out << " "; + if (s) + out << '-' << s << ", "; + else + out << " "; + + out << "--" << l << ' '; + + if (!t.empty()) + out << t; + + for (int i = long_size(); i < max_long; ++i) + out << ' '; + + out << " " << help; + + if (!d.empty()) + { + out << " [default: " << d << "]"; + } + out << '\n'; + } + + virtual bool flag() const { return false; } + virtual bool parse(int argc, char** argv, int& i, int j, IsShort is_short); + virtual bool set(std::string arg) =0; + + char s; + std::string l; + std::string d; + std::string t; + std::string help; +}; + +// Option +template +struct OptionContainer: public BasicOption +{ + OptionContainer(char s_, + const std::string& l_, + T& var_, + const std::string& help_, + const std::string& type_ = Traits::type_string()): + BasicOption(s_, l_, default_value(var_), type_, help_), + var(&var_) {} + + static + std::string default_value(const T& def) + { + std::ostringstream oss; + oss << def; + return oss.str(); + } + + bool set(std::string s) override { return Converter::convert(s, *var); } + + T* var; +}; + +template<> +struct OptionContainer: public BasicOption +{ + OptionContainer(char s_, + const std::string& l_, + bool& var_, + const std::string& help_): + BasicOption(s_, l_, "", "", help_), + var(&var_) { *var = false; } + + bool parse(int, char**, int&, int, IsShort) override { *var = true; return true; } + bool set(std::string) override { return true; } + bool flag() const override { return true; } + + bool* var; +}; + +template +struct OptionContainer< std::vector >: public BasicOption +{ + OptionContainer(char s_, + const std::string& l_, + std::vector& var_, + const std::string& help_, + const std::string& type_ = "SEQUENCE"): + BasicOption(s_, l_, default_value(var_), type_, help_), + var(&var_), first(true) { } + + static + std::string default_value(const std::vector& def) + { + std::ostringstream oss; + oss << "("; + if (def.size()) + oss << def[0]; + for (size_t i = 1; i < def.size(); ++i) + oss << ", " << def[i]; + oss << ")"; + return oss.str(); + } + + bool set(std::string s) override + { + if (first) + { + var->clear(); + first = false; + } + + T x; + bool result = Converter::convert(s,x); + var->emplace_back(std::move(x)); + return result; + } + + std::vector* var; + mutable bool first; +}; + + +template +std::unique_ptr +Option(char s, const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help)}; } + +template +std::unique_ptr +Option(char s, const std::string& l, T& var, + const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help, type)}; } + +template +std::unique_ptr +Option(const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help)}; } + +template +std::unique_ptr +Option(const std::string& l, T& var, + const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help, type)}; } + +// PosOption +template +struct PosOptionContainer +{ + PosOptionContainer(T& var_): + var(&var_) {} + + bool parse(std::list& args) const + { + if (args.empty()) + return false; + + bool result = Converter::convert(args.front(), *var); + if (!result) + std::cerr << "error: failed to parse " << args.front() << '\n'; + args.pop_front(); + return result; + } + + T* var; +}; + +template +PosOptionContainer +PosOption(T& var) { return PosOptionContainer(var); } + + +// Options +struct Options +{ + Options(): + failed(false) {} + + inline + Options& operator>>(std::unique_ptr opt); + template + Options& operator>>(const PosOptionContainer& poc); + + operator bool() { return !failed; } + + + friend + std::ostream& + operator<<(std::ostream& out, const Options& ops) + { + int max_long = 0; + for (auto& cur : ops.options) + { + int cur_long = cur->long_size(); + if (cur_long > max_long) + max_long = cur_long; + } + + out << "Options:\n"; + for (auto& cur : ops.options) + cur->output(out, max_long); + + return out; + } + + bool parse(int argc, char** argv); + + void unrecognized_option(std::string arg) const + { + std::cerr << "error: unrecognized option " << arg << '\n'; + } + + static bool dashed_non_option(char* arg, BasicOption::IsShort is_short) + { + return arg[ 0 ] == '-' + && (std::isdigit(arg[ 1 ]) || arg[ 1 ] == '.') + && !is_short(arg[ 1 ]); + } + + private: + std::list args; + std::list> options; + bool failed; +}; + +bool +BasicOption::parse(int argc, char** argv, int& i, int j, IsShort is_short) +{ + char* argument; + char* cur_arg = argv[i]; + // -v... + if (argv[i][j] == '\0') + { + // -v data + if (i + 1 < argc && (argv[i+1][0] != '-' || Options::dashed_non_option(argv[i+1], is_short))) + { + ++i; + argument = argv[i]; + } else + { + std::cerr << "error: cannot find the argument; ignoring " << argv[i] << '\n'; + return false; + } + } else if (argv[i][j] == '=') + { + // -v=data + argument = &argv[i][j+1]; + } else if( j == 2 ) { // only for short options + // -vdata + argument = &argv[i][j]; + } else + { + std::cerr << "error: unexpected character \'" << argv[i][j] << "\' ignoring " << argv[i] << '\n'; + return false; + } + bool result = set(argument); + if (!result) + std::cerr << "error: failed to parse " << argument << " in " << cur_arg << '\n'; + return result; +} + +bool +Options::parse(int argc, char** argv) +{ + std::map short_opts; + std::map long_opts; + + for (auto& opt : options) + { + if (opt->s) + short_opts[opt->s] = opt.get(); + + long_opts[opt->l] = opt.get(); + } + + auto is_short = [&short_opts](char c) -> bool { return short_opts.find(c) != short_opts.end(); }; + + for (int i = 1; i < argc; ++i) + { + if( argv[ i ][ 0 ] == '\0' ) + continue; + if( argv[ i ][ 0 ] != '-' || dashed_non_option(argv[i], is_short)) + args.push_back(argv[i]); + else + { + // -... + if( argv[ i ][ 1 ] == '\0' ) + { + // - + args.push_back(argv[i]); + } else if( argv[ i ][ 1 ] == '-' ) + { + if( argv[ i ][ 2 ] == '\0' ) + { + // -- + while( ++i < argc ) + args.push_back(argv[i]); + } else { + // --... + char* first = &argv[ i ][ 2 ]; + char* last = first; + for(; *last != '=' && *last != '\0'; ++last); + if (first == last) + { + failed = true; + unrecognized_option(argv[i]); + } else + { + auto opt_it = long_opts.find(std::string{first,last}); + if (opt_it == long_opts.end()) + { + failed = true; + unrecognized_option(argv[i]); + } else + { + failed |= !opt_it->second->parse(argc, argv, i, last - argv[i], is_short); + } + } + } + } else + { + // -f... + auto opt_it = short_opts.find(argv[i][1]); + if (opt_it == short_opts.end()) + { + failed = true; + unrecognized_option(argv[i]); + } else if (opt_it->second->flag()) + { + opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag + + // -fgh + char c; + for(int j = 1; (c = argv[i][j]) != '\0'; ++j) + { + if (!std::isprint(c) || c == '-') + { + failed = true; + std::cerr << "error: invalid character\'" << c << " ignoring " << &argv[i][j] << '\n'; + break; + } + opt_it = short_opts.find(c); + if (opt_it == short_opts.end()) + { + failed = true; + unrecognized_option("-" + std::string(1, c)); + continue; + } + if (!opt_it->second->flag()) + { + failed = true; + std::cerr << "error: non-void options not allowed in option packs; ignoring " << c << '\n'; + continue; + } + opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag + } + } else + { + failed |= !opt_it->second->parse(argc, argv, i, 2, is_short); + } + } + } + } + + return !failed; +} + +Options& +Options::operator>>(std::unique_ptr opt) +{ + options.emplace_back(std::move(opt)); + return *this; +} + +template +Options& +Options::operator>>(const PosOptionContainer& poc) +{ + if (!failed) + failed = !poc.parse(args); + return *this; +} + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h new file mode 100644 index 0000000000..5f26bd2a1b --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h @@ -0,0 +1,64 @@ +#ifndef DIONYSUS_ORDINARY_PERSISTENCE_H +#define DIONYSUS_ORDINARY_PERSISTENCE_H + +#include "reduced-matrix.h" + +namespace dionysus +{ + +/* Move this into a ReducedMatrix class */ + +// Ordinary D -> R reduction +template, + template class... Visitors> +using OrdinaryPersistence = ReducedMatrix; + +// No negative optimization +template> +struct NoNegative +{ + template + struct Visitor: public EmptyVisitor + { + template + void chain_initialized(Self* matrix, Chain& c) + { + for (auto cur = std::begin(c); cur != std::end(c); ++cur) + { + Index i = cur->index(); + Index p = matrix->pair(i); + if (!(p == Self::unpaired() || (*matrix)[i].empty())) + c.erase(cur--); + } + } + }; + + template + using V2 = EmptyVisitor; +}; + +template, + template class... Visitors> +using OrdinaryPersistenceNoNegative = ReducedMatrix::template Visitor, + Visitors...>; + +// TODO: add clearing optimization (possibly bake it into the code itself) + +template, + template class... Visitors> +using FastPersistence = ReducedMatrix::template Visitor, + //Clearing::template Visitor, // FIXME + Visitors...>; + + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h new file mode 100644 index 0000000000..81c066bda6 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h @@ -0,0 +1,78 @@ +#ifndef DIONYSUS_PAIR_RECORDER_H +#define DIONYSUS_PAIR_RECORDER_H + +namespace dionysus +{ + +template +struct PairRecorder: public Persistence_ +{ + typedef Persistence_ Persistence; + typedef typename Persistence::Index Index; + + + using Persistence::Persistence; + + template + Index add(const ChainRange& chain) + { + Index p = Persistence::add(chain); + pairs_.push_back(p); + if (p != unpaired()) + pairs_[p] = pairs_.size() - 1; + + return p; + } + + Index pair(Index i) const { return pairs_[i]; } + + void resize(size_t s) { Persistence::resize(s); pairs_.resize(s, unpaired()); } + size_t size() const { return pairs_.size(); } + static const Index unpaired() { return Reduction::unpaired; } + + std::vector pairs_; +}; + +template +struct PairChainRecorder: public PairRecorder +{ + using Persistence = Persistence_; + using Parent = PairRecorder; + using Index = typename Persistence_::Index; + using Chain = typename Persistence_::Chain; + + using Parent::Parent; + + template + Index add(const ChainRange& chain) + { + auto p_chain = Persistence::add(chain, keep_cocycles); + Index p = std::get<0>(p_chain); + + pairs_.push_back(p); + chains_.emplace_back(); + + if (p != unpaired()) + { + pairs_[p] = pairs_.size() - 1; + chains_[p] = std::move(std::get<1>(p_chain)); + } + + return p; + } + + using Parent::unpaired; + + Index pair(Index i) const { return pairs_[i]; } + const Chain& chain(Index i) const { return chains_[i]; } // chain that dies at i + void resize(size_t s) { Parent::resize(s); chains_.resize(s); } + + std::vector chains_; + using Parent::pairs_; + + bool keep_cocycles = true; +}; + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h new file mode 100644 index 0000000000..f7a04b130d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h @@ -0,0 +1,170 @@ +#ifndef DIONYSUS_REDUCED_MATRIX_H +#define DIONYSUS_REDUCED_MATRIX_H + +#include +#include + +#include "chain.h" +#include "reduction.h" + +namespace dionysus +{ + +template, template class... Visitors> +class ReducedMatrix +{ + public: + typedef ReducedMatrix Self; + + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef std::tuple...> VisitorsTuple; + template + using Visitor = std::tuple_element; + + typedef typename Field::Element FieldElement; + typedef ChainEntry Entry; + typedef std::vector Chain; + + typedef std::vector Chains; + typedef std::vector Indices; + typedef std::vector SkipFlags; + + public: + ReducedMatrix(const Field& field): + field_(field) {} + + ReducedMatrix(const Field& field, + const Comparison& cmp, + const Visitors&... visitors): + field_(field), + cmp_(cmp), + visitors_(visitors...) {} + + ReducedMatrix(Field&& field, + Comparison&& cmp, + Visitors&&... visitors): + field_(std::move(field)), + cmp_(std::move(cmp)), + visitors_(visitors...) {} + + ReducedMatrix(Self&& m) = default; + ReducedMatrix(const Self& m) = default; + + template class... OtherVisitors> + ReducedMatrix(ReducedMatrix&& other): + field_(other.field_), + cmp_(other.cmp_), + reduced_(std::move(other.reduced_)), + pairs_(std::move(other.pairs_)), + skip_(std::move(other.skip_)) {} + + template + Index add(const ChainRange& chain) { return add(Chain(std::begin(chain), std::end(chain))); } + Index add(Chain&& chain); + + template + void set(Index i, const ChainRange& chain) { return set(i, Chain(std::begin(chain), std::end(chain))); } + void set(Index i, Chain&& chain); + + Index reduce(Index i); + Index reduce(Chain& c) { return reduce(c, reduced_, pairs_); } + template + Index reduce(Chain& c, const ChainsLookup& chains, const LowLookup& low); + + Index reduce_upto(Index i); // TODO + + size_t size() const { return pairs_.size(); } + void clear() { Chains().swap(reduced_); Indices().swap(pairs_); } + + void sort(Chain& c) { std::sort(c.begin(), c.end(), [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }); } + + const Chain& operator[](Index i) const { return reduced_[i]; } + Index pair(Index i) const { return pairs_[i]; } + void set_pair(Index i, Index j) { pairs_[i] = j; pairs_[j] = i; } + + Chain& column(Index i) { return reduced_[i]; } + + bool skip(Index i) const { return skip_[i]; } + void add_skip(); + void set_skip(Index i, bool flag = true) { skip_[i] = flag; } + + const Field& field() const { return field_; } + const Comparison& cmp() const { return cmp_; } + void reserve(size_t s) { reduced_.reserve(s); pairs_.reserve(s); } + void resize(size_t s); + + const Chains& columns() const { return reduced_; } + + template + Visitor& visitor() { return std::get(visitors_); } + + static const Index unpaired() { return Reduction::unpaired; } + + private: + template class... Vs> + friend class ReducedMatrix; // let's all be friends + + public: + // Visitors::chain_initialized(c) + template + typename std::enable_if::type + visitors_chain_initialized(Chain& c) {} + + template + typename std::enable_if::type + visitors_chain_initialized(Chain& c) { std::get(visitors_).chain_initialized(this, c); visitors_chain_initialized(c); } + + // Visitors::addto(m, cl) + template + typename std::enable_if::type + visitors_addto(FieldElement m, Index cl) {} + + template + typename std::enable_if::type + visitors_addto(FieldElement m, Index cl) { std::get(visitors_).addto(this, m, cl); visitors_addto(m, cl); } + + // Visitors::reduction_finished(m, cl) + template + typename std::enable_if::type + visitors_reduction_finished() {} + + template + typename std::enable_if::type + visitors_reduction_finished() { std::get(visitors_).reduction_finished(this); visitors_reduction_finished(); } + + private: + Field field_; + Comparison cmp_; + Chains reduced_; // matrix R + Indices pairs_; + SkipFlags skip_; // indicates whether the column should be skipped (e.g., for relative homology) + VisitorsTuple visitors_; +}; + +/* Visitors */ + +// The prototypical visitor. Others may (and probably should) inherit from it. +template +struct EmptyVisitor +{ + EmptyVisitor() = default; + + template + EmptyVisitor(const EmptyVisitor&) {} + + + template + void chain_initialized(Self*, Chain& c) {} + + void addto(Self*, typename Field::Element m, Index cl) {} + void reduction_finished(Self*) {} +}; + +} + +#include "reduced-matrix.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp new file mode 100644 index 0000000000..3e4aca8f29 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp @@ -0,0 +1,78 @@ +template class... V> +void +dionysus::ReducedMatrix:: +resize(size_t s) +{ + reduced_.resize(s); + pairs_.resize(s, unpaired()); + skip_.resize(s, false); +} + +template class... V> +typename dionysus::ReducedMatrix::Index +dionysus::ReducedMatrix:: +add(Chain&& chain) +{ + // TODO: skip the computation entirely if we already know this is positive (in case of the clearing optimization) + Index i = pairs_.size(); + pairs_.emplace_back(unpaired()); + reduced_.emplace_back(); + skip_.push_back(false); + + set(i, std::move(chain)); + + return reduce(i); +} + +template class... V> +void +dionysus::ReducedMatrix:: +add_skip() +{ + pairs_.emplace_back(unpaired()); + reduced_.emplace_back(); + skip_.push_back(true); +} + +template class... V> +void +dionysus::ReducedMatrix:: +set(Index i, Chain&& c) +{ + sort(c); + visitors_chain_initialized(c); + reduced_[i] = std::move(c); +} + +template class... V> +typename dionysus::ReducedMatrix::Index +dionysus::ReducedMatrix:: +reduce(Index i) +{ + Chain& c = column(i); + Index pair = reduce(c); + + if (pair != unpaired()) + pairs_[pair] = i; + + pairs_[i] = pair; + visitors_reduction_finished<>(); + + return pair; +} + +template class... V> +template +typename dionysus::ReducedMatrix::Index +dionysus::ReducedMatrix:: +reduce( Chain& c, + const ChainsLookup& chains, + const LowLookup& lows) +{ + auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; + return Reduction::reduce(c, chains, lows, field_, + [this](FieldElement m, Index cl) + { this->visitors_addto<>(m, cl); }, + entry_cmp); +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h new file mode 100644 index 0000000000..2afd333d41 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h @@ -0,0 +1,109 @@ +#ifndef DIONYSUS_REDUCTION_H +#define DIONYSUS_REDUCTION_H + +#include +#include +#include +#include +#include "chain.h" + +namespace dionysus +{ + +namespace detail +{ + +template +struct Unpaired +{ static constexpr Index value() { return std::numeric_limits::max(); } }; + +} + +template +struct Reduction +{ + typedef Index_ Index; + + template + using AddtoVisitor = std::function; + + template + struct CallToSub; + + static const Index unpaired; + + template> + static + Index reduce(Chain1& c, + const ChainsLookup& chains, + const LowLookup& lows, + const Field& field, + const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, + const Comparison& cmp = Comparison()) + { + typedef typename Field::Element FieldElement; + + while (!c.empty()) + { + //auto& low = c.back(); + auto& low = *(std::prev(c.end())); + Index l = low.index(); + Index cl = lows(l); + // std::cout << "idx: " << std::get<0>(cl) << ", " << std::get<1>(cl) << "\n"; + if (cl == unpaired) + return l; + else + { + // Reduce further + auto& co = chains(cl); + auto& co_low = co.back(); + FieldElement m = field.neg(field.div(low.element(), co_low.element())); + // c += m*co + Chain::addto(c, m, co, field, cmp); + visitor(m, cl); + } + } + return unpaired; + } + + template> + static + Index reduce(Chain1& c, + const std::vector& chains, + const std::vector& lows, + const Field& field, + const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, + const Comparison& cmp = Comparison()) + { + return reduce(c, + CallToSub(chains), + CallToSub(lows), + field, visitor, cmp); + } + + // This is a work-around a bug in GCC (should really be a lambda function) + template + struct CallToSub + { + CallToSub(const std::vector& items_): + items(items_) {} + const Item& operator()(Index i) const { return items[i]; } + const std::vector& items; + }; +}; + + +template +const Index +Reduction::unpaired = detail::Unpaired::value(); + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h new file mode 100644 index 0000000000..167a32779d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h @@ -0,0 +1,84 @@ +#ifndef RELATIVE_HOMOLOGY_ZIGZAG_H +#define RELATIVE_HOMOLOGY_ZIGZAG_H + +#include +#include + +#include "zigzag-persistence.h" + +namespace dionysus +{ + +namespace ba = boost::adaptors; + +template> +class RelativeHomologyZigzag +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef ZigzagPersistence ZZP; + typedef typename ZZP::IndexChain IndexChain; + typedef typename ZZP::FieldElement FieldElement; + typedef typename IndexChain::value_type ChainEntry; + + + typedef Comparison Cmp; + + RelativeHomologyZigzag(const Field& field, + const Comparison& cmp = Comparison()): + zzp_(field, cmp) + { + zzp_.add( IndexChain() ); // vertex w + ++zzp_op_; + ++zzp_cell_; + } + + template + void add_both(const ChainRange& chain); + + void remove_both(Index cell); + + // index of the absolute cell; chain = its boundary + template + Index add(Index cell, const ChainRange& chain); // add to the relative part + + Index remove(Index cell); // remove from the relative part + + const Field& field() const { return zzp_.field(); } + const Cmp& cmp() const { return zzp_.cmp(); } + + size_t alive_size() const { return zzp_.alive_size() - 1; } // -1 for the cone vertex + + static + const Index unpaired() { return ZZP::unpaired(); } + + private: + template + IndexChain relative_chain(Index cell, const ChainRange& chain) const; + + template + IndexChain absolute_chain(const ChainRange& chain) const; + + Index abs_index(Index idx) const { return absolute_.left.find(idx)->second; } + Index rel_index(Index idx) const { return relative_.left.find(idx)->second; } + Index decode_pair(Index pair); + + private: + ZZP zzp_; // underlying (cone) implementation + boost::bimap absolute_; // bimap between our cells and zzp absolute cells + boost::bimap relative_; // bimap between our cells and zzp relative cells + std::unordered_map op_map_; // map from zzp_op to our op + Index op_ = 0, + zzp_op_ = 0, + cell_ = 0, + zzp_cell_ = 0; +}; + +} + +#include "relative-homology-zigzag.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp new file mode 100644 index 0000000000..499807106c --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp @@ -0,0 +1,122 @@ +template +template +void +dionysus::RelativeHomologyZigzag:: +add_both(const ChainRange& chain) +{ + zzp_.add(absolute_chain(chain)); + op_map_.insert( { zzp_op_++, op_ } ); + absolute_.left.insert( { cell_, zzp_cell_++ } ); + + zzp_.add(relative_chain(cell_, chain)); + op_map_.insert( { zzp_op_++, op_ } ); + relative_.left.insert( { cell_, zzp_cell_++ } ); + + cell_++; + op_++; +} + +template +void +dionysus::RelativeHomologyZigzag:: +remove_both(Index cell) +{ + Index abs_cell = absolute_.left.find(cell)->second; + Index rel_cell = relative_.left.find(cell)->second; + + zzp_.remove(rel_cell); + zzp_.remove(abs_cell); + + absolute_.left.erase(cell); + relative_.left.erase(cell); + + op_map_.insert( { zzp_op_++, op_ } ); + op_map_.insert( { zzp_op_++, op_ } ); + + op_++; +} + +template +template +typename dionysus::RelativeHomologyZigzag::Index +dionysus::RelativeHomologyZigzag:: +add(Index cell, const ChainRange& chain) +{ + Index pair = zzp_.add(relative_chain(cell, chain)); + op_map_.insert( { zzp_op_++, op_++ } ); + relative_.left.insert( { cell, zzp_cell_++ } ); + + return decode_pair(pair); +} + + +template +typename dionysus::RelativeHomologyZigzag::Index +dionysus::RelativeHomologyZigzag:: +decode_pair(Index pair) +{ + if (pair == unpaired()) + return pair; + + Index decoded = op_map_.find(pair)->second; + op_map_.erase(pair); + return decoded; +} + +template +template +typename dionysus::RelativeHomologyZigzag::IndexChain +dionysus::RelativeHomologyZigzag:: +absolute_chain(const ChainRange& chain) const +{ + IndexChain res; + for (const auto& e : chain) + res.push_back(ChainEntry(e.element(), abs_index(e.index()))); + return res; +} + +template +template +typename dionysus::RelativeHomologyZigzag::IndexChain +dionysus::RelativeHomologyZigzag:: +relative_chain(Index cell, const ChainRange& chain) const +{ + // NB: to compute the signs correctly, + // this assumes that the cone vertex w is the last vertex in some total order + + typedef typename IndexChain::value_type ChainEntry; + + IndexChain res; + if (!chain.empty()) + { + for (const auto& e : chain) + res.push_back(ChainEntry(e.element(), rel_index(e.index()))); + + FieldElement a = field().id(); + if (chain.size() % 2 == 0) // TODO: double-check + a = field().neg(a); + res.push_back(ChainEntry(a, abs_index(cell))); // add the base space cell + } else + { + res.reserve(2); + res.push_back(ChainEntry(field().id(), abs_index(cell))); + res.push_back(ChainEntry(field().neg(field().id()), 0)); + } + return res; +} + + +template +typename dionysus::RelativeHomologyZigzag::Index +dionysus::RelativeHomologyZigzag:: +remove(Index cell) +{ + Index rel_cell = rel_index(cell); + Index pair = zzp_.remove(rel_cell); + pair = decode_pair(pair); + + op_map_.insert( { zzp_op_++, op_++ } ); + relative_.left.erase(cell); + + return pair; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h new file mode 100644 index 0000000000..c7ccb1189e --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h @@ -0,0 +1,147 @@ +#ifndef DIONYSUS_RIPS_H +#define DIONYSUS_RIPS_H + +#include +#include + +#include + +#include "simplex.h" + +namespace dionysus +{ + +/** + * Rips class + * + * Class providing basic operations to work with Rips complexes. It implements Bron-Kerbosch algorithm, + * and provides simple wrappers for various functions. + * + * Distances_ is expected to define types IndexType and DistanceType as well as + * provide operator()(...) which given two IndexTypes should return + * the distance between them. There should be methods begin() and end() + * for iterating over IndexTypes as well as a method size(). + */ +template > +class Rips +{ + public: + typedef Distances_ Distances; + typedef typename Distances::IndexType IndexType; + typedef typename Distances::DistanceType DistanceType; + + typedef Simplex_ Simplex; + typedef typename Simplex::Vertex Vertex; // should be the same as IndexType + typedef std::vector VertexContainer; + + typedef short unsigned Dimension; + + class Evaluator; + class Comparison; + + public: + Rips(const Distances& distances): + distances_(distances) {} + + // Calls functor f on each simplex in the k-skeleton of the Rips complex + template + void generate(Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + // Calls functor f on all the simplices of the Rips complex that contain the given vertex v + template + void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + // Calls functor f on all the simplices of the Rips complex that contain the given edge [u,v] + template + void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + // Calls functor f on all the simplices of the Rips complex that contain the given Simplex s + // (unlike the previous methods it does not call the functor on the Simplex s itself) + template + void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, + Iterator candidates_begin, Iterator candidates_end) const; + + + /* No Iterator argument means Iterator = IndexType and the range is [distances().begin(), distances().end()) */ + template + void generate(Dimension k, DistanceType max, const Functor& f) const + { generate(k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + template + void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f) const + { vertex_cofaces(v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + template + void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f) const + { edge_cofaces(u, v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + template + void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f) const + { cofaces(s, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } + + + const Distances& distances() const { return distances_; } + DistanceType max_distance() const; + + DistanceType distance(const Simplex& s1, const Simplex& s2) const; + + + template + static void bron_kerbosch(VertexContainer& current, + const VertexContainer& candidates, + typename VertexContainer::const_iterator excluded, + Dimension max_dim, + const NeighborTest& neighbor, + const Functor& functor, + bool check_initial = true); + + protected: + const Distances& distances_; +}; + +template +class Rips::Evaluator +{ + public: + typedef Simplex_ Simplex; + + Evaluator(const Distances& distances): + distances_(distances) {} + + DistanceType operator()(const Simplex& s) const; + + protected: + const Distances& distances_; +}; + +template +class Rips::Comparison +{ + public: + typedef Simplex_ Simplex; + + Comparison(const Distances& distances): + eval_(distances) {} + + bool operator()(const Simplex& s1, const Simplex& s2) const + { + DistanceType e1 = eval_(s1), + e2 = eval_(s2); + if (e1 == e2) + return s1.dimension() < s2.dimension(); + + return e1 < e2; + } + + protected: + Evaluator eval_; +}; + +} + +#include "rips.hpp" + +#endif // DIONYSUS_RIPS_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp new file mode 100644 index 0000000000..2fdda34a7a --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp @@ -0,0 +1,162 @@ +#include +#include +#include +#include + +#include +#include + +template +template +void +dionysus::Rips:: +generate(Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = empty + // candidates = everything + VertexContainer current; + VertexContainer candidates(bg, end); + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); +} + +template +template +void +dionysus::Rips:: +vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = [v] + // candidates = everything - [v] + VertexContainer current; current.push_back(v); + VertexContainer candidates; + for (Iterator cur = bg; cur != end; ++cur) + if (*cur != v && neighbor(v, *cur)) + candidates.push_back(*cur); + + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); +} + +template +template +void +dionysus::Rips:: +edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = [u,v] + // candidates = everything - [u,v] + VertexContainer current; current.push_back(u); current.push_back(v); + + VertexContainer candidates; + for (Iterator cur = bg; cur != end; ++cur) + if (*cur != u && *cur != v && neighbor(v,*cur) && neighbor(u,*cur)) + candidates.push_back(*cur); + + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); +} + +template +template +void +dionysus::Rips:: +cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const +{ + namespace ba = boost::adaptors; + + auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; + + // current = s + VertexContainer current(s.begin(), s.end()); + + // candidates = everything - s that is a neighbor of every vertex in the simplex + VertexContainer candidates; + boost::set_difference(std::make_pair(bg, end) | + ba::filtered([this,&s,&neighbor](Vertex cur) + { for (auto& v : s) + if (!neighbor(v, cur)) + return false; + }), + s, + std::back_inserter(candidates)); + + bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f, false); +} + + +template +template +void +dionysus::Rips:: +bron_kerbosch(VertexContainer& current, + const VertexContainer& candidates, + typename VertexContainer::const_iterator excluded, + Dimension max_dim, + const NeighborTest& neighbor, + const Functor& functor, + bool check_initial) +{ + if (check_initial && !current.empty()) + functor(Simplex(current)); + + if (current.size() == static_cast(max_dim) + 1) + return; + + for (auto cur = std::next(excluded); cur != candidates.end(); ++cur) + { + current.push_back(*cur); + + VertexContainer new_candidates; + for (auto ccur = candidates.begin(); ccur != cur; ++ccur) + if (neighbor(*ccur, *cur)) + new_candidates.push_back(*ccur); + size_t ex = new_candidates.size(); + for (auto ccur = std::next(cur); ccur != candidates.end(); ++ccur) + if (neighbor(*ccur, *cur)) + new_candidates.push_back(*ccur); + excluded = new_candidates.begin() + (ex - 1); + + bron_kerbosch(current, new_candidates, excluded, max_dim, neighbor, functor); + current.pop_back(); + } +} + +template +typename dionysus::Rips::DistanceType +dionysus::Rips:: +distance(const Simplex& s1, const Simplex& s2) const +{ + DistanceType mx = 0; + for (auto a : s1) + for (auto b : s2) + mx = std::max(mx, distances_(a,b)); + return mx; +} + +template +typename dionysus::Rips::DistanceType +dionysus::Rips:: +max_distance() const +{ + DistanceType mx = 0; + for (IndexType a = distances_.begin(); a != distances_.end(); ++a) + for (IndexType b = std::next(a); b != distances_.end(); ++b) + mx = std::max(mx, distances_(a,b)); + return mx; +} + +template +typename dionysus::Rips::DistanceType +dionysus::Rips::Evaluator:: +operator()(const Simplex& s) const +{ + DistanceType mx = 0; + for (auto a = s.begin(); a != s.end(); ++a) + for (auto b = std::next(a); b != s.end(); ++b) + mx = std::max(mx, distances_(*a,*b)); + return mx; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h new file mode 100644 index 0000000000..e2481ce080 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h @@ -0,0 +1,54 @@ +#ifndef DIONYSUS_ROW_REDUCTION_H +#define DIONYSUS_ROW_REDUCTION_H + +#include "reduced-matrix.h" + +namespace dionysus +{ + +// Mid-level interface +template, template class... Visitors> +class RowReduction +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef ReducedMatrix Persistence; + + public: + RowReduction(const Field& field): + persistence_(field) {} + + RowReduction(const Field& field, + const Comparison& cmp, + const Visitors&... visitors): + persistence_(field, cmp, visitors...) {} + + template + void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); + + template + void operator()(const Filtration& f, const ReportPair& report_pair); + + template + void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } + + static void no_report_pair(int, Index, Index) {} + static void no_progress() {} + + const Persistence& + persistence() const { return persistence_; } + Persistence& persistence() { return persistence_; } + + private: + Persistence persistence_; +}; + +} + +#include "row-reduction.hpp" + +#endif + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp new file mode 100644 index 0000000000..edb1652872 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp @@ -0,0 +1,103 @@ +#include +namespace ba = boost::adaptors; + +template class... V> +template +void +dionysus::RowReduction:: +operator()(const Filtration& filtration, const ReportPair& report_pair) +{ + using Cell = typename Filtration::Cell; + (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); +} + +template class... V> +template +void +dionysus::RowReduction:: +operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) +{ + persistence_.resize(filtration.size()); + + typedef typename Persistence::Index Index; + typedef typename Persistence::FieldElement Element; + typedef typename Persistence::Chain Chain; + typedef typename Filtration::Cell Cell; + typedef ChainEntry CellChainEntry; + typedef ChainEntry ChainEntry; + + std::vector rows(persistence_.size()); + + auto& field = persistence_.field(); + + // fill the matrix + Index i = 0; + for(auto& c : filtration) + { + progress(); + + if (relative(c)) + { + persistence_.set_skip(i); + ++i; + continue; + } + + persistence_.set(i, c.boundary(field) | + ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | + ba::transformed([this,&filtration](const CellChainEntry& e) + { return ChainEntry(e.element(), filtration.index(e.index())); })); + if (!persistence_[i].empty()) + { + auto& x = persistence_[i].back(); + rows[x.index()].emplace_back(x.element(),i); + } + ++i; + } + + auto entry_cmp = [this](const ChainEntry& e1, const ChainEntry& e2) { return this->persistence_.cmp()(e1.index(), e2.index()); }; + + // reduce the matrix from the bottom up + for (auto it = rows.rbegin(); it != rows.rend(); ++it) + { + auto& row = *it; + Index r = rows.rend() - it - 1; + + if (row.empty()) + continue; + + // add the first column to every other column + Index c = row.front().index(); + Element e = row.front().element(); + Chain& first = persistence_.column(c); + for (size_t i = 1; i < row.size(); ++i) + { + Index cur_idx = row[i].index(); + Element cur_elem = row[i].element(); + Chain& cur = persistence_.column(cur_idx); + if (cur.empty()) // zeroed out by the clearing optimization + continue; + + Element m = field.neg(field.div(cur_elem, e)); + // cur += m*first + ::dionysus::Chain::addto(cur, m, first, field, entry_cmp); + + // update row + if (!cur.empty()) + { + ChainEntry ce = cur.back(); + auto& new_row = rows[ce.index()]; + new_row.emplace_back(ce.element(), cur_idx); + if (entry_cmp(new_row.back(), new_row.front())) + std::swap(new_row.back(), new_row.front()); + } + } + + persistence_.set_pair(r,c); + report_pair(filtration[r].dimension(), r, c); + + // zero out the corresponding column (the clearing optimization) + persistence_.column(r).clear(); + } +} + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h new file mode 100644 index 0000000000..4ac5cb6945 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h @@ -0,0 +1,280 @@ +#ifndef DIONYSUS_SIMPLEX_H +#define DIONYSUS_SIMPLEX_H + +#include +#include + +//#include +#include +#include +#include + +#include "chain.h" + +namespace dionysus +{ + +struct Empty {}; + +template +class Simplex +{ + public: + typedef Vertex_ Vertex; + typedef T Data; + typedef std::unique_ptr Vertices; + + template + struct BoundaryChainIterator; + struct BoundaryIterator; + + template + using BoundaryChainRange = boost::iterator_range>; + using BoundaryRange = boost::iterator_range; + + template + using Entry = ChainEntry; + + public: + Simplex(const Data& d = Data()): + dim_(-1), data_(d) {} + + Simplex(const std::initializer_list& vertices, + Data&& d = Data()): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) + {} + + Simplex(const std::initializer_list& vertices, + const Data& d): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} + + Simplex(short unsigned dim, Vertices&& vertices, Data&& data = Data()): + dim_(dim), vertices_(std::move(vertices)), data_(std::move(data)) { std::sort(begin(), end()); } + + template + Simplex(const VertexRange& vertices, + Data&& d = Data()): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) + {} + + template + Simplex(const VertexRange& vertices, + const Data& d): + Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} + + Simplex(const Simplex& other): + Simplex(other.dim_, other.begin(), other.end(), other.data_) {} + Simplex& operator=(const Simplex& other) { dim_ = other.dim_; vertices_ = Vertices(new Vertex[dim_+1]); std::copy(other.begin(), other.end(), begin()); data_ = other.data_; return *this; } + + Simplex(Simplex&& other) noexcept: + dim_(other.dim_), + vertices_(std::move(other.vertices_)), + data_(std::move(other.data_)) {} + Simplex& operator=(Simplex&& other) = default; + + template + Simplex(short unsigned dim, + Iterator b, Iterator e, + Data&& d = Data()): + dim_(dim), + vertices_(new Vertex[dim_+1]), + data_(std::move(d)) { std::copy(b, e, begin()); std::sort(begin(), end()); } + + template + Simplex(short unsigned dim, + Iterator b, Iterator e, + const Data& d): + dim_(dim), + vertices_(new Vertex[dim_+1]), + data_(d) { std::copy(b, e, begin()); std::sort(begin(), end()); } + + short unsigned dimension() const { return dim_; } + + BoundaryRange boundary() const { return BoundaryRange(boundary_begin(), boundary_end()); } + BoundaryIterator boundary_begin() const; + BoundaryIterator boundary_end() const; + + template + BoundaryChainRange + boundary(const Field& field) const { return BoundaryChainRange(boundary_begin(field), boundary_end(field)); } + + template + BoundaryChainIterator + boundary_begin(const Field& field) const; + template + BoundaryChainIterator + boundary_end(const Field& field) const; + + const Vertex* begin() const { return vertices_.get(); } + const Vertex* end() const { return begin() + dim_ + 1; } + size_t size() const { return dim_ + 1; } + + std::pair + range() const { return std::make_pair(begin(), end()); } + + Simplex join(const Vertex& v) const { Vertices vertices(new Vertex[dim_+2]); std::copy(begin(), end(), vertices.get()); vertices[dim_+1] = v; return Simplex(dim_ + 1, std::move(vertices), Data(data_)); } + + bool operator==(const Simplex& other) const { return dim_ == other.dim_ && std::equal(begin(), end(), other.begin()); } + bool operator!=(const Simplex& other) const { return !operator==(other); } + bool operator<(const Simplex& other) const { return dim_ < other.dim_ || (dim_ == other.dim_ && std::lexicographical_compare(begin(), end(), other.begin(), other.end())); } + bool operator>(const Simplex& other) const { return other < (*this); } + + Vertex operator[](short unsigned i) const { return vertices_[i]; } + const Data& data() const { return data_; } + Data& data() { return data_; } + + friend + std::ostream& operator<<(std::ostream& out, const Simplex& s) + { out << '<' << *s.begin(); for (auto it = s.begin() + 1; it != s.end(); ++it) out << ',' << *it; out << '>'; return out; } + + private: + Vertex* begin() { return vertices_.get(); } + Vertex* end() { return begin() + dim_ + 1; } + + private: + short unsigned dim_; + //boost::compressed_pair vertices_data_; + Vertices vertices_; + Data data_; // TODO: optimize +}; + +template +size_t hash_value(const Simplex& s) { return boost::hash_range(s.begin(), s.end()); } + + +template +struct Simplex::BoundaryIterator: + public boost::iterator_adaptor, // Value + boost::use_default, + Simplex> // Reference +{ + public: + typedef const V* Iterator; + typedef Simplex Value; + + typedef boost::iterator_adaptor Parent; + + BoundaryIterator() {} + explicit BoundaryIterator(short unsigned dim, Iterator iter, Iterator bg, Iterator end): + Parent(iter), dim_(dim), bg_(bg), end_(end) {} + + Iterator begin() const { return bg_; } + + private: + friend class boost::iterator_core_access; + Value dereference() const + { + typedef std::not_equal_to NotEqualVertex; + + using std::placeholders::_1; + return Simplex(dim_ - 1, + boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), bg_, end_), + boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), end_, end_)); + } + + short unsigned dim_; + Iterator bg_; + Iterator end_; +}; + +template +template +struct Simplex::BoundaryChainIterator: + public boost::iterator_adaptor, // Derived + BoundaryIterator, + ChainEntry>, // Value + boost::use_default, + ChainEntry>> // Reference +{ + public: + typedef F Field; + typedef BoundaryIterator Iterator; + typedef ChainEntry> Value; + + typedef boost::iterator_adaptor Parent; + + BoundaryChainIterator() {} + explicit BoundaryChainIterator(const Field& field, Iterator iter): + Parent(iter), field_(&field) {} + + private: + friend class boost::iterator_core_access; + Value dereference() const + { + return Value(((this->base().base() - this->base().begin()) % 2 == 0)? field_->id() : field_->neg(field_->id()), + *(this->base())); + } + + const Field* field_ = nullptr; +}; + + +/* Simplex */ +template +typename Simplex::BoundaryIterator +Simplex:: +boundary_begin() const +{ + if (dimension() == 0) return boundary_end(); + return BoundaryIterator(dimension(), begin(), begin(), end()); +} + +template +typename Simplex::BoundaryIterator +Simplex:: +boundary_end() const +{ + return BoundaryIterator(dimension(), end(), begin(), end()); +} + +template +template +#if defined(_MSC_VER) +typename Simplex::BoundaryChainIterator +#else +typename Simplex::template BoundaryChainIterator +#endif +Simplex:: +boundary_begin(const F& field) const +{ + if (dimension() == 0) return boundary_end(field); + return BoundaryChainIterator(field, boundary_begin()); +} + +template +template +#if defined(_MSC_VER) +typename Simplex::BoundaryChainIterator +#else +typename Simplex::template BoundaryChainIterator +#endif +Simplex:: +boundary_end(const F& field) const +{ + return BoundaryChainIterator(field, boundary_end()); +} + +} // dionysus + +namespace std +{ + +template +struct hash> +{ + size_t operator()(const dionysus::Simplex& s) const { return hash_value(s); } +}; + +} // std + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h new file mode 100644 index 0000000000..fb1e929e02 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h @@ -0,0 +1,184 @@ +#ifndef DIONYSUS_SPARSE_ROW_MATRIX_H +#define DIONYSUS_SPARSE_ROW_MATRIX_H + +#include +#include +#include +#include // for debugging output + +#include + +#include "chain.h" +#include "reduction.h" + +namespace dionysus +{ + +namespace bi = boost::intrusive; + +namespace detail +{ + typedef bi::list_base_hook> auto_unlink_hook; + + template + struct SparseRowMatrixEntry: + public ChainEntry, auto_unlink_hook> + { + typedef I Index; + typedef typename F::Element FieldElement; + typedef std::tuple IndexPair; // (id, pair) + typedef ChainEntry Parent; + typedef SparseRowMatrixEntry Entry; + + SparseRowMatrixEntry(FieldElement e, const IndexPair& ip): + Parent(e,ip) {} + + SparseRowMatrixEntry(FieldElement e, const Index& r, const Index& c): + Parent(e,IndexPair(r,c)) {} + + SparseRowMatrixEntry(const Entry& other) = default; + SparseRowMatrixEntry(Entry&& other) = default; + Entry& operator=(Entry&& other) = default; + + void unlink() { auto_unlink_hook::unlink(); } + bool is_linked() const { return auto_unlink_hook::is_linked(); } + }; +} + +template, + template class Column_ = std::vector> +class SparseRowMatrix +{ + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef typename Field::Element FieldElement; + + typedef detail::SparseRowMatrixEntry Entry; + typedef Column_ Column; + typedef typename Entry::IndexPair IndexPair; + typedef bi::list> Row; + + typedef std::vector> IndexChain; + + typedef std::unordered_map Columns; + typedef std::unordered_map Rows; + typedef std::unordered_map LowMap; + + public: + SparseRowMatrix(const Field& field, + const Comparison& cmp = Comparison()): + field_(field), cmp_(cmp) {} + + SparseRowMatrix(SparseRowMatrix&& other) = default; + + + template + Column reduce(const ChainRange& chain, IndexChain& trail); + + Index set(Index i, Column&& chain); // returns previous column with this low + void fix(Index c, Column& column); + void fix(Index c) { fix(c, col(c)); } + + const Row& prepend_row(Index r, FieldElement m, const Row& chain); // could be horribly inefficient if Column is chosen poorly + + void drop_row(Index r) { rows_.erase(r); if (is_low(r)) lows_.erase(r); } + void drop_col(Index c) + { + auto cit = columns_.find(c); + Column& column = cit->second; + if (!column.empty()) + { + Index rlow = std::get<0>(column.back().index()); + auto it = lows_.find(rlow); + if (it != lows_.end() && it->second == c) + lows_.erase(it); + } + columns_.erase(cit); + } + void drop_low(Index r) { lows_.erase(r); } + + // accessors + Row& row(Index r) { return rows_[r]; } + Column& col(Index c) { assert(col_exists(c)); return columns_.find(c)->second; } + const Column& col(Index c) const { assert(col_exists(c)); return columns_.find(c)->second; } + Index low(Index r) const { return lows_.find(r)->second; } + bool is_low(Index r) const { return lows_.find(r) != lows_.end(); } + void update_low(Index c) { lows_[std::get<0>(col(c).back().index())] = c; } + + const Field& field() const { return field_; } + void reserve(size_t) {} // here for compatibility only + const Comparison& cmp() const { return cmp_; } + + // debug + bool col_exists(Index c) const { return columns_.find(c) != columns_.end(); } + const Columns& columns() const { return columns_; } + void check_columns() const + { + for (auto& x : columns_) + { + Index c = x.first; + if (x.second.empty()) + std::cout << "Warning: empty column " << c << std::endl; + Index rl = std::get<0>(x.second.back().index()); + if (!is_low(rl) || low(rl) != c) + { + std::cout << "Columns don't check out: lows don't match" << std::endl; + std::cout << " " << c << ' ' << rl << ' ' << ' ' << low(rl) << std::endl; + std::cout << "---\n"; + for (auto& x : col(c)) + std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; + std::cout << "---\n"; + for (auto& x : col(low(rl))) + std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; + assert(0); + } + + for (auto& x : lows_) + { + if (!col_exists(x.second)) + { + std::cout << "Still keeping low of a removed column" << std::endl; + assert(0); + } + else if (std::get<0>(col(x.second).back().index()) != x.first) + { + std::cout << "Low mismatch: " << x.second << ' ' << std::get<0>(col(x.second).back().index()) << ' ' << x.first << '\n'; + assert(0); + } + } + } + } + + private: + Field field_; + Comparison cmp_; + + Columns columns_; + Rows rows_; + LowMap lows_; // column that has this low +}; + + +namespace detail +{ + +template +struct Unpaired> +{ + static + constexpr std::tuple + value() + { return std::make_tuple(std::numeric_limits::max(), + std::numeric_limits::max()); } +}; + +} + +} + +#include "sparse-row-matrix.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp new file mode 100644 index 0000000000..10f4808c17 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp @@ -0,0 +1,103 @@ +template class Col> +template +typename dionysus::SparseRowMatrix::Column +dionysus::SparseRowMatrix:: +reduce(const ChainRange& chain_, IndexChain& trail) +{ + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp_(std::get<0>(e1.index()), std::get<0>(e2.index())); }; + +#define __DIONYSUS_USE_VECTOR_CHAINS 1 + +#if !(__DIONYSUS_USE_VECTOR_CHAINS) + std::set chain(row_cmp); + for (auto x : chain_) + chain.insert(Entry(x.element(), IndexPair(x.index(), 0))); +#else + Column chain; + for (auto x : chain_) + chain.emplace_back(x.element(), IndexPair(x.index(), 0)); + std::sort(chain.begin(), chain.end(), row_cmp); +#endif + + typedef Reduction ReductionIP; + + auto chains = [this](const IndexPair& rc) -> const Column& { return this->col(std::get<1>(rc)); }; + auto lows = [this](const IndexPair& rc) -> IndexPair + { + Index r = std::get<0>(rc); + auto it = this->lows_.find(r); + if (it == this->lows_.end()) + return ReductionIP::unpaired; + else + { + Index rr = std::get<0>(col(it->second).back().index()); + if (rr != r) + std::cout << "Mismatch: " << rr << ' ' << r << std::endl; + return IndexPair(r, it->second); + } + }; + + auto addto = [&trail](FieldElement m, const IndexPair& rc) { trail.emplace_back(m, std::get<1>(rc)); }; + + ReductionIP::reduce(chain, + chains, lows, + field_, addto, row_cmp); + +#if !(__DIONYSUS_USE_VECTOR_CHAINS) + return Column(std::begin(chain), std::end(chain)); +#else + return chain; +#endif +} + +template class Col> +typename dionysus::SparseRowMatrix::Index +dionysus::SparseRowMatrix:: +set(Index col, Column&& chain) +{ + Column& column = columns_.emplace(col, std::move(chain)).first->second; + + fix(col, column); + + Index r = std::get<0>(column.back().index()); + Index res; + if (is_low(r)) + res = low(r); + else + res = col; + lows_[r] = col; + + return res; +} + +template class Col> +void +dionysus::SparseRowMatrix:: +fix(Index col, Column& column) +{ + for (auto& x : column) + { + std::get<1>(x.index()) = col; + Index r = std::get<0>(x.index()); + row(r).push_back(x); + } +} + +template class Col> +const typename dionysus::SparseRowMatrix::Row& +dionysus::SparseRowMatrix:: +prepend_row(Index r, FieldElement m, const Row& chain) +{ + Row& new_row = row(r); + + for (auto& x : chain) + { + Index c = std::get<1>(x.index()); + Column& column = col(c); + auto it = column.emplace(column.begin(), field().mul(x.element(), m), r, c); + new_row.push_back(*it); + } + + return new_row; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h new file mode 100644 index 0000000000..0477d4683f --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h @@ -0,0 +1,44 @@ +#ifndef DIONYSUS_STANDARD_REDUCTION_H +#define DIONYSUS_STANDARD_REDUCTION_H + +namespace dionysus +{ + +// Mid-level interface +template +class StandardReduction +{ + public: + typedef Persistence_ Persistence; + typedef typename Persistence::Field Field; + typedef typename Persistence::Index Index; + + public: + StandardReduction(Persistence& persistence): + persistence_(persistence) {} + + template + void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); + + template + void operator()(const Filtration& f, const ReportPair& report_pair); + + template + void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } + + static void no_report_pair(int, Index, Index) {} + static void no_progress() {} + + const Persistence& + persistence() const { return persistence_; } + Persistence& persistence() { return persistence_; } + + private: + Persistence& persistence_; +}; + +} + +#include "standard-reduction.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp new file mode 100644 index 0000000000..9aa3396a8c --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp @@ -0,0 +1,47 @@ +#include +namespace ba = boost::adaptors; + +template +template +void +dionysus::StandardReduction

:: +operator()(const Filtration& filtration, const ReportPair& report_pair) +{ + using Cell = typename Filtration::Cell; + (*this)(filtration, [](const Cell&) { return false; }, report_pair, no_progress); +} + +template +template +void +dionysus::StandardReduction

:: +operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) +{ + persistence_.reserve(filtration.size()); + + typedef typename Filtration::Cell Cell; + typedef ChainEntry CellChainEntry; + typedef ChainEntry ChainEntry; + + unsigned i = 0; + for(auto& c : filtration) + { + progress(); + + if (relative(c)) + { + ++i; + persistence_.add_skip(); + continue; + } + + //std::cout << "Adding: " << c << " : " << boost::distance(c.boundary(persistence_.field())) << std::endl; + Index pair = persistence_.add(c.boundary(persistence_.field()) | + ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | + ba::transformed([this,&filtration](const CellChainEntry& e) + { return ChainEntry(e.element(), filtration.index(e.index())); })); + if (pair != persistence_.unpaired()) + report_pair(c.dimension(), pair, i); + ++i; + } +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h new file mode 100644 index 0000000000..f18ff897e4 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h @@ -0,0 +1,17 @@ +#ifndef DIONYSUS_TRAILS_CHAINS_H +#define DIONYSUS_TRAILS_CHAINS_H + +#include "ordinary-persistence.h" + +template +struct ChainsVisitor: public EmptyVisitor +{ + template + void chain_initialized(Chain& c) { } + + void addto(typename Field::Element m, Index cl) {} + void reduction_finished() {} +}; + + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h new file mode 100644 index 0000000000..e9423099aa --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h @@ -0,0 +1,142 @@ +#ifndef DIONYSUS_ZIGZAG_PERSISTENCE_H +#define DIONYSUS_ZIGZAG_PERSISTENCE_H + +#include +#include +#include + +#include +#include + +#include "sparse-row-matrix.h" + +namespace dionysus +{ + +namespace ba = boost::adaptors; + +template> +class ZigzagPersistence +{ + static_assert(std::is_signed::value, "Index type used in ZigzagPersistence must be a *signed* integer"); + + public: + typedef Field_ Field; + typedef Index_ Index; + typedef Comparison_ Comparison; + + typedef SparseRowMatrix RowMatrix; + typedef SparseRowMatrix DequeRowMatrix; + typedef typename RowMatrix::IndexPair IndexPair; + typedef typename RowMatrix::FieldElement FieldElement; + typedef typename RowMatrix::IndexChain IndexChain; + typedef typename RowMatrix::Column Column; + typedef typename RowMatrix::Row Row; + typedef typename DequeRowMatrix::Column DequeColumn; + typedef typename DequeRowMatrix::Row DequeRow; + + typedef std::unordered_map BirthIndexMap; + + + ZigzagPersistence(const Field& field, + const Comparison& cmp = Comparison()): + Z(field, cmp), C(field, cmp), B(field, cmp), + operations(0), + cell_indices(0), + z_indicies_last(0), + z_indicies_first(-1), + b_indices(0) {} + + template + Index add(const ChainRange& chain) // returns the id of the dying cycle (or unpaired) + { + Index res = add_impl(chain); +#ifdef DIONYSUS_ZIGZAG_DEBUG + check_sorted(); + check_b_cols(); + Z.check_columns(); +#endif + return res; + } + Index remove(Index cell) + { + Index res = remove_impl(cell); +#ifdef DIONYSUS_ZIGZAG_DEBUG + check_sorted(); + check_b_cols(); + Z.check_columns(); +#endif + return res; + } + + struct IsAlive + { + IsAlive(const ZigzagPersistence& zz_): zz(&zz_) {} + bool operator()(const std::pair& x) const { return zz->is_alive(x.first); } + const ZigzagPersistence* zz; + }; + + bool is_alive(Index x) const { return !B.is_low(x); } + + auto alive_ops() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_values) + { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_values; } + + auto alive_cycles() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_keys) + { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_keys; } + + size_t alive_size() const { return Z.columns().size() - B.columns().size(); } + + void reserve(size_t) {} // here for compatibility only + const Field& field() const { return Z.field(); } + const Comparison& cmp() const { return Z.cmp(); } + + template + static Index row(const Entry& e) { return std::get<0>(e.index()); } + template + static Index col(const Entry& e) { return std::get<1>(e.index()); } + + static + const Index unpaired() { return Reduction::unpaired; } + + const Column& cycle(Index i) const { return Z.col(i); } + + // debug + void check_b_cols() const; + + template + void check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + template + void check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + + Column zb_dot(Index c) const; + + template + Column dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + + template + Column boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; + + void check_sorted() const; + + private: + template + Index add_impl(const ChainRange& chain); + Index remove_impl(Index cell); + + private: + RowMatrix Z, C; + DequeRowMatrix B; + + BirthIndexMap birth_index; + Index operations; + Index cell_indices; + Index z_indicies_last, z_indicies_first; + Index b_indices; +}; + +} + +#include "zigzag-persistence.hpp" + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp new file mode 100644 index 0000000000..96331a3b15 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp @@ -0,0 +1,541 @@ +#include + +template +template +typename dionysus::ZigzagPersistence::Index +dionysus::ZigzagPersistence:: +add_impl(const ChainRange& chain_) +{ + // std::cout << "add(" << cell_indices << ")" << std::endl; + Index op = operations++; + + IndexChain cycles; // chain_ -> Z*cycles + Column z_remainder = Z.reduce(chain_, cycles); + // std::cout << "cycle: "; + // for (auto& v : cycles){ + // std::cout << v.index() << " "; + // } + // std::cout << "\n"; + assert(z_remainder.empty()); + + IndexChain boundaries; + DequeColumn b_remainder = B.reduce(cycles, boundaries); + + // add up columns of C indexed by boundaries + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column chain; + for (auto& x : boundaries) + Chain::addto(chain, x.element(), C.col(x.index()), field(), row_cmp); + chain.push_back(Entry(field().neg(field().id()), IndexPair(cell_indices++,0))); + + if (b_remainder.empty()) // birth + { + // std::cout << " birth" << std::endl; + Index z_col = z_indicies_last++; + Z.set(z_col, std::move(chain)); + birth_index[z_col] = op; + return unpaired(); + } + else // death + { + // std::cout << " death" << std::endl; + Index b_col = b_indices++; + Index pair = row(b_remainder.back()); + B.set(b_col, std::move(b_remainder)); + C.set(b_col, std::move(chain)); + return birth_index[pair]; + } +} + +template +typename dionysus::ZigzagPersistence::Index +dionysus::ZigzagPersistence:: +remove_impl(Index cell) +{ + //std::cout << "remove(" << cell << ")" << std::endl; + + Index op = operations++; + + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + typedef typename DequeColumn::value_type DequeEntry; + auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) + { return this->cmp()(row(e1), row(e2)); }; + + IndexChain z_row; + for (auto& x : Z.row(cell)) + z_row.emplace_back(x.element(), col(x)); + + if (z_row.empty()) // birth + { + //std::cout << " birth" << std::endl; + Row& c_row = C.row(cell); + // c_row.front() may not be the first column in order, but that doesn't really matter, does it? (TODO) + auto& c_front = c_row.front(); + + Index j = col(c_front); + Index l = row(B.col(j).back()); + + //std::cout << j << ' ' << l << std::endl; + + // cycle = ZB[j] = DC[j] + Column cycle; + for (auto& x : B.col(j)) + Chain::addto(cycle, x.element(), Z.col(row(x)), field(), row_cmp); + + //std::cout << "Cycle:" << std::endl; + //for (auto& x : cycle) + // std::cout << x.element() << ' ' << row(x) << std::endl; + + // 1: prepend the cycle + Index znew = z_indicies_first--; + Index oth = Z.set(znew, std::move(cycle)); // oth records our collision (used in step 6) + birth_index[znew] = op; + + //std::cout << "znew oth: " << znew << ' ' << oth << std::endl; + //std::cout << "oth column:" << std::endl; + //for (auto& x : Z.col(oth)) + // std::cout << x.element() << ' ' << row(x) << std::endl; + + // 2: prepend the row to B + FieldElement m = field().neg(field().inv(c_front.element())); // m = -1/c + const DequeRow& b_row = B.prepend_row(znew, m, c_row); + //std::cout << "Prepended row with multiplier: " << m << " (" << b_row.size() << ")" << std::endl; + + // 3: subtract C[j] from every C[k] + const Column& Cj = C.col(j); + + // use the copy of c_row in B, since c_row will be modified in the following loop + for (auto it = std::next(b_row.begin()); it != b_row.end(); ++it) + { + Index c = col(*it); + assert(c != j); + //std::cout << "adding to " << c << " in C" << std::endl; + Chain::addto(C.col(c), it->element(), Cj, field(), row_cmp); // using it->element() since b_row = m*c_row + C.fix(c); // old elements got removed via auto_unlink_hook + // we don't need lows in C, so not updating them + } + //std::cout << "Done with step 3" << std::endl; + + // 4: subtract B[j] from every B[k] that has l + // (we don't need to update C because ZB[j] = 0 after step 2) + DequeColumn& Bj = B.col(j); + FieldElement bm = field().neg(field().inv(Bj.back().element())); // bm = -1/B[l,j] + IndexChain Bl_row; // make a copy of Bl_row, since it will be changing + for (auto& x : B.row(l)) + { + if (col(x) == j) + continue; + Bl_row.emplace_back(x.element(), col(x)); + } + for (auto& x : Bl_row) + { + Index c = x.index(); + assert(c != j); + Chain::addto(B.col(c), field().mul(bm, x.element()), Bj, field(), b_row_cmp); + B.fix(c); // old elements got removed via auto_unlink_hook + // l cannot be the low in c, so no need to update lows + } + //std::cout << "Done with step 4" << std::endl; + + // 5: drop row l and column j from B; drop column l from Z; drop column j from C + B.drop_col(j); + assert(B.row(l).empty()); + B.drop_row(l); + Index Zl_low = row(Z.col(l).back()); + Z.drop_col(l); + birth_index.erase(l); + C.drop_col(j); + assert(Z.row(cell).empty()); + assert(C.row(cell).empty()); + C.drop_row(cell); + Z.drop_row(cell); + //std::cout << "Done with step 5" << std::endl; + if (oth == l) // we just dropped our collision in Z + oth = znew; + else + Z.drop_low(Zl_low); + + // 6: reduce Z + std::unordered_map b_changes; // the columns to add in B to apply row changes + Index cur = znew; + while (oth != cur) + { + Column& cur_col = Z.col(cur); + Column& oth_col = Z.col(oth); + assert(row(cur_col.back()) == row(oth_col.back())); + //std::cout << "--- " << cur << " (" << cur_col.size() << ") " << oth << " (" << oth_col.size() << ")" << std::endl; + FieldElement m1 = cur_col.back().element(); + FieldElement m2 = oth_col.back().element(); + FieldElement m2_div_m1 = field().div(m2, m1); + Chain::addto(oth_col, field().neg(m2_div_m1), cur_col, field(), row_cmp); + Z.fix(oth, oth_col); + + // record the changes we need to make in B; + // because there is only one collision in the matrix during the reduction, + // once we use a row as the source, we never revisit it. This means once the row is updated in B, + // we never touch it again, so below record is fine. + for (auto& x : this->B.row(oth)) + b_changes[col(x)].emplace_back(field().mul(x.element(), m2_div_m1), cur, col(x)); + + cur = oth; + Index low = row(oth_col.back()); + if (Z.is_low(low)) + oth = Z.low(low); + //std::cout << "--- -- new low: " << low << ' ' << cur << ' ' << oth << std::endl; + + if (cmp()(oth, cur)) + std::swap(oth, cur); + else + Z.update_low(cur); + } + + // apply changes in B (the complexity here could get ugly) + for (auto& bx : b_changes) + { + std::sort(bx.second.begin(), bx.second.end(), b_row_cmp); + Chain::addto(B.col(bx.first), field().id(), bx.second, field(), b_row_cmp); + B.fix(bx.first); + // no need to update low (additions from bottom up) + } + //std::cout << "Done with step 6" << std::endl; + + return unpaired(); + } + else // death + { + //std::cout << " death" << std::endl; + + auto index_chain_cmp = [this](const typename IndexChain::value_type& e1, const typename IndexChain::value_type& e2) + { return this->cmp()(e1.index(), e2.index()); }; + + // 1: change basis to clear z_row + std::sort(z_row.begin(), z_row.end(), index_chain_cmp); // this adds a log factor, but it makes life easier + Index j = z_row.front().index(); + FieldElement e = z_row.front().element(); + + if (z_row.size() > 1) + { + // figure out the columns we use for reduction + typedef typename IndexChain::const_iterator RowIterator; + std::vector reducers; + reducers.push_back(z_row.begin()); + for (RowIterator it = std::next(z_row.begin()); it != z_row.end(); ++it) + { + Index c = it->index(); + + assert(Z.col_exists(c)); + assert(Z.col_exists(reducers.back()->index())); + if (cmp()(row(Z.col(c).back()), + row(Z.col(reducers.back()->index()).back()))) + reducers.push_back(it); + } + reducers.push_back(z_row.end()); + //std::cout << "reducers.size(): " << reducers.size() << std::endl; + //std::cout << "z_row.size(): " << z_row.size() << std::endl; + + + std::map b_changes; // the rows to add to B + auto add_in_z = [this,&b_changes,&row_cmp,&index_chain_cmp](Index to, Index from, FieldElement m, FieldElement e) + { + //std::cout << " add_in_z: " << from << ' ' << to << std::endl; + + FieldElement mult = this->field().mul(m, e); + assert(Z.col_exists(to)); + assert(Z.col_exists(from)); + Chain::addto(Z.col(to), mult, Z.col(from), this->field(), row_cmp); + assert(!Z.col(to).empty()); + this->Z.fix(to); // NB: rows will be linked in the back, so the iterators are Ok + this->Z.update_low(to); + + // subtract B.row(to) from B.row(from) + IndexChain Bto_row; + for (auto& x : this->B.row(to)) + Bto_row.emplace_back(x.element(), col(x)); + std::sort(Bto_row.begin(), Bto_row.end(), index_chain_cmp); + +#if 0 + for (auto& x : this->B.row(to)) + std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; + + std::cout << "---\n"; + + for (auto& x : this->B.row(from)) + std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; +#endif + + Chain::addto(b_changes[from], this->field().neg(mult), Bto_row, this->field(), index_chain_cmp); + + // if there is b_changes[to] add it, too + auto it = b_changes.find(to); + if (it != b_changes.end()) + Chain::addto(b_changes[from], this->field().neg(mult), it->second, this->field(), index_chain_cmp); + }; + Index last_low = row(Z.col(reducers[reducers.size() - 2]->index()).back()); + for (int i = reducers.size() - 2; i >= 0; --i) + { + auto rit = reducers[i]; + FieldElement m = field().neg(field().inv(rit->element())); + + for (auto it = std::next(rit); it != reducers[i+1]; ++it) + add_in_z(it->index(), rit->index(), m, it->element()); + + if (static_cast(i + 1) != reducers.size() - 1) + { + auto it = reducers[i+1]; + add_in_z(it->index(), rit->index(), m, it->element()); + } + } + if (reducers.size() > 2) + Z.drop_low(last_low); + + // apply changes in b (the complexity here could get ugly) + // Specifically, transpose b_changes and add it in + std::unordered_map b_changes_transposed; + for (auto& b_row : b_changes) + for (auto& bx : b_row.second) + b_changes_transposed[bx.index()].emplace_back(bx.element(), b_row.first, bx.index()); + + for (auto& b_col : b_changes_transposed) + { +#if 0 + std::cout << "Adding:" << std::endl; + for (auto& x : b_col.second) + std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; +#endif + Chain::addto(B.col(b_col.first), field().id(), b_col.second, field(), b_row_cmp); + assert(!B.col(b_col.first).empty()); + B.fix(b_col.first); + // no need to update low (additions from bottom up) + } + } // z_row.size() > 1 + + // 2: subtract cycle from every chain in C + const Column& Zj = Z.col(j); + //std::cout << "Zj:" << std::endl; + //for (auto& x : Zj) + // std::cout << x.element() << " * " << row(x) << std::endl; + + IndexChain Ccols; // save the columns in C, we'll be modifying C.row(cell) + for (auto& x : C.row(cell)) + Ccols.emplace_back(x.element(), col(x)); + + for (auto& x : Ccols) + { + Index c = x.index(); + FieldElement m = field().neg(field().div(x.element(), e)); // m = -C[k][cell]/Z[j][cell] + //std::cout << "Adding to C: " << c << std::endl; + Chain::addto(C.col(c), m, Zj, field(), row_cmp); + C.fix(c); + // we don't care about lows in C, so don't update them + } + + // 3: drop + assert(Z.row(cell).size() == 1); + Z.drop_col(j); + assert(Z.row(cell).empty()); + assert(C.row(cell).empty()); + Z.drop_row(cell); + C.drop_row(cell); + assert(B.row(j).empty()); + B.drop_row(j); + + Index birth = birth_index[j]; + birth_index.erase(j); + + return birth; + } +} + + +/* debug routines */ +template +void +dionysus::ZigzagPersistence:: +check_b_cols() const +{ + // check that entries in B refer to existing Z columns + bool stop = false; + for (auto& b : B.columns()) + for (auto& x : b.second) + if (!Z.col_exists(row(x))) + { + std::cout << "B refers to a non-existent column in Z: " << row(x) << std::endl; + stop = true; + } + if (stop) + assert(0); +} + +template +template +void +dionysus::ZigzagPersistence:: +check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + + for (auto& z : Z.columns()) + { + Column res; + for (auto& x : z.second) + { + Column bdry = boundary(row(x), s2i, i2s); + Chain::addto(res, x.element(), bdry, field(), row_cmp); + } + assert(res.empty()); + } +} + +template +template +void +dionysus::ZigzagPersistence:: +check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + check_cycles(s2i, i2s); + + for (auto& x : B.columns()) + if (!C.col_exists(x.first)) + { + std::cout << x.first << " in B, but not in C" << std::endl; + assert(0); + } + + for (auto& x : C.columns()) + if (!B.col_exists(x.first)) + { + std::cout << x.first << " in B, but not in C" << std::endl; + assert(0); + } + + for (auto& x : B.columns()) + { + auto zb = zb_dot(x.first); + auto dc = dc_dot(x.first, s2i, i2s); + + auto it_zb = zb.begin(), + it_dc = dc.begin(); + for (; it_zb != zb.end(); ++it_zb, ++it_dc) + { + if (it_zb->element() != it_dc->element() || row(*it_zb) != row(*it_dc)) + { + std::cout << "Boundary mismatch: " << x.first << std::endl; + std::cout << "===" << std::endl; + for (auto& x : zb) + std::cout << " " << x.element() << ' ' << row(x) << std::endl; + for (auto& y : B.col(x.first)) + { + std::cout << " " << y.element() << " * " << row(y) << std::endl; + for (auto& z : Z.col(row(y))) + std::cout << " " << z.element() << ' ' << row(z) << std::endl; + std::cout << " ---" << std::endl; + } + std::cout << "===" << std::endl; + for (auto& x : dc) + std::cout << " " << x.element() << ' ' << row(x) << std::endl; + for (auto& y : C.col(x.first)) + { + std::cout << " " << y.element() << " * " << row(y) << std::endl; + for (auto& z : boundary(row(y), s2i, i2s)) + std::cout << " " << z.element() << ' ' << row(z) << std::endl; + std::cout << " ---" << std::endl; + } + assert(0); + } + } + if (it_zb != zb.end() || it_dc != dc.end()) + { + std::cout << "zb.end() doesn't match dc.end()" << std::endl; + assert(0); + } + } +} + +template +typename dionysus::ZigzagPersistence::Column +dionysus::ZigzagPersistence:: +zb_dot(Index c) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column res; + for (auto& x : B.col(c)) + Chain::addto(res, x.element(), Z.col(row(x)), field(), row_cmp); + + return res; +} + +template +template +typename dionysus::ZigzagPersistence::Column +dionysus::ZigzagPersistence:: +dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column res; + for (auto& x : C.col(c)) + { + Column bdry = boundary(row(x), s2i, i2s); + Chain::addto(res, x.element(), bdry, field(), row_cmp); + } + return res; +} + +template +template +typename dionysus::ZigzagPersistence::Column +dionysus::ZigzagPersistence:: +boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + Column bdry; + auto s = i2s(i); + for (auto y : s.boundary(field())) + bdry.emplace_back(y.element(), s2i(y.index()), 0); + std::sort(bdry.begin(), bdry.end(), row_cmp); + return bdry; +} + +template +void +dionysus::ZigzagPersistence:: +check_sorted() const +{ + typedef typename Column::value_type Entry; + auto row_cmp = [this](const Entry& e1, const Entry& e2) + { return this->cmp()(row(e1), row(e2)); }; + typedef typename DequeColumn::value_type DequeEntry; + auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) + { return this->cmp()(row(e1), row(e2)); }; + + for (auto& x : Z.columns()) + if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) + { + std::cout << "Z column not sorted: " << x.first << std::endl; + assert(0); + } + for (auto& x : C.columns()) + if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) + { + std::cout << "C column not sorted: " << x.first << std::endl; + assert(0); + } + for (auto& x : B.columns()) + if (!std::is_sorted(x.second.begin(), x.second.end(), b_row_cmp)) + { + std::cout << "B column not sorted: " << x.first << std::endl; + assert(0); + } +} + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp new file mode 100644 index 0000000000..5217a4f38c --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp @@ -0,0 +1,206 @@ +#include "fzz.h" + +#include + +// phat headers +// wrapper algorithm that computes the persistence pairs of a given boundary matrix using a specified algorithm +#include + +// main data structure (choice affects performance) +#include +#include + +// algorithm (choice affects performance) +#include +#include +#include +#include + +namespace FZZ { + +template +class VecHash { +public: + size_t operator()(const std::vector& v) const; +}; + +template +size_t VecHash + ::operator()(const std::vector& v) const { + + std::size_t seed = 0; + + for (auto e : v) { boost::hash_combine(seed, e); } + + return seed; +} + +template +class VecEqual { +public: + bool operator()(const std::vector& v1, + const std::vector& v2) const; +}; + +template +bool VecEqual + ::operator()(const std::vector& v1, + const std::vector& v2) const { + + if (v1.size() != v2.size()) { return false; } + + for (unsigned int i = 0; i < v1.size(); i ++) { + if (v1[i] != v2[i]) { + return false; + } + } + + return true; +} + +typedef std::unordered_map< Simplex, Integer, + VecHash, VecEqual > SimplexIdMap; + +void getBoundaryChainPhat(const std::vector &id_maps, + const Simplex &simp, std::vector &bound_c) { + + bound_c.clear(); + + if (simp.size() <= 1) { return; } + + bound_c.reserve(simp.size()); + + Simplex bound_simp(simp.begin()+1, simp.end()); + bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); + + for (unsigned int i = 0; i < simp.size()-1; ++i) { + bound_simp[i] = simp[i]; + bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); + } + + std::sort(bound_c.begin(), bound_c.end()); +} + +inline Integer getDim(const std::vector &bound_c) { + if (bound_c.size() == 0) { return 0; } + return bound_c.size() - 1; +} + +void FastZigzag::compute(const std::vector &filt_simp, + const std::vector &filt_op, + std::vector< std::tuple > *persistence) { + + orig_f_add_id.clear(); + orig_f_del_id.clear(); + persistence->clear(); + + simp_num = 0; + Integer max_dim = 0; + for (unsigned int i = 0; i < filt_op.size(); ++i) { + if (filt_op[i]) { + ++simp_num; + if (static_cast(filt_simp[i].size()) - 1 > max_dim) { max_dim = filt_simp[i].size() - 1; } + } + } + + std::vector bound_c; + // phat::boundary_matrix< phat::vector_vector > bound_chains; + phat::boundary_matrix< phat::bit_tree_pivot_column > bound_chains; + bound_chains.set_num_cols(simp_num * 2 + 1); + + // Add the Omega vertex for the coning + bound_chains.set_col(0, bound_c); + bound_chains.set_dim(0, 0); + + orig_f_add_id.reserve(simp_num); + orig_f_del_id.reserve(simp_num); + + std::vector del_ids; + del_ids.reserve(simp_num); + + std::vector *p_id_maps = new std::vector(max_dim+1); + std::vector &id_maps = *p_id_maps; + + Integer orig_f_id = 0; + Integer s_id = 1; + + for (unsigned int i = 0; i < filt_simp.size(); ++i) { + const Simplex &simp = filt_simp[i]; + + if (filt_op[i]) { + getBoundaryChainPhat(id_maps, simp, bound_c); + bound_chains.set_col(s_id, bound_c); + bound_chains.set_dim(s_id, getDim(bound_c)); + + // assert(s_id == bound_chains.size()-1); + id_maps.at(simp.size() - 1)[simp] = s_id; + orig_f_add_id.push_back(orig_f_id); + s_id ++; + } else { + del_ids.push_back(id_maps.at(simp.size() - 1)[simp]); + id_maps.at(simp.size() - 1).erase(simp); + orig_f_del_id.push_back(orig_f_id); + } + + orig_f_id ++; + } + + for (Integer i = id_maps.size() - 1; i >= 0; -- i) { + for (const auto &it : id_maps.at(i)) { + del_ids.push_back(it.second); + orig_f_del_id.push_back(orig_f_id); + orig_f_id ++; + } + } + + assert(del_ids.size() == s_id-1); + delete p_id_maps; + + assert(simp_num == del_ids.size()); + + std::vector cone_sid(simp_num+1); + + for (auto del_id_it = del_ids.rbegin(); del_id_it != del_ids.rend(); ++del_id_it) { + bound_c.clear(); + bound_c.push_back(*del_id_it); + + std::vector orig_bound_c; + bound_chains.get_col(*del_id_it, orig_bound_c); + + if (orig_bound_c.size() == 0) { + bound_c.push_back(0); + } else { + for (auto bsimp : orig_bound_c) { + // assert(cone_sid[bsimp] >= 0); + bound_c.push_back(cone_sid[bsimp]); + } + } + + std::sort(bound_c.begin(), bound_c.end()); + + bound_chains.set_col(s_id, bound_c); + bound_chains.set_dim(s_id, getDim(bound_c)); + + cone_sid[*del_id_it] = s_id; + + s_id ++; + } + + phat::persistence_pairs pairs; + phat::compute_persistence_pairs< phat::twist_reduction >( pairs, bound_chains ); + + for (phat::index idx = 0; idx < pairs.get_num_pairs(); idx++) { + Integer b = pairs.get_pair(idx).first; + Integer d = pairs.get_pair(idx).second - 1; + Integer p = bound_chains.get_dim(b); + + if (d < simp_num) { mapOrdIntv(b, d); } + else { mapRelExtIntv(p, b, d); } + + if (b > static_cast(filt_simp.size())) { continue; } + if (d > static_cast(filt_simp.size())) { d = filt_simp.size(); } + persistence->emplace_back(b, d, p); + } +} + +} // namespace FZZ { diff --git a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h new file mode 100644 index 0000000000..8613bc3793 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h @@ -0,0 +1,87 @@ +#ifndef _FZZ_H_ +#define _FZZ_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace FZZ { + +typedef int Integer; +typedef std::vector Simplex; + +class FastZigzag { +public: + /* + 'filt_simp' and 'filt_op' should have the same length which altogether + specify the input zigzag filtration. 'filt_simp' specifies the simplices + being added or deleted (following the order of the filtration) and + 'filt_op' specifies whether it's an addition (true) or deletion (false). + 'persistence' returns the barcode, with the first element of the tuple + being the birth, the second element being the death, and the third + being the dimension. + */ + void compute( + const std::vector &filt_simp, + const std::vector &filt_op, + std::vector< std::tuple > *persistence); + +private: + void mapOrdIntv(Integer &b, Integer &d) { + // assert(b-1 > 0); + // assert(d < orig_f_add_id.size()); + + // Up-down interval is same, + // so directly map to interval of input filtration + b = orig_f_add_id[b-1] + 1; + d = orig_f_add_id[d]; + } + + void mapRelExtIntv(Integer &p, Integer &b, Integer &d) { + // assert(d >= simp_num); + + if (b > simp_num) { // Open-closed + // Map to up-down interval + std::swap(b, d); + b = 3*simp_num - b; + d = 3*simp_num - d; + p --; + + // Map to interval of input filtration + b = orig_f_del_id[b-1-simp_num] + 1; + d = orig_f_del_id[d-simp_num]; + } else { // Closed-closed + // Map to up-down interval + d = 3*simp_num - d-1; + + // Map to interval of input filtration + b = orig_f_add_id[b-1]; + d = orig_f_del_id[d-simp_num]; + + if (b < d) { + b = b+1; + } else { + std::swap(b, d); + b = b+1; + p = p-1; + } + } + } + +private: + // 'orig_f_add_id' and 'orig_f_del_id' form a mapping + // from the up-down filtration to the original filtration + std::vector orig_f_add_id; + std::vector orig_f_del_id; + + Integer simp_num; +}; + +} + +#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h new file mode 100644 index 0000000000..179702312f --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h @@ -0,0 +1,223 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class chunk_reduction { + public: + enum column_type { GLOBAL + , LOCAL_POSITIVE + , LOCAL_NEGATIVE }; + + public: + template< typename Representation > + void operator() ( boundary_matrix< Representation >& boundary_matrix ) { + + + const index nr_columns = boundary_matrix.get_num_cols(); + if( omp_get_max_threads( ) > nr_columns ) + omp_set_num_threads( 1 ); + + const dimension max_dim = boundary_matrix.get_max_dim(); + + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + std::vector < column_type > column_type( nr_columns, GLOBAL ); + std::vector< char > is_active( nr_columns, false ); + + const index chunk_size = omp_get_max_threads() == 1 ? (index)sqrt( (double)nr_columns ) : nr_columns / omp_get_max_threads(); + + std::vector< index > chunk_boundaries; + for( index cur_boundary = 0; cur_boundary < nr_columns; cur_boundary += chunk_size ) + chunk_boundaries.push_back( cur_boundary ); + chunk_boundaries.push_back( nr_columns ); + + for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { + // Phase 1: Reduce chunks locally -- 1st pass + #pragma omp parallel for schedule( guided, 1 ) + for( index chunk_id = 0; chunk_id < (index)chunk_boundaries.size() - 1; chunk_id++ ) + _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, + chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id ] ); + boundary_matrix.sync(); + + // Phase 1: Reduce chunks locally -- 2nd pass + #pragma omp parallel for schedule( guided, 1 ) + for( index chunk_id = 1; chunk_id < (index)chunk_boundaries.size( ) - 1; chunk_id++ ) + _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, + chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id - 1 ] ); + boundary_matrix.sync( ); + } + + // get global columns + std::vector< index > global_columns; + for( index cur_col_idx = 0; cur_col_idx < nr_columns; cur_col_idx++ ) + if( column_type[ cur_col_idx ] == GLOBAL ) + global_columns.push_back( cur_col_idx ); + + // get active columns + #pragma omp parallel for + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) + is_active[ global_columns[ idx ] ] = true; + _get_active_columns( boundary_matrix, lowest_one_lookup, column_type, global_columns, is_active ); + + // Phase 2+3: Simplify columns and reduce them + for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { + // Phase 2: Simplify columns + std::vector< index > temp_col; + #pragma omp parallel for schedule( guided, 1 ), private( temp_col ) + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) + if( boundary_matrix.get_dim( global_columns[ idx ] ) == cur_dim ) + _global_column_simplification( global_columns[ idx ], boundary_matrix, lowest_one_lookup, column_type, is_active, temp_col ); + boundary_matrix.sync(); + + // Phase 3: Reduce columns + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { + index cur_col = global_columns[ idx ]; + if( boundary_matrix.get_dim( cur_col ) == cur_dim && column_type[ cur_col ] == GLOBAL ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + lowest_one_lookup[ lowest_one ] = cur_col; + boundary_matrix.clear( lowest_one ); + } + boundary_matrix.finalize( cur_col ); + } + } + } + + boundary_matrix.sync(); + } + + protected: + template< typename Representation > + void _local_chunk_reduction( boundary_matrix< Representation >& boundary_matrix + , std::vector& lowest_one_lookup + , std::vector< column_type >& column_type + , const dimension cur_dim + , const index chunk_begin + , const index chunk_end + , const index row_begin ) { + + for( index cur_col = chunk_begin; cur_col < chunk_end; cur_col++ ) { + if( column_type[ cur_col ] == GLOBAL && boundary_matrix.get_dim( cur_col ) == cur_dim ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one >= row_begin && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one >= row_begin ) { + lowest_one_lookup[ lowest_one ] = cur_col; + column_type[ cur_col ] = LOCAL_NEGATIVE; + column_type[ lowest_one ] = LOCAL_POSITIVE; + boundary_matrix.clear( lowest_one ); + boundary_matrix.finalize( cur_col ); + } + } + } + } + + template< typename Representation > + void _get_active_columns( const boundary_matrix< Representation >& boundary_matrix + , const std::vector< index >& lowest_one_lookup + , const std::vector< column_type >& column_type + , const std::vector< index >& global_columns + , std::vector< char >& is_active ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< char > finished( nr_columns, false ); + + std::vector< std::pair < index, index > > stack; + std::vector< index > cur_col_values; + #pragma omp parallel for schedule( guided, 1 ), private( stack, cur_col_values ) + for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { + bool pop_next = false; + index start_col = global_columns[ idx ]; + stack.push_back( std::pair< index, index >( start_col, -1 ) ); + while( !stack.empty() ) { + index cur_col = stack.back().first; + index prev_col = stack.back().second; + if( pop_next ) { + stack.pop_back(); + pop_next = false; + if( prev_col != -1 ) { + if( is_active[ cur_col ] ) { + is_active[ prev_col ] = true; + } + if( prev_col == stack.back().first ) { + finished[ prev_col ] = true; + pop_next = true; + } + } + } else { + pop_next = true; + boundary_matrix.get_col( cur_col, cur_col_values ); + for( index idx = 0; idx < (index) cur_col_values.size(); idx++ ) { + index cur_row = cur_col_values[ idx ]; + if( ( column_type[ cur_row ] == GLOBAL ) ) { + is_active[ cur_col ] = true; + } else if( column_type[ cur_row ] == LOCAL_POSITIVE ) { + index next_col = lowest_one_lookup[ cur_row ]; + if( next_col != cur_col && !finished[ cur_col ] ) { + stack.push_back( std::make_pair( next_col, cur_col ) ); + pop_next = false; + } + } + } + } + } + } + } + + template< typename Representation > + void _global_column_simplification( const index col_idx + , boundary_matrix< Representation >& boundary_matrix + , const std::vector< index >& lowest_one_lookup + , const std::vector< column_type >& column_type + , const std::vector< char >& is_active + , std::vector< index >& temp_col ) + { + temp_col.clear(); + while( !boundary_matrix.is_empty( col_idx ) ) { + index cur_row = boundary_matrix.get_max_index( col_idx ); + switch( column_type[ cur_row ] ) { + case GLOBAL: + temp_col.push_back( cur_row ); + boundary_matrix.remove_max( col_idx ); + break; + case LOCAL_NEGATIVE: + boundary_matrix.remove_max( col_idx ); + break; + case LOCAL_POSITIVE: + if( is_active[ lowest_one_lookup[ cur_row ] ] ) + boundary_matrix.add_to( lowest_one_lookup[ cur_row ], col_idx ); + else + boundary_matrix.remove_max( col_idx ); + break; + } + } + std::reverse( temp_col.begin(), temp_col.end() ); + boundary_matrix.set_col( col_idx, temp_col ); + } + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h new file mode 100644 index 0000000000..cdd1a8fd18 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h @@ -0,0 +1,56 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class row_reduction { + public: + template< typename Representation > + void operator() ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< std::vector< index > > lowest_one_lookup( nr_columns ); + + for( index cur_col = nr_columns - 1; cur_col >= 0; cur_col-- ) { + if( !boundary_matrix.is_empty( cur_col ) ) + lowest_one_lookup[ boundary_matrix.get_max_index( cur_col ) ].push_back( cur_col ); + + if( !lowest_one_lookup[ cur_col ].empty() ) { + boundary_matrix.clear( cur_col ); + boundary_matrix.finalize( cur_col ); + std::vector< index >& cols_with_cur_lowest = lowest_one_lookup[ cur_col ]; + index source = *min_element( cols_with_cur_lowest.begin(), cols_with_cur_lowest.end() ); + for( index idx = 0; idx < (index)cols_with_cur_lowest.size(); idx++ ) { + index target = cols_with_cur_lowest[ idx ]; + if( target != source && !boundary_matrix.is_empty( target ) ) { + boundary_matrix.add_to( source, target ); + if( !boundary_matrix.is_empty( target ) ) { + index lowest_one_of_target = boundary_matrix.get_max_index( target ); + lowest_one_lookup[ lowest_one_of_target ].push_back( target ); + } + } + } + } + } + } + }; +} \ No newline at end of file diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h new file mode 100644 index 0000000000..bf442e6089 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h @@ -0,0 +1,80 @@ +/* Copyright 2013 IST Austria + Contributed by: Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class spectral_sequence_reduction { + public: + template< typename Representation > + void operator () ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + + //const index num_stripes = (index) sqrt( (double)nr_columns ); + const index num_stripes = omp_get_max_threads(); + + index block_size = ( nr_columns % num_stripes == 0 ) ? nr_columns / num_stripes : block_size = nr_columns / num_stripes + 1; + + std::vector< std::vector< index > > unreduced_cols_cur_pass( num_stripes ); + std::vector< std::vector< index > > unreduced_cols_next_pass( num_stripes ); + + for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { + #pragma omp parallel for schedule( guided, 1 ) + for( index cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { + index col_begin = cur_stripe * block_size; + index col_end = std::min( (cur_stripe+1) * block_size, nr_columns ); + for( index cur_col = col_begin; cur_col < col_end; cur_col++ ) + if( boundary_matrix.get_dim( cur_col ) == cur_dim && boundary_matrix.get_max_index( cur_col ) != -1 ) + unreduced_cols_cur_pass[ cur_stripe ].push_back( cur_col ); + } + for( index cur_pass = 0; cur_pass < num_stripes; cur_pass++ ) { + boundary_matrix.sync(); + #pragma omp parallel for schedule( guided, 1 ) + for( int cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { + index row_begin = (cur_stripe - cur_pass) * block_size; + index row_end = row_begin + block_size; + unreduced_cols_next_pass[ cur_stripe ].clear(); + for( index idx = 0; idx < (index)unreduced_cols_cur_pass[ cur_stripe ].size(); idx++ ) { + index cur_col = unreduced_cols_cur_pass[ cur_stripe ][ idx ]; + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one >= row_begin && lowest_one < row_end && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + if( lowest_one >= row_begin && lowest_one < row_end ) { + lowest_one_lookup[ lowest_one ] = cur_col; + boundary_matrix.clear( lowest_one ); + boundary_matrix.finalize( cur_col ); + } else { + unreduced_cols_next_pass[ cur_stripe ].push_back( cur_col ); + } + } + } + unreduced_cols_next_pass[ cur_stripe ].swap( unreduced_cols_cur_pass[ cur_stripe ] ); + } + } + } + } + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h new file mode 100644 index 0000000000..e490a5e0d1 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h @@ -0,0 +1,47 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class standard_reduction { + public: + template< typename Representation > + void operator() ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + lowest_one_lookup[ lowest_one ] = cur_col; + } + boundary_matrix.finalize( cur_col ); + } + } + }; +} + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h new file mode 100644 index 0000000000..2357df0256 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h @@ -0,0 +1,51 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class twist_reduction { + public: + template< typename Representation > + void operator () ( boundary_matrix< Representation >& boundary_matrix ) { + + const index nr_columns = boundary_matrix.get_num_cols(); + std::vector< index > lowest_one_lookup( nr_columns, -1 ); + + for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + if( boundary_matrix.get_dim( cur_col ) == cur_dim ) { + index lowest_one = boundary_matrix.get_max_index( cur_col ); + while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { + boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); + lowest_one = boundary_matrix.get_max_index( cur_col ); + } + if( lowest_one != -1 ) { + lowest_one_lookup[ lowest_one ] = cur_col; + boundary_matrix.clear( lowest_one ); + } + boundary_matrix.finalize( cur_col ); + } + } + } + } + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h new file mode 100644 index 0000000000..10c66cca13 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h @@ -0,0 +1,343 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +// interface class for the main data structure -- implementations of the interface can be found in ./representations +namespace phat { + template< class Representation = bit_tree_pivot_column > + class boundary_matrix + { + + protected: + Representation rep; + + // interface functions -- actual implementation and complexity depends on chosen @Representation template + public: + // get overall number of columns in boundary_matrix + index get_num_cols() const { return rep._get_num_cols(); } + + // set overall number of columns in boundary_matrix + void set_num_cols( index nr_of_columns ) { rep._set_num_cols( nr_of_columns ); } + + // get dimension of given index + dimension get_dim( index idx ) const { return rep._get_dim( idx ); } + + // set dimension of given index + void set_dim( index idx, dimension dim ) { rep._set_dim( idx, dim ); } + + // replaces content of @col with boundary of given index + void get_col( index idx, column& col ) const { col.clear(); rep._get_col( idx, col ); } + + // set column @idx to the values contained in @col + void set_col( index idx, const column& col ) { rep._set_col( idx, col ); } + + // true iff boundary of given column is empty + bool is_empty( index idx ) const { return rep._is_empty( idx ); } + + // largest index of given column (new name for lowestOne()) -- NOT thread-safe + index get_max_index( index idx ) const { return rep._get_max_index( idx ); } + + // removes maximal index from given column + void remove_max( index idx ) { rep._remove_max( idx ); } + + // adds column @source to column @target' + void add_to( index source, index target ) { rep._add_to( source, target ); } + + // clears given column + void clear( index idx ) { rep._clear( idx ); } + + // finalizes given column + void finalize( index idx ) { rep._finalize( idx ); } + + // syncronizes all internal data structures -- has to be called before and after any multithreaded access! + void sync() { rep._sync(); } + + // info functions -- independent of chosen 'Representation' + public: + // maximal dimension + dimension get_max_dim() const { + dimension cur_max_dim = 0; + for( index idx = 0; idx < get_num_cols(); idx++ ) + cur_max_dim = get_dim( idx ) > cur_max_dim ? get_dim( idx ) : cur_max_dim; + return cur_max_dim; + } + + // number of nonzero rows for given column @idx + index get_num_rows( index idx ) const { + column cur_col; + get_col( idx, cur_col ); + return cur_col.size(); + } + + // maximal number of nonzero rows of all columns + index get_max_col_entries() const { + index max_col_entries = -1; + const index nr_of_columns = get_num_cols(); + for( index idx = 0; idx < nr_of_columns; idx++ ) + max_col_entries = get_num_rows( idx ) > max_col_entries ? get_num_rows( idx ) : max_col_entries; + return max_col_entries; + } + + // maximal number of nonzero cols of all rows + index get_max_row_entries() const { + size_t max_row_entries = 0; + const index nr_of_columns = get_num_cols(); + std::vector< std::vector< index > > transposed_matrix( nr_of_columns ); + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + get_col( cur_col, temp_col ); + for( index idx = 0; idx < (index)temp_col.size(); idx++) + transposed_matrix[ temp_col[ idx ] ].push_back( cur_col ); + } + for( index idx = 0; idx < nr_of_columns; idx++ ) + max_row_entries = transposed_matrix[ idx ].size() > max_row_entries ? transposed_matrix[ idx ].size() : max_row_entries; + return max_row_entries; + } + + // overall number of entries in the matrix + index get_num_entries() const { + index number_of_nonzero_entries = 0; + const index nr_of_columns = get_num_cols(); + for( index idx = 0; idx < nr_of_columns; idx++ ) + number_of_nonzero_entries += get_num_rows( idx ); + return number_of_nonzero_entries; + } + + // operators / constructors + public: + boundary_matrix() {}; + + template< class OtherRepresentation > + boundary_matrix( const boundary_matrix< OtherRepresentation >& other ) { + *this = other; + } + + template< typename OtherRepresentation > + bool operator==( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { + const index number_of_columns = this->get_num_cols(); + + if( number_of_columns != other_boundary_matrix.get_num_cols() ) + return false; + + column temp_col; + column other_temp_col; + for( index idx = 0; idx < number_of_columns; idx++ ) { + this->get_col( idx, temp_col ); + other_boundary_matrix.get_col( idx, other_temp_col ); + if( temp_col != other_temp_col || this->get_dim( idx ) != other_boundary_matrix.get_dim( idx ) ) + return false; + } + return true; + } + + template< typename OtherRepresentation > + bool operator!=( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { + return !( *this == other_boundary_matrix ); + } + + template< typename OtherRepresentation > + boundary_matrix< Representation >& operator=( const boundary_matrix< OtherRepresentation >& other ) + { + const index nr_of_columns = other.get_num_cols(); + this->set_num_cols( nr_of_columns ); + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + this->set_dim( cur_col, other.get_dim( cur_col ) ); + other.get_col( cur_col, temp_col ); + this->set_col( cur_col, temp_col ); + } + + // by convention, always return *this + return *this; + } + + // I/O -- independent of chosen 'Representation' + public: + + // initializes boundary_matrix from (vector, vector) pair -- untested + template< typename index_type, typename dimemsion_type > + void load_vector_vector( const std::vector< std::vector< index_type > >& input_matrix, const std::vector< dimemsion_type >& input_dims ) { + const index nr_of_columns = (index)input_matrix.size(); + this->set_num_cols( nr_of_columns ); + column temp_col; + #pragma omp parallel for private( temp_col ) + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + this->set_dim( cur_col, (dimension)input_dims[ cur_col ] ); + + index num_rows = input_matrix[ cur_col ].size(); + temp_col.resize( num_rows ); + for( index cur_row = 0; cur_row < num_rows; cur_row++ ) + temp_col[ cur_row ] = (index)input_matrix[ cur_col ][ cur_row ]; + this->set_col( cur_col, temp_col ); + } + } + + template< typename index_type, typename dimemsion_type > + void save_vector_vector( std::vector< std::vector< index_type > >& output_matrix, std::vector< dimemsion_type >& output_dims ) { + const index nr_of_columns = get_num_cols(); + output_matrix.resize( nr_of_columns ); + output_dims.resize( nr_of_columns ); + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + output_dims[ cur_col ] = (dimemsion_type)get_dim( cur_col ); + get_col( cur_col, temp_col ); + index num_rows = temp_col.size(); + output_matrix[ cur_col ].clear(); + output_matrix[ cur_col ].resize( num_rows ); + for( index cur_row = 0; cur_row < num_rows; cur_row++ ) + output_matrix[ cur_col ][ cur_row ] = (index_type)temp_col[ cur_row ]; + } + } + + + // Loads the boundary_matrix from given file in ascii format + // Format: each line represents a column, first number is dimension, other numbers are the content of the column. + // Ignores empty lines and lines starting with a '#'. + bool load_ascii( std::string filename ) { + // first count number of columns: + std::string cur_line; + std::ifstream dummy( filename .c_str() ); + if( dummy.fail() ) + return false; + + index number_of_columns = 0; + while( getline( dummy, cur_line ) ) { + cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); + if( cur_line != "" && cur_line[ 0 ] != '#' ) + number_of_columns++; + + } + this->set_num_cols( number_of_columns ); + dummy.close(); + + std::ifstream input_stream( filename.c_str() ); + if( input_stream.fail() ) + return false; + + column temp_col; + index cur_col = -1; + while( getline( input_stream, cur_line ) ) { + cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); + if( cur_line != "" && cur_line[ 0 ] != '#' ) { + cur_col++; + std::stringstream ss( cur_line ); + + int64_t temp_dim; + ss >> temp_dim; + this->set_dim( cur_col, (dimension) temp_dim ); + + int64_t temp_index; + temp_col.clear(); + while( ss.good() ) { + ss >> temp_index; + temp_col.push_back( (index)temp_index ); + } + std::sort( temp_col.begin(), temp_col.end() ); + this->set_col( cur_col, temp_col ); + } + } + + input_stream.close(); + return true; + } + + // Saves the boundary_matrix to given file in ascii format + // Format: each line represents a column, first number is dimension, other numbers are the content of the column + bool save_ascii( std::string filename ) { + std::ofstream output_stream( filename.c_str() ); + if( output_stream.fail() ) + return false; + + const index nr_columns = this->get_num_cols(); + column tempCol; + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + output_stream << (int64_t)this->get_dim( cur_col ); + this->get_col( cur_col, tempCol ); + for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size(); cur_row_idx++ ) + output_stream << " " << tempCol[ cur_row_idx ]; + output_stream << std::endl; + } + + output_stream.close(); + return true; + } + + // Loads boundary_matrix from given file + // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... + bool load_binary( std::string filename ) + { + std::ifstream input_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::in ); + if( input_stream.fail( ) ) + return false; + + int64_t nr_columns; + input_stream.read( (char*)&nr_columns, sizeof( int64_t ) ); + this->set_num_cols( (index)nr_columns ); + + column temp_col; + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + int64_t cur_dim; + input_stream.read( (char*)&cur_dim, sizeof( int64_t ) ); + this->set_dim( cur_col, (dimension)cur_dim ); + int64_t nr_rows; + input_stream.read( (char*)&nr_rows, sizeof( int64_t ) ); + temp_col.resize( ( std::size_t )nr_rows ); + for( index idx = 0; idx < nr_rows; idx++ ) { + int64_t cur_row; + input_stream.read( (char*)&cur_row, sizeof( int64_t ) ); + temp_col[ idx ] = (index)cur_row; + } + this->set_col( cur_col, temp_col ); + } + + input_stream.close( ); + return true; + } + + // Saves the boundary_matrix to given file in binary format + // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... + bool save_binary( std::string filename ) + { + std::ofstream output_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::out ); + if( output_stream.fail( ) ) + return false; + + const int64_t nr_columns = this->get_num_cols( ); + output_stream.write( (char*)&nr_columns, sizeof( int64_t ) ); + column tempCol; + for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { + int64_t cur_dim = this->get_dim( cur_col ); + output_stream.write( (char*)&cur_dim, sizeof( int64_t ) ); + this->get_col( cur_col, tempCol ); + int64_t cur_nr_rows = tempCol.size( ); + output_stream.write( (char*)&cur_nr_rows, sizeof( int64_t ) ); + for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size( ); cur_row_idx++ ) { + int64_t cur_row = tempCol[ cur_row_idx ]; + output_stream.write( (char*)&cur_row, sizeof( int64_t ) ); + } + } + + output_stream.close( ); + return true; + } + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h new file mode 100644 index 0000000000..48be65c28e --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h @@ -0,0 +1,128 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include +#include +#include + +namespace phat { + // Extracts persistence pairs in separate dimensions from a reduced + // boundary matrix representing ``double`` filtration. The pairs + // give persistent relative homology of the pair of filtrations. + // TODO: Use it with standard reduction algorithm (no template option). + template< typename ReductionAlgorithm, typename Representation > + void compute_relative_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix, const std::map& L) { + ReductionAlgorithm reduce; + reduce(boundary_matrix); + std::map free; + std::map invL; + for (std::map::const_iterator it = L.begin(); it != L.end(); ++it) { invL[it->second] = it->first; } + for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } + for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { + int dimension = boundary_matrix.get_dim(idx); + if (L.find(idx) != L.end()) { ++dimension; } + free[idx] = true; + if (!boundary_matrix.is_empty(idx)) { + index birth = boundary_matrix.get_max_index(idx); + index death = idx; + pairs[dimension-1].append_pair(birth, death); + free[birth] = false; + free[death] = false; + } else { + // This is an L-simplex and a (dimension+1)-dimensional cycle + if (L.find(idx) != L.end()) { + assert(dimension < pairs.size()); + pairs[dimension].append_pair(idx, -1); + } + } + } + for (std::map::iterator it = free.begin(); it != free.end(); ++it) { + if (it->second) { + int dimension = boundary_matrix.get_dim(it->first); + if (invL.find(it->first) == invL.end() && L.find(it->first) == L.end()) { + assert(dimension < pairs.size()); + pairs[dimension].append_pair(it->first, -1); + } + } + } + } + + // Extracts persistence pairs in separate dimensions; expects a d-dimensional vector of persistent_pairs + template< typename ReductionAlgorithm, typename Representation > + void compute_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix) { + ReductionAlgorithm reduce; + reduce(boundary_matrix); + std::map free; + for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } + for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { + int dimension = boundary_matrix.get_dim(idx); + free[idx] = true; + if (!boundary_matrix.is_empty(idx)) { + index birth = boundary_matrix.get_max_index(idx); + index death = idx; + pairs[dimension-1].append_pair(birth, death); + // Cannot be of the form (a, infinity) + free[birth] = false; + free[death] = false; + } + } + for (std::map::iterator it = free.begin(); it != free.end(); ++it) { + if (it->second) { + int dimension = boundary_matrix.get_dim(it->first); + pairs[dimension].append_pair(it->first, -1); + } + } + } + + template< typename ReductionAlgorithm, typename Representation > + void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + ReductionAlgorithm reduce; + reduce( boundary_matrix ); + pairs.clear(); + for( index idx = 0; idx < boundary_matrix.get_num_cols(); idx++ ) { + if( !boundary_matrix.is_empty( idx ) ) { + index birth = boundary_matrix.get_max_index( idx ); + index death = idx; + pairs.append_pair( birth, death ); + } + } + } + + template< typename ReductionAlgorithm, typename Representation > + void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + + dualize( boundary_matrix ); + compute_persistence_pairs< ReductionAlgorithm >( pairs, boundary_matrix ); + dualize_persistence_pairs( pairs, boundary_matrix.get_num_cols() ); + } + + template< typename Representation > + void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + phat::compute_persistence_pairs< twist_reduction >( pairs, boundary_matrix ); + } + + + template< typename Representation > + void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { + compute_persistence_pairs_dualized< twist_reduction >( pairs, boundary_matrix ); + } + +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h new file mode 100644 index 0000000000..3ffedf875f --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h @@ -0,0 +1,74 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include +#include + + +namespace phat { + template< typename Representation > + void dualize( boundary_matrix< Representation >& boundary_matrix ) { + + std::vector< dimension > dual_dims; + std::vector< std::vector< index > > dual_matrix; + + index nr_of_columns = boundary_matrix.get_num_cols(); + dual_matrix.resize( nr_of_columns ); + dual_dims.resize( nr_of_columns ); + + std::vector< index > dual_sizes( nr_of_columns, 0 ); + + column temp_col; + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + boundary_matrix.get_col( cur_col, temp_col ); + for( index idx = 0; idx < (index)temp_col.size(); idx++) + dual_sizes[ nr_of_columns - 1 - temp_col[ idx ] ]++; + } + + #pragma omp parallel for + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) + dual_matrix[cur_col].reserve(dual_sizes[cur_col]); + + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { + boundary_matrix.get_col( cur_col, temp_col ); + for( index idx = 0; idx < (index)temp_col.size(); idx++) + dual_matrix[ nr_of_columns - 1 - temp_col[ idx ] ].push_back( nr_of_columns - 1 - cur_col ); + } + + const dimension max_dim = boundary_matrix.get_max_dim(); + #pragma omp parallel for + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) + dual_dims[ nr_of_columns - 1 - cur_col ] = max_dim - boundary_matrix.get_dim( cur_col ); + + #pragma omp parallel for + for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) + std::reverse( dual_matrix[ cur_col ].begin(), dual_matrix[ cur_col ].end() ); + + boundary_matrix.load_vector_vector( dual_matrix, dual_dims ); + } + + void dualize_persistence_pairs( persistence_pairs& pairs, const index n ) { + for (index i = 0; i < pairs.get_num_pairs(); ++i) { + std::pair< index, index > pair = pairs.get_pair( i ); + pairs.set_pair( i , n - 1 - pair.second, n - 1 - pair.first); + } + } +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h new file mode 100644 index 0000000000..fb5c07acb0 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h @@ -0,0 +1,75 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +// STL includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// VS2008 and below unfortunately do not support stdint.h +#if defined(_MSC_VER)&& _MSC_VER < 1600 + typedef __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef __int16 int16_t; + typedef unsigned __int16 uint16_t; + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +#else + #include +#endif + +// basic types. index can be changed to int32_t to save memory on small instances +namespace phat { + typedef int64_t index; + typedef int8_t dimension; + typedef std::vector< index > column; +} + +// OpenMP (proxy) functions +#if defined _OPENMP + #include +#else + #define omp_get_thread_num() 0 + #define omp_get_max_threads() 1 + #define omp_get_num_threads() 1 + void omp_set_num_threads( int ) {}; + #include + #define omp_get_wtime() (float)clock() / (float)CLOCKS_PER_SEC +#endif + +#include + + + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h new file mode 100644 index 0000000000..d0b5332bc1 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h @@ -0,0 +1,52 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +// should ideally be equal to the cache line size of the CPU +#define PHAT_TLS_SPACING_FACTOR 64 + +// ThreadLocalStorage with some spacing to avoid "false sharing" (see wikipedia) +template< typename T > +class thread_local_storage +{ +public: + + thread_local_storage() : per_thread_storage( omp_get_max_threads() * PHAT_TLS_SPACING_FACTOR ) {}; + + T& operator()() { + return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; + } + + const T& operator()() const { + return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; + } + + T& operator[]( int tid ) { + return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; + } + + const T& operator[]( int tid ) const { + return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; + } + +protected: + std::vector< T > per_thread_storage; +}; diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h new file mode 100644 index 0000000000..eafc6389e2 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h @@ -0,0 +1,155 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class persistence_pairs { + + protected: + std::vector< std::pair< index, index > > pairs; + + public: + index get_num_pairs() const { + return (index)pairs.size(); + } + + void append_pair( index birth, index death ) { + pairs.push_back( std::make_pair( birth, death ) ); + } + + std::pair< index, index > get_pair( index idx ) const { + return pairs[ idx ]; + } + + void set_pair( index idx, index birth, index death ) { + pairs[ idx ] = std::make_pair( birth, death ); + } + + void clear() { + pairs.clear(); + } + + void sort() { + std::sort( pairs.begin(), pairs.end() ); + } + + // Loads the persistence pairs from given file in asci format + // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... + bool load_ascii( std::string filename ) { + std::ifstream input_stream( filename.c_str() ); + if( input_stream.fail() ) + return false; + + int64_t nr_pairs; + input_stream >> nr_pairs; + pairs.clear(); + for( index idx = 0; idx < nr_pairs; idx++ ) { + int64_t birth; + input_stream >> birth; + int64_t death; + input_stream >> death; + append_pair( (index)birth, (index)death ); + } + + input_stream.close(); + return true; + } + + // Saves the persistence pairs to given file in binary format + // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... + bool save_ascii( std::string filename ) { + std::ofstream output_stream( filename.c_str() ); + if( output_stream.fail() ) + return false; + + this->sort(); + output_stream << get_num_pairs() << std::endl; + for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { + output_stream << pairs[idx].first << " " << pairs[idx].second << std::endl; + } + + output_stream.close(); + return true; + } + + // Loads the persistence pairs from given file in binary format + // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... + bool load_binary( std::string filename ) { + std::ifstream input_stream( filename.c_str(), std::ios_base::binary | std::ios_base::in ); + if( input_stream.fail() ) + return false; + + int64_t nr_pairs; + input_stream.read( (char*)&nr_pairs, sizeof( int64_t ) ); + for( index idx = 0; idx < nr_pairs; idx++ ) { + int64_t birth; + input_stream.read( (char*)&birth, sizeof( int64_t ) ); + int64_t death; + input_stream.read( (char*)&death, sizeof( int64_t ) ); + append_pair( (index)birth, (index)death ); + } + + input_stream.close(); + return true; + } + + // Saves the persistence pairs to given file in binary format + // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... + bool save_binary( std::string filename ) { + std::ofstream output_stream( filename.c_str(), std::ios_base::binary | std::ios_base::out ); + if( output_stream.fail() ) + return false; + + this->sort(); + int64_t nr_pairs = get_num_pairs(); + output_stream.write( (char*)&nr_pairs, sizeof( int64_t ) ); + for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { + int64_t birth = pairs[ idx ].first; + output_stream.write( (char*)&birth, sizeof( int64_t ) ); + int64_t death = pairs[ idx ].second; + output_stream.write( (char*)&death, sizeof( int64_t ) ); + } + + output_stream.close(); + return true; + } + + bool operator==( persistence_pairs& other_pairs ) { + this->sort(); + other_pairs.sort(); + if( pairs.size() != (std::size_t)other_pairs.get_num_pairs() ) + return false; + + for( index idx = 0; idx < (index)pairs.size(); idx++ ) + if( get_pair( idx ) != other_pairs.get_pair( idx ) ) + return false; + + return true; + } + + bool operator!=( persistence_pairs& other_pairs ) { + return !( *this == other_pairs ); + } + }; + + + +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h new file mode 100644 index 0000000000..e16d7a5d13 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h @@ -0,0 +1,102 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + + // Note: We could even make the rep generic in the underlying Const representation + // But I cannot imagine that anything else than vector> would + // make sense + template< typename PivotColumn > + class abstract_pivot_column : public vector_vector { + + protected: + typedef vector_vector Base; + typedef PivotColumn pivot_col; + + // For parallization purposes, it could be more than one full column + mutable thread_local_storage< pivot_col > pivot_cols; + mutable thread_local_storage< index > idx_of_pivot_cols; + + pivot_col& get_pivot_col() const { + return pivot_cols(); + } + + bool is_pivot_col( index idx ) const { + return idx_of_pivot_cols() == idx; + } + + void release_pivot_col() { + index idx = idx_of_pivot_cols(); + if( idx != -1 ) { + this->matrix[ idx ].clear(); + pivot_cols().get_col_and_clear( this->matrix[ idx ] ); + } + idx_of_pivot_cols() = -1; + } + + void make_pivot_col( index idx ) { + release_pivot_col(); + idx_of_pivot_cols() = idx; + get_pivot_col().add_col( matrix[ idx ] ); + } + + public: + + void _set_num_cols( index nr_of_cols ) { + #pragma omp parallel for + for( int tid = 0; tid < omp_get_num_threads(); tid++ ) { + pivot_cols[ tid ].init( nr_of_cols ); + idx_of_pivot_cols[ tid ] = -1; + } + Base::_set_num_cols( nr_of_cols ); + } + + void _add_to( index source, index target ) { + if( !is_pivot_col( target ) ) + make_pivot_col( target ); + get_pivot_col().add_col( matrix[source] ); + } + + void _sync() { + #pragma omp parallel for + for( int tid = 0; tid < omp_get_num_threads(); tid++ ) + release_pivot_col(); + } + + void _get_col( index idx, column& col ) const { is_pivot_col( idx ) ? get_pivot_col().get_col( col ) : Base::_get_col( idx, col ); } + + bool _is_empty( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().is_empty() : Base::_is_empty( idx ); } + + index _get_max_index( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().get_max_index() : Base::_get_max_index( idx ); } + + void _clear( index idx ) { is_pivot_col( idx ) ? get_pivot_col().clear() : Base::_clear( idx ); } + + void _set_col( index idx, const column& col ) { is_pivot_col( idx ) ? get_pivot_col().set_col( col ) : Base::_set_col( idx, col ); } + + void _remove_max( index idx ) { is_pivot_col( idx ) ? get_pivot_col().remove_max() : Base::_remove_max( idx ); } + + void finalize( index idx ) { Base::_finalize( idx ); } + }; +} + + diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h new file mode 100644 index 0000000000..4d48e8853d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h @@ -0,0 +1,165 @@ +/* Copyright 2013 IST Austria + Contributed by: Hubert Wagner + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + + // This is a bitset indexed with a 64-ary tree. Each node in the index + // has 64 bits; i-th bit says that the i-th subtree is non-empty. + // Supports practically O(1), inplace, zero-allocation: insert, remove, max_element + // and clear in O(number of ones in the bitset). + // 'add_index' is still the real bottleneck in practice. + class bit_tree_column + { + protected: + + size_t offset; // data[i + offset] = ith block of the data-bitset + typedef uint64_t block_type; + std::vector< block_type > data; + + + size_t debrujin_magic_table[ 64 ]; + + enum { block_size_in_bits = 64 }; + enum { block_shift = 6 }; + + // Some magic: http://graphics.stanford.edu/~seander/bithacks.html + // Gets the position of the rightmost bit of 'x'. 0 means the most significant bit. + // (-x)&x isolates the rightmost bit. + // The whole method is much faster than calling log2i, and very comparable to using ScanBitForward/Reverse intrinsic, + // which should be one CPU instruction, but is not portable. + size_t rightmost_pos( const block_type value ) const { + return 64 - 1 - debrujin_magic_table[ ( (value & (-(int64_t)value) ) * 0x07EDD5E59A4E28C2 ) >> 58 ]; + } + + public: + + void init( index num_cols ) { + int64_t n = 1; // in case of overflow + int64_t bottom_blocks_needed = ( num_cols + block_size_in_bits - 1 ) / block_size_in_bits; + int64_t upper_blocks = 1; + + // How many blocks/nodes of index needed to index the whole bitset? + while( n * block_size_in_bits < bottom_blocks_needed ) { + n *= block_size_in_bits; + upper_blocks += n; + } + + offset = upper_blocks; + data.resize( upper_blocks + bottom_blocks_needed, 0 ); + + std::size_t temp_array[ 64 ] = { + 63, 0, 58, 1, 59, 47, 53, 2, + 60, 39, 48, 27, 54, 33, 42, 3, + 61, 51, 37, 40, 49, 18, 28, 20, + 55, 30, 34, 11, 43, 14, 22, 4, + 62, 57, 46, 52, 38, 26, 32, 41, + 50, 36, 17, 19, 29, 10, 13, 21, + 56, 45, 25, 31, 35, 16, 9, 12, + 44, 24, 15, 8, 23, 7, 6, 5 }; + + std::copy( &temp_array[ 0 ], &temp_array[ 64 ], &debrujin_magic_table[ 0 ] ); + } + + index get_max_index() const { + if( !data[ 0 ] ) + return -1; + + size_t n = 0; + size_t newn = 0; + size_t index = 0; + while( newn < data.size() ) { + n = newn; + index = rightmost_pos( data[ n ] ); + newn = ( n << block_shift ) + index + 1; + } + + return ( ( n - offset ) << block_shift ) + index; + } + + bool is_empty() const { + return data[ 0 ] == 0; + } + + void add_index( const size_t entry ) { + const block_type ONE = 1; + const block_type block_modulo_mask = ( ONE << block_shift ) - 1; + size_t index_in_level = entry >> block_shift; + size_t address = index_in_level + offset; + size_t index_in_block = entry & block_modulo_mask; + + block_type mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); + + data[ address ] ^= mask; + + // Check if we reached the root. Also, if anyone else was in this block, we don't need to update the path up. + while( address && !( data[ address ] & ~mask ) ) { + index_in_block = index_in_level & block_modulo_mask; + index_in_level >>= block_shift; + --address; + address >>= block_shift; + mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); + data[ address ] ^= mask; + } + } + + void get_col_and_clear( column &out ) { + index mx = this->get_max_index(); + while( mx != -1 ) { + out.push_back( mx ); + add_index( mx ); + mx = this->get_max_index(); + } + + std::reverse( out.begin(), out.end() ); + } + + void add_col(const column &col) { + for( size_t i = 0; i < col.size(); ++i ) + add_index(col[i]); + } + + void clear() { + index mx = this->get_max_index(); + while( mx != -1 ) { + add_index( mx ); + mx = this->get_max_index(); + } + } + + void remove_max() { + add_index( get_max_index() ); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column bit_tree_pivot_column; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h new file mode 100644 index 0000000000..c2e9e3c574 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h @@ -0,0 +1,100 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class full_column { + + protected: + std::priority_queue< index > history; + std::vector< char > is_in_history; + std::vector< char > col_bit_field; + + public: + void init( const index total_size ) { + col_bit_field.resize( total_size, false ); + is_in_history.resize( total_size, false ); + } + + void add_col( const column& col ) { + for( index idx = 0; idx < (index) col.size(); idx++ ) { + add_index( col[ idx ] ); + } + } + + void add_index( const index idx ) { + if( !is_in_history[ idx ] ) { + history.push( idx ); + is_in_history[ idx ] = true; + } + + col_bit_field[ idx ] = !col_bit_field[ idx ]; + } + + index get_max_index() { + while( history.size() > 0 ) { + index topIndex = history.top(); + if( col_bit_field[ topIndex ] ) { + return topIndex; + } else { + history.pop(); + is_in_history[ topIndex ] = false; + } + } + + return -1; + } + + void get_col_and_clear( column& col ) { + while( !is_empty() ) { + col.push_back( get_max_index() ); + add_index( get_max_index() ); + } + std::reverse( col.begin(), col.end() ); + } + + bool is_empty() { + return (get_max_index() == -1); + } + + void clear() { + while( !is_empty() ) + add_index( get_max_index() ); + } + + void remove_max() { + add_index( get_max_index() ); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column< full_column > full_pivot_column; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h new file mode 100644 index 0000000000..33cd07b40d --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h @@ -0,0 +1,126 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class heap_column { + + protected: + std::priority_queue< index > data; + + column temp_col; + index inserts_since_last_prune; + + void prune() + { + temp_col.clear( ); + index max_index = pop_max_index( ); + while( max_index != -1 ) { + temp_col.push_back( max_index ); + max_index = pop_max_index( ); + } + + for( index idx = 0; idx < (index)temp_col.size( ); idx++ ) + data.push( temp_col[ idx ] ); + + inserts_since_last_prune = 0; + } + + index pop_max_index() + { + if( data.empty( ) ) + return -1; + else { + index max_element = data.top( ); + data.pop(); + while( !data.empty( ) && data.top( ) == max_element ) { + data.pop( ); + if( data.empty( ) ) + return -1; + else { + max_element = data.top( ); + data.pop( ); + } + } + return max_element; + } + } + + public: + void init( const index total_size ) { + inserts_since_last_prune = 0; + clear(); + } + + void add_col( const column& col ) { + for( index idx = 0; idx < (index) col.size(); idx++ ) + data.push( col[ idx ] ); + inserts_since_last_prune += col.size( ); + if( 2 * inserts_since_last_prune >( index ) data.size( ) ) + prune(); + } + + index get_max_index() { + index max_element = pop_max_index( ); + if( max_element == -1 ) + return -1; + else { + data.push( max_element ); + return max_element; + } + } + + void get_col_and_clear( column& col ) { + col.clear(); + index max_index = pop_max_index( ); + while( max_index != -1 ) { + col.push_back( max_index ); + max_index = pop_max_index( ); + } + std::reverse( col.begin(), col.end() ); + } + + bool is_empty() { + return get_max_index() == -1; + } + + void clear() { + data = std::priority_queue< index >(); + } + + void remove_max() { + pop_max_index(); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column< heap_column > heap_pivot_column; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h new file mode 100644 index 0000000000..390fd91a99 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h @@ -0,0 +1,79 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include +#include + +namespace phat { + class sparse_column { + + protected: + std::set< index > data; + + void add_index( const index idx ) { + std::pair< std::set< index >::iterator, bool > result = data.insert( idx ); + if( result.second == false ) + data.erase( result.first ); + } + + public: + void init( const index total_size ) { + data.clear(); + } + + void add_col( const column& col ) { + for( index idx = 0; idx < (index) col.size(); idx++ ) + add_index( col[ idx ] ); + } + + index get_max_index() { + return data.empty() ? -1 : *data.rbegin(); + } + + void get_col_and_clear( column& col ) { + col.assign( data.begin(), data.end() ); + data.clear(); + } + + bool is_empty() { + return data.empty(); + } + + void clear() { + data.clear(); + } + + void remove_max() { + add_index( get_max_index() ); + } + + void set_col( const column& col ) { + clear(); + add_col( col ); + } + + void get_col( column& col ) { + get_col_and_clear( col ); + add_col( col ); + } + }; + + typedef abstract_pivot_column< sparse_column > sparse_pivot_column; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h new file mode 100644 index 0000000000..db0420ff23 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h @@ -0,0 +1,170 @@ +/* Copyright 2013 IST Austria +Contributed by: Jan Reininghaus + +This file is part of PHAT. + +PHAT is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +PHAT 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_heap { + + protected: + std::vector< dimension > dims; + std::vector< column > matrix; + + std::vector< index > inserts_since_last_prune; + + mutable thread_local_storage< column > temp_column_buffer; + + protected: + void _prune( index idx ) + { + column& col = matrix[ idx ]; + column& temp_col = temp_column_buffer(); + temp_col.clear(); + index max_index = _pop_max_index( col ); + while( max_index != -1 ) { + temp_col.push_back( max_index ); + max_index = _pop_max_index( col ); + } + col = temp_col; + std::reverse( col.begin( ), col.end( ) ); + std::make_heap( col.begin( ), col.end( ) ); + inserts_since_last_prune[ idx ] = 0; + } + + index _pop_max_index( index idx ) + { + return _pop_max_index( matrix[ idx ] ); + } + + index _pop_max_index( column& col ) const + { + if( col.empty( ) ) + return -1; + else { + index max_element = col.front( ); + std::pop_heap( col.begin( ), col.end( ) ); + col.pop_back( ); + while( !col.empty( ) && col.front( ) == max_element ) { + std::pop_heap( col.begin( ), col.end( ) ); + col.pop_back( ); + if( col.empty( ) ) + return -1; + else { + max_element = col.front( ); + std::pop_heap( col.begin( ), col.end( ) ); + col.pop_back( ); + } + } + return max_element; + } + } + + public: + // overall number of cells in boundary_matrix + index _get_num_cols( ) const + { + return (index)matrix.size( ); + } + void _set_num_cols( index nr_of_columns ) + { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + inserts_since_last_prune.assign( nr_of_columns, 0 ); + } + + // dimension of given index + dimension _get_dim( index idx ) const + { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) + { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const + { + temp_column_buffer( ) = matrix[ idx ]; + + index max_index = _pop_max_index( temp_column_buffer() ); + while( max_index != -1 ) { + col.push_back( max_index ); + max_index = _pop_max_index( temp_column_buffer( ) ); + } + std::reverse( col.begin( ), col.end( ) ); + } + void _set_col( index idx, const column& col ) + { + matrix[ idx ] = col; + std::make_heap( matrix[ idx ].begin( ), matrix[ idx ].end( ) ); + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const + { + return _get_max_index( idx ) == -1; + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const + { + column& col = const_cast< column& >( matrix[ idx ] ); + index max_element = _pop_max_index( col ); + col.push_back( max_element ); + std::push_heap( col.begin( ), col.end( ) ); + return max_element; + } + + // removes the maximal index of a column + void _remove_max( index idx ) + { + _pop_max_index( idx ); + } + + // clears given column + void _clear( index idx ) + { + matrix[ idx ].clear( ); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync( ) {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) + { + for( index idx = 0; idx < (index)matrix[ source ].size( ); idx++ ) { + matrix[ target ].push_back( matrix[ source ][ idx ] ); + std::push_heap( matrix[ target ].begin(), matrix[ target ].end() ); + } + inserts_since_last_prune[ target ] += matrix[ source ].size(); + + if( 2 * inserts_since_last_prune[ target ] > ( index )matrix[ target ].size() ) + _prune( target ); + } + + // finalizes given column + void _finalize( index idx ) { + _prune( idx ); + } + + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h new file mode 100644 index 0000000000..ca0b5b8e79 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h @@ -0,0 +1,101 @@ +/* Copyright 2013 IST Austria + Contributed by: Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_list { + + protected: + std::vector< dimension > dims; + std::vector< std::list< index > > matrix; + + public: + // overall number of cells in boundary_matrix + index _get_num_cols() const { + return (index)matrix.size(); + } + void _set_num_cols( index nr_of_columns ) { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + } + + // dimension of given index + dimension _get_dim( index idx ) const { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const { + col.clear(); + col.reserve( matrix[idx].size() ); + std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); + } + + void _set_col( index idx, const column& col ) { + matrix[ idx ].clear(); + matrix[ idx ].resize( col.size() ); + std::copy (col.begin(), col.end(), matrix[ idx ].begin() ); + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const { + return matrix[ idx ].empty(); + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const { + return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); + } + + // removes the maximal index of a column + void _remove_max( index idx ) { + std::list< index >::iterator it = matrix[ idx ].end(); + it--; + matrix[ idx ].erase( it ); + } + + // clears given column + void _clear( index idx ) { + matrix[ idx ].clear(); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync() {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) { + std::list< index >& source_col = matrix[ source ]; + std::list< index >& target_col = matrix[ target ]; + std::list< index > temp_col; + target_col.swap( temp_col ); + std::set_symmetric_difference( temp_col.begin(), temp_col.end(), + source_col.begin(), source_col.end(), + std::back_inserter( target_col ) ); + } + + // finalizes given column + void _finalize( index idx ) { + } + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h new file mode 100644 index 0000000000..6878a270c0 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h @@ -0,0 +1,99 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_set { + + protected: + std::vector< dimension > dims; + std::vector< std::set< index > > matrix; + + public: + // overall number of cells in boundary_matrix + index _get_num_cols() const { + return (index)matrix.size(); + } + void _set_num_cols( index nr_of_columns ) { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + } + + // dimension of given index + dimension _get_dim( index idx ) const { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const { + col.clear(); + col.reserve( matrix[idx].size() ); + std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); + } + void _set_col( index idx, const column& col ) { + matrix[ idx ].clear(); + matrix[ idx ].insert( col.begin(), col.end() ); + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const { + return matrix[ idx ].empty(); + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const { + return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); + } + + // removes the maximal index of a column + void _remove_max( index idx ) { + std::set< index >::iterator it = matrix[ idx ].end(); + it--; + matrix[ idx ].erase( it ); + } + + // clears given column + void _clear( index idx ) { + matrix[ idx ].clear(); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync() {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) { + for( std::set< index >::iterator it = matrix[ source ].begin(); it != matrix[ source ].end(); it++ ) { + std::set< index >& col = matrix[ target ]; + std::pair< std::set< index >::iterator, bool > result = col.insert( *it ); + if( !result.second ) + col.erase( result.first ); + } + } + + // finalizes given column + void _finalize( index idx ) { + } + + }; +} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h new file mode 100644 index 0000000000..f111d6b572 --- /dev/null +++ b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h @@ -0,0 +1,107 @@ +/* Copyright 2013 IST Austria + Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus + + This file is part of PHAT. + + PHAT is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + PHAT 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PHAT. If not, see . */ + +#pragma once + +#include + +namespace phat { + class vector_vector { + + protected: + std::vector< dimension > dims; + std::vector< column > matrix; + + thread_local_storage< column > temp_column_buffer; + + public: + // overall number of cells in boundary_matrix + index _get_num_cols() const { + return (index)matrix.size(); + } + void _set_num_cols( index nr_of_columns ) { + dims.resize( nr_of_columns ); + matrix.resize( nr_of_columns ); + } + + // dimension of given index + dimension _get_dim( index idx ) const { + return dims[ idx ]; + } + void _set_dim( index idx, dimension dim ) { + dims[ idx ] = dim; + } + + // replaces(!) content of 'col' with boundary of given index + void _get_col( index idx, column& col ) const { + col = matrix[ idx ]; + } + void _set_col( index idx, const column& col ) { + matrix[ idx ] = col; + } + + // true iff boundary of given idx is empty + bool _is_empty( index idx ) const { + return matrix[ idx ].empty(); + } + + // largest row index of given column idx (new name for lowestOne()) + index _get_max_index( index idx ) const { + return matrix[ idx ].empty() ? -1 : matrix[ idx ].back(); + } + + // removes the maximal index of a column + void _remove_max( index idx ) { + matrix[ idx ].pop_back(); + } + + // clears given column + void _clear( index idx ) { + matrix[ idx ].clear(); + } + + // syncronizes all data structures (essential for openmp stuff) + void _sync() {} + + // adds column 'source' to column 'target' + void _add_to( index source, index target ) { + column& source_col = matrix[ source ]; + column& target_col = matrix[ target ]; + column& temp_col = temp_column_buffer(); + + + size_t new_size = source_col.size() + target_col.size(); + + if (new_size > temp_col.size()) temp_col.resize(new_size); + + std::vector::iterator col_end = std::set_symmetric_difference( target_col.begin(), target_col.end(), + source_col.begin(), source_col.end(), + temp_col.begin() ); + temp_col.erase(col_end, temp_col.end()); + + + target_col.swap(temp_col); + } + + // finalizes given column + void _finalize( index idx ) { + column& col = matrix[ idx ]; + column(col.begin(), col.end()).swap(col); + } + }; +} diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt index f0b424423d..446456e0e5 100644 --- a/src/Zigzag_persistence/example/CMakeLists.txt +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -13,6 +13,12 @@ endif() file(COPY "zigzag_filtration_example.txt" DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) add_test(NAME Zigzag_persistence_example_zzfiltration_from_file COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/zigzag_filtration_example.txt") +add_executable ( Zigzag_persistence_example_oscillating_rips_persistence example_oscillating_rips_persistence.cpp ) +if(TARGET TBB::tbb) + target_link_libraries(Zigzag_persistence_example_oscillating_rips_persistence TBB::tbb) +endif() +add_test(NAME Zigzag_persistence_example_oscillating_rips_persistence COMMAND $ "2" "3" "5" "10") + add_executable ( comp comparison_for_tests.cpp ) if(TARGET TBB::tbb) target_link_libraries(comp TBB::tbb) diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp index bca280febf..2b55bdd648 100644 --- a/src/Zigzag_persistence/example/comparison_for_tests.cpp +++ b/src/Zigzag_persistence/example/comparison_for_tests.cpp @@ -23,13 +23,15 @@ #include #include #include +#include using ST = Gudhi::Simplex_tree; using Filtration_value = ST::Filtration_value; +using Simplex_handle = ST::Simplex_handle; using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; using ZE = Gudhi::zigzag_persistence::Zigzag_edge; using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; -using ORi = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; +using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::iterator>; using Point = std::vector; @@ -41,6 +43,11 @@ void print_points(const std::vector& points) { std::cout << "\n"; } +void print_res(const std::tuple& t, ST& st){ + for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; + std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; +} + std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { std::vector finalPoints; std::set points; @@ -220,9 +227,9 @@ void test_edges_timings(const std::vector& points, double nu, double mu, } void test_edges(const std::vector& points, double nu, double mu) { - // ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; + ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; - ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; +// ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; // test_edges_comp(points, nu, mu, p); // test_edges_canonical_sort(points, nu, mu, p); @@ -235,14 +242,14 @@ void test_simplices_print(const std::vector& points, double nu, double mu auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); auto end = ORE::end(); - for (auto& t : ORi::get_iterator_range(start, end, st, maxDim)) { - for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; - std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; + for (auto& t : OR::get_iterator_range(start, end, st, maxDim)) { + print_res(t, st); } } -void test_simplices_comp(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p){ - ST st; +void test_simplices_comp(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { + ST stIt; + ST stVec; auto startEIt = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); auto endEIt = ORE::end(); @@ -250,25 +257,109 @@ void test_simplices_comp(const std::vector& points, double nu, double mu, auto startEVec = vec.begin(); auto endEVec = vec.end(); - auto startIt = ORi::begin(startEIt, endEIt, st, maxDim); - auto endIt = ORi::end(); - auto rangeVec = ORv::get_iterator_range(startEVec, endEVec, st, maxDim); + auto startIt = OR::begin(startEIt, endEIt, stIt, maxDim); + auto endIt = OR::end(); + auto rangeVec = ORv::get_iterator_range(startEVec, endEVec, stVec, maxDim); auto startVec = rangeVec.begin(); auto endVec = rangeVec.end(); - for (; startIt != endIt && startVec != endVec; ++startIt,++startVec) { - if () - for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; - std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; + unsigned int i = 0; + for (; startIt != endIt && startVec != endVec; ++startIt, ++startVec) { + const auto& tIt = *startIt; + const auto& tVec = *startVec; + if (std::get<1>(tIt) != std::get<1>(tVec) || std::get<2>(tIt) != std::get<2>(tVec)) { + std::cout << "[" << i << "] Different:\n"; + print_res(tIt, stIt); + print_res(tVec, stVec); + return; + } + auto verIt = stIt.simplex_vertex_range(std::get<0>(tIt)); + auto verVec = stVec.simplex_vertex_range(std::get<0>(tVec)); + auto itIt = verIt.begin(); + auto itVec = verVec.begin(); + while (itIt != verIt.end() && itVec != verVec.end()) { + if (*itIt != *itVec) { + std::cout << "[" << i << "] Different:\n"; + print_res(tIt, stIt); + print_res(tVec, stVec); + return; + } + ++itIt; + ++itVec; + } + if (itIt != verIt.end() || itVec != verVec.end()) { + std::cout << "[" << i << "] Different:\n"; + print_res(tIt, stIt); + print_res(tVec, stVec); + return; + } + ++i; + } +} + +void test_simplices_timings(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { +// bool dir = false; + { + Gudhi::Clock time1("Vector version"); + ST st; + unsigned int i = 0; + auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + // std::cout << vec.size() << "\n"; + auto start = vec.begin(); + auto end = vec.end(); + for ([[maybe_unused]] const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { + // if (dir != std::get<2>(t)){ + // dir = !dir; + // std::cout << st.num_simplices() << "\n"; + // } + ++i; + } + std::cout << "Number of iterations: " << i << "\n"; + time1.end(); + std::cout << time1; } + + { + Gudhi::Clock time2("Iterator version"); + ST st; + unsigned int i = 0; + auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto end = ORE::end(); + for ([[maybe_unused]] const auto& t : OR::get_iterator_range(start, end, st, maxDim)) { + // if (dir != std::get<2>(t)){ + // dir = !dir; + // std::cout << st.num_simplices() << "\n"; + // } + ++i; + } + std::cout << "Number of iterations: " << i << "\n"; + time2.end(); + std::cout << time2; + } + +// { +// Gudhi::Clock time1("Vector version"); +// ST st; +// unsigned int i = 0; +// auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); +// auto start = vec.begin(); +// auto end = vec.end(); +// for ([[maybe_unused]] const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { +// ++i; +// } +// std::cout << i << "\n"; +// time1.end(); +// std::cout << time1; +// } } void test_simplices(const std::vector& points, double nu, double mu, int maxDim) { ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; - // ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; +// ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; // test_simplices_print(points, nu, mu, maxDim, p); - test_simplices_comp(points, nu, mu, maxDim, p); +// test_simplices_comp(points, nu, mu, maxDim, p); + test_simplices_timings(points, nu, mu, maxDim, p); } int main(int argc, char* const argv[]) { @@ -286,13 +377,16 @@ int main(int argc, char* const argv[]) { if (argc == 6) seed = std::stoi(argv[5]); std::cout << "nu, mu: " << nu << ", " << mu << "\n"; + std::cout << "max dimension: " << maxDim << "\n"; std::cout << "number of points: " << numberOfPoints << "\n"; std::cout << "seed: " << seed << "\n"; std::vector points = build_point_cloud(numberOfPoints, seed); // test_edges(points, nu, mu); - test_simplices(points, nu, mu, maxDim); +// test_simplices(points, nu, mu, maxDim); + + Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); return 0; } diff --git a/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp b/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp new file mode 100644 index 0000000000..4e027fdb43 --- /dev/null +++ b/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp @@ -0,0 +1,95 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include +#include + +#include +#include +#include + +using ST = Gudhi::Simplex_tree; +using Filtration_value = ST::Filtration_value; +using Simplex_handle = ST::Simplex_handle; +using Point = std::vector; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +using Barcode = std::vector; + +void print_barcode(const Barcode& bars) { + std::clog << "Resulting barcode:" << std::endl; + for (auto& bar : bars) { + std::clog << "[" << bar.dim() << "] " << bar.birth() << " - "; + if (bar.death() == std::numeric_limits::infinity()) { + std::clog << "inf"; + } else { + std::clog << bar.death(); + } + std::clog << std::endl; + } +} + +void print_points(const std::vector& points) { + std::clog << "Number of points: " << points.size() << std::endl; + for (const Point& p : points) { + std::clog << "(" << p[0] << ", " << p[1] << ")" << std::endl; + } + std::clog << std::endl; +} + +std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { + std::vector finalPoints; + std::set points; + std::random_device dev; + std::mt19937 rng(dev()); + if (seed > -1) rng.seed(seed); + std::uniform_real_distribution dist(0, 10); + + for (unsigned int i = 0; i < numberOfPoints; ++i) { + auto res = points.insert({dist(rng), dist(rng)}); + while (!res.second) { + res = points.insert({dist(rng), dist(rng)}); + } + finalPoints.push_back(*res.first); + } + + print_points(finalPoints); + + return finalPoints; +} + +int main(int argc, char* const argv[]) { + if (argc != 5 && argc != 6) { + std::clog << "Usage: ./comp nu mu max_dim nomberOfPoints [seed]" << std::endl; + return 0; + } + + double nu = std::stod(argv[1]); + double mu = std::stod(argv[2]); + int maxDim = std::stoi(argv[3]); + unsigned int numberOfPoints = std::stoi(argv[4]); + int seed = -1; + + if (argc == 6) seed = std::stoi(argv[5]); + + std::clog << "nu, mu: " << nu << ", " << mu << std::endl; + std::clog << "max dimension: " << maxDim << std::endl; + std::clog << "number of points: " << numberOfPoints << std::endl; + std::clog << "seed: " << seed << std::endl; + + std::clog << "********** Computing " << numberOfPoints << " random points in a 10 x 10 square" << std::endl; + std::vector points = build_point_cloud(numberOfPoints, seed); + + std::clog << "********** Computing oscillating rips filtration and persistence" << std::endl; + //with default templates and parameters. See documention for more information. + Barcode res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); + print_barcode(res); + + return 0; +} diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h new file mode 100644 index 0000000000..5382b1c68a --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -0,0 +1,93 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +/** + * @file Oscillating_rips_persistence.h + * @author Hannah Schreiber + * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::compute_oscillating_rips_persistence + * method. + */ + +#ifndef OSCILLATING_RIPS_PERSISTENCE_H_ +#define OSCILLATING_RIPS_PERSISTENCE_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { + +enum Edge_range_type { VECTOR, BOOST_RANGE }; + +template , + typename std::conditional< + edge_range_type == Edge_range_type::BOOST_RANGE, + Oscillating_rips_edge_range::Filtration_value>::Oscillating_rips_edge_iterator, + std::vector::Filtration_value> >::iterator>::type>, + typename ZigzagPersistenceOptions = Gudhi::persistence_matrix::Zigzag_options<> > +std::vector, + ZigzagPersistenceOptions>::filtration_value_interval> +compute_oscillating_rips_persistence( + const PointRange& points, double nu, double mu, int maxDim, + typename Oscillating_rips_edge_range< + Gudhi::Simplex_tree::Filtration_value>::Order_policy p = + Oscillating_rips_edge_range::Filtration_value>::Order_policy::FARTHEST_POINT_ORDERING) +{ + using ST = Gudhi::Simplex_tree; + using Filtration_value = ST::Filtration_value; + using EdgeRange = Oscillating_rips_edge_range; + + Zigzag_persistence zp; + ST& st = zp.get_complex(); + + if constexpr (edge_range_type == Edge_range_type::BOOST_RANGE) { + auto start = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto end = EdgeRange::end(); + for (const auto& t : OscillatingRipsSimplexRange::get_iterator_range(start, end, st, maxDim)) { + // std::cout << (std::get<2>(t) ? "in" : "out") << ": "; + // for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; + // std::cout << " - "; + // for (auto sh : st.boundary_simplex_range(std::get<0>(t))) std::cout << st.key(sh) << " "; + // std::cout << "\n"; + if (std::get<2>(t)) + zp.insert_simplex_handle(std::get<0>(t)); + else + zp.remove_simplex_handle(std::get<0>(t), std::get<1>(t)); + } + } else { + auto vec = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto start = vec.begin(); + auto end = vec.end(); + for (const auto& t : OscillatingRipsSimplexRange::get_iterator_range(start, end, st, maxDim)) { + if (std::get<2>(t)) + zp.insert_simplex_handle(std::get<0>(t)); + else + zp.remove_simplex_handle(std::get<0>(t), std::get<1>(t)); + } + } + + return zp.get_persistence_diagram(); +} + +} // namespace zigzag_persistence + +} // namespace Gudhi + +#endif // OSCILLATING_RIPS_PERSISTENCE_H_ diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index a263aede5a..42fa03e7a0 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -19,13 +19,6 @@ #ifndef ZIGZAG_PERSISTENCE_H_ #define ZIGZAG_PERSISTENCE_H_ -#include -#include -#include -#include -#include -#include - #include #include #include @@ -33,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -398,6 +390,62 @@ class Zigzag_persistence } } + /** + * @brief Updates the zigzag persistence diagram after the given simplex was ``manually'' inserted in the complex + * obtained with @ref get_complex. + * + * @param simplex Simplex handle of the simplex having been inserted. A filtration value and a key must be assigned + * to it. The key must correspond to the ``arrow number'' inserting the simplex, i.e., its position in the filtration. + * + * @warning Other simplices can have been inserted in the complex before calling this method, there will simply not + * be token into account as long as the method is not called for them. It it just important that a key was assigned + * to the given simplex handle which corresponds to the position of the simplex in the filtration and + * that this method is called in order. + * @warning No simplex given to this method should be removed before calling @ref remove_simplex_handle. + * @warning Do not modify the key even after calling this method, not before calling @ref remove_simplex_handle. + */ + void insert_simplex_handle(Simplex_handle simplex) { + if (dim_max_ != -1 && cpx_.dimension(simplex) > dim_max_) return; + + ++num_arrow_; + Filtration_value filtration_value = cpx_.filtration(simplex); + + if (filtration_value != previous_filtration_value_) // check whether the filt value has changed + { // consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have + previous_filtration_value_ = filtration_value; // filtration value f + filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + } + + _process_forward_arrow(simplex); + } + + /** + * @brief Updates the zigzag persistence diagram with the intention that the given simplex will be removed + * by the caller afterwards from the complex obtained with @ref get_complex. + * + * @param simplex Simplex handle of the simplex that will be removed. + * @param filtration_value Filtration value corresponding to the removal of the given simplex. The filtration value + * stored inside the handle does not need to be updated. + * + * @warning Do not remove simplices before calling this method on them (at least if those were token into account + * with @ref insert_simplex_handle or @ref insert_simplex). + * @warning The associated key should not have been modified since calling @ref insert_simplex_handle + * or @ref insert_simplex. + */ + void remove_simplex_handle(Simplex_handle simplex, Filtration_value filtration_value) { + if (dim_max_ != -1 && cpx_.dimension(simplex) > dim_max_) return; + + ++num_arrow_; + + if (filtration_value != previous_filtration_value_) // check whether the filt value has changed + { // consecutive pairs (i,f), (j,f') mean simplices of index k in [i,j-1] have + previous_filtration_value_ = filtration_value; // filtration value f + filtration_values_.emplace_back(num_arrow_, previous_filtration_value_); + } + + _process_backward_arrow(simplex); + } + /** * @brief Returns the ``index persistence diagram'' of the current filtration, that is, the pairs of atomic arrow * numbers corresponding to a birth-death pair. Does not contain points at infinity, only the cycle classes which @@ -492,11 +540,12 @@ class Zigzag_persistence /** * @brief Returns a reference to the complex storing the simplices. * A simplex is added in a call of @ref insert_simplex and is removed in a call of @ref remove_simplex. - * @warning The complex is not const for now for technical reasons, but DO NOT modify it. + * The complex is allowed to be modified by the caller only under certain restrictions. + * See @ref insert_simplex_handle and @ref remove_simplex_handle. * * @return Reference to the complex. */ - ZigzagFilteredComplex& get_complex() const{ + ZigzagFilteredComplex& get_complex(){ return cpx_; } diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 446aeb54ca..973352a678 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -8,6 +8,16 @@ * - YYYY/MM Author: Description of the modification */ + /** + * @file oscillating_rips_iterators.h + * @author Clément Maria, Hannah Schreiber + * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Zigzag_edge class, + * @ref Gudhi::zigzag_persistence::Identity_edge_modifier class, + * @ref Gudhi::zigzag_persistence::Square_root_edge_modifier class, + * @ref Gudhi::zigzag_persistence::Oscillating_rips_edge_range class and + * @ref Gudhi::zigzag_persistence::Oscillating_rips_simplex_range class. + */ + #ifndef ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ #define ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ @@ -331,12 +341,12 @@ class Oscillating_rips_edge_range { }; template - static std::vector > compute_vector_range(Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Order_policy orderPolicy) - { + static std::vector > compute_vector_range( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Order_policy orderPolicy = Order_policy::FARTHEST_POINT_ORDERING) { std::vector > edgeFiltration; std::vector epsilonValues; std::vector > > distanceMatrix; @@ -395,7 +405,7 @@ class Oscillating_rips_edge_range { Filtration_value mu, const PointRange& points, DistanceFunction&& distance, - Order_policy orderPolicy) + Order_policy orderPolicy = Order_policy::FARTHEST_POINT_ORDERING) { return boost::iterator_range( Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy), Oscillating_rips_edge_iterator()); @@ -408,7 +418,7 @@ class Oscillating_rips_edge_range { Filtration_value mu, const PointRange& points, DistanceFunction&& distance, - Order_policy orderPolicy) + Order_policy orderPolicy = Order_policy::FARTHEST_POINT_ORDERING) { return Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); } @@ -858,9 +868,16 @@ class Oscillating_rips_simplex_range { std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; std::get<1>(currentArrow_) = complex_->filtration(currentSimplices_[currentSimplexIndex_]); std::get<2>(currentArrow_) = currentDirection_; + + complex_->assign_key(currentSimplices_[currentSimplexIndex_], 0); } - Oscillating_rips_iterator() : complex_(nullptr), currentSimplexIndex_(0), maxDimension_(0) {} + Oscillating_rips_iterator() + : complex_(nullptr), + currentSimplexIndex_(0), + currentDirection_(true), + maxDimension_(0), + currentArrowNumber_(0) {} private: friend class boost::iterator_core_access; @@ -909,6 +926,8 @@ class Oscillating_rips_simplex_range { } void increment() { + if (!currentDirection_) complex_->remove_maximal_simplex(currentSimplices_[currentSimplexIndex_]); + ++currentSimplexIndex_; ++currentArrowNumber_; @@ -918,14 +937,17 @@ class Oscillating_rips_simplex_range { return; } - if (!currentDirection_) { - for (auto& sh : currentSimplices_) complex_->remove_maximal_simplex(sh); - } + // if (!currentDirection_) { + // for (auto& sh : currentSimplices_) complex_->remove_maximal_simplex(sh); + // } currentSimplices_.clear(); auto fil = currentEdgeIt_->get_filtration_value(); currentDirection_ = currentEdgeIt_->get_direction(); + std::get<1>(currentArrow_) = fil; + std::get<2>(currentArrow_) = currentDirection_; + if (currentDirection_) { _update_positive_current_simplices(fil); } else { @@ -935,8 +957,6 @@ class Oscillating_rips_simplex_range { } std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; - std::get<1>(currentArrow_) = complex_->filtration(currentSimplices_[currentSimplexIndex_]); - std::get<2>(currentArrow_) = currentDirection_; if (currentDirection_) complex_->assign_key(currentSimplices_[currentSimplexIndex_], currentArrowNumber_); } From 63aad6a55f4ed44255e6b759babb061806f8fdbe Mon Sep 17 00:00:00 2001 From: hschreiber Date: Wed, 9 Aug 2023 18:21:10 +0200 Subject: [PATCH 04/21] doc --- .../Oscillating_rips_comp_benchmark.cpp | 67 ++ .../gudhi/Oscillating_rips_persistence.h | 49 +- .../oscillating_rips_iterators.h | 701 +++++++++++++++--- 3 files changed, 702 insertions(+), 115 deletions(-) diff --git a/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp b/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp index 5e8f512f61..6385fe8919 100644 --- a/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp +++ b/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp @@ -45,6 +45,23 @@ using DIndex = typename DZZ::Index; using DChain = dionysus::ChainEntry; using DIChain = dionysus::ChainEntry; +struct Simplex_tree_options_test { + typedef Gudhi::linear_indexing_tag Indexing_tag; + typedef int Vertex_handle; + typedef double Filtration_value; + typedef std::int32_t Simplex_key; + static const bool store_key = true; + static const bool store_filtration = true; + static const bool contiguous_vertices = true; + static const bool link_nodes_by_label = false; + static const bool simplex_handle_strong_validity = false; +}; + +// using ST_std = Gudhi::Simplex_tree; +using ST_std = Gudhi::Simplex_tree; +using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; +using Barcode = std::vector; + std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { std::vector finalPoints; std::set points; @@ -148,6 +165,24 @@ std::vector > compute_with_ return persistence; } +Barcode compute_with_gudhi_v2(const std::vector& points, double nu, double mu, int maxDim) { + ZP zp; + ST st; + + auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), ORE::Order_policy::FARTHEST_POINT_ORDERING); + auto end = ORE::end(); + for (const auto& t : OR::get_iterator_range(start, end, st, maxDim)) { + auto r = st.simplex_vertex_range(std::get<0>(t)); + std::vector simplex(r.begin(), r.end()); + if (std::get<2>(t)) + zp.insert_simplex(simplex, std::get<1>(t)); + else + zp.remove_simplex(simplex, std::get<1>(t)); + } + + return zp.get_persistence_diagram(); +} + static void ORP_with_gudhi(benchmark::State& state) { double nu = state.range(0); double mu = state.range(1); @@ -180,6 +215,38 @@ BENCHMARK(ORP_with_gudhi) // benchmark::CreateRange(256, 500, 4)}) // ->Unit(benchmark::kMillisecond); +// static void ORP_with_gudhi_v2(benchmark::State& state) { +// double nu = state.range(0); +// double mu = state.range(1); +// int maxDim = state.range(2); +// unsigned int numberOfPoints = state.range(3); +// int seed = 0; + +// std::vector points = build_point_cloud(numberOfPoints, seed); + +// for (auto _ : state) { +// auto res = compute_with_gudhi_v2(points, nu, mu, maxDim); +// } +// } +// BENCHMARK(ORP_with_gudhi_v2) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(2, 5, 4), +// benchmark::CreateRange(5, 5, 4)}) +// ->Unit(benchmark::kMicrosecond); +// BENCHMARK(ORP_with_gudhi_v2) +// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), +// benchmark::CreateDenseRange(3, 5, 1), +// benchmark::CreateRange(4, 50, 4), +// benchmark::CreateRange(20, 64, 4)}) +// ->Unit(benchmark::kMillisecond); +// // BENCHMARK(ORP_with_gudhi_v2) +// // ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), +// // benchmark::CreateDenseRange(3, 4, 1), +// // benchmark::CreateRange(4, 50, 4), +// // benchmark::CreateRange(256, 500, 4)}) +// // ->Unit(benchmark::kMillisecond); + static void ORP_with_dionysus(benchmark::State& state) { double nu = state.range(0); double mu = state.range(1); diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index 5382b1c68a..93bab9be02 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -18,19 +18,57 @@ #ifndef OSCILLATING_RIPS_PERSISTENCE_H_ #define OSCILLATING_RIPS_PERSISTENCE_H_ -#include #include #include #include #include #include -#include namespace Gudhi { namespace zigzag_persistence { -enum Edge_range_type { VECTOR, BOOST_RANGE }; +/** + * @brief Determines the type of the oscillanting Rips edge range. + * + * The two options are usually quite equivalent, as the base algorithm behind them is the same. + * They mostly differ by their memory managment, so it can be worth it to try both out. + */ +enum Edge_range_type { + VECTOR, /**< The edges are computed all at once and stored in a vector. */ + BOOST_RANGE /**< The edges are computed one by one at each incrementation of + * the range iterator and therefore not stored. */ +}; +/** + * @brief Computes the oscillating Rips filtration based on the given parameters and computes the + * corresponding zigzag barcode. + * + * @ingroup zigzag_persistence + * + * @details It is thought to be a helper function to easily compute oscillating Rips persistence with the + * possibility to switch out a few common options. But additional options exists for the iterators + * (e.g. replacing the Euclidian distance by another one, or using your own epsilon values; see + * the documention of @ref Oscillating_rips_edge_range and @ref Oscillating_rips_simplex_range for + * more information). You can easily create your own method based on this one. + * + * @tparam PointRange Range containing the point cloud. + * @tparam edge_range_type Either Edge_range_type::BOOST_RANGE or Edge_range_type::VECTOR. + * Default value: Edge_range_type::BOOST_RANGE. + * @tparam OscillatingRipsSimplexRange Type of the oscilliating Rips simplex range. + * Default value: Oscillating_rips_simplex_range with templates depending on @p edge_range_type. + * @tparam ZigzagPersistenceOptions Options for the matrix used by the zigzag persistence algorithm. + * Default value: @ref Gudhi::persistence_matrix::Zigzag_options<>. + * @param points Point cloud. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param maxDim Maximum dimension to which to expand the Rips complex. If set to -1, there is no limit. + * @param p Order policy for the points. + * Can be either @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_range::Order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_range::Order_policy::RANDOM_POINT_ORDERING. + * Default value: @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING. + * @return The persistence diagram of the oscillating Rips filtration. + */ template , @@ -61,11 +99,6 @@ compute_oscillating_rips_persistence( auto start = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); auto end = EdgeRange::end(); for (const auto& t : OscillatingRipsSimplexRange::get_iterator_range(start, end, st, maxDim)) { - // std::cout << (std::get<2>(t) ? "in" : "out") << ": "; - // for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; - // std::cout << " - "; - // for (auto sh : st.boundary_simplex_range(std::get<0>(t))) std::cout << st.key(sh) << " "; - // std::cout << "\n"; if (std::get<2>(t)) zp.insert_simplex_handle(std::get<0>(t)); else diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 973352a678..0f6d4f48ee 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -40,23 +40,62 @@ namespace Gudhi { namespace zigzag_persistence { +/** + * @class Zigzag_edge oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Edge structure for the oscilliating Rips filtration. + * + * @ingroup zigzag_persistence + * + * @details The edges of the filtration are computed first and the remaining simplices are deduced from them. + * Is also used to represent the vertices for technical reasons, by giving both vertices the same value. + * + * @tparam Filtration_value Type of the filtration value. + */ template class Zigzag_edge { public: + /** + * @brief Constructor. + * + * @param u First boundary vertex ID + * @param v Second boundary vertex ID. If same than @p u, the edge is considered a vertex. + * @param fil Filtration value + * @param direction If true, forwards. If false, backwards. + */ Zigzag_edge(int u, int v, Filtration_value fil, bool direction) : u_(u), v_(v), fil_(fil), direction_(direction) { if (u > v) std::swap(u_, v_); } + /** + * @brief Default constructor. Initialize everything to zero and the direction to true. + */ Zigzag_edge() : u_(0), v_(0), fil_(0), direction_(true) {} - /* Returns vertex with smaller label. */ + /** + * @brief Returns vertex with smaller ID. + * + * @return Smallest ID among the boundary vertices. + */ int get_smallest_vertex() const { return u_; } - /* Returns vertex with bigger label. */ + /** + * @brief Returns vertex with bigger ID. + * + * @return Biggest ID among the boundary vertices. + */ int get_biggest_vertex() const { return v_; } - /* Returns the filtration value of the edge. */ + /** + * @brief Returns the filtration value of the edge. + * + * @return Filtration value of the edge. + */ Filtration_value get_filtration_value() const { return fil_; } - /* Returns true if insertion of the edge, false if removal. */ + /** + * @brief Gives the direction of the arrow corresponding to the edge. + * + * @return True, if forward, i.e., an insertion. + * @return False, if backward, i.e., a removal. + */ bool get_direction() const { return direction_; } void set(int u, int v, Filtration_value fil, bool direction){ @@ -66,56 +105,139 @@ class Zigzag_edge { direction_ = direction; } + /** + * @brief Equality test + * + * @param e Edge to compare. + * @return True, if both edges are equal. + * @return False, if both edges are not equal. + */ bool operator==(const Zigzag_edge& e) const { return ((e.u_ == u_) && (e.v_ == v_) && (e.fil_ == fil_) && (e.direction_ == direction_)); } -// bool operator<(const Zigzag_edge& e) const { -// if (e.fil_ != fil_) return fil_ < e.fil_; -// if (e.direction_ != direction_) return direction_; -// if (e.u_ != u_) return u_ < e.u_; -// return v_ < e.v_; -// } - private: - int u_; - int v_; - Filtration_value fil_; - bool direction_; + int u_; /**< Smaller vertex. */ + int v_; /**< Bigger vertex. */ + Filtration_value fil_; /**< Filtration value. */ + bool direction_; /**< Direction. True = forward, false = backward. */ }; -template +/** + * @class Identity_edge_modifier oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Identity modifier, i.e., does nothing. + * + * @ingroup zigzag_persistence + */ class Identity_edge_modifier { public: - static constexpr bool isActive_ = false; + static constexpr bool isActive_ = false; /**< Indicates that the modifier should be ignored. */ private: + /** + * @brief Default constructor. Should not be called and therefore private. + */ Identity_edge_modifier() {} }; +/** + * @class Square_root_edge_modifier oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Modifier that square roots the filtration value of an edge. + * + * @ingroup zigzag_persistence + * + * @details Useful in particular when geometric computations (edge length, etc) are + * run with squared Euclidean distance for performance. + * + * @tparam Filtration_value Filtration value type. + */ template class Square_root_edge_modifier { public: + /** + * @brief Returns the square root of the given value. The parameter it-self is not modified. + * + * @param f Value to modify. + * @return The modified value of @p f. + */ static Filtration_value apply_modifier(Filtration_value f) { return std::sqrt(f); } + /** + * @brief Returns the square of the given value. The parameter it-self is not modified. + * + * @param f Value to modify. + * @return The modified value of @p f. + */ static Filtration_value apply_inverse_modifier(Filtration_value f) { return f * f; } - static constexpr bool isActive_ = true; + static constexpr bool isActive_ = true; /**< Indicates that the modifier should not be ignored. */ private: + /** + * @brief Default constructor. Should not be called and therefore private. + */ Square_root_edge_modifier() {} }; -//assumes that eps_n-1 == 0 -template > +/** + * @class Oscillating_rips_edge_range oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Gives access and computes different edge range types for an oscilliating Rips filtration. + * + * @ingroup zigzag_persistence + * + * @details There are two different kind of ranges: a vector range containing all computed edges and + * a Boost range made from a custom iterator computing an edge on the fly at each incrementation. + * The custom iterator is therefore only a forward iterator and can only be incremented. + * + * @tparam Filtration_value Filtration value type + * @tparam EdgeModifier Modifier for the edge filtration values. If no modifications are wanted, + * use @ref Identity_edge_modifier. Default value: @ref Identity_edge_modifier. + * + * @warning As the custom iterator used for the Boost ranges stores possibly large ranges, avoid copying it. + * Use @ref get_iterator_range, @ref begin and @ref end wisely. If one needs to do something else than to simply + * iterate over the edges in order, then the vector range is for sure the better option. + */ +template class Oscillating_rips_edge_range { public: - enum Order_policy { ALREADY_ORDERED, FARTHEST_POINT_ORDERING, RANDOM_POINT_ORDERING }; + /** + * @brief Order policy for the points. + */ + enum Order_policy { + ALREADY_ORDERED, /**< The given range of points is considered ordered. */ + FARTHEST_POINT_ORDERING, /**< The points are reordered using @ref Gudhi::subsampling::choose_n_farthest_points.*/ + RANDOM_POINT_ORDERING /**< The points are shuffled randomly. */ + }; + /** + * @class Oscillating_rips_edge_iterator oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Custom iterator over the edges of an oscillating rips filtration. + * + * It inherits from boost::iterator_facade. + * + * The iterator stores the distance matrix and epsilon values to compute the current edge on the fly + * at each incrementation. + * + * @warning As the iterator stores possibly large ranges, avoid copying it. + */ class Oscillating_rips_edge_iterator : public boost::iterator_facade&, boost::forward_traversal_tag> { public: + /** + * @brief Constructor. Computes the distance matrix and the epsilon values it-self from the given parameters. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either @ref Order_policy::FARTHEST_POINT_ORDERING, + * @ref Order_policy::ALREADY_ORDERED or @ref Order_policy::RANDOM_POINT_ORDERING. + */ template Oscillating_rips_edge_iterator(Filtration_value nu, Filtration_value mu, @@ -138,6 +260,22 @@ class Oscillating_rips_edge_range { columnIndex_ = it - distanceMatrix_[1].begin(); } + /** + * @brief Constructor. Takes already computed epsilon values as input, but assumes that the points are + * already ordered accordingly. Assumes also that the last epsilon value is 0. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. The order of the points should be in correspondence with the order of + * the epsilon values. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. + * And the last value should be 0. + */ template Oscillating_rips_edge_iterator(Filtration_value nu, Filtration_value mu, @@ -172,6 +310,9 @@ class Oscillating_rips_edge_range { columnIndex_ = it - distanceMatrix_[1].begin(); } + /** + * @brief Default constructor. Corresponds to the end iterator. + */ Oscillating_rips_edge_iterator() : nu_(0), mu_(0), @@ -183,22 +324,39 @@ class Oscillating_rips_edge_range { insertVertex_(true) {} private: + //mandatory for the boost::iterator_facade inheritance. friend class boost::iterator_core_access; - std::vector epsilonValues_; - std::vector > > distanceMatrix_; - Filtration_value nu_; - Filtration_value mu_; - Zigzag_edge currentEdge_; - size_t epsilonIndex_, rowIndex_, columnIndex_; - bool inPositiveDirection_, insertVertex_; - + std::vector epsilonValues_; /**< Epsilon values. */ + std::vector > > distanceMatrix_; /**< Distance matrix. */ + Filtration_value nu_; /**< Lower multiplicator. */ + Filtration_value mu_; /**< Upper multiplicator. */ + Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ + size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ + bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ``edge'' is a vertex. */ + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if to iterators are equal. + * + * @param other Iterator to compare. + * @return True, the iterators are pointing to the same position. + * @return False, otherwise. + */ bool equal(Oscillating_rips_edge_iterator const& other) const { return rowIndex_ == other.rowIndex_ && currentEdge_ == other.currentEdge_; } + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Returns the value of the dereferenced iterator. + * + * @return Current edge. + */ const Zigzag_edge& dereference() const { return currentEdge_; } + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. + * + */ void increment() { if (epsilonIndex_ < distanceMatrix_.size() - 1) { if (insertVertex_) { @@ -286,11 +444,17 @@ class Oscillating_rips_edge_range { _set_end(); } + /** + * @brief Set the iterator as the end iterator. + */ void _set_end(){ rowIndex_ = 0; currentEdge_ = Zigzag_edge(); } + /** + * @brief Initialize the column index for a new sequence of positive edges. + */ void _initialize_positive_col_index() { auto it = std::upper_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), @@ -299,6 +463,9 @@ class Oscillating_rips_edge_range { columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); } + /** + * @brief Initialize the column index for a new sequence of negative edges. + */ void _initialize_negative_col_index() { auto it = std::lower_bound(distanceMatrix_[rowIndex_].begin(), distanceMatrix_[rowIndex_].end(), @@ -308,17 +475,29 @@ class Oscillating_rips_edge_range { columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); } + /** + * @brief Indicates if the column index needs to be initialized or not. + */ bool _positive_col_index_is_not_valid() { return columnIndex_ == 0 || (rowIndex_ != (epsilonIndex_ + 1) && distanceMatrix_[rowIndex_][columnIndex_ - 1].second <= nu_ * epsilonValues_[epsilonIndex_]); } + /** + * @brief Indicates if the column index needs to be initialized or not. + */ bool _negative_col_index_is_not_valid() { return columnIndex_ == distanceMatrix_[rowIndex_].size() || distanceMatrix_[rowIndex_][columnIndex_].second > mu_ * epsilonValues_[epsilonIndex_]; } + /** + * @brief Updates the current edge. + * + * @param i Epsilon value index. + * @param direction Direction. + */ void _update_edge(size_t i, bool direction) { if constexpr (EdgeModifier::isActive_) currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, @@ -327,6 +506,9 @@ class Oscillating_rips_edge_range { currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], direction); } + /** + * @brief Update the current edge as a vertex to insert. + */ void _update_edge_as_positive_vertex() { if constexpr (EdgeModifier::isActive_) currentEdge_.set(epsilonIndex_ + 1, epsilonIndex_ + 1, @@ -335,11 +517,30 @@ class Oscillating_rips_edge_range { currentEdge_.set(epsilonIndex_ + 1, epsilonIndex_ + 1, epsilonValues_[epsilonIndex_], true); } + /** + * @brief Update the current edge as a vertex to remove. + */ void _update_edge_as_negative_vertex() { currentEdge_.set(rowIndex_ - 1, rowIndex_ - 1, -std::numeric_limits::infinity(), false); } }; + /** + * @brief Computes and return a vector with all edges in order of the oscillating Rips filtration. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either @ref Order_policy::FARTHEST_POINT_ORDERING, + * @ref Order_policy::ALREADY_ORDERED or @ref Order_policy::RANDOM_POINT_ORDERING. + * + * @return Vector of @ref Zigzag_edge. + */ template static std::vector > compute_vector_range( Filtration_value nu, @@ -399,6 +600,26 @@ class Oscillating_rips_edge_range { return edgeFiltration; } + /** + * @brief Returns a boost::iterator_range from @ref Oscillating_rips_edge_iterator. The edges are computed + * on the fly at each incrementation. The iterator is a forward iterator only. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either @ref Order_policy::FARTHEST_POINT_ORDERING, + * @ref Order_policy::ALREADY_ORDERED or @ref Order_policy::RANDOM_POINT_ORDERING. + * + * @return boost::iterator_range of @ref Oscillating_rips_edge_iterator. + * + * @warning Avoid copying the iterators as they are heavier than usual iterators. If begin and end iterators + * are needed but not the structure of the range, use @ref begin and @ref end instead. + */ template static boost::iterator_range get_iterator_range( Filtration_value nu, @@ -411,7 +632,59 @@ class Oscillating_rips_edge_range { Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy), Oscillating_rips_edge_iterator()); } - //as Oscillating_rips_edge_iterator is a heavy iterator to copy, it should not be used as a usual iterator + /** + * @brief Returns a boost::iterator_range from @ref Oscillating_rips_edge_iterator. The edges are computed + * on the fly at each incrementation. The iterator is a forward iterator only. + * Takes already computed epsilon values as input, but assumes that the points are already ordered accordingly. + * Assumes also that the last epsilon value is 0. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. The order of the points should be in correspondence with the order of + * the epsilon values. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. + * And the last value should be 0. + * + * @return boost::iterator_range of @ref Oscillating_rips_edge_iterator. + * + * @warning Avoid copying the iterators as they are heavier than usual iterators. If begin and end iterators + * are needed but not the structure of the range, use @ref begin and @ref end instead. + */ + template + static boost::iterator_range get_iterator_range( + Filtration_value nu, + Filtration_value mu, + const PointRange& orderedPoints, + DistanceFunction&& distance, + const std::vector& epsilonValues) + { + return boost::iterator_range( + Oscillating_rips_edge_iterator(nu, mu, orderedPoints, distance, epsilonValues), Oscillating_rips_edge_iterator()); + } + + /** + * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either @ref Order_policy::FARTHEST_POINT_ORDERING, + * @ref Order_policy::ALREADY_ORDERED or @ref Order_policy::RANDOM_POINT_ORDERING. + * + * @return Instianciation of @ref Oscillating_rips_edge_iterator. + * + * @warning Avoid copying the iterator as it is heavier than usual iterators. + */ template static Oscillating_rips_edge_iterator begin( Filtration_value nu, @@ -423,19 +696,60 @@ class Oscillating_rips_edge_range { return Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); } + /** + * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. + * Takes already computed epsilon values as input, but assumes that the points are already ordered accordingly. + * Assumes also that the last epsilon value is 0. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplicator. + * @param mu Upper multiplicator. + * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. The order of the points should be in correspondence with the order of + * the epsilon values. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. + * And the last value should be 0. + * + * @return Instianciation of @ref Oscillating_rips_edge_iterator. + * + * @warning Avoid copying the iterator as it is heavier than usual iterators. + */ + template + static Oscillating_rips_edge_iterator begin( + Filtration_value nu, + Filtration_value mu, + const PointRange& orderedPoints, + DistanceFunction&& distance, + const std::vector& epsilonValues) + { + return Oscillating_rips_edge_iterator(nu, mu, orderedPoints, distance, epsilonValues); + } + + /** + * @brief Returns the end iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. + * + * @return Default instianciation of @ref Oscillating_rips_edge_iterator. + */ static Oscillating_rips_edge_iterator end() { return Oscillating_rips_edge_iterator(); } private: + /** + * @brief Default constructor. Should not be called and therfore private. Use as a ``static'' class only. + */ Oscillating_rips_edge_range(){}; - /* The two input types std::pair encode pairs - * (j, d(p_i,p_j)) and (k, d(p_i,p_k)) for some fixed point p_i. + /** + * @brief The two input types std::pair encode pairs + * @f$(j, d(p_i,p_j))@f$ and @f$(k, d(p_i,p_k))@f$ for some fixed point @f$p_i@f$. * The operator() orders edges by length. By convention, if lengths are equal, - * it orders pairs by taking the smaller vertex label between j and k. - */ + * it orders pairs by taking the smaller vertex label between @f$j@f$ and @f$k@f$. + */ struct Point_distance_comp { bool operator()(const std::pair& p1, const std::pair& p2) const { { @@ -447,6 +761,23 @@ class Oscillating_rips_edge_range { } }; + /** + * @brief Initialize the distance function and epsilon values. Updates also the multiplicators if + * the edge modifier is active. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param[out] nu Lower multiplicator. + * @param[out] mu Upper multiplicator. + * @param[out] epsilonValues Container for the epsilon values. + * @param[out] distanceMatrix Container for the distance matrices. + * @param[in] points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param[in] distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param[in] orderPolicy Order policy for the points. Can be either @ref Order_policy::FARTHEST_POINT_ORDERING, + * @ref Order_policy::ALREADY_ORDERED or @ref Order_policy::RANDOM_POINT_ORDERING. + */ template static void _initialize(Filtration_value& nu, Filtration_value& mu, std::vector& epsilonValues, std::vector > >& distanceMatrix, @@ -487,26 +818,28 @@ class Oscillating_rips_edge_range { distanceMatrix = _compute_distance_matrix(sortedPoints, distance); } - /** \brief Compute the epsilon values for an ordered set of points, measuring the + /** + * @brief Compute the epsilon values for an ordered set of points, measuring the * sparsity of the ordering. * - * \details Let \f$P = \{p_0, \ldots, p_{n-1}\f$ be the ordered set of points. Then + * @details Let \f$P = \{p_0, \ldots, p_{n-1}\f$ be the ordered set of points. Then * the method sets eps_range[i]<\CODE> with the value \$f\varepsilon_i\$f, * defined as \f$\varpesilon_i = d_H(P_i,P)\f$, the Hausdorff between the points * \f$P_i= \{p_0, \ldots, p_{i}\}\f$ and the entire point cloud * \f$P = \{p_0, \ldots, p_{n-1}\}\f$. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param sortedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. * - * @param[in] points Range of points. - * @param[in] distance Distance function that can be called on two points. - * @param[in] eps_range Vector in which the epsilon values are written, - * eps_range[i]<\CODE> is \$f\varepsilon_i\$f. - * Satisfyies epsilonValues[i] >= - * epsilonValues[j] >= 0<\CODE> whenever i>=j<\CODE>. - * The range must be of same size as the number of points. + * @return Vector of decreasing epsilon values ending with 0. */ template - static std::vector _compute_epsilon_values(const PointRange& points, DistanceFunction&& distance) { - size_t n = points.size(); + static std::vector _compute_epsilon_values(const PointRange& sortedPoints, DistanceFunction&& distance) { + size_t n = sortedPoints.size(); std::vector eps_range(n, std::numeric_limits::infinity()); // compute all \f$\varepsilon_i\f$ values, such that eps_range[i] == @@ -518,7 +851,7 @@ class Oscillating_rips_edge_range { tbb::parallel_for(size_t(i + 1), n, [&](size_t k) { // set eps_range[k] <- d(p_k, P_i) == // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. - double dist_i_k = distance(points[i], points[k]); + double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); if (dist_i_k < eps_range[k]) { eps_range[k] = dist_i_k; } @@ -548,6 +881,20 @@ class Oscillating_rips_edge_range { return eps_range; } + /** + * @brief Returns the sparse distance matrix. A cell is represented as a pair of its row index and its value, + * i.e., @f$(j, d(p_i,p_j))@f$ is stored in column @f$i@f$ to indicate that the distance of the @f$i^{th}@f$ + * point has distance @f$d(p_i,p_j)@f$ from the @f$j^{th}@f$ point. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param sortedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * + * @return A vector of vectors of pairs of row indices and distances. + */ template static std::vector > > _compute_distance_matrix( const PointRange& sortedPoints, DistanceFunction&& distance) { @@ -577,6 +924,19 @@ class Oscillating_rips_edge_range { return distanceMatrix; } + /** + * @brief Computes the edges that are added and the edges that are removed and stores them in two separate containers. + * The edge container @f$e@f$ stores the @f$i^{th}@f$ edge induced by vertex @f$j@f$ at @f$e[j][i]@f$. + * + * @param[in] nu Lower multiplicator. + * @param[in] mu Upper multiplicator. + * @param[in] epsilonValues Epsilon values in decreasing order finishing with 0. + * @param[in] distanceMatrix Spare distance matrix. + * @param[out] edgesAdded Container for positive edges. + * @param[out] edgesRemoved Container for negative edges. + * + * @return Total number of edges. + */ static size_t _compute_edges(Filtration_value nu, Filtration_value mu, const std::vector& epsilonValues, std::vector > >& distanceMatrix, @@ -759,46 +1119,50 @@ class Oscillating_rips_edge_range { return number_of_arrows; } + /** + * @brief Sorts canonically the edges: as much as possible, edges should be removed in + * the reverse order of their insertion. We decide to insert shorted edges first, + * with increasing lexicographical order, and remove larger edges first, with + * decreasing lexicographic order. + * + * @param edges Edges to sort. + */ static void _canonically_sort_edges(std::vector >& edges) { - // canonical sort of the edges: as much as possible, edges should be removed in - // the reverse order of their insertion. We decide to insert shorted edges first, - // with increasing lexicographical order, and remove larger edges first, with - // decreasing lexicographic order. - // filtration then dimension, then lex order for insertion auto edge_cmp = [](const Zigzag_edge& e1, const Zigzag_edge& e2) { if (e1.get_filtration_value() != e2.get_filtration_value()) { - return e1.get_filtration_value() < e2.get_filtration_value(); - } // lower fil first + return e1.get_filtration_value() < e2.get_filtration_value(); // lower fil first + } - if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first + if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); - } //-> vertex of lower label - else { - return true; - } //-> always vertices before edges + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); //-> vertex of lower label + } else { + return true; //-> always vertices before edges + } } // e1 is an edge if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { - return false; - } // e2 vertex, -> put it first + return false; // e2 vertex, -> put it first + } // both are edges, lexigraphic compare if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) { - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); - } // lex order + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); // lex order + } if (e1.get_biggest_vertex() != e2.get_biggest_vertex()) { return e1.get_biggest_vertex() < e2.get_biggest_vertex(); } - return false; // equality + return false; // equality }; + // the inverse ordering for deletions auto inv_edge_cmp = [&](const Zigzag_edge& e1, const Zigzag_edge& e2) { if (e1.get_smallest_vertex() == e2.get_smallest_vertex() && e1.get_biggest_vertex() == e2.get_biggest_vertex()) { - return false; - } //== => false - return !(edge_cmp(e1, e2)); // reverse order + return false; //equality => false + } + return !(edge_cmp(e1, e2)); // reverse order }; + // sort sequences of inclusions of same filtration with edge_cmp // sort sequences of removals of same filtration with inv_edge_cmp auto beg = edges.begin(); @@ -809,20 +1173,19 @@ class Oscillating_rips_edge_range { while (end != edges.end() && end->get_filtration_value() == curr_fil && end->get_direction() == curr_type) { ++end; } - if (curr_type) { + if (curr_type) { // sequence of insertions #ifdef GUDHI_USE_TBB tbb::parallel_sort(beg, end, edge_cmp); #else std::sort(beg, end, edge_cmp); #endif - } // sequence of insertions - else { + } else { // sequence of removals #ifdef GUDHI_USE_TBB tbb::parallel_sort(beg, end, inv_edge_cmp); #else std::sort(beg, end, inv_edge_cmp); #endif - } // sequence of removals + } beg = end; curr_fil = beg->get_filtration_value(); curr_type = beg->get_direction(); @@ -830,26 +1193,81 @@ class Oscillating_rips_edge_range { } }; -//needs Filtered_complex to have stable simplex handles -template +/** + * @class Oscillating_rips_simplex_range oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Gives access to a range of the ordered simplices in an oscilliating Rips filtration. + * + * @ingroup zigzag_persistence + * + * @details The simplices are returned as tuples of three elements: + * the first is the simplex handle of the simplex in the given complex, + * the second is the filtration value of the corresponding arrow, + * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. + * For more information, see @ref Oscillating_rips_iterator. + * + * @tparam StableFilteredComplex Filtered complex structure that has stable simplex handles, + * that is, they do not invalidates after an insertion or a removal (except for the removed simplices). + * @tparam EdgeRangeIterator Type of the edge range iterator. + * + * @warning As the custom iterator stores a possibly large range, avoid copying it. + * Use @ref get_iterator_range, @ref begin and @ref end wisely. + */ +template class Oscillating_rips_simplex_range { public: + /** + * @class Oscillating_rips_iterator oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h + * @brief Custom iterator over the simplices of an oscillating rips filtration. Is a forward iterator only. + * + * It inherits from boost::iterator_facade. + * + * For each positive edge given by the edge range iterator, the given complex computes and adds all possible + * cofaces and they respective simplex handle are stored. For each negative edge, the given complex computes + * the simplex handles of all cofaces and they are stored. + * The simplex handles induced by an edge are stored only the time needed and are removed when reaching the next edge. + * That is, we start by storing the possible cofaces of an edge, then at each incrementation, + * the next element within the stored simplex handles is retrieved and once the end is reached, + * the simplex handles are erased and replaced by the possible cofaces of the next edge and so on... + * If the edge is negative, then a simplex is removed from the complex at the next incrementation. + * So, the maximum size of the complex corresponds to the maximum size of a complex in the zigzag filtration. + * + * The simplices are returned by the iterator as tuples of three elements: + * the first is the simplex handle of the simplex in the given complex, + * the second is the filtration value of the corresponding arrow, + * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. + * + * @warning As the iterator stores possibly a large range, avoid copying it. + */ class Oscillating_rips_iterator : public boost::iterator_facade&, - boost::forward_traversal_tag> { + const std::tuple&, + boost::forward_traversal_tag> + { public: - using Filtration_value = typename Filtered_complex::Filtration_value; - using Simplex_handle = typename Filtered_complex::Simplex_handle; - using Simplex_key = typename Filtered_complex::Simplex_key; - - // edges and complex are not copied, so do not modifiy outside as long as iterator != end. - // TODO: move constructor for iterator? + using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ + using Simplex_handle = typename StableFilteredComplex::Simplex_handle; /**< Simplex handle type. */ + using Simplex_key = typename StableFilteredComplex::Simplex_key; /**< Key type. */ + + /** + * @brief Constructor. The edge iterators and the complex are not copied, so do not modifiy or invalidate + * them outside as long as this iterator is different from the end iterator. + * + * @param edgeStartIterator Begin iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param edgeEndIterator End iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param complex Empty complex which will be used to store current simplices. + * Only the address of the complex is stored, so the state of the complex can be + * consulted between each incrementation. + * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. + * Default value: -1. + */ Oscillating_rips_iterator(EdgeRangeIterator& edgeStartIterator, EdgeRangeIterator& edgeEndIterator, - Filtered_complex& complex, int maxDimension = -1) + StableFilteredComplex& complex, int maxDimension = -1) : complex_(&complex), currentSimplexIndex_(0), - currentEdgeIt_(std::move(edgeStartIterator)), + currentEdgeIt_(std::move(edgeStartIterator)), // TODO: move constructor for custom iterator? endEdgeIt_(std::move(edgeEndIterator)), currentDirection_(true), maxDimension_(maxDimension), @@ -872,6 +1290,9 @@ class Oscillating_rips_simplex_range { complex_->assign_key(currentSimplices_[currentSimplexIndex_], 0); } + /** + * @brief Default constructor. Corresponds to the end iterator. + */ Oscillating_rips_iterator() : complex_(nullptr), currentSimplexIndex_(0), @@ -880,10 +1301,14 @@ class Oscillating_rips_simplex_range { currentArrowNumber_(0) {} private: + //mandatory for the boost::iterator_facade inheritance. friend class boost::iterator_core_access; + /** + * @brief Reverse lexicographical order for the simplex handles. + */ struct reverse_lexicographic_order { - explicit reverse_lexicographic_order(Filtered_complex* st) : st_(st) {} + explicit reverse_lexicographic_order(StableFilteredComplex* st) : st_(st) {} bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const { auto rg1 = st_->simplex_vertex_range(sh1); @@ -900,20 +1325,26 @@ class Oscillating_rips_simplex_range { } return ((it1 == rg1.end()) && (it2 != rg2.end())); } - Filtered_complex* st_; + StableFilteredComplex* st_; }; - std::vector currentSimplices_; - Filtered_complex* complex_; - size_t currentSimplexIndex_; - EdgeRangeIterator currentEdgeIt_; - EdgeRangeIterator endEdgeIt_; - bool currentDirection_; - const int maxDimension_; - // bool isEnd_; - std::tuple currentArrow_; - Simplex_key currentArrowNumber_; - + std::vector currentSimplices_; /**< Stores current simplex handles. */ + StableFilteredComplex* complex_; /**< Pointer to the complex. */ + size_t currentSimplexIndex_; /**< Index to current position in currentSimplices_. */ + EdgeRangeIterator currentEdgeIt_; /**< Iterator pointing to the next edge. */ + EdgeRangeIterator endEdgeIt_; /**< End edge iterator. */ + bool currentDirection_; /**< Current direction. */ + const int maxDimension_; /**< Maximal dimension of expansion. */ + std::tuple currentArrow_; /**< Current return element. */ + Simplex_key currentArrowNumber_; /**< Number of incrementations. */ + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if two iterators are equal. + * + * @param other Iterator to compare. + * @return True, if the two iterators point to the same position. + * @return False, otherwise. + */ bool equal(Oscillating_rips_iterator const& other) const { if (complex_ == nullptr) return other.complex_ == nullptr; @@ -921,10 +1352,18 @@ class Oscillating_rips_simplex_range { currentSimplexIndex_ == other.currentSimplexIndex_; } + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Dereference the iterator. + * + * @return Value of the current return element. + */ const std::tuple& dereference() const { return currentArrow_; } + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. + */ void increment() { if (!currentDirection_) complex_->remove_maximal_simplex(currentSimplices_[currentSimplexIndex_]); @@ -937,9 +1376,6 @@ class Oscillating_rips_simplex_range { return; } - // if (!currentDirection_) { - // for (auto& sh : currentSimplices_) complex_->remove_maximal_simplex(sh); - // } currentSimplices_.clear(); auto fil = currentEdgeIt_->get_filtration_value(); @@ -960,20 +1396,22 @@ class Oscillating_rips_simplex_range { if (currentDirection_) complex_->assign_key(currentSimplices_[currentSimplexIndex_], currentArrowNumber_); } + /** + * @brief Sets the iterator as the end iterator. + */ void _set_end() { complex_ = nullptr; } + /** + * @brief Updates @p currentSimplices_ for insertions. + * + * @param fil Current filtration value. + */ void _update_positive_current_simplices(Filtration_value fil) { while (currentEdgeIt_ != endEdgeIt_ && currentEdgeIt_->get_direction() && currentEdgeIt_->get_filtration_value() == fil) { complex_->insert_edge_as_flag(currentEdgeIt_->get_smallest_vertex(), currentEdgeIt_->get_biggest_vertex(), currentEdgeIt_->get_filtration_value(), maxDimension_, currentSimplices_); - // std::cout << "add edge: " << currentEdgeIt_->get_smallest_vertex() << " " << currentEdgeIt_->get_biggest_vertex() << " - " << currentEdgeIt_->get_filtration_value() << "\n"; - // std::cout << "current:\n"; - // for (auto sh : currentSimplices_){ - // for (auto v : complex_->simplex_vertex_range(sh)) std::cout << v << " "; - // std::cout << "\n"; - // } ++currentEdgeIt_; } #ifdef GUDHI_USE_TBB @@ -985,6 +1423,11 @@ class Oscillating_rips_simplex_range { #endif } + /** + * @brief Updates @p currentSimplices_ for removals. + * + * @param fil Current filtration value. + */ void _update_negative_current_simplices(Filtration_value fil) { unsigned int count = 0; while (currentEdgeIt_ != endEdgeIt_ && !currentEdgeIt_->get_direction() && @@ -1018,23 +1461,67 @@ class Oscillating_rips_simplex_range { } }; + /** + * @brief Returns a boost::iterator_range from @ref Oscillating_rips_iterator. + * The iterator is a forward iterator only. + * + * @param edgeStartIterator Begin iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param edgeEndIterator End iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param complex Empty complex which will be used to store current simplices. + * Only the address of the complex is stored, so the state of the complex can be + * consulted between each incrementation. + * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. + * Default value: -1. + * + * @return boost::iterator_range of @ref Oscillating_rips_iterator. + * + * @warning Avoid copying the iterators as they are heavier than usual iterators. If begin and end iterators + * are needed but not the structure of the range, use @ref begin and @ref end instead. + */ static boost::iterator_range get_iterator_range(EdgeRangeIterator& edgeStartIterator, EdgeRangeIterator& edgeEndIterator, - Filtered_complex& complex, + StableFilteredComplex& complex, int maxDimension = -1) { return boost::iterator_range( Oscillating_rips_iterator(edgeStartIterator, edgeEndIterator, complex, maxDimension), Oscillating_rips_iterator()); } + /** + * @brief Returns the begin iterator of a the range of simplices based on @ref Oscillating_rips_iterator. + * + * @param edgeStartIterator Begin iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param edgeEndIterator End iterator of the oscilliating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param complex Empty complex which will be used to store current simplices. + * Only the address of the complex is stored, so the state of the complex can be + * consulted between each incrementation. + * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. + * Default value: -1. + * + * @return Instianciation of @ref Oscillating_rips_iterator. + * + * @warning Avoid copying the iterator as it is heavier than usual iterators. + */ static Oscillating_rips_iterator begin(EdgeRangeIterator& edgeStartIterator, EdgeRangeIterator& edgeEndIterator, - Filtered_complex& complex, int maxDimension = -1) { + StableFilteredComplex& complex, int maxDimension = -1) { return Oscillating_rips_iterator(edgeStartIterator, edgeEndIterator, complex, maxDimension); } + /** + * @brief Returns the end iterator of a the range of simplices based on @ref Oscillating_rips_iterator. + * + * @return Default instianciation of @ref Oscillating_rips_iterator. + */ static Oscillating_rips_iterator end() { return Oscillating_rips_iterator(); } private: + /** + * @brief Default constructor. Should not be called and therfore private. Use as a ``static'' class only. + */ Oscillating_rips_simplex_range(){}; }; From 951cbb89f574eb860bb8b576020acfecd2c81806 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Mon, 21 Aug 2023 14:10:10 +0200 Subject: [PATCH 05/21] doc --- .../include/gudhi/Oscillating_rips_persistence.h | 2 +- src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h | 4 ++-- .../gudhi/Zigzag_persistence/oscillating_rips_iterators.h | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index 93bab9be02..a701d8cabd 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -49,7 +49,7 @@ enum Edge_range_type { * possibility to switch out a few common options. But additional options exists for the iterators * (e.g. replacing the Euclidian distance by another one, or using your own epsilon values; see * the documention of @ref Oscillating_rips_edge_range and @ref Oscillating_rips_simplex_range for - * more information). You can easily create your own method based on this one. + * more information). One can easily create their own method based on this one. * * @tparam PointRange Range containing the point cloud. * @tparam edge_range_type Either Edge_range_type::BOOST_RANGE or Edge_range_type::VECTOR. diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index 42fa03e7a0..0b89ef3457 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -256,7 +256,7 @@ class Zigzag_persistence * @param filtration_value Filtration value associated to the simplex. * Assumed to be larger or equal to previously used filtration values. */ - template > + template > void insert_simplex(const VertexRange& simplex, Filtration_value filtration_value) { if (dim_max_ != -1 && simplex.size() > static_cast(dim_max_) + 1) return; @@ -333,7 +333,7 @@ class Zigzag_persistence * are removed in the filtration and ``contiguous'' in the filtration, that is, no other simplex * which is not in the range is inserted or removed between two simplices in the range. * @param filtration_values Filtration values associated to the removal of the given simplices. Has therefore the - * same size as @a simplices. The order has to correspond to the order in @a simplices. Their values have to + * same size as @p simplices. The order has to correspond to the order in @p simplices. Their values have to * ascending in this order and they are assumed to be larger or equal to previously used filtration values. */ template >, diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 0f6d4f48ee..3703fdafd2 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -1207,7 +1207,8 @@ class Oscillating_rips_edge_range { * * @tparam StableFilteredComplex Filtered complex structure that has stable simplex handles, * that is, they do not invalidates after an insertion or a removal (except for the removed simplices). - * @tparam EdgeRangeIterator Type of the edge range iterator. + * @tparam EdgeRangeIterator Type of the edge range iterator. Each element of the range is assumed to have + * type @ref Zigzag_edge or at least to be a class with the same public methods than @ref Zigzag_edge. * * @warning As the custom iterator stores a possibly large range, avoid copying it. * Use @ref get_iterator_range, @ref begin and @ref end wisely. From 8f2dc9cd6b0e6b9a613c306a94411eb2c2b8b808 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 31 Aug 2023 16:54:57 +0200 Subject: [PATCH 06/21] doc --- biblio/bibliography.bib | 12 ++ .../concept/DistanceFunction.h | 31 +++++ src/Zigzag_persistence/concept/EdgeModifier.h | 54 ++++++++ .../concept/OscillatingRipsSimplexRange.h | 69 ++++++++++ src/Zigzag_persistence/concept/PointRange.h | 67 ++++++++++ .../concept/StableFilteredComplex.h | 121 ++++++++++++++++++ .../concept/ZigzagComplex.h | 3 +- .../doc/Intro_zigzag_persistence.h | 9 ++ .../include/gudhi/Zigzag_persistence.h | 14 +- .../oscillating_rips_iterators.h | 30 ++--- src/common/doc/examples.h | 1 + 11 files changed, 382 insertions(+), 29 deletions(-) create mode 100644 src/Zigzag_persistence/concept/DistanceFunction.h create mode 100644 src/Zigzag_persistence/concept/EdgeModifier.h create mode 100644 src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h create mode 100644 src/Zigzag_persistence/concept/PointRange.h create mode 100644 src/Zigzag_persistence/concept/StableFilteredComplex.h diff --git a/biblio/bibliography.bib b/biblio/bibliography.bib index 907391c607..d5a7f07d5c 100644 --- a/biblio/bibliography.bib +++ b/biblio/bibliography.bib @@ -70,6 +70,18 @@ @inproceedings{zigzag_morse doi = {10.1007/978-3-030-24766-9\_39} } +@inproceedings{osc_zz, +author = {Oudot, Steve and Sheehy, Donald}, +year = {2013}, +month = {06}, +pages = {387-396}, +title = {{Zigzag Zoology: Rips Zigzags for Homology Inference}}, +volume = {15}, + booktitle = {Foundations of Computational Mathematics}, +url = {https://doi.org/10.1145/2462356.2462371}, +doi = {10.1145/2462356.2462371} +} + @article{Cohen-Steiner2009, author = {Cohen-Steiner, David and Edelsbrunner, Herbert and Harer, John}, journal = {Foundations of Computational Mathematics}, diff --git a/src/Zigzag_persistence/concept/DistanceFunction.h b/src/Zigzag_persistence/concept/DistanceFunction.h new file mode 100644 index 0000000000..031da7069d --- /dev/null +++ b/src/Zigzag_persistence/concept/DistanceFunction.h @@ -0,0 +1,31 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_DISTANCE_FUNCTION_H_ +#define CONCEPT_ZZ_DISTANCE_FUNCTION_H_ + +/** @file DistanceFunction.h + * @brief Contains @ref Gudhi::zigzag_persistence::DistanceFunction concept. + */ + +#include "PointRange.h" + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Distance function taking two points as input and returning the distance between them. + */ +using DistanceFunction = double (*)(const Point&, const Point&); + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_DISTANCE_FUNCTION_H_ diff --git a/src/Zigzag_persistence/concept/EdgeModifier.h b/src/Zigzag_persistence/concept/EdgeModifier.h new file mode 100644 index 0000000000..180460d3d6 --- /dev/null +++ b/src/Zigzag_persistence/concept/EdgeModifier.h @@ -0,0 +1,54 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_EDGE_MODIFIER_H_ +#define CONCEPT_ZZ_EDGE_MODIFIER_H_ + +/** @file EdgeModifier.h + * @brief Contains @ref Gudhi::zigzag_persistence::EdgeModifier concept. + */ + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Methods whose purposes are to modify the filtration value of a given edge following a rule. + * The concept is for example realized by @ref Square_root_edge_modifier. + */ + template +class EdgeModifier { + public: + /** + * @brief Indicates that the modifier should not be ignored. + */ + static constexpr bool isActive_ = true; + + /** + * @brief Applies the modifier to the given value and returns it. + * + * @param f Value to modify. + * @return The modified value of @p f. + */ + static Filtration_value apply_modifier(Filtration_value f); + + /** + * @brief Applies the inverse modifier to the given value and returns it. + * So, apply_inverse_modifier(apply_modifier(f)) == f (modulo some possible precision errors.). + * + * @param f Value to modify. + * @return The modified value of @p f. + */ + static Filtration_value apply_inverse_modifier(Filtration_value f); +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_EDGE_MODIFIER_H_ diff --git a/src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h b/src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h new file mode 100644 index 0000000000..899d96276a --- /dev/null +++ b/src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h @@ -0,0 +1,69 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_OSCI_RIPS_RANGE_H_ +#define CONCEPT_ZZ_OSCI_RIPS_RANGE_H_ + +/** @file OscillatingRipsSimplexRange.h + * @brief Contains @ref Gudhi::zigzag_persistence::OscillatingRipsSimplexRange concept. + */ + +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Class giving access to a range over the simplices in an oscilliating Rips filtration + * in order of the filtration. + * + * A simplex has to be represented by a tuple of three elements: + * the first is the simplex handle of the simplex in the given complex, + * the second is the filtration value of the corresponding arrow, + * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. + */ +class OscillatingRipsSimplexRange { + public: + /** + * @brief Returns a range over the simplices in an oscilliating Rips filtration + * in order of the filtration. + * + * @param edgeStartIterator Begin iterator of the edge range. + * @param edgeEndIterator End iterator of the edge range. + * @param complex Structure storing the complex at each step. + * @param maxDimension Maximal dimension of the expansion. + * @return A range with begin() and end() methods. + */ + static auto get_iterator_range(Oscillating_rips_edge_range::Oscillating_rips_edge_iterator& edgeStartIterator, + Oscillating_rips_edge_range::Oscillating_rips_edge_iterator& edgeEndIterator, + Gudhi::Simplex_tree& complex, + int maxDimension); + + /** + * @brief Returns a range over the simplices in an oscilliating Rips filtration + * in order of the filtration. + * + * @param edgeStartIterator Begin iterator of the edge range. + * @param edgeEndIterator End iterator of the edge range. + * @param complex Structure storing the complex at each step. + * @param maxDimension Maximal dimension of the expansion. + * @return A range with begin() and end() methods. + */ + static auto get_iterator_range(std::vector >::iterator& edgeStartIterator, + std::vector >::iterator& edgeEndIterator, + Gudhi::Simplex_tree& complex, + int maxDimension); +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_OSCI_RIPS_RANGE_H_ diff --git a/src/Zigzag_persistence/concept/PointRange.h b/src/Zigzag_persistence/concept/PointRange.h new file mode 100644 index 0000000000..b09a9123e7 --- /dev/null +++ b/src/Zigzag_persistence/concept/PointRange.h @@ -0,0 +1,67 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_POINT_RANGE_H_ +#define CONCEPT_ZZ_POINT_RANGE_H_ + +/** @file PointRange.h + * @brief Contains @ref Gudhi::zigzag_persistence::Point and @ref Gudhi::zigzag_persistence::PointRange concept. + */ + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Data structure representing a point of fixed dimension. The structure of the point does not matter + * it-self as long as it corresponds to the input type of the @ref DistanceFunction concept. + */ +class Point{}; + +/** + * @brief Range of @ref Point. Used with @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING order policy, + * it has to be a random access range. + */ +class PointRange { + public: + /** + * @brief Returns begin iterator. + * + * @return Begin iterator. + */ + auto begin(); + + /** + * @brief Returns end iterator. + * + * @return End iterator. + */ + auto end(); + + /** + * @brief Returns size of the range. + * + * @return Size of the range. + */ + std::size_t size(); + + /** + * @brief Necessary only if used with @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING. Returns the element + * at the given index. + * + * @param index Index of the element to return. + * @return Point at index @p index. + */ + Point operator[](std::size_t index); +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_POINT_RANGE_H_ diff --git a/src/Zigzag_persistence/concept/StableFilteredComplex.h b/src/Zigzag_persistence/concept/StableFilteredComplex.h new file mode 100644 index 0000000000..6d430ec52a --- /dev/null +++ b/src/Zigzag_persistence/concept/StableFilteredComplex.h @@ -0,0 +1,121 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#ifndef CONCEPT_ZZ_STABLE_COMPLEX_TYPE_H_ +#define CONCEPT_ZZ_STABLE_COMPLEX_TYPE_H_ + +/** @file StableFilteredComplex.h + * @brief Contains @ref Gudhi::zigzag_persistence::StableFilteredComplex concept. + */ + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Data structure storing the simplices and their filtration values in the current complex. + * The concept is realized for example by @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_oscillating_rips >. + * + * This concept is not incompatible with the @ref ZigzagComplex concept, so it is possible to use the same complex + * for the @ref Oscillating_rips_iterator class and the @ref ZigzagPersistence class, as long as it realizes the two + * concepts (which is the case for @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_oscillating_rips >). + * This avoids having two complexes in memory and give the possibility to directly use the simplex handle instead + * of the vector of vertices to communicate between the two classes. + */ +class StableFilteredComplex { + public: + /** + * @brief Integer type that needs to be long enough to store the numbers of arrows in the zigzag filtration. + */ + typename Simplex_key; + + /** + * @brief Handle to specify a simplex. Different from the @ref ZigzagComplex concept, + * the simplex handles have to be stable, that is, they do not invalidate when a simplex is + * added or removed from the complex (except for the removed simplices them selves of course). + */ + typename Simplex_handle; + + /** + * @brief Type for filtration values. Usually 'double'. + */ + typename Filtration_value; + + /** + * @brief Removes the given simplex. Assumes that the simplex is maximal and can be safely removed. + * + * @param sh Simplex handle representing the simplex to remove. + */ + void remove_maximal_simplex(Simplex_handle sh); + + /** + * @brief Adds a vertex or an edge in a flag complex, as well as all + * simplices of its star, defined to maintain the property + * of the complex to be a flag complex, truncated at dimension dim_max. + * + * @param u ID of one end of the edge. + * @param v ID of the other end of the edge. If @p u == @p v, then the input is considered as a vertex. + * @param fil Filtration value of the edge. + * @param dim_max Maximal dimension of the expansion. If set to -1, the expansion goes as far as possible. + * @param added_simplices Container for all new simplices induced by the insertion of the edge. + * If not empty at start, the content of the container should @b not be erased by the method. + */ + void insert_edge_as_flag(int u, + int v, + Filtration_value fil, + int dim_max, + std::vector& added_simplices); + + /** + * @brief Returns the key associated to the given simplex. + * + * @param sh Simplex handle representing the simplex. + * @return The key. + */ + Simplex_key key(Simplex_handle sh); + + /** + * @brief Assignes the given value to the given simplex as a key. + * + * @param sh Simplex handle representing the simplex. + * @param key Values to associate as key. + */ + void assign_key(Simplex_handle sh, Simplex_key key); + + /** + * @brief Finds the given simplex in the complex and returns the associated simplex handle. + * + * @tparam VertexRange Range over the vertices of a simplex. + * @param simplex Simplex to find represented by its vertices. + * @return The simplex handle associated to @p simplex if the simplex is found, @ref null_simplex() otherwise. + */ + template + Simplex_handle find(const VertexRange& simplex); + + /** + * @brief Returns the filtration value of the given simplex. + * + * @param sh + * @return Filtration_value + */ + Filtration_value filtration(Simplex_handle sh); + + /** + * @brief Returns a range (with begin() and end() methods) over the star of the given simplex, including the simplex. + * + * @param simplex Simplex to compute the star from. + * @return A iterable range over the star of @p simplex. + */ + auto star_simplex_range(const Simplex_handle simplex); +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // CONCEPT_ZZ_STABLE_COMPLEX_TYPE_H_ diff --git a/src/Zigzag_persistence/concept/ZigzagComplex.h b/src/Zigzag_persistence/concept/ZigzagComplex.h index 6b0faa757b..0a76441a4b 100644 --- a/src/Zigzag_persistence/concept/ZigzagComplex.h +++ b/src/Zigzag_persistence/concept/ZigzagComplex.h @@ -20,8 +20,7 @@ namespace zigzag_persistence { /** * @brief Data structure storing the simplices in the current complex. - * The concept is realized for example by @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence > - * or @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence_long >. + * The concept is realized for example by @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_zigzag_persistence >. */ class ZigzagComplex { public: diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 5fe392a659..779ad4cecf 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -34,6 +34,12 @@ namespace zigzag_persistence { * which can be processed without overreaching memory space. For this purpose, it is possible to feed the module with * information about the filtration "on the fly" to avoid loading the whole filtration at once. Information about the * current complex and current barcode can be retrieved between any steps. + * + * \subsection zigzagrips Oscillating Rips + * + * A typical example of zigzag filtrations are oscillating rips filtrations. Similar to standart Rips filtrations, they + * completely depend on their edges. But here we look at neighborhoods ''oscillating'' in size around the points, so + * edges are added but also removed. We refer for example to \cite osc_zz. * * \subsection zigzagexamples Examples * @@ -43,6 +49,9 @@ namespace zigzag_persistence { * * \li \gudhi_example_link{Zigzag_persistence,example_zzfiltration_from_file.cpp} - An example of a "stream-like" usage * by reading of the filtration from a file. + * + * \li \gudhi_example_link{Zigzag_persistence,example_oscillating_rips_persistence.cpp} - An example of a how to + * compute the persistence of an oscillating rips filtration. * * @} */ diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h index 5892f6e2d8..a5d75e7d3c 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence.h @@ -304,7 +304,7 @@ class Zigzag_persistence * @tparam SimplexRange Range type needing begin and end members. * @tparam FiltrationRange Range type needing begin and end members. * @param simplices Simplices which are inserted, represented by their vertices. They have to be in the order they - * are inserted in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * are inserted in the filtration and ''contiguous'' in the filtration, that is, no other simplex * which is not in the range is inserted or removed between two simplices in the range. * @param filtration_values Filtration values associated to the insertion of the given simplices. * The order has to correspond to the order in @a simplices. Their values have to ascending in this order and @@ -326,7 +326,7 @@ class Zigzag_persistence * @tparam SimplexRange Range type needing begin and end members. * @tparam FiltrationRange Range type needing begin and end members. * @param simplices Simplices which are removed, represented by their vertices. They have to be in the order they - * are removed in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * are removed in the filtration and ''contiguous'' in the filtration, that is, no other simplex * which is not in the range is inserted or removed between two simplices in the range. * @param filtration_values Filtration values associated to the removal of the given simplices. Has therefore the * same size as @p simplices. The order has to correspond to the order in @p simplices. Their values have to @@ -349,7 +349,7 @@ class Zigzag_persistence * @tparam FiltrationRangeIterators Forward iterator of a range. * @param simplex_range_start Iterator pointing to the begining of the range of simplices to insert. * The simplices should be represented by their vertices. They have to be in the order they - * are inserted in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * are inserted in the filtration and ''contiguous'' in the filtration, that is, no other simplex * which is not in the range is inserted or removed between two simplices in the range. * @param simplex_range_end Iterator pointing to the end of the range of simplices to insert. * @param filtration_range_start Iterator pointing to the begining of the range of filtration values. The range is @@ -371,7 +371,7 @@ class Zigzag_persistence * @tparam FiltrationRangeIterators Forward iterator of a range. * @param simplex_range_start Iterator pointing to the begining of the range of simplices to remove. * The simplices should be represented by their vertices. They have to be in the order they - * are removed in the filtration and ``contiguous'' in the filtration, that is, no other simplex + * are removed in the filtration and ''contiguous'' in the filtration, that is, no other simplex * which is not in the range is inserted or removed between two simplices in the range. * @param simplex_range_end Iterator pointing to the end of the range of simplices to remove. * @param filtration_range_start Iterator pointing to the begining of the range of filtration values. The range is @@ -387,11 +387,11 @@ class Zigzag_persistence } /** - * @brief Updates the zigzag persistence diagram after the given simplex was ``manually'' inserted in the complex + * @brief Updates the zigzag persistence diagram after the given simplex was ''manually'' inserted in the complex * obtained with @ref get_complex. * * @param simplex Simplex handle of the simplex having been inserted. A filtration value and a key must be assigned - * to it. The key must correspond to the ``arrow number'' inserting the simplex, i.e., its position in the filtration. + * to it. The key must correspond to the ''arrow number'' inserting the simplex, i.e., its position in the filtration. * * @warning Other simplices can have been inserted in the complex before calling this method, there will simply not * be token into account as long as the method is not called for them. It it just important that a key was assigned @@ -443,7 +443,7 @@ class Zigzag_persistence } /** - * @brief Returns the ``index persistence diagram'' of the current filtration, that is, the pairs of atomic arrow + * @brief Returns the ''index persistence diagram'' of the current filtration, that is, the pairs of atomic arrow * numbers corresponding to a birth-death pair. Does not contain points at infinity, only the cycle classes which * already died are represented. * diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 3703fdafd2..4f05d1fc42 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -333,7 +333,7 @@ class Oscillating_rips_edge_range { Filtration_value mu_; /**< Upper multiplicator. */ Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ - bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ``edge'' is a vertex. */ + bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ''edge'' is a vertex. */ /** * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if to iterators are equal. @@ -547,7 +547,8 @@ class Oscillating_rips_edge_range { Filtration_value mu, const PointRange& points, DistanceFunction&& distance, - Order_policy orderPolicy = Order_policy::FARTHEST_POINT_ORDERING) { + Order_policy orderPolicy = Order_policy::FARTHEST_POINT_ORDERING) + { std::vector > edgeFiltration; std::vector epsilonValues; std::vector > > distanceMatrix; @@ -560,10 +561,6 @@ class Oscillating_rips_edge_range { // only at the very last step of the oRzz filtration. std::vector > > edgesAdded, edgesRemoved; - // auto it = std::upper_bound(distanceMatrix[1].begin(), distanceMatrix[1].end(), - // std::pair(distanceMatrix.size(), mu * epsilonValues[0]), - // Point_distance_comp()); - // std::cout << "start vect colind: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); // Now, sort edges according to lengths, and put everything in edgeFiltration @@ -740,7 +737,7 @@ class Oscillating_rips_edge_range { private: /** - * @brief Default constructor. Should not be called and therfore private. Use as a ``static'' class only. + * @brief Default constructor. Should not be called and therfore private. Use as a ''static'' class only. */ Oscillating_rips_edge_range(){}; @@ -838,7 +835,8 @@ class Oscillating_rips_edge_range { * @return Vector of decreasing epsilon values ending with 0. */ template - static std::vector _compute_epsilon_values(const PointRange& sortedPoints, DistanceFunction&& distance) { + static std::vector _compute_epsilon_values(const PointRange& sortedPoints, + DistanceFunction&& distance) { size_t n = sortedPoints.size(); std::vector eps_range(n, std::numeric_limits::infinity()); @@ -897,7 +895,8 @@ class Oscillating_rips_edge_range { */ template static std::vector > > _compute_distance_matrix( - const PointRange& sortedPoints, DistanceFunction&& distance) { + const PointRange& sortedPoints, DistanceFunction&& distance) + { std::vector > > distanceMatrix(sortedPoints.size()); #ifdef GUDHI_USE_TBB tbb::parallel_for(size_t(0), sortedPoints.size(), [&](size_t i) { @@ -982,7 +981,6 @@ class Oscillating_rips_edge_range { } else { edgesAdded[i].emplace_back(it->first, j, epsilonValues[i], true); } - // std::cout << j << ", " << (it - distanceMatrix[j].begin()) << ", " << it->first << "\n"; ++number_of_arrows; } } @@ -1001,7 +999,6 @@ class Oscillating_rips_edge_range { } else { edgesAdded[i].emplace_back(it->first, i + 1, epsilonValues[i], true); } - // std::cout << (i + 1) << ", " << (it - distanceMatrix[i+1].begin()) << ", " << it->first << "\n"; ++number_of_arrows; } @@ -1023,13 +1020,11 @@ class Oscillating_rips_edge_range { if (it->second <= nu * epsilonValues[i + 1]) { break; } - // edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i+1], false); if constexpr (EdgeModifier::isActive_){ edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); } else { edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i], false); } - // std::cout << j << ", " << (it - distanceMatrix[j].begin()) << ", " << it->first << "\n"; ++number_of_arrows; } } @@ -1072,16 +1067,12 @@ class Oscillating_rips_edge_range { // first striclty longer edge it = std::upper_bound(distanceMatrix[i + 1].begin(), distanceMatrix[i + 1].end(), std::pair(n, mu * epsilonValues[i]), Point_distance_comp()); -// if (i == 0){ -// std::cout << "start vect colind2: " << (it - distanceMatrix[1].begin()) << ", (" << it->first << ", " << it->second << "), (" << distanceMatrix[1].begin()->first << ", " << distanceMatrix[1].begin()->second << ")\n"; -// } while (it != distanceMatrix[i + 1].begin()) { --it; if constexpr (EdgeModifier::isActive_) { edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); } else { edgesAdded[i].emplace_back(it->first, i + 1, epsilonValues[i], true); - // if (i==0) std::cout << "added: " << it->first << ", " << (i + 1) << ", " << epsilonValues[i] << "\n"; } ++number_of_arrows; } @@ -1104,7 +1095,6 @@ class Oscillating_rips_edge_range { if (it->second <= nu * epsilonValues[i + 1]) { break; } - // edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i+1], false); if constexpr (EdgeModifier::isActive_){ edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); } else { @@ -1121,7 +1111,7 @@ class Oscillating_rips_edge_range { /** * @brief Sorts canonically the edges: as much as possible, edges should be removed in - * the reverse order of their insertion. We decide to insert shorted edges first, + * the reverse order of their insertion. We decide to insert shortest edges first, * with increasing lexicographical order, and remove larger edges first, with * decreasing lexicographic order. * @@ -1521,7 +1511,7 @@ class Oscillating_rips_simplex_range { private: /** - * @brief Default constructor. Should not be called and therfore private. Use as a ``static'' class only. + * @brief Default constructor. Should not be called and therfore private. Use as a ''static'' class only. */ Oscillating_rips_simplex_range(){}; }; diff --git a/src/common/doc/examples.h b/src/common/doc/examples.h index 913d95b0f8..cef69f9b7c 100644 --- a/src/common/doc/examples.h +++ b/src/common/doc/examples.h @@ -132,5 +132,6 @@ * \section Zigzag_persistence_example_section Zigzag_persistence * @example example_simple_zigzag_filtration.cpp * @example example_zzfiltration_from_file.cpp + * @example example_oscillating_rips_persistence.cpp */ From 67637b95b63eb39ac0a97c3c7898e2f64e5833b7 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 31 Aug 2023 16:59:02 +0200 Subject: [PATCH 07/21] cleanup --- .../benchmark/CMakeLists.txt | 19 - .../benchmark/Oscillating_rips_benchmark.cpp | 213 -- .../Oscillating_rips_comp_benchmark.cpp | 317 -- .../benchmark/ext_zz/dionysus/chain.h | 153 - .../benchmark/ext_zz/dionysus/chain.hpp | 188 -- .../ext_zz/dionysus/clearing-reduction.h | 45 - .../ext_zz/dionysus/clearing-reduction.hpp | 60 - .../benchmark/ext_zz/dionysus/cnpy.h | 241 -- .../ext_zz/dionysus/cohomology-persistence.h | 116 - .../dionysus/cohomology-persistence.hpp | 61 - .../benchmark/ext_zz/dionysus/common.h | 25 - .../benchmark/ext_zz/dionysus/diagram.h | 114 - .../benchmark/ext_zz/dionysus/distances.h | 93 - .../benchmark/ext_zz/dionysus/distances.hpp | 30 - .../benchmark/ext_zz/dionysus/dlog/progress.h | 57 - .../benchmark/ext_zz/dionysus/fields/q.h | 63 - .../benchmark/ext_zz/dionysus/fields/z2.h | 31 - .../benchmark/ext_zz/dionysus/fields/zp.h | 55 - .../benchmark/ext_zz/dionysus/filtration.h | 124 - .../benchmark/ext_zz/dionysus/format.h | 8 - .../ext_zz/dionysus/format/format.cc | 1156 -------- .../benchmark/ext_zz/dionysus/format/format.h | 2546 ----------------- .../benchmark/ext_zz/dionysus/grid/box.h | 136 - .../benchmark/ext_zz/dionysus/grid/box.hpp | 141 - .../benchmark/ext_zz/dionysus/grid/grid.h | 143 - .../benchmark/ext_zz/dionysus/grid/point.h | 132 - .../benchmark/ext_zz/dionysus/grid/vertices.h | 86 - .../ext_zz/dionysus/matrix-filtration.h | 120 - .../ext_zz/dionysus/omni-field-persistence.h | 145 - .../dionysus/omni-field-persistence.hpp | 250 -- .../benchmark/ext_zz/dionysus/opts/opts.h | 499 ---- .../ext_zz/dionysus/ordinary-persistence.h | 64 - .../benchmark/ext_zz/dionysus/pair-recorder.h | 78 - .../ext_zz/dionysus/reduced-matrix.h | 170 -- .../ext_zz/dionysus/reduced-matrix.hpp | 78 - .../benchmark/ext_zz/dionysus/reduction.h | 109 - .../dionysus/relative-homology-zigzag.h | 84 - .../dionysus/relative-homology-zigzag.hpp | 122 - .../benchmark/ext_zz/dionysus/rips.h | 147 - .../benchmark/ext_zz/dionysus/rips.hpp | 162 -- .../benchmark/ext_zz/dionysus/row-reduction.h | 54 - .../ext_zz/dionysus/row-reduction.hpp | 103 - .../benchmark/ext_zz/dionysus/simplex.h | 280 -- .../ext_zz/dionysus/sparse-row-matrix.h | 184 -- .../ext_zz/dionysus/sparse-row-matrix.hpp | 103 - .../ext_zz/dionysus/standard-reduction.h | 44 - .../ext_zz/dionysus/standard-reduction.hpp | 47 - .../benchmark/ext_zz/dionysus/trails-chains.h | 17 - .../ext_zz/dionysus/zigzag-persistence.h | 142 - .../ext_zz/dionysus/zigzag-persistence.hpp | 541 ---- .../benchmark/ext_zz/fzz/fzz.cpp | 206 -- .../benchmark/ext_zz/fzz/fzz.h | 87 - .../ext_zz/phat/algorithms/chunk_reduction.h | 223 -- .../ext_zz/phat/algorithms/row_reduction.h | 56 - .../algorithms/spectral_sequence_reduction.h | 80 - .../phat/algorithms/standard_reduction.h | 47 - .../ext_zz/phat/algorithms/twist_reduction.h | 51 - .../benchmark/ext_zz/phat/boundary_matrix.h | 343 --- .../ext_zz/phat/compute_persistence_pairs.h | 128 - .../benchmark/ext_zz/phat/helpers/dualize.h | 74 - .../benchmark/ext_zz/phat/helpers/misc.h | 75 - .../phat/helpers/thread_local_storage.h | 52 - .../benchmark/ext_zz/phat/persistence_pairs.h | 155 - .../representations/abstract_pivot_column.h | 102 - .../representations/bit_tree_pivot_column.h | 165 -- .../phat/representations/full_pivot_column.h | 100 - .../phat/representations/heap_pivot_column.h | 126 - .../representations/sparse_pivot_column.h | 79 - .../ext_zz/phat/representations/vector_heap.h | 170 -- .../ext_zz/phat/representations/vector_list.h | 101 - .../ext_zz/phat/representations/vector_set.h | 99 - .../phat/representations/vector_vector.h | 107 - src/Zigzag_persistence/example/CMakeLists.txt | 4 - .../example/comparison_for_tests.cpp | 392 --- 74 files changed, 12918 deletions(-) delete mode 100644 src/Zigzag_persistence/benchmark/CMakeLists.txt delete mode 100644 src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp delete mode 100644 src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h delete mode 100644 src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h delete mode 100644 src/Zigzag_persistence/example/comparison_for_tests.cpp diff --git a/src/Zigzag_persistence/benchmark/CMakeLists.txt b/src/Zigzag_persistence/benchmark/CMakeLists.txt deleted file mode 100644 index 3d21cbe72a..0000000000 --- a/src/Zigzag_persistence/benchmark/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -project(Zigzag_benchmark) - -find_package(benchmark REQUIRED) - -add_executable(Oscillating_rips_benchmark Oscillating_rips_benchmark.cpp) -target_link_libraries(Oscillating_rips_benchmark benchmark::benchmark) -if(TARGET TBB::tbb) - target_link_libraries(Oscillating_rips_benchmark TBB::tbb) -endif() - -add_executable(Comp_benchmark Oscillating_rips_comp_benchmark.cpp ./ext_zz/fzz/fzz.cpp) -target_link_libraries(Comp_benchmark benchmark::benchmark) -target_include_directories(Comp_benchmark PUBLIC . ./ext_zz) -if(TARGET TBB::tbb) - target_link_libraries(Comp_benchmark TBB::tbb) -endif() -target_compile_options(Comp_benchmark PUBLIC "-fopenmp") -target_link_options(Comp_benchmark PUBLIC "-fopenmp") - diff --git a/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp b/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp deleted file mode 100644 index 179eecaebb..0000000000 --- a/src/Zigzag_persistence/benchmark/Oscillating_rips_benchmark.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - * Author(s): Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include // for pair -#include - -#include -#include -#include -#include -#include -#include - -using ST = Gudhi::Simplex_tree; -using Filtration_value = ST::Filtration_value; -using Simplex_handle = ST::Simplex_handle; -using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; -using ZE = Gudhi::zigzag_persistence::Zigzag_edge; -using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; -using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; -using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::iterator>; -using Point = std::vector; - -std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { - std::vector finalPoints; - std::set points; - std::random_device dev; - std::mt19937 rng(dev()); - if (seed > -1) rng.seed(seed); - std::uniform_real_distribution dist(0, 10); - - for (unsigned int i = 0; i < numberOfPoints; ++i) { - auto res = points.insert({dist(rng), dist(rng)}); - while (!res.second) { - res = points.insert({dist(rng), dist(rng)}); - } - finalPoints.push_back(*res.first); - } - - return finalPoints; -} - -// static void ORP_with_custom_iterator(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::FARTHEST_POINT_ORDERING); -// } -// } -// BENCHMARK(ORP_with_custom_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(2, 5, 4), -// benchmark::CreateRange(5, 5, 4)}) -// ->Unit(benchmark::kMicrosecond); -// BENCHMARK(ORP_with_custom_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(20, 64, 4)}) -// ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_custom_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(256, 500, 4)}) -// ->Unit(benchmark::kMillisecond); - -// static void ORP_with_vector_iterator(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< -// std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::FARTHEST_POINT_ORDERING); -// } -// } -// BENCHMARK(ORP_with_vector_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(2, 5, 4), -// benchmark::CreateRange(5, 5, 4)}) -// ->Unit(benchmark::kMicrosecond); -// BENCHMARK(ORP_with_vector_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(20, 64, 4)}) -// ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_vector_iterator) -// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(256, 500, 4)}) -// ->Unit(benchmark::kMillisecond); - -// static void ORP_with_custom_iterator_random(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::RANDOM_POINT_ORDERING); -// } -// } -// BENCHMARK(ORP_with_custom_iterator_random) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateDenseRange(5, 15, 5)}) -// ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_custom_iterator_random) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateDenseRange(20, 50, 30)}) -// ->Unit(benchmark::kMillisecond); - -static void ORP_with_vector_iterator_random(benchmark::State& state) { - double nu = state.range(0); - double mu = state.range(1); - int maxDim = state.range(2); - unsigned int numberOfPoints = state.range(3); - int seed = 0; - - std::vector points = build_point_cloud(numberOfPoints, seed); - - for (auto _ : state) { - auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< - std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::RANDOM_POINT_ORDERING); - } -} -BENCHMARK(ORP_with_vector_iterator_random) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(4, 50, 4), - benchmark::CreateDenseRange(5, 15, 5)}) - ->Unit(benchmark::kMillisecond); -BENCHMARK(ORP_with_vector_iterator_random) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 4, 1), - benchmark::CreateRange(4, 50, 4), - benchmark::CreateDenseRange(20, 50, 30)}) - ->Unit(benchmark::kMillisecond); - -// static void ORP_with_custom_iterator_ordered(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim, ORE::Order_policy::ALREADY_ORDERED); -// } -// } -// BENCHMARK(ORP_with_custom_iterator_ordered) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(5, 50, 4)}) -// ->Unit(benchmark::kMillisecond); - -// static void ORP_with_vector_iterator_ordered(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence< -// std::vector, Gudhi::zigzag_persistence::Edge_range_type::VECTOR>(points, nu, mu, maxDim, ORE::Order_policy::ALREADY_ORDERED); -// } -// } -// BENCHMARK(ORP_with_vector_iterator_ordered) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(5, 50, 4)}) -// ->Unit(benchmark::kMillisecond); - -BENCHMARK_MAIN(); - diff --git a/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp b/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp deleted file mode 100644 index 6385fe8919..0000000000 --- a/src/Zigzag_persistence/benchmark/Oscillating_rips_comp_benchmark.cpp +++ /dev/null @@ -1,317 +0,0 @@ -/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - * Author(s): Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include // for pair -#include - -#include -#include -#include -#include -#include -#include - -#include "ext_zz/fzz/fzz.h" -#include "ext_zz/dionysus/simplex.h" -#include "ext_zz/dionysus/filtration.h" -#include "ext_zz/dionysus/zigzag-persistence.h" - -using ST = Gudhi::Simplex_tree; -using Vertex_handle = ST::Vertex_handle; -using Filtration_value = ST::Filtration_value; -using Simplex_handle = ST::Simplex_handle; -using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; -using ZE = Gudhi::zigzag_persistence::Zigzag_edge; -using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; -using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; -using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::const_iterator>; -using Point = std::vector; - -using DField = dionysus::Z2Field; -using Simplex = dionysus::Simplex<>; -using DFiltration = dionysus::Filtration; -using DZZ = dionysus::ZigzagPersistence; -using DIndex = typename DZZ::Index; -using DChain = dionysus::ChainEntry; -using DIChain = dionysus::ChainEntry; - -struct Simplex_tree_options_test { - typedef Gudhi::linear_indexing_tag Indexing_tag; - typedef int Vertex_handle; - typedef double Filtration_value; - typedef std::int32_t Simplex_key; - static const bool store_key = true; - static const bool store_filtration = true; - static const bool contiguous_vertices = true; - static const bool link_nodes_by_label = false; - static const bool simplex_handle_strong_validity = false; -}; - -// using ST_std = Gudhi::Simplex_tree; -using ST_std = Gudhi::Simplex_tree; -using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; -using Barcode = std::vector; - -std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { - std::vector finalPoints; - std::set points; - std::random_device dev; - std::mt19937 rng(dev()); - if (seed > -1) rng.seed(seed); - std::uniform_real_distribution dist(0, 10); - - for (unsigned int i = 0; i < numberOfPoints; ++i) { - auto res = points.insert({dist(rng), dist(rng)}); - while (!res.second) { - res = points.insert({dist(rng), dist(rng)}); - } - finalPoints.push_back(*res.first); - } - - return finalPoints; -} - -std::vector > compute_with_dionysus(const std::vector& edges, int maxDim) { - ST st; - DField k; - std::unordered_map indices; - DZZ persistence(k); - std::vector > res; - - std::set essentials; - - unsigned int op = 0; - unsigned int idx = 0; - - auto start = edges.begin(); - auto end = edges.end(); - for (const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { - auto r = st.simplex_vertex_range(std::get<0>(t)); - std::vector simplex(r.begin(), r.end()); - Simplex c(simplex); - DIndex pair; - if (std::get<2>(t)) { - indices.try_emplace(c, idx++); - // int dim = boost::distance(c.boundary(persistence.field())); - // dim = dim == 0 ? 0 : dim -1; - // fmt::print("[{}] Adding: {} : {}\n", op, c, dim); - pair = - persistence.add(c.boundary(persistence.field()) | boost::adaptors::transformed([&indices](const DChain& e) { - return DIChain(e.element(), indices.find(e.index())->second); - })); - } else { - // fmt::print("[{}] Removing: {} : {}\n", op, c, boost::distance(c.boundary(persistence.field())) - 1); - auto idxIt = indices.find(c); - pair = persistence.remove(idxIt->second); - indices.erase(idxIt); - } - - if (pair != DZZ::unpaired()) { - // fmt::print("{} - {}\n", pair, op); - res.emplace_back(pair, op); - essentials.erase(pair); - } else { - essentials.insert(essentials.end(), op); - } - op++; - } - - for (unsigned int v : essentials) { - // fmt::print("{} - inf\n", v); - res.emplace_back(v, op); - } - - return res; -} - -std::vector > compute_with_fzz(const std::vector& edges, - int maxDim) { - std::vector > persistence; - FZZ::FastZigzag fzz; - std::vector > simplices; - std::vector dirs; - ST st; - - auto start = edges.begin(); - auto end = edges.end(); - for (const auto& t : ORv::get_iterator_range(start, end, st, maxDim)){ - auto r = st.simplex_vertex_range(std::get<0>(t)); - simplices.emplace_back(r.begin(), r.end()); - dirs.push_back(std::get<2>(t)); - } - - fzz.compute(simplices, dirs, &persistence); - - std::sort(persistence.begin(), persistence.end(), - [](const std::tuple& p1, - const std::tuple& p2) { - if (std::get<1>(p1) == std::get<1>(p2)) { - return std::get<0>(p1) < std::get<0>(p2); - } - - return std::get<1>(p1) < std::get<1>(p2); - }); - - return persistence; -} - -Barcode compute_with_gudhi_v2(const std::vector& points, double nu, double mu, int maxDim) { - ZP zp; - ST st; - - auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), ORE::Order_policy::FARTHEST_POINT_ORDERING); - auto end = ORE::end(); - for (const auto& t : OR::get_iterator_range(start, end, st, maxDim)) { - auto r = st.simplex_vertex_range(std::get<0>(t)); - std::vector simplex(r.begin(), r.end()); - if (std::get<2>(t)) - zp.insert_simplex(simplex, std::get<1>(t)); - else - zp.remove_simplex(simplex, std::get<1>(t)); - } - - return zp.get_persistence_diagram(); -} - -static void ORP_with_gudhi(benchmark::State& state) { - double nu = state.range(0); - double mu = state.range(1); - int maxDim = state.range(2); - unsigned int numberOfPoints = state.range(3); - int seed = 0; - - std::vector points = build_point_cloud(numberOfPoints, seed); - - for (auto _ : state) { - auto res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); - } -} -BENCHMARK(ORP_with_gudhi) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(2, 5, 4), - benchmark::CreateRange(5, 5, 4)}) - ->Unit(benchmark::kMicrosecond); -BENCHMARK(ORP_with_gudhi) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(4, 50, 4), - benchmark::CreateRange(20, 64, 4)}) - ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_gudhi) -// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(256, 500, 4)}) -// ->Unit(benchmark::kMillisecond); - -// static void ORP_with_gudhi_v2(benchmark::State& state) { -// double nu = state.range(0); -// double mu = state.range(1); -// int maxDim = state.range(2); -// unsigned int numberOfPoints = state.range(3); -// int seed = 0; - -// std::vector points = build_point_cloud(numberOfPoints, seed); - -// for (auto _ : state) { -// auto res = compute_with_gudhi_v2(points, nu, mu, maxDim); -// } -// } -// BENCHMARK(ORP_with_gudhi_v2) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(2, 5, 4), -// benchmark::CreateRange(5, 5, 4)}) -// ->Unit(benchmark::kMicrosecond); -// BENCHMARK(ORP_with_gudhi_v2) -// ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), -// benchmark::CreateDenseRange(3, 5, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(20, 64, 4)}) -// ->Unit(benchmark::kMillisecond); -// // BENCHMARK(ORP_with_gudhi_v2) -// // ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// // benchmark::CreateDenseRange(3, 4, 1), -// // benchmark::CreateRange(4, 50, 4), -// // benchmark::CreateRange(256, 500, 4)}) -// // ->Unit(benchmark::kMillisecond); - -static void ORP_with_dionysus(benchmark::State& state) { - double nu = state.range(0); - double mu = state.range(1); - int maxDim = state.range(2); - unsigned int numberOfPoints = state.range(3); - int seed = 0; - - std::vector points = build_point_cloud(numberOfPoints, seed); - - for (auto _ : state) { - auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance()); - auto res = compute_with_dionysus(vec, maxDim); - } -} -BENCHMARK(ORP_with_dionysus) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(2, 5, 4), - benchmark::CreateRange(5, 5, 4)}) - ->Unit(benchmark::kMicrosecond); -BENCHMARK(ORP_with_dionysus) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(4, 50, 4), - benchmark::CreateRange(20, 64, 4)}) - ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_dionysus) -// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(256, 500, 4)}) -// ->Unit(benchmark::kMillisecond); - -static void ORP_with_fzz(benchmark::State& state) { - double nu = state.range(0); - double mu = state.range(1); - int maxDim = state.range(2); - unsigned int numberOfPoints = state.range(3); - int seed = 0; - - std::vector points = build_point_cloud(numberOfPoints, seed); - - for (auto _ : state) { - auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance()); - auto res = compute_with_fzz(vec, maxDim); - } -} -BENCHMARK(ORP_with_fzz) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(2, 5, 4), - benchmark::CreateRange(5, 5, 4)}) - ->Unit(benchmark::kMicrosecond); -BENCHMARK(ORP_with_fzz) - ->ArgsProduct({benchmark::CreateDenseRange(0, 2, 1), - benchmark::CreateDenseRange(3, 5, 1), - benchmark::CreateRange(4, 50, 4), - benchmark::CreateRange(20, 64, 4)}) - ->Unit(benchmark::kMillisecond); -// BENCHMARK(ORP_with_fzz) -// ->ArgsProduct({benchmark::CreateDenseRange(1, 2, 1), -// benchmark::CreateDenseRange(3, 4, 1), -// benchmark::CreateRange(4, 50, 4), -// benchmark::CreateRange(256, 500, 4)}) -// ->Unit(benchmark::kMillisecond); - -BENCHMARK_MAIN(); - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h deleted file mode 100644 index 00c983623a..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.h +++ /dev/null @@ -1,153 +0,0 @@ -#ifndef DIONYSUS_CHAIN_H -#define DIONYSUS_CHAIN_H - -#include -#include -#include - -#include "fields/z2.h" - -namespace dionysus -{ - -template -struct FieldElement -{ - typedef typename Field::Element Element; - FieldElement(Element e_): - e(e_) {} - Element element() const { return e; } - void set_element(Element e_) { e = e_; } - Element e; -}; - -template<> -struct FieldElement -{ - typedef Z2Field::Element Element; - FieldElement(Element) {} - Element element() const { return Z2Field::id(); } - void set_element(Element) {} -}; - -template -struct ChainEntry: public FieldElement, public Extra... -{ - typedef Field_ Field; - typedef Index_ Index; - - typedef FieldElement Parent; - typedef typename Parent::Element Element; - - ChainEntry(): Parent(Element()), i(Index()) {} // need for serialization - - ChainEntry(ChainEntry&& other) = default; - ChainEntry(const ChainEntry& other) = default; - ChainEntry& operator=(ChainEntry&& other) = default; - - ChainEntry(Element e_, const Index& i_): - Parent(e_), i(i_) {} - - ChainEntry(Element e_, Index&& i_): - Parent(e_), i(std::move(i_)) {} - - const Index& index() const { return i; } - Index& index() { return i; } - - // debug - bool operator==(const ChainEntry& other) const { return i == other.i; } - - Index i; -}; - -template -struct Chain -{ - struct Visitor - { - template - void first(Iter it) const {} - - template - void second(Iter it) const {} - - template - void equal_keep(Iter it) const {} - - template - void equal_drop(Iter it) const {} - }; - - // x += a*y - template - static void addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); -}; - -template -struct Chain> -{ - struct Visitor - { - template - void first(Iter it) const {} - - template - void second(Iter it) const {} - - template - void equal_keep(Iter it) const {} - - template - void equal_drop(Iter it) const {} - }; - - // x += a*y - template - static void addto(std::list& x, typename Field::Element a, const C2& y, - const Field& field, const Cmp& cmp, const Visitor_& visitor = Visitor_()); -}; - - -template -struct Chain> -{ - struct Visitor - { - template - void first(Iter it) const {} - - template - void second(Iter it) const {} - - template - void equal_keep(Iter it) const {} - - template - void equal_drop(Iter it) const {} - }; - - // x += a*y - template - static void addto(std::set& x, typename Field::Element a, const C2& y, - const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); - - template - static void addto(std::set& x, typename Field::Element a, T&& y, - const Field& field, const Cmp& cmp, const Visitor_& = Visitor_()); -}; - -} - -//namespace std -//{ -// template -// void swap(::dionysus::ChainEntry& x, ::dionysus::ChainEntry& y) -// { -// std::swap(x.e, y.e); -// std::swap(x.i, y.i); -// } -//} - -#include "chain.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp deleted file mode 100644 index 4da9f44615..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/chain.hpp +++ /dev/null @@ -1,188 +0,0 @@ -template -template -void -dionysus::Chain>:: -addto(std::list& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - auto cur_x = std::begin(x), - end_x = std::end(x); - auto cur_y = std::begin(y), - end_y = std::end(y); - - while (cur_x != end_x && cur_y != end_y) - { - if (cmp(cur_x->index(), cur_y->index())) - { - visitor.first(cur_x++); - } else if (cmp(cur_y->index(), cur_x->index())) - { - // multiply and add - Element ay = field.mul(a, cur_y->element()); - auto nw_x = x.insert(cur_x, *cur_y); - nw_x->set_element(ay); - ++cur_y; - visitor.second(nw_x); - } else - { - Element ay = field.mul(a, cur_y->element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - { - visitor.equal_drop(cur_x); - x.erase(cur_x++); - } - else - { - cur_x->set_element(r); - visitor.equal_keep(cur_x); - ++cur_x; - } - ++cur_y; - } - } - - for (auto it = cur_y; it != end_y; ++it) - { - Element ay = field.mul(a, it->element()); - x.push_back(*it); - x.back().set_element(ay); - visitor.second(--x.end()); - } -} - -template -template -void -dionysus::Chain>:: -addto(std::set& x, typename Field::Element a, const C2& y, const Field& field, const Cmp&, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - auto cur_y = std::begin(y), - end_y = std::end(y); - - while (cur_y != end_y) - { - auto cur_x = x.find(*cur_y); - if (cur_x == x.end()) - { - auto nw = x.insert(*cur_y).first; - Element ay = field.mul(a, nw->element()); - const_cast(*nw).set_element(ay); - visitor.second(nw); - } else - { - Element ay = field.mul(a, cur_y->element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - { - visitor.equal_drop(cur_x); - x.erase(cur_x); - } - else - { - const_cast(*cur_x).set_element(r); - visitor.equal_keep(cur_x); - } - } - ++cur_y; - } -} - -template -template -void -dionysus::Chain>:: -addto(std::set& x, typename Field::Element a, T&& y, const Field& field, const Cmp&, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - auto cur_x = x.find(y); - if (cur_x == x.end()) - { - auto nw = x.insert(std::move(y)).first; - Element ay = field.mul(a, nw->element()); - const_cast(*nw).set_element(ay); - visitor.second(nw); - } else - { - Element ay = field.mul(a, y.element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - { - visitor.equal_drop(cur_x); - x.erase(cur_x); - } - else - { - const_cast(*cur_x).set_element(r); - visitor.equal_keep(cur_x); - } - } -} - -template -template -void -dionysus::Chain:: -addto(C1& x, typename Field::Element a, const C2& y, const Field& field, const Cmp& cmp, const Visitor_& visitor) -{ - typedef typename Field::Element Element; - - C1 res; - - auto cur_x = std::begin(x), - end_x = std::end(x); - auto cur_y = std::begin(y), - end_y = std::end(y); - - while (cur_x != end_x && cur_y != end_y) - { - if (cmp(*cur_x, *cur_y)) - { - res.emplace_back(std::move(*cur_x)); - visitor.first(--res.end()); - ++cur_x; - } else if (cmp(*cur_y, *cur_x)) - { - // multiply and add - Element ay = field.mul(a, cur_y->element()); - res.emplace_back(ay, cur_y->index()); - visitor.second(--res.end()); - ++cur_y; - } else - { - Element ay = field.mul(a, cur_y->element()); - Element r = field.add(cur_x->element(), ay); - if (field.is_zero(r)) - visitor.equal_drop(cur_x); - else - { - res.emplace_back(std::move(*cur_x)); - res.back().set_element(r); - visitor.equal_keep(--res.end()); - } - ++cur_x; - ++cur_y; - } - } - - while (cur_y != end_y) - { - Element ay = field.mul(a, cur_y->element()); - res.emplace_back(ay, cur_y->index()); - visitor.second(--res.end()); - ++cur_y; - } - - while (cur_x != end_x) - { - res.emplace_back(std::move(*cur_x)); - visitor.first(--res.end()); - ++cur_x; - } - - x.swap(res); -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h deleted file mode 100644 index 8651e9a69a..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef DIONYSUS_CLEARING_REDUCTION_H -#define DIONYSUS_CLEARING_REDUCTION_H - -namespace dionysus -{ - -// Mid-level interface -template -class ClearingReduction -{ - public: - using Persistence = Persistence_; - using Field = typename Persistence::Field; - using Index = typename Persistence::Index; - - public: - ClearingReduction(Persistence& persistence): - persistence_(persistence) {} - - template - void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); - - template - void operator()(const Filtration& f, const ReportPair& report_pair); - - template - void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } - - static void no_report_pair(int, Index, Index) {} - static void no_progress() {} - - const Persistence& - persistence() const { return persistence_; } - Persistence& persistence() { return persistence_; } - - private: - Persistence& persistence_; -}; - -} - -#include "clearing-reduction.hpp" - -#endif - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp deleted file mode 100644 index ceac11879d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/clearing-reduction.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include - -#include -namespace ba = boost::adaptors; - -template -template -void -dionysus::ClearingReduction

:: -operator()(const Filtration& filtration, const ReportPair& report_pair) -{ - using Cell = typename Filtration::Cell; - (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); -} - -template -template -void -dionysus::ClearingReduction

:: -operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) -{ - persistence_.resize(filtration.size()); - - // sort indices by decreasing dimension - std::vector indices(filtration.size()); - std::iota(indices.begin(), indices.end(), 0); - std::stable_sort(indices.begin(), indices.end(), - [&filtration](size_t x, size_t y) - { return filtration[x].dimension() > filtration[y].dimension(); }); - - typedef typename Filtration::Cell Cell; - typedef ChainEntry CellChainEntry; - typedef ChainEntry ChainEntry; - - for(size_t i : indices) - { - progress(); - const auto& c = filtration[i]; - - if (relative(c)) - { - persistence_.set_skip(i); - continue; - } - - if (persistence_.pair(i) != persistence_.unpaired()) - continue; - - persistence_.set(i, c.boundary(persistence_.field()) | - ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | - ba::transformed([this,&filtration](const CellChainEntry& e) - { return ChainEntry(e.element(), filtration.index(e.index())); })); - - Index pair = persistence_.reduce(i); - if (pair != persistence_.unpaired()) - report_pair(c.dimension(), pair, i); - } -} - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h deleted file mode 100644 index b11013b9d7..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cnpy.h +++ /dev/null @@ -1,241 +0,0 @@ -//Copyright (C) 2011 Carl Rogers -//Released under MIT License -//license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php - -#ifndef LIBCNPY_H_ -#define LIBCNPY_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cnpy { - - struct NpyArray { - char* data; - std::vector shape; - unsigned int word_size; - bool fortran_order; - void destruct() {delete[] data;} - }; - - struct npz_t : public std::map - { - void destruct() - { - npz_t::iterator it = this->begin(); - for(; it != this->end(); ++it) (*it).second.destruct(); - } - }; - - char BigEndianTest(); - char map_type(const std::type_info& t); - template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims); - void parse_npy_header(FILE* fp,unsigned int& word_size, unsigned int*& shape, unsigned int& ndims, bool& fortran_order); - void parse_zip_footer(FILE* fp, unsigned short& nrecs, unsigned int& global_header_size, unsigned int& global_header_offset); - npz_t npz_load(std::string fname); - NpyArray npz_load(std::string fname, std::string varname); - NpyArray npy_load(std::string fname); - - template std::vector& operator+=(std::vector& lhs, const T rhs) { - //write in little endian - for(char byte = 0; byte < sizeof(T); byte++) { - char val = *((char*)&rhs+byte); - lhs.push_back(val); - } - return lhs; - } - - template<> std::vector& operator+=(std::vector& lhs, const std::string rhs); - template<> std::vector& operator+=(std::vector& lhs, const char* rhs); - - - template std::string tostring(T i, int pad = 0, char padval = ' ') { - std::stringstream s; - s << i; - return s.str(); - } - - template void npy_save(std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") { - FILE* fp = NULL; - - if(mode == "a") fp = fopen(fname.c_str(),"r+b"); - - if(fp) { - //file exists. we need to append to it. read the header, modify the array size - unsigned int word_size, tmp_dims; - unsigned int* tmp_shape = 0; - bool fortran_order; - parse_npy_header(fp,word_size,tmp_shape,tmp_dims,fortran_order); - assert(!fortran_order); - - if(word_size != sizeof(T)) { - std::cout<<"libnpy error: "< header = create_npy_header(data,tmp_shape,ndims); - fwrite(&header[0],sizeof(char),header.size(),fp); - fseek(fp,0,SEEK_END); - - delete[] tmp_shape; - } - else { - fp = fopen(fname.c_str(),"wb"); - std::vector header = create_npy_header(data,shape,ndims); - fwrite(&header[0],sizeof(char),header.size(),fp); - } - - unsigned int nels = 1; - for(int i = 0;i < ndims;i++) nels *= shape[i]; - - fwrite(data,sizeof(T),nels,fp); - fclose(fp); - } - - template void npz_save(std::string zipname, std::string fname, const T* data, const unsigned int* shape, const unsigned int ndims, std::string mode = "w") - { - //first, append a .npy to the fname - fname += ".npy"; - - //now, on with the show - FILE* fp = NULL; - unsigned short nrecs = 0; - unsigned int global_header_offset = 0; - std::vector global_header; - - if(mode == "a") fp = fopen(zipname.c_str(),"r+b"); - - if(fp) { - //zip file exists. we need to add a new npy file to it. - //first read the footer. this gives us the offset and size of the global header - //then read and store the global header. - //below, we will write the the new data at the start of the global header then append the global header and footer below it - unsigned int global_header_size; - parse_zip_footer(fp,nrecs,global_header_size,global_header_offset); - fseek(fp,global_header_offset,SEEK_SET); - global_header.resize(global_header_size); - size_t res = fread(&global_header[0],sizeof(char),global_header_size,fp); - if(res != global_header_size){ - throw std::runtime_error("npz_save: header read error while adding to existing zip"); - } - fseek(fp,global_header_offset,SEEK_SET); - } - else { - fp = fopen(zipname.c_str(),"wb"); - } - - std::vector npy_header = create_npy_header(data,shape,ndims); - - unsigned long nels = 1; - for (int m=0; m local_header; - local_header += "PK"; //first part of sig - local_header += (unsigned short) 0x0403; //second part of sig - local_header += (unsigned short) 20; //min version to extract - local_header += (unsigned short) 0; //general purpose bit flag - local_header += (unsigned short) 0; //compression method - local_header += (unsigned short) 0; //file last mod time - local_header += (unsigned short) 0; //file last mod date - local_header += (unsigned int) crc; //crc - local_header += (unsigned int) nbytes; //compressed size - local_header += (unsigned int) nbytes; //uncompressed size - local_header += (unsigned short) fname.size(); //fname length - local_header += (unsigned short) 0; //extra field length - local_header += fname; - - //build global header - global_header += "PK"; //first part of sig - global_header += (unsigned short) 0x0201; //second part of sig - global_header += (unsigned short) 20; //version made by - global_header.insert(global_header.end(),local_header.begin()+4,local_header.begin()+30); - global_header += (unsigned short) 0; //file comment length - global_header += (unsigned short) 0; //disk number where file starts - global_header += (unsigned short) 0; //internal file attributes - global_header += (unsigned int) 0; //external file attributes - global_header += (unsigned int) global_header_offset; //relative offset of local file header, since it begins where the global header used to begin - global_header += fname; - - //build footer - std::vector footer; - footer += "PK"; //first part of sig - footer += (unsigned short) 0x0605; //second part of sig - footer += (unsigned short) 0; //number of this disk - footer += (unsigned short) 0; //disk where footer starts - footer += (unsigned short) (nrecs+1); //number of records on this disk - footer += (unsigned short) (nrecs+1); //total number of records - footer += (unsigned int) global_header.size(); //nbytes of global headers - footer += (unsigned int) (global_header_offset + nbytes + local_header.size()); //offset of start of global headers, since global header now starts after newly written array - footer += (unsigned short) 0; //zip file comment length - - //write everything - fwrite(&local_header[0],sizeof(char),local_header.size(),fp); - fwrite(&npy_header[0],sizeof(char),npy_header.size(),fp); - fwrite(data,sizeof(T),nels,fp); - fwrite(&global_header[0],sizeof(char),global_header.size(),fp); - fwrite(&footer[0],sizeof(char),footer.size(),fp); - fclose(fp); - } - - template std::vector create_npy_header(const T* data, const unsigned int* shape, const unsigned int ndims) { - - std::vector dict; - dict += "{'descr': '"; - dict += BigEndianTest(); - dict += map_type(typeid(T)); - dict += tostring(sizeof(T)); - dict += "', 'fortran_order': False, 'shape': ("; - dict += tostring(shape[0]); - for(int i = 1;i < ndims;i++) { - dict += ", "; - dict += tostring(shape[i]); - } - if(ndims == 1) dict += ","; - dict += "), }"; - //pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to end with \n - int remainder = 16 - (10 + dict.size()) % 16; - dict.insert(dict.end(),remainder,' '); - dict.back() = '\n'; - - std::vector header; - header += (char) 0x93; - header += "NUMPY"; - header += (char) 0x01; //major version of numpy format - header += (char) 0x00; //minor version of numpy format - header += (unsigned short) dict.size(); - header.insert(header.end(),dict.begin(),dict.end()); - - return header; - } - - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h deleted file mode 100644 index 8d2019e89d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef DIONYSUS_COHOMOLOGY_PERSISTENCE_H -#define DIONYSUS_COHOMOLOGY_PERSISTENCE_H - -#include -#include - -#include -namespace bi = boost::intrusive; - -#include "reduction.h" -#include "chain.h" - -namespace dionysus -{ - -template> -class CohomologyPersistence -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef typename Field::Element FieldElement; - - typedef bi::list_base_hook> auto_unlink_hook; - struct Entry; - struct ColumnHead; - - typedef std::vector Column; - typedef bi::list> Row; - typedef std::list Columns; - typedef typename Columns::iterator ColumnsIterator; - typedef Column Chain; - - using IndexColumn = std::tuple; - - CohomologyPersistence(const Field& field, - const Comparison& cmp = Comparison()): - field_(field), cmp_(cmp) {} - - CohomologyPersistence(Field&& field, - const Comparison& cmp = Comparison()): - field_(std::move(field)), - cmp_(cmp) {} - - CohomologyPersistence(CohomologyPersistence&& other): - field_(std::move(other.field_)), - cmp_(std::move(other.cmp_)), - columns_(std::move(other.columns_)), - rows_(std::move(other.rows_)) {} - - template - Index add(const ChainRange& chain); - - template - IndexColumn add(const ChainRange& chain, bool keep_cocycle); - - // TODO: no skip support for now - bool skip(Index) const { return false; } - void add_skip() {} - void set_skip(Index, bool flag = true) {} - - const Field& field() const { return field_; } - const Columns& columns() const { return columns_; } - void reserve(size_t s) { rows_.reserve(s); } - - struct AddtoVisitor; - - static const Index unpaired() { return Reduction::unpaired; } - - private: - Field field_; - Comparison cmp_; - Columns columns_; - std::vector rows_; -}; - - -template -struct CohomologyPersistence::ColumnHead -{ - ColumnHead(Index i): index_(i) {} - - Index index() const { return index_; } - - Index index_; - Column chain; -}; - -template -struct CohomologyPersistence::Entry: - public ChainEntry -{ - typedef ChainEntry Parent; - - Entry(FieldElement e, const Index& i): // slightly dangerous - Parent(e,i) {} - - Entry(FieldElement e, const Index& i, ColumnsIterator it): - Parent(e,i), column(it) {} - - Entry(const Entry& other) = default; - Entry(Entry&& other) = default; - - void unlink() { auto_unlink_hook::unlink(); } - bool is_linked() const { return auto_unlink_hook::is_linked(); } - - ColumnsIterator column; // TODO: I really don't like this overhead -}; - -} - -#include "cohomology-persistence.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp deleted file mode 100644 index b2334f99e1..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/cohomology-persistence.hpp +++ /dev/null @@ -1,61 +0,0 @@ -template -template -typename dionysus::CohomologyPersistence::Index -dionysus::CohomologyPersistence:: -add(const ChainRange& chain) -{ - return std::get<0>(add(chain, false)); // return just the index -} - - -template -template -typename dionysus::CohomologyPersistence::IndexColumn -dionysus::CohomologyPersistence:: -add(const ChainRange& chain, bool keep_cocycle) -{ - auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; - std::set row_sum(entry_cmp); - for (auto it = std::begin(chain); it != std::end(chain); ++it) - for (auto& re : rows_[it->index()]) - dionysus::Chain::addto(row_sum, it->element(), Entry(re.element(), re.column->index(), re.column), field_, cmp_); - - if (row_sum.empty()) // Birth - { - columns_.emplace_back(rows_.size()); - auto before_end = columns_.end(); - --before_end; - columns_.back().chain.push_back(Entry(field_.id(), rows_.size(), before_end)); - rows_.emplace_back(); - rows_.back().push_back(columns_.back().chain.front()); - return std::make_tuple(unpaired(), Column()); - } else // Death - { - // Select front element in terms of comparison (rows are unsorted) - auto it = std::max_element(std::begin(row_sum), std::end(row_sum), entry_cmp); - - Entry first = std::move(*it); - row_sum.erase(it); - - for (auto& ce : row_sum) - { - FieldElement ay = field_.neg(field_.div(ce.element(), first.element())); - dionysus::Chain::addto(ce.column->chain, ay, first.column->chain, field_, - [this](const Entry& e1, const Entry& e2) - { return this->cmp_(e1.index(), e2.index()); }); - - for (auto& x : ce.column->chain) - { - x.column = ce.column; - rows_[x.index()].push_back(x); - } - } - Index pair = first.column->index(); - Column cocycle; - if (keep_cocycle) - cocycle = std::move(first.column->chain); - columns_.erase(first.column); - rows_.emplace_back(); // useless row; only present to make indices match - return std::make_tuple(pair, cocycle); - } -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h deleted file mode 100644 index e012b10539..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/common.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef DIONYSUS_EXAMPLES_COMMON_H -#define DIONYSUS_EXAMPLES_COMMON_H - -#include -#include - -template -void read_points(const std::string& infilename, PointContainer& points) -{ - typedef typename PointContainer::value_type Point; - - std::ifstream in(infilename.c_str()); - std::string line; - while(std::getline(in, line)) - { - if (line[0] == '#') continue; // comment line in the file - std::stringstream linestream(line); - double x; - points.push_back(Point()); - while (linestream >> x) - points.back().push_back(x); - } -} - -#endif // DIONYSUS_EXAMPLES_COMMON_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h deleted file mode 100644 index 04eb29a927..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/diagram.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef DIONYSUS_DIAGRAM_H -#define DIONYSUS_DIAGRAM_H - -#include -#include -#include - -namespace dionysus -{ - -template -class Diagram -{ - public: - using Value = Value_; - using Data = Data_; - struct Point: public std::pair - { - using Parent = std::pair; - - Point(Value b, Value d, Data dd): - Parent(b,d), data(dd) {} - - Value birth() const { return Parent::first; } - Value death() const { return Parent::second; } - - // FIXME: temporary hack - Value operator[](size_t i) const { if (i == 0) return birth(); return death(); } - - Data data; - }; - - using Points = std::vector; - using iterator = typename Points::iterator; - using const_iterator = typename Points::const_iterator; - using value_type = Point; - - public: - const_iterator begin() const { return points.begin(); } - const_iterator end() const { return points.end(); } - iterator begin() { return points.begin(); } - iterator end() { return points.end(); } - - const Point& operator[](size_t i) const { return points[i]; } - - size_t size() const { return points.size(); } - void push_back(const Point& p) { points.push_back(p); } - template - void emplace_back(Args&&... args) { points.emplace_back(std::forward(args)...); } - - private: - std::vector points; -}; - -namespace detail -{ - template - struct Diagrams - { - using Value = decltype(std::declval()(std::declval())); - using Data = decltype(std::declval()(std::declval())); - using type = std::vector>; - }; -} - -template -typename detail::Diagrams::type -init_diagrams(const ReducedMatrix& m, const Filtration& f, const GetValue& get_value, const GetData& get_data) -{ - using Result = typename detail::Diagrams::type; - - Result diagrams; - for (typename ReducedMatrix::Index i = 0; i < m.size(); ++i) - { - if (m.skip(i)) - continue; - - auto s = f[i]; - auto d = s.dimension(); - - while (d + 1 > diagrams.size()) - diagrams.emplace_back(); - - auto pair = m.pair(i); - if (pair == m.unpaired()) - { - auto birth = get_value(s); - using Value = decltype(birth); - Value death = std::numeric_limits::infinity(); - diagrams[d].emplace_back(birth, death, get_data(i)); - } else if (pair > i) // positive - { - auto birth = get_value(s); - auto death = get_value(f[pair]); - - // hack to work with coboundaries - auto pd = f[pair].dimension(); - if (pd < d) - { - d = pd; - std::swap(birth, death); - } - - if (birth != death) // skip diagonal - diagrams[d].emplace_back(birth, death, get_data(i)); - } // else negative: do nothing - } - - return diagrams; -} - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h deleted file mode 100644 index 29cac601af..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef DIONYSUS_DISTANCES_H -#define DIONYSUS_DISTANCES_H - -#include -#include - -namespace dionysus -{ - -/** - * Class: ExplicitDistances - * Stores the pairwise distances of Distances_ instance passed at construction. - * It's a protypical Distances template argument for the Rips complex. - */ -template -class ExplicitDistances -{ - public: - typedef Distances_ Distances; - typedef size_t IndexType; - typedef typename Distances::DistanceType DistanceType; - - ExplicitDistances(IndexType size): - size_(size), - distances_(size*(size + 1)/2 + size) {} - ExplicitDistances(const Distances& distances); - - DistanceType operator()(IndexType a, IndexType b) const; - DistanceType& operator()(IndexType a, IndexType b); - - size_t size() const { return size_; } - IndexType begin() const { return 0; } - IndexType end() const { return size(); } - - private: - std::vector distances_; - size_t size_; -}; - - -/** - * Class: PairwiseDistances - * Given a Container_ of points and a Distance_, it computes distances between elements - * in the container (given as instances of Index_ defaulted to unsigned) using the Distance_ functor. - * - * Container_ is assumed to be an std::vector. That simplifies a number of things. - */ -template -class PairwiseDistances -{ - public: - typedef Container_ Container; - typedef Distance_ Distance; - typedef Index_ IndexType; - typedef typename Distance::result_type DistanceType; - - - PairwiseDistances(const Container& container, - const Distance& distance = Distance()): - container_(container), distance_(distance) {} - - DistanceType operator()(IndexType a, IndexType b) const { return distance_(container_[a], container_[b]); } - - size_t size() const { return container_.size(); } - IndexType begin() const { return 0; } - IndexType end() const { return size(); } - - private: - const Container& container_; - Distance distance_; -}; - -template -struct L2Distance -{ - typedef Point_ Point; - typedef decltype(Point()[0] + 0) result_type; - - result_type operator()(const Point& p1, const Point& p2) const - { - result_type sum = 0; - for (size_t i = 0; i < p1.size(); ++i) - sum += (p1[i] - p2[i])*(p1[i] - p2[i]); - - return sqrt(sum); - } -}; - -} - -#include "distances.hpp" - -#endif // DIONYSUS_DISTANCES_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp deleted file mode 100644 index 9b1f20aa2b..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/distances.hpp +++ /dev/null @@ -1,30 +0,0 @@ -template -dionysus::ExplicitDistances:: -ExplicitDistances(const Distances& distances): - size_(distances.size()), distances_((distances.size() * (distances.size() + 1))/2) -{ - IndexType i = 0; - for (typename Distances::IndexType a = distances.begin(); a != distances.end(); ++a) - for (typename Distances::IndexType b = a; b != distances.end(); ++b) - { - distances_[i++] = distances(a,b); - } -} - -template -typename dionysus::ExplicitDistances::DistanceType -dionysus::ExplicitDistances:: -operator()(IndexType a, IndexType b) const -{ - if (a > b) std::swap(a,b); - return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; -} - -template -typename dionysus::ExplicitDistances::DistanceType& -dionysus::ExplicitDistances:: -operator()(IndexType a, IndexType b) -{ - if (a > b) std::swap(a,b); - return distances_[a*size_ - ((a*(a-1))/2) + (b-a)]; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h deleted file mode 100644 index 12bf86a4a2..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/dlog/progress.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef DLOG_PROGRESS_H -#define DLOG_PROGRESS_H - -#include -#include -#include -#include - -namespace dlog -{ - -struct progress -{ - progress(size_t total): - current_(0), total_(total) { show_progress(); } - - progress& operator++() { current_++; if (current_ * 100 / total_ > (current_ - 1) * 100 / total_) show_progress(); check_done(); return *this; } - progress& operator=(size_t cur) { current_ = cur; show_progress(); check_done(); return *this; } - progress& operator()(const std::string& s) { message_ = s; show_progress(); check_done(); return *this; } - template - progress& operator()(const T& x) { std::ostringstream oss; oss << x; return (*this)(oss.str()); } - - inline void show_progress() const; - void check_done() const { if (current_ >= total_) std::cout << "\n" << std::flush; } - - private: - size_t current_, total_; - std::string message_; -}; - -} - -void -dlog::progress:: -show_progress() const -{ - int barWidth = 70; - - std::cout << "["; - int pos = barWidth * current_ / total_; - for (int i = 0; i < barWidth; ++i) - { - if (i < pos) - std::cout << "="; - else if (i == pos) - std::cout << ">"; - else - std::cout << " "; - } - std::cout << "] " << std::setw(3) << current_ * 100 / total_ << "%"; - if (!message_.empty()) - std::cout << " (" << message_ << ")"; - std::cout << "\r"; - std::cout.flush(); -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h deleted file mode 100644 index 8972ae2b5a..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/q.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef DIONYSUS_Q_H -#define DIONYSUS_Q_H - -#include - -// TODO: eventually need to be able to adaptively switch to arbitrary precision arithmetic - -namespace dionysus -{ - -template -class Q -{ - public: - using BaseElement = Element_; - struct Element - { - BaseElement numerator, denominator; - - bool operator==(Element o) const { return numerator == o.numerator && denominator == o.denominator; } - bool operator!=(Element o) const { return !((*this) == o); } - - friend - std::ostream& operator<<(std::ostream& out, Element e) { out << e.numerator << '/' << e.denominator; return out; } - }; - - Element id() const { return { 1,1 }; } - Element zero() const { return { 0,1 }; } - Element init(BaseElement a) const { return { a,1 }; } - - Element neg(Element a) const { return { -a.numerator, a.denominator }; } - Element add(Element a, Element b) const { Element x { a.numerator*b.denominator + b.numerator*a.denominator, a.denominator*b.denominator }; normalize(x); return x; } - - Element inv(Element a) const { return { a.denominator, a.numerator }; } - Element mul(Element a, Element b) const { Element x { a.numerator*b.numerator, a.denominator*b.denominator }; normalize(x); return x; } - Element div(Element a, Element b) const { return mul(a, inv(b)); } - - bool is_zero(Element a) const { return a.numerator == 0; } - - BaseElement numerator(const Element& x) const { return x.numerator; } - BaseElement denominator(const Element& x) const { return x.denominator; } - - static void normalize(Element& x) - { - BaseElement q = gcd(abs(x.numerator), abs(x.denominator)); - x.numerator /= q; - x.denominator /= q; - if (x.denominator < 0) - { - x.numerator = -x.numerator; - x.denominator = -x.denominator; - } - } - - static BaseElement abs(BaseElement x) { if (x < 0) return -x; return x; } - static BaseElement gcd(BaseElement a, BaseElement b) { if (b < a) return gcd(b,a); while (a != 0) { b %= a; std::swap(a,b); } return b; } - - static bool is_prime(BaseElement x) { return false; } // Ok, since is_prime is only used as a shortcut -}; - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h deleted file mode 100644 index 6317ace6df..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/z2.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef DIONYSUS_Z2_H -#define DIONYSUS_Z2_H - -namespace dionysus -{ - -class Z2Field -{ - public: - typedef short Element; - - Z2Field() {} - - static Element id() { return 1; } - static Element zero() { return 0; } - static Element init(int a) { return (a % 2 + 2) % 2; } - - Element neg(Element a) const { return 2 - a; } - Element add(Element a, Element b) const { return (a+b) % 2; } - - Element inv(Element a) const { return a; } - Element mul(Element a, Element b) const { return a*b; } - Element div(Element a, Element b) const { return a; } - - bool is_zero(Element a) const { return a == 0; } -}; - -} - -#endif - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h deleted file mode 100644 index c70c61cc87..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/fields/zp.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef DIONYSUS_ZP_H -#define DIONYSUS_ZP_H - -#include - -namespace dionysus -{ - -template -class ZpField -{ - public: - typedef Element_ Element; - - ZpField(Element p); - ZpField(const ZpField& other) = default; - ZpField(ZpField&& other) = default; - - Element id() const { return 1; } - Element zero() const { return 0; } - Element init(int a) const { return (a % p_ + p_) % p_; } - - Element neg(Element a) const { return p_ - a; } - Element add(Element a, Element b) const { return (a+b) % p_; } - - Element inv(Element a) const { while (a < 0) a += p_; return inverses_[a]; } - Element mul(Element a, Element b) const { return (a*b) % p_; } - Element div(Element a, Element b) const { return mul(a, inv(b)); } - - bool is_zero(Element a) const { return (a % p_) == 0; } - - Element prime() const { return p_; } - - private: - Element p_; - std::vector inverses_; -}; - -template -ZpField:: -ZpField(Element p): - p_(p), inverses_(p_) -{ - for (Element i = 1; i < p_; ++i) - for (Element j = 1; j < p_; ++j) - if (mul(i,j) == 1) - { - inverses_[i] = j; - break; - } -} - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h deleted file mode 100644 index caf871cdfb..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/filtration.h +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef DIONYSUS_FILTRATION_H -#define DIONYSUS_FILTRATION_H - -#include -#include - -#include -#include -#include -#include - -namespace b = boost; -namespace bmi = boost::multi_index; - -namespace dionysus -{ - -// Filtration stores a filtered cell complex as boost::multi_index_container<...>. -// It allows for bidirectional translation between a cell and its index. -template>, - bool checked_index = false> -class Filtration -{ - public: - struct order {}; - - typedef Cell_ Cell; - typedef CellLookupIndex_ CellLookupIndex; - - typedef b::multi_index_container> - >> Container; - typedef typename Container::value_type value_type; - - typedef typename Container::template nth_index<0>::type Complex; - typedef typename Container::template nth_index<1>::type Order; - typedef typename Order::const_iterator OrderConstIterator; - typedef typename Order::iterator OrderIterator; - - - public: - Filtration() = default; - Filtration(Filtration&& other) = default; - Filtration& operator=(Filtration&& other) = default; - - Filtration(const std::initializer_list& cells): - Filtration(std::begin(cells), std::end(cells)) {} - - template - Filtration(Iterator bg, Iterator end): - cells_(bg, end) {} - - template - Filtration(const CellRange& cells): - Filtration(std::begin(cells), std::end(cells)) {} - - // Lookup - const Cell& operator[](size_t i) const { return cells_.template get()[i]; } - OrderConstIterator iterator(const Cell& s) const { return bmi::project(cells_, cells_.find(s)); } - size_t index(const Cell& s) const; - bool contains(const Cell& s) const { return cells_.find(s) != cells_.end(); } - - void push_back(const Cell& s) { cells_.template get().push_back(s); } - void push_back(Cell&& s) { cells_.template get().push_back(s); } - - void replace(size_t i, const Cell& s) { cells_.template get().replace(begin() + i, s); } - - // return index of the cell, adding it, if necessary - size_t add(const Cell& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(s); return i; } - size_t add(Cell&& s) { size_t i = (iterator(s) - begin()); if (i == size()) emplace_back(std::move(s)); return i; } - - template - void emplace_back(Args&&... args) { cells_.template get().emplace_back(std::forward(args)...); } - - template> - void sort(const Cmp& cmp = Cmp()) { cells_.template get().sort(cmp); } - - void rearrange(const std::vector& indices); - - OrderConstIterator begin() const { return cells_.template get().begin(); } - OrderConstIterator end() const { return cells_.template get().end(); } - OrderIterator begin() { return cells_.template get().begin(); } - OrderIterator end() { return cells_.template get().end(); } - size_t size() const { return cells_.size(); } - void clear() { return Container().swap(cells_); } - - Cell& back() { return const_cast(cells_.template get().back()); } - const Cell& back() const { return cells_.template get().back(); } - - private: - Container cells_; -}; - -} - -template -size_t -dionysus::Filtration:: -index(const Cell& s) const -{ - auto it = iterator(s); - if (checked_index && it == end()) - { - std::ostringstream oss; - oss << "Trying to access non-existent cell: " << s; - throw std::runtime_error(oss.str()); - } - return it - begin(); -} - -template -void -dionysus::Filtration:: -rearrange(const std::vector& indices) -{ - std::vector> references; references.reserve(indices.size()); - for (size_t i : indices) - references.push_back(std::cref((*this)[i])); - cells_.template get().rearrange(references.begin()); -} - - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h deleted file mode 100644 index 7f7ba83318..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef DIONYSUS_FORMAT_H -#define DIONYSUS_FORMAT_H - -#define FMT_HEADER_ONLY - -#include "format/format.h" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc deleted file mode 100644 index a01e272fd7..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.cc +++ /dev/null @@ -1,1156 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2014, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "format.h" - -#include - -#include -#include -#include -#include -#include - -#ifdef _WIN32 -# ifdef __MINGW32__ -# include -# endif -# include -#endif - -using fmt::internal::Arg; - -// Check if exceptions are disabled. -#if __GNUC__ && !__EXCEPTIONS -# define FMT_EXCEPTIONS 0 -#endif -#if _MSC_VER && !_HAS_EXCEPTIONS -# define FMT_EXCEPTIONS 0 -#endif -#ifndef FMT_EXCEPTIONS -# define FMT_EXCEPTIONS 1 -#endif - -#if FMT_EXCEPTIONS -# define FMT_TRY try -# define FMT_CATCH(x) catch (x) -#else -# define FMT_TRY if (true) -# define FMT_CATCH(x) if (false) -#endif - -#ifndef FMT_THROW -# if FMT_EXCEPTIONS -# define FMT_THROW(x) throw x -# define FMT_RETURN_AFTER_THROW(x) -# else -# define FMT_THROW(x) assert(false) -# define FMT_RETURN_AFTER_THROW(x) return x -# endif -#endif - -#ifdef FMT_HEADER_ONLY -# define FMT_FUNC inline -#else -# define FMT_FUNC -#endif - -#if _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4127) // conditional expression is constant -# pragma warning(disable: 4702) // unreachable code -#endif - -namespace { - -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else // _MSC_VER -inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { - va_list args; - va_start(args, format); - int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); - va_end(args); - return result; -} -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - -// Checks if a value fits in int - used to avoid warnings about comparing -// signed and unsigned integers. -template -struct IntChecker { - template - static bool fits_in_int(T value) { - unsigned max = INT_MAX; - return value <= max; - } -}; - -template <> -struct IntChecker { - template - static bool fits_in_int(T value) { - return value >= INT_MIN && value <= INT_MAX; - } -}; - -const char RESET_COLOR[] = "\x1b[0m"; - -typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef); - -// Portable thread-safe version of strerror. -// Sets buffer to point to a string describing the error code. -// This can be either a pointer to a string stored in buffer, -// or a pointer to some static immutable string. -// Returns one of the following values: -// 0 - success -// ERANGE - buffer is not large enough to store the error message -// other - failure -// Buffer should be at least of size 1. -int safe_strerror( - int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { - assert(buffer != 0 && buffer_size != 0); - int result = 0; -#if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) || __ANDROID__ - // XSI-compliant version of strerror_r. - result = strerror_r(error_code, buffer, buffer_size); - if (result != 0) - result = errno; -#elif _GNU_SOURCE - // GNU-specific version of strerror_r. - char *message = strerror_r(error_code, buffer, buffer_size); - // If the buffer is full then the message is probably truncated. - if (message == buffer && strlen(buffer) == buffer_size - 1) - result = ERANGE; - buffer = message; -#elif __MINGW32__ - errno = 0; - (void)buffer_size; - buffer = strerror(error_code); - result = errno; -#elif _WIN32 - result = strerror_s(buffer, buffer_size, error_code); - // If the buffer is full then the message is probably truncated. - if (result == 0 && std::strlen(buffer) == buffer_size - 1) - result = ERANGE; -#else - result = strerror_r(error_code, buffer, buffer_size); - if (result == -1) - result = errno; // glibc versions before 2.13 return result in errno. -#endif - return result; -} - -void format_error_code(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT { - // Report error code making sure that the output fits into - // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential - // bad_alloc. - out.clear(); - static const char SEP[] = ": "; - static const char ERR[] = "error "; - fmt::internal::IntTraits::MainType ec_value = error_code; - // Subtract 2 to account for terminating null characters in SEP and ERR. - std::size_t error_code_size = - sizeof(SEP) + sizeof(ERR) + fmt::internal::count_digits(ec_value) - 2; - if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size) - out << message << SEP; - out << ERR << error_code; - assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE); -} - -void report_error(FormatFunc func, - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - fmt::MemoryWriter full_message; - func(full_message, error_code, message); - // Use Writer::data instead of Writer::c_str to avoid potential memory - // allocation. - std::fwrite(full_message.data(), full_message.size(), 1, stderr); - std::fputc('\n', stderr); -} - -// IsZeroInt::visit(arg) returns true iff arg is a zero integer. -class IsZeroInt : public fmt::internal::ArgVisitor { - public: - template - bool visit_any_int(T value) { return value == 0; } -}; - -// Parses an unsigned integer advancing s to the end of the parsed input. -// This function assumes that the first character of s is a digit. -template -int parse_nonnegative_int(const Char *&s) { - assert('0' <= *s && *s <= '9'); - unsigned value = 0; - do { - unsigned new_value = value * 10 + (*s++ - '0'); - // Check if value wrapped around. - if (new_value < value) { - value = UINT_MAX; - break; - } - value = new_value; - } while ('0' <= *s && *s <= '9'); - if (value > INT_MAX) - FMT_THROW(fmt::FormatError("number is too big")); - return value; -} - -inline void require_numeric_argument(const Arg &arg, char spec) { - if (arg.type > Arg::LAST_NUMERIC_TYPE) { - std::string message = - fmt::format("format specifier '{}' requires numeric argument", spec); - FMT_THROW(fmt::FormatError(message)); - } -} - -template -void check_sign(const Char *&s, const Arg &arg) { - char sign = static_cast(*s); - require_numeric_argument(arg, sign); - if (arg.type == Arg::UINT || arg.type == Arg::ULONG_LONG) { - FMT_THROW(fmt::FormatError(fmt::format( - "format specifier '{}' requires signed argument", sign))); - } - ++s; -} - -// Checks if an argument is a valid printf width specifier and sets -// left alignment if it is negative. -class WidthHandler : public fmt::internal::ArgVisitor { - private: - fmt::FormatSpec &spec_; - - FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); - - public: - explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {} - - unsigned visit_unhandled_arg() { - FMT_THROW(fmt::FormatError("width is not integer")); - FMT_RETURN_AFTER_THROW(0); - } - - template - unsigned visit_any_int(T value) { - typedef typename fmt::internal::IntTraits::MainType UnsignedType; - UnsignedType width = value; - if (fmt::internal::is_negative(value)) { - spec_.align_ = fmt::ALIGN_LEFT; - width = 0 - width; - } - if (width > INT_MAX) - FMT_THROW(fmt::FormatError("number is too big")); - return static_cast(width); - } -}; - -class PrecisionHandler : - public fmt::internal::ArgVisitor { - public: - unsigned visit_unhandled_arg() { - FMT_THROW(fmt::FormatError("precision is not integer")); - FMT_RETURN_AFTER_THROW(0); - } - - template - int visit_any_int(T value) { - if (!IntChecker::is_signed>::fits_in_int(value)) - FMT_THROW(fmt::FormatError("number is too big")); - return static_cast(value); - } -}; - -// Converts an integer argument to an integral type T for printf. -template -class ArgConverter : public fmt::internal::ArgVisitor, void> { - private: - fmt::internal::Arg &arg_; - wchar_t type_; - - FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); - - public: - ArgConverter(fmt::internal::Arg &arg, wchar_t type) - : arg_(arg), type_(type) {} - - template - void visit_any_int(U value) { - bool is_signed = type_ == 'd' || type_ == 'i'; - using fmt::internal::Arg; - if (sizeof(T) <= sizeof(int)) { - // Extra casts are used to silence warnings. - if (is_signed) { - arg_.type = Arg::INT; - arg_.int_value = static_cast(static_cast(value)); - } else { - arg_.type = Arg::UINT; - arg_.uint_value = static_cast( - static_cast::Type>(value)); - } - } else { - if (is_signed) { - arg_.type = Arg::LONG_LONG; - arg_.long_long_value = - static_cast::Type>(value); - } else { - arg_.type = Arg::ULONG_LONG; - arg_.ulong_long_value = - static_cast::Type>(value); - } - } - } -}; - -// Converts an integer argument to char for printf. -class CharConverter : public fmt::internal::ArgVisitor { - private: - fmt::internal::Arg &arg_; - - FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); - - public: - explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {} - - template - void visit_any_int(T value) { - arg_.type = Arg::CHAR; - arg_.int_value = static_cast(value); - } -}; - -// This function template is used to prevent compile errors when handling -// incompatible string arguments, e.g. handling a wide string in a narrow -// string formatter. -template -Arg::StringValue ignore_incompatible_str(Arg::StringValue); - -template <> -inline Arg::StringValue ignore_incompatible_str( - Arg::StringValue) { return Arg::StringValue(); } - -template <> -inline Arg::StringValue ignore_incompatible_str( - Arg::StringValue s) { return s; } -} // namespace - -FMT_FUNC void fmt::SystemError::init( - int err_code, StringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - internal::format_system_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -template -int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, value) : - FMT_SNPRINTF(buffer, size, format, precision, value); - } - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, width, value) : - FMT_SNPRINTF(buffer, size, format, width, precision, value); -} - -template -int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - swprintf(buffer, size, format, value) : - swprintf(buffer, size, format, precision, value); - } - return precision < 0 ? - swprintf(buffer, size, format, width, value) : - swprintf(buffer, size, format, width, precision, value); -} - -template -const char fmt::internal::BasicData::DIGITS[] = - "0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"; - -#define FMT_POWERS_OF_10(factor) \ - factor * 10, \ - factor * 100, \ - factor * 1000, \ - factor * 10000, \ - factor * 100000, \ - factor * 1000000, \ - factor * 10000000, \ - factor * 100000000, \ - factor * 1000000000 - -template -const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = { - 0, FMT_POWERS_OF_10(1) -}; - -template -const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = { - 0, - FMT_POWERS_OF_10(1), - FMT_POWERS_OF_10(fmt::ULongLong(1000000000)), - // Multiply several constants instead of using a single long long constant - // to avoid warnings about C++98 not supporting long long. - fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10 -}; - -FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) { - if (std::isprint(static_cast(code))) { - FMT_THROW(fmt::FormatError( - fmt::format("unknown format code '{}' for {}", code, type))); - } - FMT_THROW(fmt::FormatError( - fmt::format("unknown format code '\\x{:02x}' for {}", - static_cast(code), type))); -} - -#ifdef _WIN32 - -FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) { - int length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0); - static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); - buffer_.resize(length); - length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length); - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); -} - -FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) { - if (int error_code = convert(s)) { - FMT_THROW(WindowsError(error_code, - "cannot convert string from UTF-16 to UTF-8")); - } -} - -FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { - int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0); - if (length == 0) - return GetLastError(); - buffer_.resize(length); - length = WideCharToMultiByte( - CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0); - if (length == 0) - return GetLastError(); - return 0; -} - -FMT_FUNC void fmt::WindowsError::init( - int err_code, StringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - internal::format_windows_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -#endif - -FMT_FUNC void fmt::internal::format_system_error( - fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT { - FMT_TRY { - MemoryBuffer buffer; - buffer.resize(INLINE_BUFFER_SIZE); - for (;;) { - char *system_message = &buffer[0]; - int result = safe_strerror(error_code, system_message, buffer.size()); - if (result == 0) { - out << message << ": " << system_message; - return; - } - if (result != ERANGE) - break; // Can't get error message, report error code instead. - buffer.resize(buffer.size() * 2); - } - } FMT_CATCH(...) {} - format_error_code(out, error_code, message); -} - -#ifdef _WIN32 -FMT_FUNC void fmt::internal::format_windows_error( - fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT { - class String { - private: - LPWSTR str_; - - public: - String() : str_() {} - ~String() { LocalFree(str_); } - LPWSTR *ptr() { return &str_; } - LPCWSTR c_str() const { return str_; } - }; - FMT_TRY { - String system_message; - if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, - error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(system_message.ptr()), 0, 0)) { - UTF16ToUTF8 utf8_message; - if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) { - out << message << ": " << utf8_message; - return; - } - } - } FMT_CATCH(...) {} - format_error_code(out, error_code, message); -} -#endif - -// An argument formatter. -template -class fmt::internal::ArgFormatter : - public fmt::internal::ArgVisitor, void> { - private: - fmt::BasicFormatter &formatter_; - fmt::BasicWriter &writer_; - fmt::FormatSpec &spec_; - const Char *format_; - - FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatter); - - public: - ArgFormatter( - fmt::BasicFormatter &f,fmt::FormatSpec &s, const Char *fmt) - : formatter_(f), writer_(f.writer()), spec_(s), format_(fmt) {} - - template - void visit_any_int(T value) { writer_.write_int(value, spec_); } - - template - void visit_any_double(T value) { writer_.write_double(value, spec_); } - - void visit_char(int value) { - if (spec_.type_ && spec_.type_ != 'c') { - spec_.flags_ |= CHAR_FLAG; - writer_.write_int(value, spec_); - return; - } - if (spec_.align_ == ALIGN_NUMERIC || spec_.flags_ != 0) - FMT_THROW(FormatError("invalid format specifier for char")); - typedef typename fmt::BasicWriter::CharPtr CharPtr; - Char fill = static_cast(spec_.fill()); - if (spec_.precision_ == 0) { - std::fill_n(writer_.grow_buffer(spec_.width_), spec_.width_, fill); - return; - } - CharPtr out = CharPtr(); - if (spec_.width_ > 1) { - out = writer_.grow_buffer(spec_.width_); - if (spec_.align_ == fmt::ALIGN_RIGHT) { - std::fill_n(out, spec_.width_ - 1, fill); - out += spec_.width_ - 1; - } else if (spec_.align_ == fmt::ALIGN_CENTER) { - out = writer_.fill_padding(out, spec_.width_, 1, fill); - } else { - std::fill_n(out + 1, spec_.width_ - 1, fill); - } - } else { - out = writer_.grow_buffer(1); - } - *out = static_cast(value); - } - - void visit_string(Arg::StringValue value) { - writer_.write_str(value, spec_); - } - void visit_wstring(Arg::StringValue value) { - writer_.write_str(ignore_incompatible_str(value), spec_); - } - - void visit_pointer(const void *value) { - if (spec_.type_ && spec_.type_ != 'p') - fmt::internal::report_unknown_type(spec_.type_, "pointer"); - spec_.flags_ = fmt::HASH_FLAG; - spec_.type_ = 'x'; - writer_.write_int(reinterpret_cast(value), spec_); - } - - void visit_custom(Arg::CustomValue c) { - c.format(&formatter_, c.value, &format_); - } -}; - -template -template -void fmt::BasicWriter::write_str( - const Arg::StringValue &s, const FormatSpec &spec) { - // Check if StrChar is convertible to Char. - internal::CharTraits::convert(StrChar()); - if (spec.type_ && spec.type_ != 's') - internal::report_unknown_type(spec.type_, "string"); - const StrChar *str_value = s.value; - std::size_t str_size = s.size; - if (str_size == 0) { - if (!str_value) - FMT_THROW(FormatError("string pointer is null")); - if (*str_value) - str_size = std::char_traits::length(str_value); - } - std::size_t precision = spec.precision_; - if (spec.precision_ >= 0 && precision < str_size) - str_size = spec.precision_; - write_str(str_value, str_size, spec); -} - -template -inline Arg fmt::BasicFormatter::parse_arg_index(const Char *&s) { - const char *error = 0; - Arg arg = *s < '0' || *s > '9' ? - next_arg(error) : get_arg(parse_nonnegative_int(s), error); - if (error) { - FMT_THROW(FormatError( - *s != '}' && *s != ':' ? "invalid format string" : error)); - } - return arg; -} - -FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( - unsigned arg_index, const char *&error) { - Arg arg = args_[arg_index]; - if (arg.type == Arg::NONE) - error = "argument index out of range"; - return arg; -} - -inline Arg fmt::internal::FormatterBase::next_arg(const char *&error) { - if (next_arg_index_ >= 0) - return do_get_arg(next_arg_index_++, error); - error = "cannot switch from manual to automatic argument indexing"; - return Arg(); -} - -inline Arg fmt::internal::FormatterBase::get_arg( - unsigned arg_index, const char *&error) { - if (next_arg_index_ <= 0) { - next_arg_index_ = -1; - return do_get_arg(arg_index, error); - } - error = "cannot switch from automatic to manual argument indexing"; - return Arg(); -} - -template -void fmt::internal::PrintfFormatter::parse_flags( - FormatSpec &spec, const Char *&s) { - for (;;) { - switch (*s++) { - case '-': - spec.align_ = ALIGN_LEFT; - break; - case '+': - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '0': - spec.fill_ = '0'; - break; - case ' ': - spec.flags_ |= SIGN_FLAG; - break; - case '#': - spec.flags_ |= HASH_FLAG; - break; - default: - --s; - return; - } - } -} - -template -Arg fmt::internal::PrintfFormatter::get_arg( - const Char *s, unsigned arg_index) { - const char *error = 0; - Arg arg = arg_index == UINT_MAX ? - next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); - if (error) - FMT_THROW(FormatError(!*s ? "invalid format string" : error)); - return arg; -} - -template -unsigned fmt::internal::PrintfFormatter::parse_header( - const Char *&s, FormatSpec &spec) { - unsigned arg_index = UINT_MAX; - Char c = *s; - if (c >= '0' && c <= '9') { - // Parse an argument index (if followed by '$') or a width possibly - // preceded with '0' flag(s). - unsigned value = parse_nonnegative_int(s); - if (*s == '$') { // value is an argument index - ++s; - arg_index = value; - } else { - if (c == '0') - spec.fill_ = '0'; - if (value != 0) { - // Nonzero value means that we parsed width and don't need to - // parse it or flags again, so return now. - spec.width_ = value; - return arg_index; - } - } - } - parse_flags(spec, s); - // Parse width. - if (*s >= '0' && *s <= '9') { - spec.width_ = parse_nonnegative_int(s); - } else if (*s == '*') { - ++s; - spec.width_ = WidthHandler(spec).visit(get_arg(s)); - } - return arg_index; -} - -template -void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, BasicStringRef format_str, - const ArgList &args) { - const Char *start = format_str.c_str(); - set_args(args); - const Char *s = start; - while (*s) { - Char c = *s++; - if (c != '%') continue; - if (*s == c) { - write(writer, start, s); - start = ++s; - continue; - } - write(writer, start, s - 1); - - FormatSpec spec; - spec.align_ = ALIGN_RIGHT; - - // Parse argument index, flags and width. - unsigned arg_index = parse_header(s, spec); - - // Parse precision. - if (*s == '.') { - ++s; - if ('0' <= *s && *s <= '9') { - spec.precision_ = parse_nonnegative_int(s); - } else if (*s == '*') { - ++s; - spec.precision_ = PrecisionHandler().visit(get_arg(s)); - } - } - - Arg arg = get_arg(s, arg_index); - if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) - spec.flags_ &= ~HASH_FLAG; - if (spec.fill_ == '0') { - if (arg.type <= Arg::LAST_NUMERIC_TYPE) - spec.align_ = ALIGN_NUMERIC; - else - spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. - } - - // Parse length and convert the argument to the required type. - switch (*s++) { - case 'h': - if (*s == 'h') - ArgConverter(arg, *++s).visit(arg); - else - ArgConverter(arg, *s).visit(arg); - break; - case 'l': - if (*s == 'l') - ArgConverter(arg, *++s).visit(arg); - else - ArgConverter(arg, *s).visit(arg); - break; - case 'j': - ArgConverter(arg, *s).visit(arg); - break; - case 'z': - ArgConverter(arg, *s).visit(arg); - break; - case 't': - ArgConverter(arg, *s).visit(arg); - break; - case 'L': - // printf produces garbage when 'L' is omitted for long double, no - // need to do the same. - break; - default: - --s; - ArgConverter(arg, *s).visit(arg); - } - - // Parse type. - if (!*s) - FMT_THROW(FormatError("invalid format string")); - spec.type_ = static_cast(*s++); - if (arg.type <= Arg::LAST_INTEGER_TYPE) { - // Normalize type. - switch (spec.type_) { - case 'i': case 'u': - spec.type_ = 'd'; - break; - case 'c': - // TODO: handle wchar_t - CharConverter(arg).visit(arg); - break; - } - } - - start = s; - - // Format argument. - switch (arg.type) { - case Arg::INT: - writer.write_int(arg.int_value, spec); - break; - case Arg::UINT: - writer.write_int(arg.uint_value, spec); - break; - case Arg::LONG_LONG: - writer.write_int(arg.long_long_value, spec); - break; - case Arg::ULONG_LONG: - writer.write_int(arg.ulong_long_value, spec); - break; - case Arg::CHAR: { - if (spec.type_ && spec.type_ != 'c') - writer.write_int(arg.int_value, spec); - typedef typename BasicWriter::CharPtr CharPtr; - CharPtr out = CharPtr(); - if (spec.width_ > 1) { - Char fill = ' '; - out = writer.grow_buffer(spec.width_); - if (spec.align_ != ALIGN_LEFT) { - std::fill_n(out, spec.width_ - 1, fill); - out += spec.width_ - 1; - } else { - std::fill_n(out + 1, spec.width_ - 1, fill); - } - } else { - out = writer.grow_buffer(1); - } - *out = static_cast(arg.int_value); - break; - } - case Arg::DOUBLE: - writer.write_double(arg.double_value, spec); - break; - case Arg::LONG_DOUBLE: - writer.write_double(arg.long_double_value, spec); - break; - case Arg::CSTRING: - arg.string.size = 0; - writer.write_str(arg.string, spec); - break; - case Arg::STRING: - writer.write_str(arg.string, spec); - break; - case Arg::WSTRING: - writer.write_str(ignore_incompatible_str(arg.wstring), spec); - break; - case Arg::POINTER: - if (spec.type_ && spec.type_ != 'p') - internal::report_unknown_type(spec.type_, "pointer"); - spec.flags_= HASH_FLAG; - spec.type_ = 'x'; - writer.write_int(reinterpret_cast(arg.pointer), spec); - break; - case Arg::CUSTOM: { - if (spec.type_) - internal::report_unknown_type(spec.type_, "object"); - const void *str_format = "s"; - arg.custom.format(&writer, arg.custom.value, &str_format); - break; - } - default: - assert(false); - break; - } - } - write(writer, start, s); -} - -template -const Char *fmt::BasicFormatter::format( - const Char *&format_str, const Arg &arg) { - const Char *s = format_str; - FormatSpec spec; - if (*s == ':') { - if (arg.type == Arg::CUSTOM) { - arg.custom.format(this, arg.custom.value, &s); - return s; - } - ++s; - // Parse fill and alignment. - if (Char c = *s) { - const Char *p = s + 1; - spec.align_ = ALIGN_DEFAULT; - do { - switch (*p) { - case '<': - spec.align_ = ALIGN_LEFT; - break; - case '>': - spec.align_ = ALIGN_RIGHT; - break; - case '=': - spec.align_ = ALIGN_NUMERIC; - break; - case '^': - spec.align_ = ALIGN_CENTER; - break; - } - if (spec.align_ != ALIGN_DEFAULT) { - if (p != s) { - if (c == '}') break; - if (c == '{') - FMT_THROW(FormatError("invalid fill character '{'")); - s += 2; - spec.fill_ = c; - } else ++s; - if (spec.align_ == ALIGN_NUMERIC) - require_numeric_argument(arg, '='); - break; - } - } while (--p >= s); - } - - // Parse sign. - switch (*s) { - case '+': - check_sign(s, arg); - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '-': - check_sign(s, arg); - spec.flags_ |= MINUS_FLAG; - break; - case ' ': - check_sign(s, arg); - spec.flags_ |= SIGN_FLAG; - break; - } - - if (*s == '#') { - require_numeric_argument(arg, '#'); - spec.flags_ |= HASH_FLAG; - ++s; - } - - // Parse width and zero flag. - if ('0' <= *s && *s <= '9') { - if (*s == '0') { - require_numeric_argument(arg, '0'); - spec.align_ = ALIGN_NUMERIC; - spec.fill_ = '0'; - } - // Zero may be parsed again as a part of the width, but it is simpler - // and more efficient than checking if the next char is a digit. - spec.width_ = parse_nonnegative_int(s); - } - - // Parse precision. - if (*s == '.') { - ++s; - spec.precision_ = 0; - if ('0' <= *s && *s <= '9') { - spec.precision_ = parse_nonnegative_int(s); - } else if (*s == '{') { - ++s; - const Arg &precision_arg = parse_arg_index(s); - if (*s++ != '}') - FMT_THROW(FormatError("invalid format string")); - ULongLong value = 0; - switch (precision_arg.type) { - case Arg::INT: - if (precision_arg.int_value < 0) - FMT_THROW(FormatError("negative precision")); - value = precision_arg.int_value; - break; - case Arg::UINT: - value = precision_arg.uint_value; - break; - case Arg::LONG_LONG: - if (precision_arg.long_long_value < 0) - FMT_THROW(FormatError("negative precision")); - value = precision_arg.long_long_value; - break; - case Arg::ULONG_LONG: - value = precision_arg.ulong_long_value; - break; - default: - FMT_THROW(FormatError("precision is not integer")); - } - if (value > INT_MAX) - FMT_THROW(FormatError("number is too big")); - spec.precision_ = static_cast(value); - } else { - FMT_THROW(FormatError("missing precision specifier")); - } - if (arg.type < Arg::LAST_INTEGER_TYPE || arg.type == Arg::POINTER) { - FMT_THROW(FormatError( - fmt::format("precision not allowed in {} format specifier", - arg.type == Arg::POINTER ? "pointer" : "integer"))); - } - } - - // Parse type. - if (*s != '}' && *s) - spec.type_ = static_cast(*s++); - } - - if (*s++ != '}') - FMT_THROW(FormatError("missing '}' in format string")); - start_ = s; - - // Format argument. - internal::ArgFormatter(*this, spec, s - 1).visit(arg); - return s; -} - -template -void fmt::BasicFormatter::format( - BasicStringRef format_str, const ArgList &args) { - const Char *s = start_ = format_str.c_str(); - set_args(args); - while (*s) { - Char c = *s++; - if (c != '{' && c != '}') continue; - if (*s == c) { - write(writer_, start_, s); - start_ = ++s; - continue; - } - if (c == '}') - FMT_THROW(FormatError("unmatched '}' in format string")); - write(writer_, start_, s - 1); - Arg arg = parse_arg_index(s); - s = format(s, arg); - } - write(writer_, start_, s); -} - -FMT_FUNC void fmt::report_system_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - report_error(internal::format_system_error, error_code, message); -} - -#ifdef _WIN32 -FMT_FUNC void fmt::report_windows_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT { - report_error(internal::format_windows_error, error_code, message); -} -#endif - -FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - std::fwrite(w.data(), 1, w.size(), f); -} - -FMT_FUNC void fmt::print(StringRef format_str, ArgList args) { - print(stdout, format_str, args); -} - -FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - os.write(w.data(), w.size()); -} - -FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) { - char escape[] = "\x1b[30m"; - escape[3] = '0' + static_cast(c); - std::fputs(escape, stdout); - print(format, args); - std::fputs(RESET_COLOR, stdout); -} - -FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) { - MemoryWriter w; - printf(w, format, args); - std::size_t size = w.size(); - return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); -} - -// Explicit instantiations for char. - -template const char *fmt::BasicFormatter::format( - const char *&format_str, const fmt::internal::Arg &arg); - -template void fmt::BasicFormatter::format( - BasicStringRef format, const ArgList &args); - -template void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, BasicStringRef format, const ArgList &args); - -template int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, double value); - -template int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, long double value); - -// Explicit instantiations for wchar_t. - -template const wchar_t *fmt::BasicFormatter::format( - const wchar_t *&format_str, const fmt::internal::Arg &arg); - -template void fmt::BasicFormatter::format( - BasicStringRef format, const ArgList &args); - -template void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, BasicStringRef format, - const ArgList &args); - -template int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, double value); - -template int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, long double value); - -#if _MSC_VER -# pragma warning(pop) -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h deleted file mode 100644 index 03ed685383..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/format/format.h +++ /dev/null @@ -1,2546 +0,0 @@ -/* - Formatting library for C++ - - Copyright (c) 2012 - 2014, Victor Zverovich - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef FMT_FORMAT_H_ -#define FMT_FORMAT_H_ - -#include - -#include -#include -#include // for std::ptrdiff_t -#include -#include -#include -#include -#include -#include - -#if _SECURE_SCL -# include -#endif - -#ifdef _MSC_VER -# include // _BitScanReverse, _BitScanReverse64 - -namespace fmt { -namespace internal { -# pragma intrinsic(_BitScanReverse) -inline uint32_t clz(uint32_t x) { - unsigned long r = 0; - _BitScanReverse(&r, x); - return 31 - r; -} -# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) -inline uint32_t clzll(uint64_t x) { - unsigned long r = 0; -# ifdef _WIN64 -# pragma intrinsic(_BitScanReverse64) - _BitScanReverse64(&r, x); -# else - // Scan the high 32 bits. - if (_BitScanReverse(&r, static_cast(x >> 32))) - return 63 - (r + 32); - - // Scan the low 32 bits. - _BitScanReverse(&r, static_cast(x)); -# endif - return 63 - r; -} -# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) -} -} -#endif - -#ifdef __GNUC__ -# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) -# define FMT_GCC_EXTENSION __extension__ -# if FMT_GCC_VERSION >= 406 -# pragma GCC diagnostic push -// Disable the warning about "long long" which is sometimes reported even -// when using __extension__. -# pragma GCC diagnostic ignored "-Wlong-long" -// Disable the warning about declaration shadowing because it affects too -// many valid cases. -# pragma GCC diagnostic ignored "-Wshadow" -# endif -# if __cplusplus >= 201103L || defined __GXX_EXPERIMENTAL_CXX0X__ -# define FMT_HAS_GXX_CXX11 1 -# endif -#else -# define FMT_GCC_EXTENSION -#endif - -#ifdef __clang__ -# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -#endif - -#ifdef __GNUC_LIBSTD__ -# define FMT_GNUC_LIBSTD_VERSION (__GNUC_LIBSTD__ * 100 + __GNUC_LIBSTD_MINOR__) -#endif - -#ifdef __has_feature -# define FMT_HAS_FEATURE(x) __has_feature(x) -#else -# define FMT_HAS_FEATURE(x) 0 -#endif - -#ifdef __has_builtin -# define FMT_HAS_BUILTIN(x) __has_builtin(x) -#else -# define FMT_HAS_BUILTIN(x) 0 -#endif - -#ifdef __has_cpp_attribute -# define FMT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) -#else -# define FMT_HAS_CPP_ATTRIBUTE(x) 0 -#endif - -#ifndef FMT_USE_VARIADIC_TEMPLATES -// Variadic templates are available in GCC since version 4.4 -// (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ -// since version 2013. -# define FMT_USE_VARIADIC_TEMPLATES \ - (FMT_HAS_FEATURE(cxx_variadic_templates) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800) -#endif - -#ifndef FMT_USE_RVALUE_REFERENCES -// Don't use rvalue references when compiling with clang and an old libstdc++ -// as the latter doesn't provide std::move. -# if defined(FMT_GNUC_LIBSTD_VERSION) && FMT_GNUC_LIBSTD_VERSION <= 402 -# define FMT_USE_RVALUE_REFERENCES 0 -# else -# define FMT_USE_RVALUE_REFERENCES \ - (FMT_HAS_FEATURE(cxx_rvalue_references) || \ - (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600) -# endif -#endif - -#if FMT_USE_RVALUE_REFERENCES -# include // for std::move -#endif - -// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature). -#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) -# define FMT_NOEXCEPT noexcept -#else -# define FMT_NOEXCEPT throw() -#endif - -// A macro to disallow the copy constructor and operator= functions -// This should be used in the private: declarations for a class -#if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800 -# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&) = delete; \ - TypeName& operator=(const TypeName&) = delete -#else -# define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ - TypeName(const TypeName&); \ - TypeName& operator=(const TypeName&) -#endif - -namespace fmt { - -// Fix the warning about long long on older versions of GCC -// that don't support the diagnostic pragma. -FMT_GCC_EXTENSION typedef long long LongLong; -FMT_GCC_EXTENSION typedef unsigned long long ULongLong; - -#if FMT_USE_RVALUE_REFERENCES -using std::move; -#endif - -template -class BasicWriter; - -typedef BasicWriter Writer; -typedef BasicWriter WWriter; - -template -class BasicFormatter; - -template -void format(BasicFormatter &f, const Char *&format_str, const T &value); - -/** - \rst - A string reference. It can be constructed from a C string or - ``std::string``. - - You can use one of the following typedefs for common character types: - - +------------+-------------------------+ - | Type | Definition | - +============+=========================+ - | StringRef | BasicStringRef | - +------------+-------------------------+ - | WStringRef | BasicStringRef | - +------------+-------------------------+ - - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: - - template - std::string format(StringRef format_str, const Args & ... args); - - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template -class BasicStringRef { - private: - const Char *data_; - std::size_t size_; - - public: - /** - Constructs a string reference object from a C string and a size. - */ - BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} - - /** - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - */ - BasicStringRef(const Char *s) - : data_(s), size_(std::char_traits::length(s)) {} - - /** - Constructs a string reference from an `std::string` object. - */ - BasicStringRef(const std::basic_string &s) - : data_(s.c_str()), size_(s.size()) {} - - /** - Converts a string reference to an `std::string` object. - */ - operator std::basic_string() const { - return std::basic_string(data_, size()); - } - - /** - Returns the pointer to a C string. - */ - const Char *c_str() const { return data_; } - - /** - Returns the string size. - */ - std::size_t size() const { return size_; } - - friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.data_ == rhs.data_; - } - friend bool operator!=(BasicStringRef lhs, BasicStringRef rhs) { - return lhs.data_ != rhs.data_; - } -}; - -typedef BasicStringRef StringRef; -typedef BasicStringRef WStringRef; - -/** - A formatting error such as invalid format string. -*/ -class FormatError : public std::runtime_error { -public: - explicit FormatError(StringRef message) - : std::runtime_error(message.c_str()) {} -}; - -namespace internal { - -// The number of characters to store in the MemoryBuffer object itself -// to avoid dynamic memory allocation. -enum { INLINE_BUFFER_SIZE = 500 }; - -#if _SECURE_SCL -// Use checked iterator to avoid warnings on MSVC. -template -inline stdext::checked_array_iterator make_ptr(T *ptr, std::size_t size) { - return stdext::checked_array_iterator(ptr, size); -} -#else -template -inline T *make_ptr(T *ptr, std::size_t) { return ptr; } -#endif - -// A buffer for POD types. It supports a subset of std::vector's operations. -template -class Buffer { - private: - FMT_DISALLOW_COPY_AND_ASSIGN(Buffer); - - protected: - T *ptr_; - std::size_t size_; - std::size_t capacity_; - - Buffer(T *ptr = 0, std::size_t capacity = 0) - : ptr_(ptr), size_(0), capacity_(capacity) {} - - virtual void grow(std::size_t size) = 0; - - public: - virtual ~Buffer() {} - - // Returns the size of this buffer. - std::size_t size() const { return size_; } - - // Returns the capacity of this buffer. - std::size_t capacity() const { return capacity_; } - - // Resizes the buffer. If T is a POD type new elements are not initialized. - void resize(std::size_t new_size) { - if (new_size > capacity_) - grow(new_size); - size_ = new_size; - } - - // Reserves space to store at least capacity elements. - void reserve(std::size_t capacity) { - if (capacity > capacity_) - grow(capacity); - } - - void clear() FMT_NOEXCEPT { size_ = 0; } - - void push_back(const T &value) { - if (size_ == capacity_) - grow(size_ + 1); - ptr_[size_++] = value; - } - - // Appends data to the end of the buffer. - void append(const T *begin, const T *end); - - T &operator[](std::size_t index) { return ptr_[index]; } - const T &operator[](std::size_t index) const { return ptr_[index]; } -}; - -template -void Buffer::append(const T *begin, const T *end) { - std::ptrdiff_t num_elements = end - begin; - if (size_ + num_elements > capacity_) - grow(size_ + num_elements); - std::copy(begin, end, make_ptr(ptr_, capacity_) + size_); - size_ += num_elements; -} - -// A memory buffer for POD types with the first SIZE elements stored in -// the object itself. -template > -class MemoryBuffer : private Allocator, public Buffer { - private: - T data_[SIZE]; - - // Free memory allocated by the buffer. - void free() { - if (this->ptr_ != data_) this->deallocate(this->ptr_, this->capacity_); - } - - protected: - void grow(std::size_t size); - - public: - explicit MemoryBuffer(const Allocator &alloc = Allocator()) - : Allocator(alloc), Buffer(data_, SIZE) {} - ~MemoryBuffer() { free(); } - -#if FMT_USE_RVALUE_REFERENCES - private: - // Move data from other to this buffer. - void move(MemoryBuffer &other) { - Allocator &this_alloc = *this, &other_alloc = other; - this_alloc = std::move(other_alloc); - this->size_ = other.size_; - this->capacity_ = other.capacity_; - if (other.ptr_ == other.data_) { - this->ptr_ = data_; - std::copy(other.data_, - other.data_ + this->size_, make_ptr(data_, this->capacity_)); - } else { - this->ptr_ = other.ptr_; - // Set pointer to the inline array so that delete is not called - // when freeing. - other.ptr_ = other.data_; - } - } - - public: - MemoryBuffer(MemoryBuffer &&other) { - move(other); - } - - MemoryBuffer &operator=(MemoryBuffer &&other) { - assert(this != &other); - free(); - move(other); - return *this; - } -#endif - - // Returns a copy of the allocator associated with this buffer. - Allocator get_allocator() const { return *this; } -}; - -template -void MemoryBuffer::grow(std::size_t size) { - std::size_t new_capacity = - (std::max)(size, this->capacity_ + this->capacity_ / 2); - T *new_ptr = this->allocate(new_capacity); - // The following code doesn't throw, so the raw pointer above doesn't leak. - std::copy(this->ptr_, - this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); - std::size_t old_capacity = this->capacity_; - T *old_ptr = this->ptr_; - this->capacity_ = new_capacity; - this->ptr_ = new_ptr; - // deallocate may throw (at least in principle), but it doesn't matter since - // the buffer already uses the new storage and will deallocate it in case - // of exception. - if (old_ptr != data_) - this->deallocate(old_ptr, old_capacity); -} - -#ifndef _MSC_VER -// Portable version of signbit. -inline int getsign(double x) { - // When compiled in C++11 mode signbit is no longer a macro but a function - // defined in namespace std and the macro is undefined. -# ifdef signbit - return signbit(x); -# else - return std::signbit(x); -# endif -} - -// Portable version of isinf. -# ifdef isinf -inline int isinfinity(double x) { return isinf(x); } -inline int isinfinity(long double x) { return isinf(x); } -# else -inline int isinfinity(double x) { return std::isinf(x); } -inline int isinfinity(long double x) { return std::isinf(x); } -# endif -#else -inline int getsign(double value) { - if (value < 0) return 1; - if (value == value) return 0; - int dec = 0, sign = 0; - char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail. - _ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign); - return sign; -} -inline int isinfinity(double x) { return !_finite(x); } -inline int isinfinity(long double x) { return !_finite(static_cast(x)); } -#endif - -template -class BasicCharTraits { - public: -#if _SECURE_SCL - typedef stdext::checked_array_iterator CharPtr; -#else - typedef Char *CharPtr; -#endif -}; - -template -class CharTraits; - -template <> -class CharTraits : public BasicCharTraits { - private: - // Conversion from wchar_t to char is not allowed. - static char convert(wchar_t); - -public: - typedef const wchar_t *UnsupportedStrType; - - static char convert(char value) { return value; } - - // Formats a floating-point number. - template - static int format_float(char *buffer, std::size_t size, - const char *format, unsigned width, int precision, T value); -}; - -template <> -class CharTraits : public BasicCharTraits { - public: - typedef const char *UnsupportedStrType; - - static wchar_t convert(char value) { return value; } - static wchar_t convert(wchar_t value) { return value; } - - template - static int format_float(wchar_t *buffer, std::size_t size, - const wchar_t *format, unsigned width, int precision, T value); -}; - -// Checks if a number is negative - used to avoid warnings. -template -struct SignChecker { - template - static bool is_negative(T value) { return value < 0; } -}; - -template <> -struct SignChecker { - template - static bool is_negative(T) { return false; } -}; - -// Returns true if value is negative, false otherwise. -// Same as (value < 0) but doesn't produce warnings if T is an unsigned type. -template -inline bool is_negative(T value) { - return SignChecker::is_signed>::is_negative(value); -} - -// Selects uint32_t if FitsIn32Bits is true, uint64_t otherwise. -template -struct TypeSelector { typedef uint32_t Type; }; - -template <> -struct TypeSelector { typedef uint64_t Type; }; - -template -struct IntTraits { - // Smallest of uint32_t and uint64_t that is large enough to represent - // all values of T. - typedef typename - TypeSelector::digits <= 32>::Type MainType; -}; - -// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. -template -struct MakeUnsigned { typedef T Type; }; - -#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ - template <> \ - struct MakeUnsigned { typedef U Type; } - -FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); -FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); -FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); -FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); - -void report_unknown_type(char code, const char *type); - -// Static data is placed in this class template to allow header-only -// configuration. -template -struct BasicData { - static const uint32_t POWERS_OF_10_32[]; - static const uint64_t POWERS_OF_10_64[]; - static const char DIGITS[]; -}; - -typedef BasicData<> Data; - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) -#endif - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -#endif - -#ifdef FMT_BUILTIN_CLZLL -// Returns the number of decimal digits in n. Leading zeros are not counted -// except for n == 0 in which case count_digits returns 1. -inline unsigned count_digits(uint64_t n) { - // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 - // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. - unsigned t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; - return t - (n < Data::POWERS_OF_10_64[t]) + 1; -} -#else -// Fallback version of count_digits used when __builtin_clz is not available. -inline unsigned count_digits(uint64_t n) { - unsigned count = 1; - for (;;) { - // Integer division is slow so do it for a group of four digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - if (n < 10) return count; - if (n < 100) return count + 1; - if (n < 1000) return count + 2; - if (n < 10000) return count + 3; - n /= 10000u; - count += 4; - } -} -#endif - -#ifdef FMT_BUILTIN_CLZ -// Optional version of count_digits for better performance on 32-bit platforms. -inline unsigned count_digits(uint32_t n) { - uint32_t t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; - return t - (n < Data::POWERS_OF_10_32[t]) + 1; -} -#endif - -// Formats a decimal unsigned integer value writing into buffer. -template -inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) { - --num_digits; - while (value >= 100) { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = (value % 100) * 2; - value /= 100; - buffer[num_digits] = Data::DIGITS[index + 1]; - buffer[num_digits - 1] = Data::DIGITS[index]; - num_digits -= 2; - } - if (value < 10) { - *buffer = static_cast('0' + value); - return; - } - unsigned index = static_cast(value * 2); - buffer[1] = Data::DIGITS[index + 1]; - buffer[0] = Data::DIGITS[index]; -} - -#ifdef _WIN32 -// A converter from UTF-8 to UTF-16. -// It is only provided for Windows since other systems support UTF-8 natively. -class UTF8ToUTF16 { - private: - MemoryBuffer buffer_; - - public: - explicit UTF8ToUTF16(StringRef s); - operator WStringRef() const { return WStringRef(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const wchar_t *c_str() const { return &buffer_[0]; } - std::wstring str() const { return std::wstring(&buffer_[0], size()); } -}; - -// A converter from UTF-16 to UTF-8. -// It is only provided for Windows since other systems support UTF-8 natively. -class UTF16ToUTF8 { - private: - MemoryBuffer buffer_; - - public: - UTF16ToUTF8() {} - explicit UTF16ToUTF8(WStringRef s); - operator StringRef() const { return StringRef(&buffer_[0], size()); } - size_t size() const { return buffer_.size() - 1; } - const char *c_str() const { return &buffer_[0]; } - std::string str() const { return std::string(&buffer_[0], size()); } - - // Performs conversion returning a system error code instead of - // throwing exception on conversion error. This method may still throw - // in case of memory allocation error. - int convert(WStringRef s); -}; -#endif - -void format_system_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; - -#ifdef _WIN32 -void format_windows_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; -#endif - -// Computes max(Arg, 1) at compile time. It is used to avoid errors about -// allocating an array of 0 size. -template -struct NonZero { - enum { VALUE = Arg }; -}; - -template <> -struct NonZero<0> { - enum { VALUE = 1 }; -}; - -// The value of a formatting argument. It is a POD type to allow storage in -// internal::MemoryBuffer. -struct Value { - template - struct StringValue { - const Char *value; - std::size_t size; - }; - - typedef void (*FormatFunc)( - void *formatter, const void *arg, void *format_str_ptr); - - struct CustomValue { - const void *value; - FormatFunc format; - }; - - union { - int int_value; - unsigned uint_value; - LongLong long_long_value; - ULongLong ulong_long_value; - double double_value; - long double long_double_value; - const void *pointer; - StringValue string; - StringValue sstring; - StringValue ustring; - StringValue wstring; - CustomValue custom; - }; -}; - -struct Arg : Value { - enum Type { - NONE, - // Integer types should go first, - INT, UINT, LONG_LONG, ULONG_LONG, CHAR, LAST_INTEGER_TYPE = CHAR, - // followed by floating-point types. - DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE, - CSTRING, STRING, WSTRING, POINTER, CUSTOM - }; - Type type; -}; - -// Makes a Value object from any type. -template -class MakeValue : public Value { - private: - // The following two methods are private to disallow formatting of - // arbitrary pointers. If you want to output a pointer cast it to - // "void *" or "const void *". In particular, this forbids formatting - // of "[const] volatile char *" which is printed as bool by iostreams. - // Do not implement! - template - MakeValue(const T *value); - template - MakeValue(T *value); - - void set_string(StringRef str) { - string.value = str.c_str(); - string.size = str.size(); - } - - void set_string(WStringRef str) { - CharTraits::convert(wchar_t()); - wstring.value = str.c_str(); - wstring.size = str.size(); - } - - // Formats an argument of a custom type, such as a user-defined class. - template - static void format_custom_arg( - void *formatter, const void *arg, void *format_str_ptr) { - format(*static_cast*>(formatter), - *static_cast(format_str_ptr), - *static_cast(arg)); - } - -public: - MakeValue() {} - -#define FMT_MAKE_VALUE(Type, field, TYPE) \ - MakeValue(Type value) { field = value; } \ - static uint64_t type(Type) { return Arg::TYPE; } - - FMT_MAKE_VALUE(bool, int_value, INT) - FMT_MAKE_VALUE(short, int_value, INT) - FMT_MAKE_VALUE(unsigned short, uint_value, UINT) - FMT_MAKE_VALUE(int, int_value, INT) - FMT_MAKE_VALUE(unsigned, uint_value, UINT) - - MakeValue(long value) { - // To minimize the number of types we need to deal with, long is - // translated either to int or to long long depending on its size. - if (sizeof(long) == sizeof(int)) - int_value = static_cast(value); - else - long_long_value = value; - } - static uint64_t type(long) { - return sizeof(long) == sizeof(int) ? Arg::INT : Arg::LONG_LONG; - } - - MakeValue(unsigned long value) { - if (sizeof(unsigned long) == sizeof(unsigned)) - uint_value = static_cast(value); - else - ulong_long_value = value; - } - static uint64_t type(unsigned long) { - return sizeof(unsigned long) == sizeof(unsigned) ? - Arg::UINT : Arg::ULONG_LONG; - } - - FMT_MAKE_VALUE(LongLong, long_long_value, LONG_LONG) - FMT_MAKE_VALUE(ULongLong, ulong_long_value, ULONG_LONG) - FMT_MAKE_VALUE(float, double_value, DOUBLE) - FMT_MAKE_VALUE(double, double_value, DOUBLE) - FMT_MAKE_VALUE(long double, long_double_value, LONG_DOUBLE) - FMT_MAKE_VALUE(signed char, int_value, CHAR) - FMT_MAKE_VALUE(unsigned char, int_value, CHAR) - FMT_MAKE_VALUE(char, int_value, CHAR) - - MakeValue(wchar_t value) { - int_value = internal::CharTraits::convert(value); - } - static uint64_t type(wchar_t) { return Arg::CHAR; } - -#define FMT_MAKE_STR_VALUE(Type, TYPE) \ - MakeValue(Type value) { set_string(value); } \ - static uint64_t type(Type) { return Arg::TYPE; } - - FMT_MAKE_VALUE(char *, string.value, CSTRING) - FMT_MAKE_VALUE(const char *, string.value, CSTRING) - FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) - FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) - FMT_MAKE_STR_VALUE(const std::string &, STRING) - FMT_MAKE_STR_VALUE(StringRef, STRING) - - FMT_MAKE_STR_VALUE(wchar_t *, WSTRING) - FMT_MAKE_STR_VALUE(const wchar_t *, WSTRING) - FMT_MAKE_STR_VALUE(const std::wstring &, WSTRING) - FMT_MAKE_STR_VALUE(WStringRef, WSTRING) - - FMT_MAKE_VALUE(void *, pointer, POINTER) - FMT_MAKE_VALUE(const void *, pointer, POINTER) - - template - MakeValue(const T &value) { - custom.value = &value; - custom.format = &format_custom_arg; - } - template - static uint64_t type(const T &) { return Arg::CUSTOM; } -}; - -#define FMT_DISPATCH(call) static_cast(this)->call - -// An argument visitor. -// To use ArgVisitor define a subclass that implements some or all of the -// visit methods with the same signatures as the methods in ArgVisitor, -// for example, visit_int(int). -// Specify the subclass name as the Impl template parameter. Then calling -// ArgVisitor::visit for some argument will dispatch to a visit method -// specific to the argument type. For example, if the argument type is -// double then visit_double(double) method of a subclass will be called. -// If the subclass doesn't contain a method with this signature, then -// a corresponding method of ArgVisitor will be called. -// -// Example: -// class MyArgVisitor : public ArgVisitor { -// public: -// void visit_int(int value) { print("{}", value); } -// void visit_double(double value) { print("{}", value ); } -// }; -// -// ArgVisitor uses the curiously recurring template pattern: -// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern -template -class ArgVisitor { - public: - Result visit_unhandled_arg() { return Result(); } - - Result visit_int(int value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_long_long(LongLong value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_uint(unsigned value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_ulong_long(ULongLong value) { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_char(int value) { - return FMT_DISPATCH(visit_any_int(value)); - } - template - Result visit_any_int(T) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit_double(double value) { - return FMT_DISPATCH(visit_any_double(value)); - } - Result visit_long_double(long double value) { - return FMT_DISPATCH(visit_any_double(value)); - } - template - Result visit_any_double(T) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit_string(Arg::StringValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_wstring(Arg::StringValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_pointer(const void *) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_custom(Arg::CustomValue) { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit(const Arg &arg) { - switch (arg.type) { - default: - assert(false); - return Result(); - case Arg::INT: - return FMT_DISPATCH(visit_int(arg.int_value)); - case Arg::UINT: - return FMT_DISPATCH(visit_uint(arg.uint_value)); - case Arg::LONG_LONG: - return FMT_DISPATCH(visit_long_long(arg.long_long_value)); - case Arg::ULONG_LONG: - return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); - case Arg::DOUBLE: - return FMT_DISPATCH(visit_double(arg.double_value)); - case Arg::LONG_DOUBLE: - return FMT_DISPATCH(visit_long_double(arg.long_double_value)); - case Arg::CHAR: - return FMT_DISPATCH(visit_char(arg.int_value)); - case Arg::CSTRING: { - Value::StringValue str = arg.string; - str.size = 0; - return FMT_DISPATCH(visit_string(str)); - } - case Arg::STRING: - return FMT_DISPATCH(visit_string(arg.string)); - case Arg::WSTRING: - return FMT_DISPATCH(visit_wstring(arg.wstring)); - case Arg::POINTER: - return FMT_DISPATCH(visit_pointer(arg.pointer)); - case Arg::CUSTOM: - return FMT_DISPATCH(visit_custom(arg.custom)); - } - } -}; - -class RuntimeError : public std::runtime_error { - protected: - RuntimeError() : std::runtime_error("") {} -}; - -template -class ArgFormatter; -} // namespace internal - -/** - An argument list. - */ -class ArgList { - private: - uint64_t types_; - const internal::Value *values_; - - public: - // Maximum number of arguments that can be passed in ArgList. - enum { MAX_ARGS = 16 }; - - ArgList() : types_(0) {} - ArgList(ULongLong types, const internal::Value *values) - : types_(types), values_(values) {} - - /** - Returns the argument at specified index. - */ - internal::Arg operator[](unsigned index) const { - using internal::Arg; - Arg arg; - if (index >= MAX_ARGS) { - arg.type = Arg::NONE; - return arg; - } - unsigned shift = index * 4; - uint64_t mask = 0xf; - Arg::Type type = - static_cast((types_ & (mask << shift)) >> shift); - arg.type = type; - if (type != Arg::NONE) { - internal::Value &value = arg; - value = values_[index]; - } - return arg; - } -}; - -struct FormatSpec; - -namespace internal { - -class FormatterBase { - private: - ArgList args_; - int next_arg_index_; - - // Returns the argument with specified index. - Arg do_get_arg(unsigned arg_index, const char *&error); - - protected: - void set_args(const ArgList &args) { - args_ = args; - next_arg_index_ = 0; - } - - // Returns the next argument. - Arg next_arg(const char *&error); - - // Checks if manual indexing is used and returns the argument with - // specified index. - Arg get_arg(unsigned arg_index, const char *&error); - - template - void write(BasicWriter &w, const Char *start, const Char *end) { - if (start != end) - w << BasicStringRef(start, end - start); - } -}; - -// A printf formatter. -template -class PrintfFormatter : private FormatterBase { - private: - void parse_flags(FormatSpec &spec, const Char *&s); - - // Returns the argument with specified index or, if arg_index is equal - // to the maximum unsigned value, the next argument. - Arg get_arg(const Char *s, - unsigned arg_index = (std::numeric_limits::max)()); - - // Parses argument index, flags and width and returns the argument index. - unsigned parse_header(const Char *&s, FormatSpec &spec); - - public: - void format(BasicWriter &writer, - BasicStringRef format_str, const ArgList &args); -}; -} // namespace internal - -// A formatter. -template -class BasicFormatter : private internal::FormatterBase { - private: - BasicWriter &writer_; - const Char *start_; - - FMT_DISALLOW_COPY_AND_ASSIGN(BasicFormatter); - - // Parses argument index and returns corresponding argument. - internal::Arg parse_arg_index(const Char *&s); - - public: - explicit BasicFormatter(BasicWriter &w) : writer_(w) {} - - BasicWriter &writer() { return writer_; } - - void format(BasicStringRef format_str, const ArgList &args); - - const Char *format(const Char *&format_str, const internal::Arg &arg); -}; - -enum Alignment { - ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC -}; - -// Flags. -enum { - SIGN_FLAG = 1, PLUS_FLAG = 2, MINUS_FLAG = 4, HASH_FLAG = 8, - CHAR_FLAG = 0x10 // Argument has char type - used in error reporting. -}; - -// An empty format specifier. -struct EmptySpec {}; - -// A type specifier. -template -struct TypeSpec : EmptySpec { - Alignment align() const { return ALIGN_DEFAULT; } - unsigned width() const { return 0; } - int precision() const { return -1; } - bool flag(unsigned) const { return false; } - char type() const { return TYPE; } - char fill() const { return ' '; } -}; - -// A width specifier. -struct WidthSpec { - unsigned width_; - // Fill is always wchar_t and cast to char if necessary to avoid having - // two specialization of WidthSpec and its subclasses. - wchar_t fill_; - - WidthSpec(unsigned width, wchar_t fill) : width_(width), fill_(fill) {} - - unsigned width() const { return width_; } - wchar_t fill() const { return fill_; } -}; - -// An alignment specifier. -struct AlignSpec : WidthSpec { - Alignment align_; - - AlignSpec(unsigned width, wchar_t fill, Alignment align = ALIGN_DEFAULT) - : WidthSpec(width, fill), align_(align) {} - - Alignment align() const { return align_; } - - int precision() const { return -1; } -}; - -// An alignment and type specifier. -template -struct AlignTypeSpec : AlignSpec { - AlignTypeSpec(unsigned width, wchar_t fill) : AlignSpec(width, fill) {} - - bool flag(unsigned) const { return false; } - char type() const { return TYPE; } -}; - -// A full format specifier. -struct FormatSpec : AlignSpec { - unsigned flags_; - int precision_; - char type_; - - FormatSpec( - unsigned width = 0, char type = 0, wchar_t fill = ' ') - : AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {} - - bool flag(unsigned f) const { return (flags_ & f) != 0; } - int precision() const { return precision_; } - char type() const { return type_; } -}; - -// An integer format specifier. -template , typename Char = char> -class IntFormatSpec : public SpecT { - private: - T value_; - - public: - IntFormatSpec(T val, const SpecT &spec = SpecT()) - : SpecT(spec), value_(val) {} - - T value() const { return value_; } -}; - -// A string format specifier. -template -class StrFormatSpec : public AlignSpec { - private: - const T *str_; - - public: - StrFormatSpec(const T *str, unsigned width, wchar_t fill) - : AlignSpec(width, fill), str_(str) {} - - const T *str() const { return str_; } -}; - -/** - Returns an integer format specifier to format the value in base 2. - */ -IntFormatSpec > bin(int value); - -/** - Returns an integer format specifier to format the value in base 8. - */ -IntFormatSpec > oct(int value); - -/** - Returns an integer format specifier to format the value in base 16 using - lower-case letters for the digits above 9. - */ -IntFormatSpec > hex(int value); - -/** - Returns an integer formatter format specifier to format in base 16 using - upper-case letters for the digits above 9. - */ -IntFormatSpec > hexu(int value); - -/** - \rst - Returns an integer format specifier to pad the formatted argument with the - fill character to the specified width using the default (right) numeric - alignment. - - **Example**:: - - MemoryWriter out; - out << pad(hex(0xcafe), 8, '0'); - // out.str() == "0000cafe" - - \endrst - */ -template -IntFormatSpec, Char> pad( - int value, unsigned width, Char fill = ' '); - -#define FMT_DEFINE_INT_FORMATTERS(TYPE) \ -inline IntFormatSpec > bin(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'b'>()); \ -} \ - \ -inline IntFormatSpec > oct(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'o'>()); \ -} \ - \ -inline IntFormatSpec > hex(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'x'>()); \ -} \ - \ -inline IntFormatSpec > hexu(TYPE value) { \ - return IntFormatSpec >(value, TypeSpec<'X'>()); \ -} \ - \ -template \ -inline IntFormatSpec > pad( \ - IntFormatSpec > f, unsigned width) { \ - return IntFormatSpec >( \ - f.value(), AlignTypeSpec(width, ' ')); \ -} \ - \ -/* For compatibility with older compilers we provide two overloads for pad, */ \ -/* one that takes a fill character and one that doesn't. In the future this */ \ -/* can be replaced with one overload making the template argument Char */ \ -/* default to char (C++11). */ \ -template \ -inline IntFormatSpec, Char> pad( \ - IntFormatSpec, Char> f, \ - unsigned width, Char fill) { \ - return IntFormatSpec, Char>( \ - f.value(), AlignTypeSpec(width, fill)); \ -} \ - \ -inline IntFormatSpec > pad( \ - TYPE value, unsigned width) { \ - return IntFormatSpec >( \ - value, AlignTypeSpec<0>(width, ' ')); \ -} \ - \ -template \ -inline IntFormatSpec, Char> pad( \ - TYPE value, unsigned width, Char fill) { \ - return IntFormatSpec, Char>( \ - value, AlignTypeSpec<0>(width, fill)); \ -} - -FMT_DEFINE_INT_FORMATTERS(int) -FMT_DEFINE_INT_FORMATTERS(long) -FMT_DEFINE_INT_FORMATTERS(unsigned) -FMT_DEFINE_INT_FORMATTERS(unsigned long) -FMT_DEFINE_INT_FORMATTERS(LongLong) -FMT_DEFINE_INT_FORMATTERS(ULongLong) - -/** - \rst - Returns a string formatter that pads the formatted argument with the fill - character to the specified width using the default (left) string alignment. - - **Example**:: - - std::string s = str(MemoryWriter() << pad("abc", 8)); - // s == "abc " - - \endrst - */ -template -inline StrFormatSpec pad( - const Char *str, unsigned width, Char fill = ' ') { - return StrFormatSpec(str, width, fill); -} - -inline StrFormatSpec pad( - const wchar_t *str, unsigned width, char fill = ' ') { - return StrFormatSpec(str, width, fill); -} - -// Generates a comma-separated list with results of applying f to -// numbers 0..n-1. -# define FMT_GEN(n, f) FMT_GEN##n(f) -# define FMT_GEN1(f) f(0) -# define FMT_GEN2(f) FMT_GEN1(f), f(1) -# define FMT_GEN3(f) FMT_GEN2(f), f(2) -# define FMT_GEN4(f) FMT_GEN3(f), f(3) -# define FMT_GEN5(f) FMT_GEN4(f), f(4) -# define FMT_GEN6(f) FMT_GEN5(f), f(5) -# define FMT_GEN7(f) FMT_GEN6(f), f(6) -# define FMT_GEN8(f) FMT_GEN7(f), f(7) -# define FMT_GEN9(f) FMT_GEN8(f), f(8) -# define FMT_GEN10(f) FMT_GEN9(f), f(9) -# define FMT_GEN11(f) FMT_GEN10(f), f(10) -# define FMT_GEN12(f) FMT_GEN11(f), f(11) -# define FMT_GEN13(f) FMT_GEN12(f), f(12) -# define FMT_GEN14(f) FMT_GEN13(f), f(13) -# define FMT_GEN15(f) FMT_GEN14(f), f(14) - -namespace internal { -inline uint64_t make_type() { return 0; } - -template -inline uint64_t make_type(const T &arg) { return MakeValue::type(arg); } - -#if FMT_USE_VARIADIC_TEMPLATES -template -inline uint64_t make_type(const Arg &first, const Args & ... tail) { - return make_type(first) | (make_type(tail...) << 4); -} -#else - -struct ArgType { - uint64_t type; - - ArgType() : type(0) {} - - template - ArgType(const T &arg) : type(make_type(arg)) {} -}; - -# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() - -inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { - return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | - (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | - (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | - (t12.type << 48) | (t13.type << 52) | (t14.type << 56); -} -#endif -} // namespace internal - -# define FMT_MAKE_TEMPLATE_ARG(n) typename T##n -# define FMT_MAKE_ARG_TYPE(n) T##n -# define FMT_MAKE_ARG(n) const T##n &v##n -# define FMT_MAKE_REF_char(n) fmt::internal::MakeValue(v##n) -# define FMT_MAKE_REF_wchar_t(n) fmt::internal::MakeValue(v##n) - -#if FMT_USE_VARIADIC_TEMPLATES -// Defines a variadic function returning void. -# define FMT_VARIADIC_VOID(func, arg_type) \ - template \ - void func(arg_type arg1, const Args & ... args) { \ - const fmt::internal::Value values[ \ - fmt::internal::NonZero::VALUE] = { \ - fmt::internal::MakeValue(args)... \ - }; \ - func(arg1, ArgList(fmt::internal::make_type(args...), values)); \ - } - -// Defines a variadic constructor. -# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ - template \ - ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ - using fmt::internal::MakeValue; \ - const fmt::internal::Value values[ \ - fmt::internal::NonZero::VALUE] = { \ - MakeValue(args)... \ - }; \ - func(arg0, arg1, ArgList(fmt::internal::make_type(args...), values)); \ - } - -#else - -# define FMT_MAKE_REF(n) fmt::internal::MakeValue(v##n) -# define FMT_MAKE_REF2(n) v##n - -// Defines a wrapper for a function taking one argument of type arg_type -// and n additional arguments of arbitrary types. -# define FMT_WRAP1(func, arg_type, n) \ - template \ - inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ - func(arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ - } - -// Emulates a variadic function returning void on a pre-C++11 compiler. -# define FMT_VARIADIC_VOID(func, arg_type) \ - inline void func(arg_type arg) { func(arg, fmt::ArgList()); } \ - FMT_WRAP1(func, arg_type, 1) FMT_WRAP1(func, arg_type, 2) \ - FMT_WRAP1(func, arg_type, 3) FMT_WRAP1(func, arg_type, 4) \ - FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ - FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ - FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) - -# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ - template \ - ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ - func(arg0, arg1, fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ - } - -// Emulates a variadic constructor on a pre-C++11 compiler. -# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ - FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) -#endif - -// Generates a comma-separated list with results of applying f to pairs -// (argument, index). -#define FMT_FOR_EACH1(f, x0) f(x0, 0) -#define FMT_FOR_EACH2(f, x0, x1) \ - FMT_FOR_EACH1(f, x0), f(x1, 1) -#define FMT_FOR_EACH3(f, x0, x1, x2) \ - FMT_FOR_EACH2(f, x0 ,x1), f(x2, 2) -#define FMT_FOR_EACH4(f, x0, x1, x2, x3) \ - FMT_FOR_EACH3(f, x0, x1, x2), f(x3, 3) -#define FMT_FOR_EACH5(f, x0, x1, x2, x3, x4) \ - FMT_FOR_EACH4(f, x0, x1, x2, x3), f(x4, 4) -#define FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5) \ - FMT_FOR_EACH5(f, x0, x1, x2, x3, x4), f(x5, 5) -#define FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6) \ - FMT_FOR_EACH6(f, x0, x1, x2, x3, x4, x5), f(x6, 6) -#define FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7) \ - FMT_FOR_EACH7(f, x0, x1, x2, x3, x4, x5, x6), f(x7, 7) -#define FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8) \ - FMT_FOR_EACH8(f, x0, x1, x2, x3, x4, x5, x6, x7), f(x8, 8) -#define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ - FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) - -/** - An error returned by an operating system or a language runtime, - for example a file opening error. -*/ -class SystemError : public internal::RuntimeError { - private: - void init(int err_code, StringRef format_str, ArgList args); - - protected: - int error_code_; - - typedef char Char; // For FMT_VARIADIC_CTOR. - - SystemError() {} - - public: - /** - \rst - Constructs a :class:`fmt::SystemError` object with the description - of the form - - .. parsed-literal:: - **: ** - - where ** is the formatted message and ** is - the system message corresponding to the error code. - *error_code* is a system error code as given by ``errno``. - If *error_code* is not a valid error code such as -1, the system message - may look like "Unknown error -1" and is platform-dependent. - - **Example**:: - - // This throws a SystemError with the description - // cannot open file 'madeup': No such file or directory - // or similar (system message may vary). - const char *filename = "madeup"; - std::FILE *file = std::fopen(filename, "r"); - if (!file) - throw fmt::SystemError(errno, "cannot open file '{}'", filename); - \endrst - */ - SystemError(int error_code, StringRef message) { - init(error_code, message, ArgList()); - } - FMT_VARIADIC_CTOR(SystemError, init, int, StringRef) - - int error_code() const { return error_code_; } -}; - -/** - \rst - This template provides operations for formatting and writing data into - a character stream. The output is stored in a buffer provided by a subclass - such as :class:`fmt::BasicMemoryWriter`. - - You can use one of the following typedefs for common character types: - - +---------+----------------------+ - | Type | Definition | - +=========+======================+ - | Writer | BasicWriter | - +---------+----------------------+ - | WWriter | BasicWriter | - +---------+----------------------+ - - \endrst - */ -template -class BasicWriter { - private: - // Output buffer. - internal::Buffer &buffer_; - - FMT_DISALLOW_COPY_AND_ASSIGN(BasicWriter); - - typedef typename internal::CharTraits::CharPtr CharPtr; - -#if _SECURE_SCL - // Returns pointer value. - static Char *get(CharPtr p) { return p.base(); } -#else - static Char *get(Char *p) { return p; } -#endif - - // Fills the padding around the content and returns the pointer to the - // content area. - static CharPtr fill_padding(CharPtr buffer, - unsigned total_size, std::size_t content_size, wchar_t fill); - - // Grows the buffer by n characters and returns a pointer to the newly - // allocated area. - CharPtr grow_buffer(std::size_t n) { - std::size_t size = buffer_.size(); - buffer_.resize(size + n); - return internal::make_ptr(&buffer_[size], n); - } - - // Prepare a buffer for integer formatting. - CharPtr prepare_int_buffer(unsigned num_digits, - const EmptySpec &, const char *prefix, unsigned prefix_size) { - unsigned size = prefix_size + num_digits; - CharPtr p = grow_buffer(size); - std::copy(prefix, prefix + prefix_size, p); - return p + size - 1; - } - - template - CharPtr prepare_int_buffer(unsigned num_digits, - const Spec &spec, const char *prefix, unsigned prefix_size); - - // Formats an integer. - template - void write_int(T value, Spec spec); - - // Formats a floating-point number (double or long double). - template - void write_double(T value, const FormatSpec &spec); - - // Writes a formatted string. - template - CharPtr write_str( - const StrChar *s, std::size_t size, const AlignSpec &spec); - - template - void write_str( - const internal::Arg::StringValue &str, const FormatSpec &spec); - - // This method is private to disallow writing a wide string to a - // char stream and vice versa. If you want to print a wide string - // as a pointer as std::ostream does, cast it to const void*. - // Do not implement! - void operator<<(typename internal::CharTraits::UnsupportedStrType); - - // Appends floating-point length specifier to the format string. - // The second argument is only used for overload resolution. - void append_float_length(Char *&format_ptr, long double) { - *format_ptr++ = 'L'; - } - - template - void append_float_length(Char *&, T) {} - - friend class internal::ArgFormatter; - friend class internal::PrintfFormatter; - - protected: - /** - Constructs a ``BasicWriter`` object. - */ - explicit BasicWriter(internal::Buffer &b) : buffer_(b) {} - - public: - /** - Destroys a ``BasicWriter`` object. - */ - virtual ~BasicWriter() {} - - /** - Returns the total number of characters written. - */ - std::size_t size() const { return buffer_.size(); } - - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; } - - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - const Char *c_str() const { - std::size_t size = buffer_.size(); - buffer_.reserve(size + 1); - buffer_[size] = '\0'; - return &buffer_[0]; - } - - /** - Returns the content of the output buffer as an `std::string`. - */ - std::basic_string str() const { - return std::basic_string(&buffer_[0], buffer_.size()); - } - - /** - \rst - Writes formatted data. - - *args* is an argument list representing arbitrary arguments. - - **Example**:: - - MemoryWriter out; - out.write("Current point:\n"); - out.write("({:+f}, {:+f})", -3.14, 3.14); - - This will write the following output to the ``out`` object: - - .. code-block:: none - - Current point: - (-3.140000, +3.140000) - - The output can be accessed using :func:`data()`, :func:`c_str` or - :func:`str` methods. - - See also :ref:`syntax`. - \endrst - */ - void write(BasicStringRef format, ArgList args) { - BasicFormatter(*this).format(format, args); - } - FMT_VARIADIC_VOID(write, BasicStringRef) - - BasicWriter &operator<<(int value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(unsigned value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(long value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(unsigned long value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(LongLong value) { - return *this << IntFormatSpec(value); - } - - /** - Formats *value* and writes it to the stream. - */ - BasicWriter &operator<<(ULongLong value) { - return *this << IntFormatSpec(value); - } - - BasicWriter &operator<<(double value) { - write_double(value, FormatSpec()); - return *this; - } - - /** - Formats *value* using the general format for floating-point numbers - (``'g'``) and writes it to the stream. - */ - BasicWriter &operator<<(long double value) { - write_double(value, FormatSpec()); - return *this; - } - - /** - Writes a character to the stream. - */ - BasicWriter &operator<<(char value) { - buffer_.push_back(value); - return *this; - } - - BasicWriter &operator<<(wchar_t value) { - buffer_.push_back(internal::CharTraits::convert(value)); - return *this; - } - - /** - Writes *value* to the stream. - */ - BasicWriter &operator<<(fmt::BasicStringRef value) { - const Char *str = value.c_str(); - buffer_.append(str, str + value.size()); - return *this; - } - - template - BasicWriter &operator<<(IntFormatSpec spec) { - internal::CharTraits::convert(FillChar()); - write_int(spec.value(), spec); - return *this; - } - - template - BasicWriter &operator<<(const StrFormatSpec &spec) { - const StrChar *s = spec.str(); - // TODO: error if fill is not convertible to Char - write_str(s, std::char_traits::length(s), spec); - return *this; - } - - void clear() FMT_NOEXCEPT { buffer_.clear(); } -}; - -template -template -typename BasicWriter::CharPtr BasicWriter::write_str( - const StrChar *s, std::size_t size, const AlignSpec &spec) { - CharPtr out = CharPtr(); - if (spec.width() > size) { - out = grow_buffer(spec.width()); - Char fill = static_cast(spec.fill()); - if (spec.align() == ALIGN_RIGHT) { - std::fill_n(out, spec.width() - size, fill); - out += spec.width() - size; - } else if (spec.align() == ALIGN_CENTER) { - out = fill_padding(out, spec.width(), size, fill); - } else { - std::fill_n(out + size, spec.width() - size, fill); - } - } else { - out = grow_buffer(size); - } - std::copy(s, s + size, out); - return out; -} - -template -typename BasicWriter::CharPtr - BasicWriter::fill_padding( - CharPtr buffer, unsigned total_size, - std::size_t content_size, wchar_t fill) { - std::size_t padding = total_size - content_size; - std::size_t left_padding = padding / 2; - Char fill_char = static_cast(fill); - std::fill_n(buffer, left_padding, fill_char); - buffer += left_padding; - CharPtr content = buffer; - std::fill_n(buffer + content_size, padding - left_padding, fill_char); - return content; -} - -template -template -typename BasicWriter::CharPtr - BasicWriter::prepare_int_buffer( - unsigned num_digits, const Spec &spec, - const char *prefix, unsigned prefix_size) { - unsigned width = spec.width(); - Alignment align = spec.align(); - Char fill = static_cast(spec.fill()); - if (spec.precision() > static_cast(num_digits)) { - // Octal prefix '0' is counted as a digit, so ignore it if precision - // is specified. - if (prefix_size > 0 && prefix[prefix_size - 1] == '0') - --prefix_size; - unsigned number_size = prefix_size + spec.precision(); - AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); - if (number_size >= width) - return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); - buffer_.reserve(width); - unsigned fill_size = width - number_size; - if (align != ALIGN_LEFT) { - CharPtr p = grow_buffer(fill_size); - std::fill(p, p + fill_size, fill); - } - CharPtr result = prepare_int_buffer( - num_digits, subspec, prefix, prefix_size); - if (align == ALIGN_LEFT) { - CharPtr p = grow_buffer(fill_size); - std::fill(p, p + fill_size, fill); - } - return result; - } - unsigned size = prefix_size + num_digits; - if (width <= size) { - CharPtr p = grow_buffer(size); - std::copy(prefix, prefix + prefix_size, p); - return p + size - 1; - } - CharPtr p = grow_buffer(width); - CharPtr end = p + width; - if (align == ALIGN_LEFT) { - std::copy(prefix, prefix + prefix_size, p); - p += size; - std::fill(p, end, fill); - } else if (align == ALIGN_CENTER) { - p = fill_padding(p, width, size, fill); - std::copy(prefix, prefix + prefix_size, p); - p += size; - } else { - if (align == ALIGN_NUMERIC) { - if (prefix_size != 0) { - p = std::copy(prefix, prefix + prefix_size, p); - size -= prefix_size; - } - } else { - std::copy(prefix, prefix + prefix_size, end - size); - } - std::fill(p, end - size, fill); - p = end; - } - return p - 1; -} - -template -template -void BasicWriter::write_int(T value, Spec spec) { - unsigned prefix_size = 0; - typedef typename internal::IntTraits::MainType UnsignedType; - UnsignedType abs_value = value; - char prefix[4] = ""; - if (internal::is_negative(value)) { - prefix[0] = '-'; - ++prefix_size; - abs_value = 0 - abs_value; - } else if (spec.flag(SIGN_FLAG)) { - prefix[0] = spec.flag(PLUS_FLAG) ? '+' : ' '; - ++prefix_size; - } - switch (spec.type()) { - case 0: case 'd': { - unsigned num_digits = internal::count_digits(abs_value); - CharPtr p = prepare_int_buffer( - num_digits, spec, prefix, prefix_size) + 1 - num_digits; - internal::format_decimal(get(p), abs_value, num_digits); - break; - } - case 'x': case 'X': { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); - } - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 4) != 0); - Char *p = get(prepare_int_buffer( - num_digits, spec, prefix, prefix_size)); - n = abs_value; - const char *digits = spec.type() == 'x' ? - "0123456789abcdef" : "0123456789ABCDEF"; - do { - *p-- = digits[n & 0xf]; - } while ((n >>= 4) != 0); - break; - } - case 'b': case 'B': { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) { - prefix[prefix_size++] = '0'; - prefix[prefix_size++] = spec.type(); - } - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 1) != 0); - Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - do { - *p-- = '0' + (n & 1); - } while ((n >>= 1) != 0); - break; - } - case 'o': { - UnsignedType n = abs_value; - if (spec.flag(HASH_FLAG)) - prefix[prefix_size++] = '0'; - unsigned num_digits = 0; - do { - ++num_digits; - } while ((n >>= 3) != 0); - Char *p = get(prepare_int_buffer(num_digits, spec, prefix, prefix_size)); - n = abs_value; - do { - *p-- = '0' + (n & 7); - } while ((n >>= 3) != 0); - break; - } - default: - internal::report_unknown_type( - spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); - break; - } -} - -template -template -void BasicWriter::write_double( - T value, const FormatSpec &spec) { - // Check type. - char type = spec.type(); - bool upper = false; - switch (type) { - case 0: - type = 'g'; - break; - case 'e': case 'f': case 'g': case 'a': - break; - case 'F': -#ifdef _MSC_VER - // MSVC's printf doesn't support 'F'. - type = 'f'; -#endif - // Fall through. - case 'E': case 'G': case 'A': - upper = true; - break; - default: - internal::report_unknown_type(type, "double"); - break; - } - - char sign = 0; - // Use getsign instead of value < 0 because the latter is always - // false for NaN. - if (internal::getsign(static_cast(value))) { - sign = '-'; - value = -value; - } else if (spec.flag(SIGN_FLAG)) { - sign = spec.flag(PLUS_FLAG) ? '+' : ' '; - } - - if (value != value) { - // Format NaN ourselves because sprintf's output is not consistent - // across platforms. - std::size_t nan_size = 4; - const char *nan = upper ? " NAN" : " nan"; - if (!sign) { - --nan_size; - ++nan; - } - CharPtr out = write_str(nan, nan_size, spec); - if (sign) - *out = sign; - return; - } - - if (internal::isinfinity(value)) { - // Format infinity ourselves because sprintf's output is not consistent - // across platforms. - std::size_t inf_size = 4; - const char *inf = upper ? " INF" : " inf"; - if (!sign) { - --inf_size; - ++inf; - } - CharPtr out = write_str(inf, inf_size, spec); - if (sign) - *out = sign; - return; - } - - std::size_t offset = buffer_.size(); - unsigned width = spec.width(); - if (sign) { - buffer_.reserve(buffer_.size() + (std::max)(width, 1u)); - if (width > 0) - --width; - ++offset; - } - - // Build format string. - enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg - Char format[MAX_FORMAT_SIZE]; - Char *format_ptr = format; - *format_ptr++ = '%'; - unsigned width_for_sprintf = width; - if (spec.flag(HASH_FLAG)) - *format_ptr++ = '#'; - if (spec.align() == ALIGN_CENTER) { - width_for_sprintf = 0; - } else { - if (spec.align() == ALIGN_LEFT) - *format_ptr++ = '-'; - if (width != 0) - *format_ptr++ = '*'; - } - if (spec.precision() >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - - append_float_length(format_ptr, value); - *format_ptr++ = type; - *format_ptr = '\0'; - - // Format using snprintf. - Char fill = static_cast(spec.fill()); - for (;;) { - std::size_t buffer_size = buffer_.capacity() - offset; -#if _MSC_VER - // MSVC's vsnprintf_s doesn't work with zero size, so reserve - // space for at least one extra character to make the size non-zero. - // Note that the buffer's capacity will increase by more than 1. - if (buffer_size == 0) { - buffer_.reserve(offset + 1); - buffer_size = buffer_.capacity() - offset; - } -#endif - Char *start = &buffer_[offset]; - int n = internal::CharTraits::format_float( - start, buffer_size, format, width_for_sprintf, spec.precision(), value); - if (n >= 0 && offset + n < buffer_.capacity()) { - if (sign) { - if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || - *start != ' ') { - *(start - 1) = sign; - sign = 0; - } else { - *(start - 1) = fill; - } - ++n; - } - if (spec.align() == ALIGN_CENTER && - spec.width() > static_cast(n)) { - width = spec.width(); - CharPtr p = grow_buffer(width); - std::copy(p, p + n, p + (width - n) / 2); - fill_padding(p, spec.width(), n, fill); - return; - } - if (spec.fill() != ' ' || sign) { - while (*start == ' ') - *start++ = fill; - if (sign) - *(start - 1) = sign; - } - grow_buffer(n); - return; - } - // If n is negative we ask to increase the capacity by at least 1, - // but as std::vector, the buffer grows exponentially. - buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1); - } -} - -/** - \rst - This template provides operations for formatting and writing data into - a character stream. The output is stored in a memory buffer that grows - dynamically. - - You can use one of the following typedefs for common character types - and the standard allocator: - - +---------------+-----------------------------------------------+ - | Type | Definition | - +===============+===============================================+ - | MemoryWriter | BasicWriter> | - +---------------+-----------------------------------------------+ - | WMemoryWriter | BasicWriter> | - +---------------+-----------------------------------------------+ - - **Example**:: - - MemoryWriter out; - out << "The answer is " << 42 << "\n"; - out.write("({:+f}, {:+f})", -3.14, 3.14); - - This will write the following output to the ``out`` object: - - .. code-block:: none - - The answer is 42 - (-3.140000, +3.140000) - - The output can be converted to an ``std::string`` with ``out.str()`` or - accessed as a C string with ``out.c_str()``. - \endrst - */ -template > -class BasicMemoryWriter : public BasicWriter { - private: - internal::MemoryBuffer buffer_; - - public: - explicit BasicMemoryWriter(const Allocator& alloc = Allocator()) - : BasicWriter(buffer_), buffer_(alloc) {} - -#if FMT_USE_RVALUE_REFERENCES - /** - Constructs a :class:`fmt::BasicMemoryWriter` object moving the content - of the other object to it. - */ - BasicMemoryWriter(BasicMemoryWriter &&other) - : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) { - } - - /** - Moves the content of the other ``BasicMemoryWriter`` object to this one. - */ - BasicMemoryWriter &operator=(BasicMemoryWriter &&other) { - buffer_ = std::move(other.buffer_); - return *this; - } -#endif -}; - -typedef BasicMemoryWriter MemoryWriter; -typedef BasicMemoryWriter WMemoryWriter; - -// Formats a value. -template -void format(BasicFormatter &f, const Char *&format_str, const T &value) { - std::basic_ostringstream os; - os << value; - internal::Arg arg; - internal::Value &arg_value = arg; - std::basic_string str = os.str(); - arg_value = internal::MakeValue(str); - arg.type = static_cast(internal::MakeValue::type(str)); - format_str = f.format(format_str, arg); -} - -// Reports a system error without throwing an exception. -// Can be used to report errors from destructors. -void report_system_error(int error_code, StringRef message) FMT_NOEXCEPT; - -#ifdef _WIN32 - -/** A Windows error. */ -class WindowsError : public SystemError { - private: - void init(int error_code, StringRef format_str, ArgList args); - - public: - /** - \rst - Constructs a :class:`fmt::WindowsError` object with the description - of the form - - .. parsed-literal:: - **: ** - - where ** is the formatted message and ** is the system - message corresponding to the error code. - *error_code* is a Windows error code as given by ``GetLastError``. - If *error_code* is not a valid error code such as -1, the system message - will look like "error -1". - - **Example**:: - - // This throws a WindowsError with the description - // cannot open file 'madeup': The system cannot find the file specified. - // or similar (system message may vary). - const char *filename = "madeup"; - LPOFSTRUCT of = LPOFSTRUCT(); - HFILE file = OpenFile(filename, &of, OF_READ); - if (file == HFILE_ERROR) - throw fmt::WindowsError(GetLastError(), "cannot open file '{}'", filename); - \endrst - */ - WindowsError(int error_code, StringRef message) { - init(error_code, message, ArgList()); - } - FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef) -}; - -// Reports a Windows error without throwing an exception. -// Can be used to report errors from destructors. -void report_windows_error(int error_code, StringRef message) FMT_NOEXCEPT; - -#endif - -enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; - -/** - Formats a string and prints it to stdout using ANSI escape sequences - to specify color (experimental). - Example: - PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23; - */ -void print_colored(Color c, StringRef format, ArgList args); - -/** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = format("The answer is {}", 42); - \endrst -*/ -inline std::string format(StringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - return w.str(); -} - -inline std::wstring format(WStringRef format_str, ArgList args) { - WMemoryWriter w; - w.write(format_str, args); - return w.str(); -} - -/** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - print(stderr, "Don't {}!", "panic"); - \endrst - */ -void print(std::FILE *f, StringRef format_str, ArgList args); - -/** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst - */ -void print(StringRef format_str, ArgList args); - -/** - \rst - Prints formatted data to the stream *os*. - - **Example**:: - - print(cerr, "Don't {}!", "panic"); - \endrst - */ -void print(std::ostream &os, StringRef format_str, ArgList args); - -template -void printf(BasicWriter &w, BasicStringRef format, ArgList args) { - internal::PrintfFormatter().format(w, format, args); -} - -/** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = fmt::sprintf("The answer is %d", 42); - \endrst -*/ -inline std::string sprintf(StringRef format, ArgList args) { - MemoryWriter w; - printf(w, format, args); - return w.str(); -} - -/** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - fmt::fprintf(stderr, "Don't %s!", "panic"); - \endrst - */ -int fprintf(std::FILE *f, StringRef format, ArgList args); - -/** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - fmt::printf("Elapsed time: %.2f seconds", 1.23); - \endrst - */ -inline int printf(StringRef format, ArgList args) { - return fprintf(stdout, format, args); -} - -/** - Fast integer formatter. - */ -class FormatInt { - private: - // Buffer should be large enough to hold all digits (digits10 + 1), - // a sign and a null character. - enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; - mutable char buffer_[BUFFER_SIZE]; - char *str_; - - // Formats value in reverse and returns the number of digits. - char *format_decimal(ULongLong value) { - char *buffer_end = buffer_ + BUFFER_SIZE - 1; - while (value >= 100) { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = (value % 100) * 2; - value /= 100; - *--buffer_end = internal::Data::DIGITS[index + 1]; - *--buffer_end = internal::Data::DIGITS[index]; - } - if (value < 10) { - *--buffer_end = static_cast('0' + value); - return buffer_end; - } - unsigned index = static_cast(value * 2); - *--buffer_end = internal::Data::DIGITS[index + 1]; - *--buffer_end = internal::Data::DIGITS[index]; - return buffer_end; - } - - void FormatSigned(LongLong value) { - ULongLong abs_value = static_cast(value); - bool negative = value < 0; - if (negative) - abs_value = 0 - abs_value; - str_ = format_decimal(abs_value); - if (negative) - *--str_ = '-'; - } - - public: - explicit FormatInt(int value) { FormatSigned(value); } - explicit FormatInt(long value) { FormatSigned(value); } - explicit FormatInt(LongLong value) { FormatSigned(value); } - explicit FormatInt(unsigned value) : str_(format_decimal(value)) {} - explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} - explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} - - /** - Returns the number of characters written to the output buffer. - */ - std::size_t size() const { return buffer_ - str_ + BUFFER_SIZE - 1; } - - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - const char *data() const { return str_; } - - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - const char *c_str() const { - buffer_[BUFFER_SIZE - 1] = '\0'; - return str_; - } - - /** - Returns the content of the output buffer as an `std::string`. - */ - std::string str() const { return std::string(str_, size()); } -}; - -// Formats a decimal integer value writing into buffer and returns -// a pointer to the end of the formatted string. This function doesn't -// write a terminating null character. -template -inline void format_decimal(char *&buffer, T value) { - typename internal::IntTraits::MainType abs_value = value; - if (internal::is_negative(value)) { - *buffer++ = '-'; - abs_value = 0 - abs_value; - } - if (abs_value < 100) { - if (abs_value < 10) { - *buffer++ = static_cast('0' + abs_value); - return; - } - unsigned index = static_cast(abs_value * 2); - *buffer++ = internal::Data::DIGITS[index]; - *buffer++ = internal::Data::DIGITS[index + 1]; - return; - } - unsigned num_digits = internal::count_digits(abs_value); - internal::format_decimal(buffer, abs_value, num_digits); - buffer += num_digits; -} -} - -#if FMT_GCC_VERSION -// Use the system_header pragma to suppress warnings about variadic macros -// because suppressing -Wvariadic-macros with the diagnostic pragma doesn't -// work. It is used at the end because we want to suppress as little warnings -// as possible. -# pragma GCC system_header -#endif - -// This is used to work around VC++ bugs in handling variadic macros. -#define FMT_EXPAND(args) args - -// Returns the number of arguments. -// Based on https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s. -#define FMT_NARG(...) FMT_NARG_(__VA_ARGS__, FMT_RSEQ_N()) -#define FMT_NARG_(...) FMT_EXPAND(FMT_ARG_N(__VA_ARGS__)) -#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N -#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 - -#define FMT_CONCAT(a, b) a##b -#define FMT_FOR_EACH_(N, f, ...) \ - FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) -#define FMT_FOR_EACH(f, ...) \ - FMT_EXPAND(FMT_FOR_EACH_(FMT_NARG(__VA_ARGS__), f, __VA_ARGS__)) - -#define FMT_ADD_ARG_NAME(type, index) type arg##index -#define FMT_GET_ARG_NAME(type, index) arg##index - -#if FMT_USE_VARIADIC_TEMPLATES -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ - template \ - ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - const Args & ... args) { \ - using fmt::internal::Value; \ - const Value values[fmt::internal::NonZero::VALUE] = { \ - fmt::internal::MakeValue(args)... \ - }; \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::make_type(args...), values)); \ - } -#else -// Defines a wrapper for a function taking __VA_ARGS__ arguments -// and n additional arguments of arbitrary types. -# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ - template \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - FMT_GEN(n, FMT_MAKE_ARG)) { \ - const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ - fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \ - } - -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ - call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ - } \ - FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) -#endif // FMT_USE_VARIADIC_TEMPLATES - -/** - \rst - Defines a variadic function with the specified return type, function name - and argument types passed as variable arguments to this macro. - - **Example**:: - - void print_error(const char *file, int line, const char *format, - fmt::ArgList args) { - fmt::print("{}: {}: ", file, line); - fmt::print(format, args); - } - FMT_VARIADIC(void, print_error, const char *, int, const char *) - - ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that - don't implement variadic templates. You don't have to use this macro if - you don't need legacy compiler support and can use variadic templates - directly:: - - template - void print_error(const char *file, int line, const char *format, - const Args & ... args) { - fmt::print("{}: {}: ", file, line); - fmt::print(format, args...); - } - \endrst - */ -#define FMT_VARIADIC(ReturnType, func, ...) \ - FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) - -#define FMT_VARIADIC_W(ReturnType, func, ...) \ - FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) - -namespace fmt { -FMT_VARIADIC(std::string, format, StringRef) -FMT_VARIADIC_W(std::wstring, format, WStringRef) -FMT_VARIADIC(void, print, StringRef) -FMT_VARIADIC(void, print, std::FILE *, StringRef) -FMT_VARIADIC(void, print, std::ostream &, StringRef) -FMT_VARIADIC(void, print_colored, Color, StringRef) -FMT_VARIADIC(std::string, sprintf, StringRef) -FMT_VARIADIC(int, printf, StringRef) -FMT_VARIADIC(int, fprintf, std::FILE *, StringRef) -} - -// Restore warnings. -#if FMT_GCC_VERSION >= 406 -# pragma GCC diagnostic pop -#endif - -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - -#ifdef FMT_HEADER_ONLY -# include "format.cc" -#endif - -#endif // FMT_FORMAT_H_ diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h deleted file mode 100644 index 5602141cb8..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.h +++ /dev/null @@ -1,136 +0,0 @@ -#ifndef BOX_H -#define BOX_H - -#include -#include - -#include "grid.h" -#include "vertices.h" - -namespace grid -{ - -namespace ba = boost::adaptors; - -template -class Box -{ - public: - typedef GridRef GridProxy; - typedef typename GridProxy::Vertex Position; - - struct InternalTest; - struct BoundaryTest; - struct BoundsTest; - struct PositionToVertex; - - class FreudenthalLinkIterator; - typedef boost::iterator_range FreudenthalLinkRange; - - typedef VerticesIterator VI; - typedef boost::transformed_range - > VertexRange; - - // Topology interface - typedef typename GridProxy::Index Vertex; - typedef boost::transformed_range - > Link; - - - Box(): g_(0, Position()) {} - Box(const Position& shape): - g_(0, shape), to_(shape - Position::one()) {} - Box(const Position& shape, - const Position& from, - const Position& to): - g_(0, shape), from_(from), to_(to) {} - - - const Position& from() const { return from_; } - const Position& to() const { return to_; } - Position& from() { return from_; } - Position& to() { return to_; } - Position shape() const { return to_ - from_ + Position::one(); } - const Position& grid_shape() const { return g_.shape(); } - static unsigned dimension() { return D; } - - size_t size() const { size_t c = 1; for (unsigned i = 0; i < D; ++i) c *= (to_[i] - from_[i] + 1); return c; } - - VertexRange vertices() const { return boost::iterator_range(VI::begin(from_, to_), VI::end(from_, to_)) - | ba::transformed(position_to_vertex()); } - Link link(const Position& p) const { return FreudenthalLinkRange(FreudenthalLinkIterator::begin(p), FreudenthalLinkIterator::end(p)) - | ba::filtered(bounds_test()) - | ba::transformed(position_to_vertex()); } - Link link(const Vertex& v) const { return link(position(v)); } - - Box intersect(const Box& other) const; - bool intersects(const Box& other) const; - void merge(const Box& other); - - bool contains(const Position& p) const; - bool contains(const Vertex& v) const { return contains(position(v)); } - - bool boundary(const Position& p, bool degenerate = false) const; - bool boundary(const Vertex& v, bool deg = false) const { return boundary(position(v), deg); } - Box side(unsigned axis, bool upper) const; - - InternalTest internal_test() const { return InternalTest(*this); } - BoundaryTest boundary_test() const { return BoundaryTest(*this); } - BoundsTest bounds_test() const { return BoundsTest(*this); } - PositionToVertex position_to_vertex() const { return PositionToVertex(*this); } - - - void swap(Box& other) { g_.swap(other.g_); std::swap(from_, other.from_); std::swap(to_, other.to_); } - - bool operator==(const Box& other) const { return from_ == other.from_ && to_ == other.to_; } - - template - friend std::basic_ostream& - operator<<(std::basic_ostream& out, const Box& b) { out << "Box: " << b.from_ << " - " << b.to_ << " inside " << b.g_.shape(); return out; } - - struct InternalTest - { - InternalTest(const Box& box): box_(box) {} - bool operator()(const Vertex& v) const { return !box_.boundary(v); } - const Box& box_; - }; - - struct BoundaryTest - { - BoundaryTest(const Box& box): box_(box) {} - bool operator()(const Vertex& v) const { return box_.boundary(v); } - const Box& box_; - }; - - struct BoundsTest - { - BoundsTest(const Box& box): box_(box) {} - bool operator()(const Position& p) const { return box_.contains(p); } - bool operator()(const Vertex& v) const { return box_.contains(v); } - const Box& box_; - }; - - struct PositionToVertex - { - typedef Vertex result_type; - PositionToVertex(const Box& box): box_(box) {} - Vertex operator()(Position p) const { for (unsigned i = 0; i < D; ++i) p[i] %= box_.grid_shape()[i]; return box_.g_.index(p); } - const Box& box_; - }; - - // computes position inside the box (adjusted for the wrap-around, if need be) - Position position(const Vertex& v) const { Position p = g_.vertex(v); for (unsigned i = 0; i < D; ++i) if (p[i] < from()[i]) p[i] += grid_shape()[i]; return p; } - - private: - GridProxy g_; - Position from_, to_; -}; - -} - -#include "box.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp deleted file mode 100644 index f3af50c047..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/box.hpp +++ /dev/null @@ -1,141 +0,0 @@ -template -grid::Box -grid::Box:: -intersect(const Box& other) const -{ - Position from, to; - for (unsigned i = 0; i < D; ++i) - { - from[i] = std::max(from_[i], other.from_[i]); - to[i] = std::min(to_[i], other.to_[i]); - } - - return Box(g_, from, to); -} - -template -bool -grid::Box:: -intersects(const Box& other) const -{ - for (unsigned i = 0; i < D; ++i) - if (std::max(from_[i], other.from_[i]) > std::min(to_[i], other.to_[i])) - return false; - - return true; -} - -template -bool -grid::Box:: -contains(const Position& p) const -{ - for (unsigned i = 0; i < D; ++i) - if (p[i] > to_[i] || p[i] < from_[i]) - return false; - return true; -} - -template -bool -grid::Box:: -boundary(const Position& p, bool degenerate) const -{ - for (unsigned i = 0; i < D; ++i) - { - if (degenerate && from_[i] == to_[i]) continue; - if (p[i] == from_[i] || p[i] == to_[i]) - return true; - } - - return false; -} - -template -grid::Box -grid::Box:: -side(unsigned axis, bool upper) const -{ - Box res(*this); - - if (upper) - res.from()[axis] = res.to()[axis]; - else - res.to()[axis] = res.from()[axis]; - - return res; -} - -template -void -grid::Box:: -merge(const Box& other) -{ - for (unsigned i = 0; i < D; ++i) - { - from_[i] = std::min(from_[i], other.from_[i]); - to_[i] = std::max(to_[i], other.to_[i]); - } -} - -/* Box::FreudenthalLinkIterator */ -template -class grid::Box::FreudenthalLinkIterator: - public boost::iterator_facade -{ - typedef boost::iterator_facade Parent; - - - public: - typedef typename Parent::value_type value_type; - typedef typename Parent::difference_type difference_type; - typedef typename Parent::reference reference; - - FreudenthalLinkIterator(): loc_(0), dir_(0) {} - FreudenthalLinkIterator(const Position& p, int loc = 0, int dir = 1): - p_(p), v_(p), loc_(loc), dir_(dir) {} - - static FreudenthalLinkIterator - begin(const Position& p) { FreudenthalLinkIterator it(p); ++it; return it; } - static FreudenthalLinkIterator - end(const Position& p) { return FreudenthalLinkIterator(p, 0, -1); } - - private: - void increment(); - bool equal(const FreudenthalLinkIterator& other) const { return v_ == other.v_; } - reference dereference() const { return v_; } - - friend class ::boost::iterator_core_access; - - private: - Position p_, v_; - int loc_; - int dir_; -}; - -template -void -grid::Box::FreudenthalLinkIterator:: -increment() -{ - loc_ += dir_; - if (loc_ == (1 << D)) - { - dir_ = -1; - loc_ += dir_; - } - - for (unsigned i = 0; i < D; ++i) - if (loc_ & (1 << i)) - v_[i] = p_[i] + dir_; - else - v_[i] = p_[i]; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h deleted file mode 100644 index c63fc7c598..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/grid.h +++ /dev/null @@ -1,143 +0,0 @@ -#ifndef GRID_H -#define GRID_H - -#include "point.h" - -namespace grid -{ - -template -struct Grid; - -template -struct GridRef -{ - public: - typedef C Value; - - typedef Point Vertex; - typedef size_t Index; - - public: - template - GridRef(C* data, const Point& shape, bool c_order = true): - data_(data), shape_(shape), c_order_(c_order) { set_stride(); } - - GridRef(Grid& g): - data_(g.data()), shape_(g.shape()), - c_order_(g.c_order()) { set_stride(); } - - template - C operator()(const Point& v) const { return data_[v*stride_]; } - - template - C& operator()(const Point& v) { return data_[v*stride_]; } - - C operator()(Index i) const { return data_[i]; } - C& operator()(Index i) { return data_[i]; } - - const Vertex& - shape() const { return shape_; } - - const C* - data() const { return data_; } - C* data() { return data_; } - - // Set every element to the given value - GridRef& operator=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] = value; return *this; } - GridRef& operator/=(C value) { Index s = size(); for (Index i = 0; i < s; ++i) data_[i] /= value; return *this; } - - Vertex vertex(Index idx) const { Vertex v; for (unsigned i = 0; i < D; ++i) { v[i] = idx / stride_[i]; idx %= stride_[i]; } return v; } - Index index(const Vertex& v) const { return v*stride_; } - - Index size() const { return size(shape()); } - void swap(GridRef& other) { std::swap(data_, other.data_); std::swap(shape_, other.shape_); std::swap(stride_, other.stride_); } - - bool c_order() const { return c_order_; } - - protected: - static Index - size(const Vertex& v) { Index res = 1; for (unsigned i = 0; i < D; ++i) res *= v[i]; return res; } - - void set_stride() - { - Index cur = 1; - if (c_order_) - for (unsigned i = D; i > 0; --i) { stride_[i-1] = cur; cur *= shape_[i-1]; } - else - for (unsigned i = 0; i < D; ++i) { stride_[i] = cur; cur *= shape_[i]; } - - } - void set_shape(const Vertex& v) { shape_ = v; set_stride(); } - void set_data(C* data) { data_ = data; } - void set_c_order(bool order) { c_order_ = order; } - - private: - C* data_; - Vertex shape_; - Vertex stride_; - bool c_order_; -}; - - -template -struct Grid: public GridRef -{ - public: - typedef GridRef Parent; - typedef typename Parent::Value Value; - typedef typename Parent::Index Index; - typedef Parent Reference; - - template - struct rebind { typedef Grid type; }; - - public: - template - Grid(const Point& shape, bool c_order = true): - Parent(new C[size(shape)], shape, c_order) - {} - - Grid(const Parent& g): - Parent(new C[size(g.shape())], g.shape(), - g.c_order()) { copy_data(g.data()); } - - template - Grid(const OtherGrid& g): - Parent(new C[size(g.shape())], - g.shape(), - g.c_order()) { copy_data(g.data()); } - - ~Grid() { delete[] Parent::data(); } - - template - Grid& operator=(const GridRef& other) - { - delete[] Parent::data(); - Parent::set_c_order(other.c_order()); // NB: order needs to be set before the shape, to set the stride correctly - Parent::set_shape(other.shape()); - Index s = size(shape()); - Parent::set_data(new C[s]); - copy_data(other.data()); - return *this; - } - - using Parent::data; - using Parent::shape; - using Parent::operator(); - using Parent::operator=; - using Parent::size; - - private: - template - void copy_data(const OC* data) - { - Index s = size(shape()); - for (Index i = 0; i < s; ++i) - Parent::data()[i] = data[i]; - } -}; - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h deleted file mode 100644 index 0e867c34aa..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/point.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef POINT_H -#define POINT_H - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -namespace grid -{ - -template -class Point: public boost::array, - private boost::addable< Point // Point + Point - , boost::subtractable< Point // Point - Point - , boost::dividable2< Point, Coordinate_ // Point / Coordinate - , boost::multipliable2< Point, Coordinate_ // Point * Coordinate, Coordinate * Point - > > > > -{ - public: - typedef Coordinate_ Coordinate; - typedef boost::array ArrayParent; - - typedef Point LPoint; - typedef Point UPoint; - - template - struct rebind { typedef Point type; }; - - public: - Point() { for (unsigned i = 0; i < D; ++i) (*this)[i] = 0; } - Point(const ArrayParent& a): - ArrayParent(a) {} - template Point(const Point& p) { for (size_t i = 0; i < D; ++i) (*this)[i] = p[i]; } - template Point(const T* a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } - template Point(const std::vector& a) { for (unsigned i = 0; i < D; ++i) (*this)[i] = a[i]; } - - static - unsigned dimension() { return D; } - - static Point zero() { return Point(); } - static Point one() { Point p; for (unsigned i = 0; i < D; ++i) p[i] = 1; return p; } - - LPoint drop(int dim) const { LPoint p; unsigned c = 0; for (unsigned i = 0; i < D; ++i) { if (i == dim) continue; p[c++] = (*this)[i]; } return p; } - UPoint lift(int dim, Coordinate x) const { UPoint p; for (unsigned i = 0; i < D+1; ++i) { if (i < dim) p[i] = (*this)[i]; else if (i == dim) p[i] = x; else if (i > dim) p[i] = (*this)[i-1]; } return p; } - - using ArrayParent::operator[]; - - Point& operator+=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] += y[i]; return *this; } - Point& operator-=(const Point& y) { for (unsigned i = 0; i < D; ++i) (*this)[i] -= y[i]; return *this; } - Point& operator*=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] *= a; return *this; } - Point& operator/=(Coordinate a) { for (unsigned i = 0; i < D; ++i) (*this)[i] /= a; return *this; } - - Point operator-() const { Point res; for (unsigned i = 0; i < D; ++i) res[i] = -(*this)[i]; return res; } - - Coordinate norm() const { return (*this)*(*this); } - - std::ostream& operator<<(std::ostream& out) const { out << (*this)[0]; for (unsigned i = 1; i < D; ++i) out << " " << (*this)[i]; return out; } - std::istream& operator>>(std::istream& in); - - friend - Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } - - template - friend - Coordinate operator*(const Point& x, const Point& y) { Coordinate n = 0; for (size_t i = 0; i < D; ++i) n += x[i] * y[i]; return n; } - - private: - friend class boost::serialization::access; - - template - void serialize(Archive& ar, const unsigned int version) { ar & boost::serialization::base_object(*this); } -}; - -template -std::istream& -Point:: -operator>>(std::istream& in) -{ - std::string point_str; - in >> point_str; // read until ' ' - std::stringstream ps(point_str); - - char x; - for (unsigned i = 0; i < dimension(); ++i) - { - ps >> (*this)[i]; - ps >> x; - } - - return in; -} - - -template -Coordinate norm2(const Point& p) -{ Coordinate res = 0; for (unsigned i = 0; i < D; ++i) res += p[i]*p[i]; return res; } - -template -std::ostream& -operator<<(std::ostream& out, const Point& p) -{ return p.operator<<(out); } - -template -std::istream& -operator>>(std::istream& in, Point& p) -{ return p.operator>>(in); } - -} - -namespace opts -{ - template - struct Traits; - - template - struct Traits< grid::Point > - { - static - std::string type_string() { return "POINT"; } - }; -} - - -#endif // POINT_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h deleted file mode 100644 index 339782e8c4..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/grid/vertices.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef VERTICES_H -#define VERTICES_H - -#include - -namespace grid -{ - -template -class VerticesIterator: - public boost::iterator_facade, - Vertex_, - boost::forward_traversal_tag, - Vertex_, - std::ptrdiff_t> -{ - typedef boost::iterator_facade Parent; - - - public: - typedef typename Parent::value_type value_type; - typedef typename Parent::difference_type difference_type; - typedef typename Parent::reference reference; - - typedef value_type Vertex; - typedef typename Vertex::Coordinate Coordinate; - - // upper bounds are non-inclusive - VerticesIterator(const Vertex& bounds): - to_(bounds - Vertex::one()) {} - - VerticesIterator(const Vertex& pos, - const Vertex& bounds): - pos_(pos), to_(bounds - Vertex::one()) {} - - VerticesIterator(const Vertex& pos, - const Vertex& from, - const Vertex& to): - pos_(pos), from_(from), - to_(to) {} - - - static VerticesIterator - begin(const Vertex& bounds) { return VerticesIterator(bounds); } - static VerticesIterator - end(const Vertex& bounds) { Vertex e; e[0] = bounds[0]; return VerticesIterator(e, bounds); } - - static VerticesIterator - begin(const Vertex& from, const Vertex& to) { return VerticesIterator(from, from, to); } - static VerticesIterator - end(const Vertex& from, const Vertex& to) { Vertex e = from; e[0] = to[0] + 1; return VerticesIterator(e, from, to); } - - private: - void increment(); - bool equal(const VerticesIterator& other) const { return pos_ == other.pos_; } - reference dereference() const { return pos_; } - - friend class ::boost::iterator_core_access; - - private: - Vertex pos_; - Vertex from_; - Vertex to_; -}; - -} - -template -void -grid::VerticesIterator:: -increment() -{ - unsigned j = Vertex::dimension() - 1; - while (j > 0 && pos_[j] == to_[j]) - { - pos_[j] = from_[j]; - --j; - } - ++pos_[j]; -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h deleted file mode 100644 index 516f27b2d1..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/matrix-filtration.h +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace dionysus -{ - - -template -class MatrixFiltrationCell; - - -// adapt Matrix as a Filtration to make it possible to feed into reduction algorithms -template -class MatrixFiltration -{ - public: - using Matrix = Matrix_; - using CellValue = CellValue_; - using Dimensions = std::vector; - using Values = std::vector; - using Cell = MatrixFiltrationCell; - - - public: - MatrixFiltration(Matrix m, Dimensions dimensions, Values values): - m_(std::move(m)), - dimensions_(dimensions), - values_(values) { assert(m_->size() == dimensions_.size()); assert(m_->size() == values_.size()); } - - Cell operator[](size_t i) const { return Cell(this, i); } - size_t size() const { return m_.size(); } - - size_t index(const Cell& c) const; - - Cell begin() const { return Cell(this, 0); } - Cell end() const { return Cell(this, size()); } - - const Dimensions& dimensions() const { return dimensions_; } - const Values& values() const { return values_; } - - private: - Matrix m_; - Dimensions dimensions_; - Values values_; - - friend class MatrixFiltrationCell; -}; - - -template -class MatrixFiltrationCell -{ - public: - using MatrixFiltration = MatrixFiltration_; - using Matrix = typename MatrixFiltration::Matrix; - using Data = typename MatrixFiltration::CellValue; - using Field = typename Matrix::Field; - - template - using Entry = ChainEntry; - - template - using BoundaryChain = std::vector>; - - public: - MatrixFiltrationCell(const MatrixFiltration* mf, size_t i): - mf_(mf), i_(i) {} - - short unsigned dimension() const { return mf_->dimensions_[i_]; } - const Data& data() const { return mf_->values_[i_]; } - - bool operator==(const MatrixFiltrationCell& other) const { return i_ == other.i_; } - bool operator!=(const MatrixFiltrationCell& other) const { return i_ != other.i_; } - - BoundaryChain<> boundary() const - { - BoundaryChain<> bdry; - for (auto& entry : (mf_->m_)[i_]) - bdry.emplace_back(Entry<> { entry.e, MatrixFiltrationCell(mf_, entry.i) }); - return bdry; - } - - template - BoundaryChain boundary(const Field_& field) const - { - BoundaryChain bdry; - for (auto& entry : (mf_->m_)[i_]) - bdry.emplace_back(Entry { field.init(entry.e), MatrixFiltrationCell(mf_, entry.i) }); - return bdry; - } - - // iterator interface - MatrixFiltrationCell operator++(int) { MatrixFiltrationCell copy = *this; i_++; return copy; } - MatrixFiltrationCell& operator++() { ++i_; return *this; } - - const MatrixFiltrationCell& operator*() const { return *this; } - MatrixFiltrationCell& operator*() { return *this; } - - size_t i() const { return i_; } - - friend - std::ostream& operator<<(std::ostream& out, const MatrixFiltrationCell& c) - { out << c.i_; return out; } - - private: - const MatrixFiltration* mf_ = nullptr; - size_t i_; -}; - -template -size_t -MatrixFiltration::index(const Cell& c) const -{ - return c.i(); -} - -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h deleted file mode 100644 index 3d83d4ae44..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef DIONYSUS_OMNI_FIELD_REDUCTION_H -#define DIONYSUS_OMNI_FIELD_REDUCTION_H - -#include -#include - -#include "reduction.h" // for unpaired -#include "fields/q.h" -#include "fields/zp.h" -#include "chain.h" - -namespace dionysus -{ - -template, class Q_ = ::dionysus::Q<>, class Zp_ = ::dionysus::ZpField> -class OmniFieldPersistence -{ - public: - using Index = Index_; - using Q = Q_; - using Field = Q; - using Comparison = Comparison_; - - using BaseElement = typename Q::BaseElement; - using Zp = Zp_; - using Zps = std::unordered_map; - - using QElement = typename Q::Element; - using QEntry = ChainEntry; - using QChain = std::vector; - - using ZpElement = typename Zp::Element; - using ZpEntry = ChainEntry; - using ZpChain = std::vector; - - using QChains = std::vector; - using ZpChains = std::unordered_map>; - - using QLows = std::unordered_map; - using ZpLows = std::unordered_map>; - - using QPairs = std::vector; - using ZpPairs = std::unordered_map>; - - using Factors = std::vector; - - using Specials = std::unordered_map>; - - const Field& field() const { return q_; } - - void sort(QChain& c) { std::sort(c.begin(), c.end(), - [this](const QEntry& e1, const QEntry& e2) - { return this->cmp_(e1.index(), e2.index()); }); } - - template - void add(const ChainRange& chain) { return add(QChain(std::begin(chain), std::end(chain))); } - void add(QChain&& chain); - - void reserve(size_t s) { q_chains_.reserve(s); q_pairs_.reserve(s); } - size_t size() const { return q_pairs_.size(); } - - void reduce(ZpChain& zp_chain, BaseElement p); - ZpChain convert(const QChain& c, const Zp& field) const; - bool special(Index i, BaseElement p) const { auto it = zp_chains_.find(i); if (it == zp_chains_.end()) return false; if (it->second.find(p) == it->second.end()) return false; return true; } - Specials specials() const - { - Specials specials; - for (auto& x : zp_chains_) - for (auto& y : x.second) - specials[x.first].push_back(y.first); - return specials; - } - - const Zp& zp(BaseElement p) const { auto it = zps_.find(p); if (it != zps_.end()) return it->second; return zps_.emplace(p, Zp(p)).first->second; } - - static Factors factor(BaseElement x); - - const QChains& q_chains() const { return q_chains_; } - const ZpChains& zp_chains() const { return zp_chains_; } - - // This is a bit of a hack; it takes advantage of the fact that zp(p) - // generates field on-demand and memoizes them. So there is an entry in - // zps_ only if something special happened over the prime. - Factors primes() const { Factors result; result.reserve(zps_.size()); for (auto& x : zps_) result.push_back(x.first); return result; } - - // TODO: no skip support for now - bool skip(Index) const { return false; } - void add_skip() {} - void set_skip(Index, bool flag = true) {} - - Index pair(Index i, BaseElement p) const; - void set_pair(Index i, Index j); - void set_pair(Index i, Index j, BaseElement p); - static const Index unpaired() { return Reduction::unpaired; } - - private: - QChains q_chains_; - ZpChains zp_chains_; - - QLows q_lows_; - ZpLows zp_lows_; - - QPairs q_pairs_; - ZpPairs zp_pairs_; - - Q q_; - mutable Zps zps_; - - Comparison cmp_; -}; - -// Make OmniFieldPersistence act like a ReducedMatrix (e.g., for the purpose of constructing a persistence diagram) -template -struct PrimeAdapter -{ - using Persistence = OmniFieldPersistence; - using Prime = typename Persistence::BaseElement; - using Index = typename Persistence::Index; - - PrimeAdapter(const Persistence& persistence, Prime p): - persistence_(persistence), p_(p) {} - - bool skip(Index i) const { return persistence_.skip(i); } - - size_t size() const { return persistence_.size(); } - Index pair(Index i) const { return persistence_.pair(i, p_); } - static const Index unpaired() { return Persistence::unpaired(); } - - const Persistence& persistence_; - Prime p_; -}; - -template -PrimeAdapter -prime_adapter(const OmniFieldPersistence& persistence, - typename PrimeAdapter::Prime p) -{ - return PrimeAdapter(persistence, p); -} - -} // dionysus - -#include "omni-field-persistence.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp deleted file mode 100644 index 68d5fbede7..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/omni-field-persistence.hpp +++ /dev/null @@ -1,250 +0,0 @@ -template -void -dionysus::OmniFieldPersistence:: -add(QChain&& chain) -{ - sort(chain); - - q_chains_.emplace_back(std::move(chain)); - q_pairs_.emplace_back(unpaired()); - Index i = q_chains_.size() - 1; - - QChain& c = q_chains_.back(); - - auto reduce = [this,&c,i](BaseElement p) - { - auto zp_chain = convert(c, zp(p)); - - this->reduce(zp_chain, p); - - if (!zp_chain.empty()) - { - auto l = zp_chain.back().index(); - zp_lows_[l].emplace(p,i); - set_pair(l,i,p); - } - - zp_chains_[i].emplace(p, std::move(zp_chain)); // empty chain is still a valid indicator that we don't need to bother with this field - }; - - // reduce - auto entry_cmp = [this](const QEntry& e1, const QEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; - while (!c.empty()) - { - auto& low = c.back(); - - auto e = low.element(); - auto l = low.index(); - assert(!q_.is_zero(e)); - if (e != q_.id()) - { - auto factors = factor(q_.numerator(e)); - for (auto p : factors) - { - if (!special(i, p)) // there is already a dedicated column over p - reduce(p); - } - } - - auto it_zp = zp_lows_.find(l); - if (it_zp != zp_lows_.end()) - for (auto& x : it_zp->second) - { - auto p = x.first; - if (!special(i,p)) - reduce(p); - } - - auto it_q = q_lows_.find(l); - if (it_q != q_lows_.end()) - { - Index j = it_q->second; - - // add the primes from j to i - auto it_zp = zp_chains_.find(j); - if (it_zp != zp_chains_.end()) - for (auto& x : it_zp->second) - { - auto p = x.first; - if (!special(i,p)) - reduce(p); - } - - // reduce over Q - auto j_chain = q_chains_[j]; - auto j_e = j_chain.back().element(); - - auto m = q_.neg(q_.div(e,j_e)); - Chain::addto(c, m, j_chain, q_, entry_cmp); - assert(c.empty() || !q_.is_zero(c.back().element())); - } else - { - q_lows_.emplace(l,i); - set_pair(l,i); - break; - } - } -} - -template -void -dionysus::OmniFieldPersistence:: -reduce(ZpChain& zp_chain, BaseElement p) -{ - auto& field = zp(p); - - auto entry_cmp = [this](const ZpEntry& e1, const ZpEntry& e2) { return this->cmp_(e1.index(), e2.index()); }; - - while (!zp_chain.empty()) - { - auto& low = zp_chain.back(); - auto j = low.index(); - - auto it = zp_lows_.find(j); - if (it != zp_lows_.end()) - { - auto it2 = it->second.find(p); - if (it2 != it->second.end()) - { - const ZpChain& co = zp_chains_[it2->second][p]; - - auto m = field.neg(field.div(low.element(), co.back().element())); - assert(m < p); - Chain::addto(zp_chain, m, co, field, entry_cmp); - continue; - } - } - - auto qit = q_lows_.find(j); - if (qit == q_lows_.end() || special(qit->second, p)) // no valid pivot over Q - return; - - // TODO: this could be optimized (add and convert on the fly) - auto& q_chain = q_chains_[qit->second]; - assert(q_chain.empty() || !q_.is_zero(q_chain.back().element())); - - auto co = convert(q_chain, field); - auto m = field.neg(field.div(low.element(), co.back().element())); - Chain::addto(zp_chain, m, co, field, entry_cmp); - - assert(!zp_chain.empty() || zp_chain.back().index() != j); - } -} - -template -typename dionysus::OmniFieldPersistence::ZpChain -dionysus::OmniFieldPersistence:: -convert(const QChain& c, const Zp& field) const -{ - ZpChain result; - result.reserve(c.size()); - auto p = field.prime(); - for (auto& x : c) - { - auto num = q_.numerator(x.element()) % p; - if (num != 0) - { - while (num < 0) num += p; - auto denom = q_.denominator(x.element()) % p; - while (denom < 0) denom += p; - assert(denom % p != 0); - result.emplace_back(field.div(num, denom), x.index()); - } - } - return result; -} - - -template -typename dionysus::OmniFieldPersistence::Factors -dionysus::OmniFieldPersistence:: -factor(BaseElement x) -{ - if (x < 0) - x = -x; - Factors result; - - if (Q::is_prime(x)) - { - result.push_back(x); - return result; - } - - BaseElement p { 2 }; - while (p*p <= x) - { - if (x % p == 0) - { - result.push_back(p); - do { x /= p; } while (x % p == 0); - if (Q::is_prime(x)) - { - result.push_back(x); - break; - } - } - ++p; - } - if (x > 1) - result.push_back(x); - - return result; -} - -template -typename dionysus::OmniFieldPersistence::Index -dionysus::OmniFieldPersistence:: -pair(Index i, BaseElement p) const -{ - if (p == 1) - return q_pairs_[i]; - else - { - auto it = zp_pairs_.find(p); - if (it == zp_pairs_.end()) - return q_pairs_[i]; - else - { - auto pit = it->second.find(i); - if (pit == it->second.end()) - return q_pairs_[i]; - else - return pit->second; - } - } -} - -template -void -dionysus::OmniFieldPersistence:: -set_pair(Index i, Index j, BaseElement p) -{ - auto& pairs = zp_pairs_[p]; - pairs[i] = j; - pairs[j] = i; -} - -template -void -dionysus::OmniFieldPersistence:: -set_pair(Index i, Index j) -{ - q_pairs_[i] = j; - q_pairs_[j] = i; - - auto it = zp_chains_.find(j); - if (it == zp_chains_.end()) - return; - - auto& chains = it->second; - for (auto& x : chains) - { - auto p = x.first; - auto& chain = x.second; - if (chain.empty()) - { - zp_pairs_[p][j] = unpaired(); - zp_pairs_[p][i] = unpaired(); - } - } -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h deleted file mode 100644 index 1a9bbf71bf..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/opts/opts.h +++ /dev/null @@ -1,499 +0,0 @@ -/** - * Author: Dmitriy Morozov - * The interface is heavily influenced by GetOptPP (https://code.google.com/p/getoptpp/). - * The parsing logic is from ProgramOptions.hxx (https://github.com/Fytch/ProgramOptions.hxx). - * - * History: - * - 2015-06-01: added Traits<...>::type_string() for long, unsigned long - * - ... - * - 2018-04-27: replace parsing logic with the one from ProgramOptions.hxx to - * make the parser compliant with [GNU Program Argument Syntax - * Conventions](https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.html) - * - 2018-05-11: add dashed_non_option(), to accept arguments that are negative numbers - */ - -#ifndef OPTS_OPTS_H -#define OPTS_OPTS_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace opts { - -// Converters -template -struct Converter -{ - Converter() {} - static - bool convert(const std::string& val, T& res) - { - std::istringstream iss(val); - iss >> res; - return !iss.fail() && iss.eof(); - } -}; - -// Type -template -struct Traits -{ - static std::string type_string() { return "UNKNOWN TYPE"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "SHORT INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "LONG"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "UNSIGNED INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "SHORT UNSIGNED INT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "UNSIGNED LONG"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "FLOAT"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "DOUBLE"; } -}; - -template<> -struct Traits -{ - static std::string type_string() { return "STRING"; } -}; - - -struct BasicOption -{ - using IsShort = std::function; - - BasicOption(char s_, - std::string l_, - std::string default_, - std::string type_, - std::string help_): - s(s_), l(l_), d(default_), t(type_), help(help_) {} - virtual ~BasicOption() {} - - int long_size() const { return l.size() + 1 + t.size(); } - - void output(std::ostream& out, int max_long) const - { - out << " "; - if (s) - out << '-' << s << ", "; - else - out << " "; - - out << "--" << l << ' '; - - if (!t.empty()) - out << t; - - for (int i = long_size(); i < max_long; ++i) - out << ' '; - - out << " " << help; - - if (!d.empty()) - { - out << " [default: " << d << "]"; - } - out << '\n'; - } - - virtual bool flag() const { return false; } - virtual bool parse(int argc, char** argv, int& i, int j, IsShort is_short); - virtual bool set(std::string arg) =0; - - char s; - std::string l; - std::string d; - std::string t; - std::string help; -}; - -// Option -template -struct OptionContainer: public BasicOption -{ - OptionContainer(char s_, - const std::string& l_, - T& var_, - const std::string& help_, - const std::string& type_ = Traits::type_string()): - BasicOption(s_, l_, default_value(var_), type_, help_), - var(&var_) {} - - static - std::string default_value(const T& def) - { - std::ostringstream oss; - oss << def; - return oss.str(); - } - - bool set(std::string s) override { return Converter::convert(s, *var); } - - T* var; -}; - -template<> -struct OptionContainer: public BasicOption -{ - OptionContainer(char s_, - const std::string& l_, - bool& var_, - const std::string& help_): - BasicOption(s_, l_, "", "", help_), - var(&var_) { *var = false; } - - bool parse(int, char**, int&, int, IsShort) override { *var = true; return true; } - bool set(std::string) override { return true; } - bool flag() const override { return true; } - - bool* var; -}; - -template -struct OptionContainer< std::vector >: public BasicOption -{ - OptionContainer(char s_, - const std::string& l_, - std::vector& var_, - const std::string& help_, - const std::string& type_ = "SEQUENCE"): - BasicOption(s_, l_, default_value(var_), type_, help_), - var(&var_), first(true) { } - - static - std::string default_value(const std::vector& def) - { - std::ostringstream oss; - oss << "("; - if (def.size()) - oss << def[0]; - for (size_t i = 1; i < def.size(); ++i) - oss << ", " << def[i]; - oss << ")"; - return oss.str(); - } - - bool set(std::string s) override - { - if (first) - { - var->clear(); - first = false; - } - - T x; - bool result = Converter::convert(s,x); - var->emplace_back(std::move(x)); - return result; - } - - std::vector* var; - mutable bool first; -}; - - -template -std::unique_ptr -Option(char s, const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help)}; } - -template -std::unique_ptr -Option(char s, const std::string& l, T& var, - const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(s, l, var, help, type)}; } - -template -std::unique_ptr -Option(const std::string& l, T& var, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help)}; } - -template -std::unique_ptr -Option(const std::string& l, T& var, - const std::string& type, const std::string& help) { return std::unique_ptr{new OptionContainer(0, l, var, help, type)}; } - -// PosOption -template -struct PosOptionContainer -{ - PosOptionContainer(T& var_): - var(&var_) {} - - bool parse(std::list& args) const - { - if (args.empty()) - return false; - - bool result = Converter::convert(args.front(), *var); - if (!result) - std::cerr << "error: failed to parse " << args.front() << '\n'; - args.pop_front(); - return result; - } - - T* var; -}; - -template -PosOptionContainer -PosOption(T& var) { return PosOptionContainer(var); } - - -// Options -struct Options -{ - Options(): - failed(false) {} - - inline - Options& operator>>(std::unique_ptr opt); - template - Options& operator>>(const PosOptionContainer& poc); - - operator bool() { return !failed; } - - - friend - std::ostream& - operator<<(std::ostream& out, const Options& ops) - { - int max_long = 0; - for (auto& cur : ops.options) - { - int cur_long = cur->long_size(); - if (cur_long > max_long) - max_long = cur_long; - } - - out << "Options:\n"; - for (auto& cur : ops.options) - cur->output(out, max_long); - - return out; - } - - bool parse(int argc, char** argv); - - void unrecognized_option(std::string arg) const - { - std::cerr << "error: unrecognized option " << arg << '\n'; - } - - static bool dashed_non_option(char* arg, BasicOption::IsShort is_short) - { - return arg[ 0 ] == '-' - && (std::isdigit(arg[ 1 ]) || arg[ 1 ] == '.') - && !is_short(arg[ 1 ]); - } - - private: - std::list args; - std::list> options; - bool failed; -}; - -bool -BasicOption::parse(int argc, char** argv, int& i, int j, IsShort is_short) -{ - char* argument; - char* cur_arg = argv[i]; - // -v... - if (argv[i][j] == '\0') - { - // -v data - if (i + 1 < argc && (argv[i+1][0] != '-' || Options::dashed_non_option(argv[i+1], is_short))) - { - ++i; - argument = argv[i]; - } else - { - std::cerr << "error: cannot find the argument; ignoring " << argv[i] << '\n'; - return false; - } - } else if (argv[i][j] == '=') - { - // -v=data - argument = &argv[i][j+1]; - } else if( j == 2 ) { // only for short options - // -vdata - argument = &argv[i][j]; - } else - { - std::cerr << "error: unexpected character \'" << argv[i][j] << "\' ignoring " << argv[i] << '\n'; - return false; - } - bool result = set(argument); - if (!result) - std::cerr << "error: failed to parse " << argument << " in " << cur_arg << '\n'; - return result; -} - -bool -Options::parse(int argc, char** argv) -{ - std::map short_opts; - std::map long_opts; - - for (auto& opt : options) - { - if (opt->s) - short_opts[opt->s] = opt.get(); - - long_opts[opt->l] = opt.get(); - } - - auto is_short = [&short_opts](char c) -> bool { return short_opts.find(c) != short_opts.end(); }; - - for (int i = 1; i < argc; ++i) - { - if( argv[ i ][ 0 ] == '\0' ) - continue; - if( argv[ i ][ 0 ] != '-' || dashed_non_option(argv[i], is_short)) - args.push_back(argv[i]); - else - { - // -... - if( argv[ i ][ 1 ] == '\0' ) - { - // - - args.push_back(argv[i]); - } else if( argv[ i ][ 1 ] == '-' ) - { - if( argv[ i ][ 2 ] == '\0' ) - { - // -- - while( ++i < argc ) - args.push_back(argv[i]); - } else { - // --... - char* first = &argv[ i ][ 2 ]; - char* last = first; - for(; *last != '=' && *last != '\0'; ++last); - if (first == last) - { - failed = true; - unrecognized_option(argv[i]); - } else - { - auto opt_it = long_opts.find(std::string{first,last}); - if (opt_it == long_opts.end()) - { - failed = true; - unrecognized_option(argv[i]); - } else - { - failed |= !opt_it->second->parse(argc, argv, i, last - argv[i], is_short); - } - } - } - } else - { - // -f... - auto opt_it = short_opts.find(argv[i][1]); - if (opt_it == short_opts.end()) - { - failed = true; - unrecognized_option(argv[i]); - } else if (opt_it->second->flag()) - { - opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag - - // -fgh - char c; - for(int j = 1; (c = argv[i][j]) != '\0'; ++j) - { - if (!std::isprint(c) || c == '-') - { - failed = true; - std::cerr << "error: invalid character\'" << c << " ignoring " << &argv[i][j] << '\n'; - break; - } - opt_it = short_opts.find(c); - if (opt_it == short_opts.end()) - { - failed = true; - unrecognized_option("-" + std::string(1, c)); - continue; - } - if (!opt_it->second->flag()) - { - failed = true; - std::cerr << "error: non-void options not allowed in option packs; ignoring " << c << '\n'; - continue; - } - opt_it->second->parse(argc, argv, i, 0, is_short); // arguments are meaningless; just sets the flag - } - } else - { - failed |= !opt_it->second->parse(argc, argv, i, 2, is_short); - } - } - } - } - - return !failed; -} - -Options& -Options::operator>>(std::unique_ptr opt) -{ - options.emplace_back(std::move(opt)); - return *this; -} - -template -Options& -Options::operator>>(const PosOptionContainer& poc) -{ - if (!failed) - failed = !poc.parse(args); - return *this; -} - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h deleted file mode 100644 index 5f26bd2a1b..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/ordinary-persistence.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef DIONYSUS_ORDINARY_PERSISTENCE_H -#define DIONYSUS_ORDINARY_PERSISTENCE_H - -#include "reduced-matrix.h" - -namespace dionysus -{ - -/* Move this into a ReducedMatrix class */ - -// Ordinary D -> R reduction -template, - template class... Visitors> -using OrdinaryPersistence = ReducedMatrix; - -// No negative optimization -template> -struct NoNegative -{ - template - struct Visitor: public EmptyVisitor - { - template - void chain_initialized(Self* matrix, Chain& c) - { - for (auto cur = std::begin(c); cur != std::end(c); ++cur) - { - Index i = cur->index(); - Index p = matrix->pair(i); - if (!(p == Self::unpaired() || (*matrix)[i].empty())) - c.erase(cur--); - } - } - }; - - template - using V2 = EmptyVisitor; -}; - -template, - template class... Visitors> -using OrdinaryPersistenceNoNegative = ReducedMatrix::template Visitor, - Visitors...>; - -// TODO: add clearing optimization (possibly bake it into the code itself) - -template, - template class... Visitors> -using FastPersistence = ReducedMatrix::template Visitor, - //Clearing::template Visitor, // FIXME - Visitors...>; - - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h deleted file mode 100644 index 81c066bda6..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/pair-recorder.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef DIONYSUS_PAIR_RECORDER_H -#define DIONYSUS_PAIR_RECORDER_H - -namespace dionysus -{ - -template -struct PairRecorder: public Persistence_ -{ - typedef Persistence_ Persistence; - typedef typename Persistence::Index Index; - - - using Persistence::Persistence; - - template - Index add(const ChainRange& chain) - { - Index p = Persistence::add(chain); - pairs_.push_back(p); - if (p != unpaired()) - pairs_[p] = pairs_.size() - 1; - - return p; - } - - Index pair(Index i) const { return pairs_[i]; } - - void resize(size_t s) { Persistence::resize(s); pairs_.resize(s, unpaired()); } - size_t size() const { return pairs_.size(); } - static const Index unpaired() { return Reduction::unpaired; } - - std::vector pairs_; -}; - -template -struct PairChainRecorder: public PairRecorder -{ - using Persistence = Persistence_; - using Parent = PairRecorder; - using Index = typename Persistence_::Index; - using Chain = typename Persistence_::Chain; - - using Parent::Parent; - - template - Index add(const ChainRange& chain) - { - auto p_chain = Persistence::add(chain, keep_cocycles); - Index p = std::get<0>(p_chain); - - pairs_.push_back(p); - chains_.emplace_back(); - - if (p != unpaired()) - { - pairs_[p] = pairs_.size() - 1; - chains_[p] = std::move(std::get<1>(p_chain)); - } - - return p; - } - - using Parent::unpaired; - - Index pair(Index i) const { return pairs_[i]; } - const Chain& chain(Index i) const { return chains_[i]; } // chain that dies at i - void resize(size_t s) { Parent::resize(s); chains_.resize(s); } - - std::vector chains_; - using Parent::pairs_; - - bool keep_cocycles = true; -}; - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h deleted file mode 100644 index f7a04b130d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.h +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef DIONYSUS_REDUCED_MATRIX_H -#define DIONYSUS_REDUCED_MATRIX_H - -#include -#include - -#include "chain.h" -#include "reduction.h" - -namespace dionysus -{ - -template, template class... Visitors> -class ReducedMatrix -{ - public: - typedef ReducedMatrix Self; - - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef std::tuple...> VisitorsTuple; - template - using Visitor = std::tuple_element; - - typedef typename Field::Element FieldElement; - typedef ChainEntry Entry; - typedef std::vector Chain; - - typedef std::vector Chains; - typedef std::vector Indices; - typedef std::vector SkipFlags; - - public: - ReducedMatrix(const Field& field): - field_(field) {} - - ReducedMatrix(const Field& field, - const Comparison& cmp, - const Visitors&... visitors): - field_(field), - cmp_(cmp), - visitors_(visitors...) {} - - ReducedMatrix(Field&& field, - Comparison&& cmp, - Visitors&&... visitors): - field_(std::move(field)), - cmp_(std::move(cmp)), - visitors_(visitors...) {} - - ReducedMatrix(Self&& m) = default; - ReducedMatrix(const Self& m) = default; - - template class... OtherVisitors> - ReducedMatrix(ReducedMatrix&& other): - field_(other.field_), - cmp_(other.cmp_), - reduced_(std::move(other.reduced_)), - pairs_(std::move(other.pairs_)), - skip_(std::move(other.skip_)) {} - - template - Index add(const ChainRange& chain) { return add(Chain(std::begin(chain), std::end(chain))); } - Index add(Chain&& chain); - - template - void set(Index i, const ChainRange& chain) { return set(i, Chain(std::begin(chain), std::end(chain))); } - void set(Index i, Chain&& chain); - - Index reduce(Index i); - Index reduce(Chain& c) { return reduce(c, reduced_, pairs_); } - template - Index reduce(Chain& c, const ChainsLookup& chains, const LowLookup& low); - - Index reduce_upto(Index i); // TODO - - size_t size() const { return pairs_.size(); } - void clear() { Chains().swap(reduced_); Indices().swap(pairs_); } - - void sort(Chain& c) { std::sort(c.begin(), c.end(), [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }); } - - const Chain& operator[](Index i) const { return reduced_[i]; } - Index pair(Index i) const { return pairs_[i]; } - void set_pair(Index i, Index j) { pairs_[i] = j; pairs_[j] = i; } - - Chain& column(Index i) { return reduced_[i]; } - - bool skip(Index i) const { return skip_[i]; } - void add_skip(); - void set_skip(Index i, bool flag = true) { skip_[i] = flag; } - - const Field& field() const { return field_; } - const Comparison& cmp() const { return cmp_; } - void reserve(size_t s) { reduced_.reserve(s); pairs_.reserve(s); } - void resize(size_t s); - - const Chains& columns() const { return reduced_; } - - template - Visitor& visitor() { return std::get(visitors_); } - - static const Index unpaired() { return Reduction::unpaired; } - - private: - template class... Vs> - friend class ReducedMatrix; // let's all be friends - - public: - // Visitors::chain_initialized(c) - template - typename std::enable_if::type - visitors_chain_initialized(Chain& c) {} - - template - typename std::enable_if::type - visitors_chain_initialized(Chain& c) { std::get(visitors_).chain_initialized(this, c); visitors_chain_initialized(c); } - - // Visitors::addto(m, cl) - template - typename std::enable_if::type - visitors_addto(FieldElement m, Index cl) {} - - template - typename std::enable_if::type - visitors_addto(FieldElement m, Index cl) { std::get(visitors_).addto(this, m, cl); visitors_addto(m, cl); } - - // Visitors::reduction_finished(m, cl) - template - typename std::enable_if::type - visitors_reduction_finished() {} - - template - typename std::enable_if::type - visitors_reduction_finished() { std::get(visitors_).reduction_finished(this); visitors_reduction_finished(); } - - private: - Field field_; - Comparison cmp_; - Chains reduced_; // matrix R - Indices pairs_; - SkipFlags skip_; // indicates whether the column should be skipped (e.g., for relative homology) - VisitorsTuple visitors_; -}; - -/* Visitors */ - -// The prototypical visitor. Others may (and probably should) inherit from it. -template -struct EmptyVisitor -{ - EmptyVisitor() = default; - - template - EmptyVisitor(const EmptyVisitor&) {} - - - template - void chain_initialized(Self*, Chain& c) {} - - void addto(Self*, typename Field::Element m, Index cl) {} - void reduction_finished(Self*) {} -}; - -} - -#include "reduced-matrix.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp deleted file mode 100644 index 3e4aca8f29..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduced-matrix.hpp +++ /dev/null @@ -1,78 +0,0 @@ -template class... V> -void -dionysus::ReducedMatrix:: -resize(size_t s) -{ - reduced_.resize(s); - pairs_.resize(s, unpaired()); - skip_.resize(s, false); -} - -template class... V> -typename dionysus::ReducedMatrix::Index -dionysus::ReducedMatrix:: -add(Chain&& chain) -{ - // TODO: skip the computation entirely if we already know this is positive (in case of the clearing optimization) - Index i = pairs_.size(); - pairs_.emplace_back(unpaired()); - reduced_.emplace_back(); - skip_.push_back(false); - - set(i, std::move(chain)); - - return reduce(i); -} - -template class... V> -void -dionysus::ReducedMatrix:: -add_skip() -{ - pairs_.emplace_back(unpaired()); - reduced_.emplace_back(); - skip_.push_back(true); -} - -template class... V> -void -dionysus::ReducedMatrix:: -set(Index i, Chain&& c) -{ - sort(c); - visitors_chain_initialized(c); - reduced_[i] = std::move(c); -} - -template class... V> -typename dionysus::ReducedMatrix::Index -dionysus::ReducedMatrix:: -reduce(Index i) -{ - Chain& c = column(i); - Index pair = reduce(c); - - if (pair != unpaired()) - pairs_[pair] = i; - - pairs_[i] = pair; - visitors_reduction_finished<>(); - - return pair; -} - -template class... V> -template -typename dionysus::ReducedMatrix::Index -dionysus::ReducedMatrix:: -reduce( Chain& c, - const ChainsLookup& chains, - const LowLookup& lows) -{ - auto entry_cmp = [this](const Entry& e1, const Entry& e2) { return this->cmp_(e1.index(), e2.index()); }; - return Reduction::reduce(c, chains, lows, field_, - [this](FieldElement m, Index cl) - { this->visitors_addto<>(m, cl); }, - entry_cmp); -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h deleted file mode 100644 index 2afd333d41..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/reduction.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef DIONYSUS_REDUCTION_H -#define DIONYSUS_REDUCTION_H - -#include -#include -#include -#include -#include "chain.h" - -namespace dionysus -{ - -namespace detail -{ - -template -struct Unpaired -{ static constexpr Index value() { return std::numeric_limits::max(); } }; - -} - -template -struct Reduction -{ - typedef Index_ Index; - - template - using AddtoVisitor = std::function; - - template - struct CallToSub; - - static const Index unpaired; - - template> - static - Index reduce(Chain1& c, - const ChainsLookup& chains, - const LowLookup& lows, - const Field& field, - const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, - const Comparison& cmp = Comparison()) - { - typedef typename Field::Element FieldElement; - - while (!c.empty()) - { - //auto& low = c.back(); - auto& low = *(std::prev(c.end())); - Index l = low.index(); - Index cl = lows(l); - // std::cout << "idx: " << std::get<0>(cl) << ", " << std::get<1>(cl) << "\n"; - if (cl == unpaired) - return l; - else - { - // Reduce further - auto& co = chains(cl); - auto& co_low = co.back(); - FieldElement m = field.neg(field.div(low.element(), co_low.element())); - // c += m*co - Chain::addto(c, m, co, field, cmp); - visitor(m, cl); - } - } - return unpaired; - } - - template> - static - Index reduce(Chain1& c, - const std::vector& chains, - const std::vector& lows, - const Field& field, - const AddtoVisitor& visitor = [](typename Field::Element, Index) {}, - const Comparison& cmp = Comparison()) - { - return reduce(c, - CallToSub(chains), - CallToSub(lows), - field, visitor, cmp); - } - - // This is a work-around a bug in GCC (should really be a lambda function) - template - struct CallToSub - { - CallToSub(const std::vector& items_): - items(items_) {} - const Item& operator()(Index i) const { return items[i]; } - const std::vector& items; - }; -}; - - -template -const Index -Reduction::unpaired = detail::Unpaired::value(); - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h deleted file mode 100644 index 167a32779d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.h +++ /dev/null @@ -1,84 +0,0 @@ -#ifndef RELATIVE_HOMOLOGY_ZIGZAG_H -#define RELATIVE_HOMOLOGY_ZIGZAG_H - -#include -#include - -#include "zigzag-persistence.h" - -namespace dionysus -{ - -namespace ba = boost::adaptors; - -template> -class RelativeHomologyZigzag -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef ZigzagPersistence ZZP; - typedef typename ZZP::IndexChain IndexChain; - typedef typename ZZP::FieldElement FieldElement; - typedef typename IndexChain::value_type ChainEntry; - - - typedef Comparison Cmp; - - RelativeHomologyZigzag(const Field& field, - const Comparison& cmp = Comparison()): - zzp_(field, cmp) - { - zzp_.add( IndexChain() ); // vertex w - ++zzp_op_; - ++zzp_cell_; - } - - template - void add_both(const ChainRange& chain); - - void remove_both(Index cell); - - // index of the absolute cell; chain = its boundary - template - Index add(Index cell, const ChainRange& chain); // add to the relative part - - Index remove(Index cell); // remove from the relative part - - const Field& field() const { return zzp_.field(); } - const Cmp& cmp() const { return zzp_.cmp(); } - - size_t alive_size() const { return zzp_.alive_size() - 1; } // -1 for the cone vertex - - static - const Index unpaired() { return ZZP::unpaired(); } - - private: - template - IndexChain relative_chain(Index cell, const ChainRange& chain) const; - - template - IndexChain absolute_chain(const ChainRange& chain) const; - - Index abs_index(Index idx) const { return absolute_.left.find(idx)->second; } - Index rel_index(Index idx) const { return relative_.left.find(idx)->second; } - Index decode_pair(Index pair); - - private: - ZZP zzp_; // underlying (cone) implementation - boost::bimap absolute_; // bimap between our cells and zzp absolute cells - boost::bimap relative_; // bimap between our cells and zzp relative cells - std::unordered_map op_map_; // map from zzp_op to our op - Index op_ = 0, - zzp_op_ = 0, - cell_ = 0, - zzp_cell_ = 0; -}; - -} - -#include "relative-homology-zigzag.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp deleted file mode 100644 index 499807106c..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/relative-homology-zigzag.hpp +++ /dev/null @@ -1,122 +0,0 @@ -template -template -void -dionysus::RelativeHomologyZigzag:: -add_both(const ChainRange& chain) -{ - zzp_.add(absolute_chain(chain)); - op_map_.insert( { zzp_op_++, op_ } ); - absolute_.left.insert( { cell_, zzp_cell_++ } ); - - zzp_.add(relative_chain(cell_, chain)); - op_map_.insert( { zzp_op_++, op_ } ); - relative_.left.insert( { cell_, zzp_cell_++ } ); - - cell_++; - op_++; -} - -template -void -dionysus::RelativeHomologyZigzag:: -remove_both(Index cell) -{ - Index abs_cell = absolute_.left.find(cell)->second; - Index rel_cell = relative_.left.find(cell)->second; - - zzp_.remove(rel_cell); - zzp_.remove(abs_cell); - - absolute_.left.erase(cell); - relative_.left.erase(cell); - - op_map_.insert( { zzp_op_++, op_ } ); - op_map_.insert( { zzp_op_++, op_ } ); - - op_++; -} - -template -template -typename dionysus::RelativeHomologyZigzag::Index -dionysus::RelativeHomologyZigzag:: -add(Index cell, const ChainRange& chain) -{ - Index pair = zzp_.add(relative_chain(cell, chain)); - op_map_.insert( { zzp_op_++, op_++ } ); - relative_.left.insert( { cell, zzp_cell_++ } ); - - return decode_pair(pair); -} - - -template -typename dionysus::RelativeHomologyZigzag::Index -dionysus::RelativeHomologyZigzag:: -decode_pair(Index pair) -{ - if (pair == unpaired()) - return pair; - - Index decoded = op_map_.find(pair)->second; - op_map_.erase(pair); - return decoded; -} - -template -template -typename dionysus::RelativeHomologyZigzag::IndexChain -dionysus::RelativeHomologyZigzag:: -absolute_chain(const ChainRange& chain) const -{ - IndexChain res; - for (const auto& e : chain) - res.push_back(ChainEntry(e.element(), abs_index(e.index()))); - return res; -} - -template -template -typename dionysus::RelativeHomologyZigzag::IndexChain -dionysus::RelativeHomologyZigzag:: -relative_chain(Index cell, const ChainRange& chain) const -{ - // NB: to compute the signs correctly, - // this assumes that the cone vertex w is the last vertex in some total order - - typedef typename IndexChain::value_type ChainEntry; - - IndexChain res; - if (!chain.empty()) - { - for (const auto& e : chain) - res.push_back(ChainEntry(e.element(), rel_index(e.index()))); - - FieldElement a = field().id(); - if (chain.size() % 2 == 0) // TODO: double-check - a = field().neg(a); - res.push_back(ChainEntry(a, abs_index(cell))); // add the base space cell - } else - { - res.reserve(2); - res.push_back(ChainEntry(field().id(), abs_index(cell))); - res.push_back(ChainEntry(field().neg(field().id()), 0)); - } - return res; -} - - -template -typename dionysus::RelativeHomologyZigzag::Index -dionysus::RelativeHomologyZigzag:: -remove(Index cell) -{ - Index rel_cell = rel_index(cell); - Index pair = zzp_.remove(rel_cell); - pair = decode_pair(pair); - - op_map_.insert( { zzp_op_++, op_++ } ); - relative_.left.erase(cell); - - return pair; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h deleted file mode 100644 index c7ccb1189e..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.h +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef DIONYSUS_RIPS_H -#define DIONYSUS_RIPS_H - -#include -#include - -#include - -#include "simplex.h" - -namespace dionysus -{ - -/** - * Rips class - * - * Class providing basic operations to work with Rips complexes. It implements Bron-Kerbosch algorithm, - * and provides simple wrappers for various functions. - * - * Distances_ is expected to define types IndexType and DistanceType as well as - * provide operator()(...) which given two IndexTypes should return - * the distance between them. There should be methods begin() and end() - * for iterating over IndexTypes as well as a method size(). - */ -template > -class Rips -{ - public: - typedef Distances_ Distances; - typedef typename Distances::IndexType IndexType; - typedef typename Distances::DistanceType DistanceType; - - typedef Simplex_ Simplex; - typedef typename Simplex::Vertex Vertex; // should be the same as IndexType - typedef std::vector VertexContainer; - - typedef short unsigned Dimension; - - class Evaluator; - class Comparison; - - public: - Rips(const Distances& distances): - distances_(distances) {} - - // Calls functor f on each simplex in the k-skeleton of the Rips complex - template - void generate(Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - // Calls functor f on all the simplices of the Rips complex that contain the given vertex v - template - void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - // Calls functor f on all the simplices of the Rips complex that contain the given edge [u,v] - template - void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - // Calls functor f on all the simplices of the Rips complex that contain the given Simplex s - // (unlike the previous methods it does not call the functor on the Simplex s itself) - template - void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, - Iterator candidates_begin, Iterator candidates_end) const; - - - /* No Iterator argument means Iterator = IndexType and the range is [distances().begin(), distances().end()) */ - template - void generate(Dimension k, DistanceType max, const Functor& f) const - { generate(k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - template - void vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f) const - { vertex_cofaces(v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - template - void edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f) const - { edge_cofaces(u, v, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - template - void cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f) const - { cofaces(s, k, max, f, boost::make_counting_iterator(distances().begin()), boost::make_counting_iterator(distances().end())); } - - - const Distances& distances() const { return distances_; } - DistanceType max_distance() const; - - DistanceType distance(const Simplex& s1, const Simplex& s2) const; - - - template - static void bron_kerbosch(VertexContainer& current, - const VertexContainer& candidates, - typename VertexContainer::const_iterator excluded, - Dimension max_dim, - const NeighborTest& neighbor, - const Functor& functor, - bool check_initial = true); - - protected: - const Distances& distances_; -}; - -template -class Rips::Evaluator -{ - public: - typedef Simplex_ Simplex; - - Evaluator(const Distances& distances): - distances_(distances) {} - - DistanceType operator()(const Simplex& s) const; - - protected: - const Distances& distances_; -}; - -template -class Rips::Comparison -{ - public: - typedef Simplex_ Simplex; - - Comparison(const Distances& distances): - eval_(distances) {} - - bool operator()(const Simplex& s1, const Simplex& s2) const - { - DistanceType e1 = eval_(s1), - e2 = eval_(s2); - if (e1 == e2) - return s1.dimension() < s2.dimension(); - - return e1 < e2; - } - - protected: - Evaluator eval_; -}; - -} - -#include "rips.hpp" - -#endif // DIONYSUS_RIPS_H diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp deleted file mode 100644 index 2fdda34a7a..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/rips.hpp +++ /dev/null @@ -1,162 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -template -template -void -dionysus::Rips:: -generate(Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = empty - // candidates = everything - VertexContainer current; - VertexContainer candidates(bg, end); - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); -} - -template -template -void -dionysus::Rips:: -vertex_cofaces(IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = [v] - // candidates = everything - [v] - VertexContainer current; current.push_back(v); - VertexContainer candidates; - for (Iterator cur = bg; cur != end; ++cur) - if (*cur != v && neighbor(v, *cur)) - candidates.push_back(*cur); - - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); -} - -template -template -void -dionysus::Rips:: -edge_cofaces(IndexType u, IndexType v, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = [u,v] - // candidates = everything - [u,v] - VertexContainer current; current.push_back(u); current.push_back(v); - - VertexContainer candidates; - for (Iterator cur = bg; cur != end; ++cur) - if (*cur != u && *cur != v && neighbor(v,*cur) && neighbor(u,*cur)) - candidates.push_back(*cur); - - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f); -} - -template -template -void -dionysus::Rips:: -cofaces(const Simplex& s, Dimension k, DistanceType max, const Functor& f, Iterator bg, Iterator end) const -{ - namespace ba = boost::adaptors; - - auto neighbor = [this, max](Vertex u, Vertex v) { return this->distances()(u,v) <= max; }; - - // current = s - VertexContainer current(s.begin(), s.end()); - - // candidates = everything - s that is a neighbor of every vertex in the simplex - VertexContainer candidates; - boost::set_difference(std::make_pair(bg, end) | - ba::filtered([this,&s,&neighbor](Vertex cur) - { for (auto& v : s) - if (!neighbor(v, cur)) - return false; - }), - s, - std::back_inserter(candidates)); - - bron_kerbosch(current, candidates, std::prev(candidates.begin()), k, neighbor, f, false); -} - - -template -template -void -dionysus::Rips:: -bron_kerbosch(VertexContainer& current, - const VertexContainer& candidates, - typename VertexContainer::const_iterator excluded, - Dimension max_dim, - const NeighborTest& neighbor, - const Functor& functor, - bool check_initial) -{ - if (check_initial && !current.empty()) - functor(Simplex(current)); - - if (current.size() == static_cast(max_dim) + 1) - return; - - for (auto cur = std::next(excluded); cur != candidates.end(); ++cur) - { - current.push_back(*cur); - - VertexContainer new_candidates; - for (auto ccur = candidates.begin(); ccur != cur; ++ccur) - if (neighbor(*ccur, *cur)) - new_candidates.push_back(*ccur); - size_t ex = new_candidates.size(); - for (auto ccur = std::next(cur); ccur != candidates.end(); ++ccur) - if (neighbor(*ccur, *cur)) - new_candidates.push_back(*ccur); - excluded = new_candidates.begin() + (ex - 1); - - bron_kerbosch(current, new_candidates, excluded, max_dim, neighbor, functor); - current.pop_back(); - } -} - -template -typename dionysus::Rips::DistanceType -dionysus::Rips:: -distance(const Simplex& s1, const Simplex& s2) const -{ - DistanceType mx = 0; - for (auto a : s1) - for (auto b : s2) - mx = std::max(mx, distances_(a,b)); - return mx; -} - -template -typename dionysus::Rips::DistanceType -dionysus::Rips:: -max_distance() const -{ - DistanceType mx = 0; - for (IndexType a = distances_.begin(); a != distances_.end(); ++a) - for (IndexType b = std::next(a); b != distances_.end(); ++b) - mx = std::max(mx, distances_(a,b)); - return mx; -} - -template -typename dionysus::Rips::DistanceType -dionysus::Rips::Evaluator:: -operator()(const Simplex& s) const -{ - DistanceType mx = 0; - for (auto a = s.begin(); a != s.end(); ++a) - for (auto b = std::next(a); b != s.end(); ++b) - mx = std::max(mx, distances_(*a,*b)); - return mx; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h deleted file mode 100644 index e2481ce080..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef DIONYSUS_ROW_REDUCTION_H -#define DIONYSUS_ROW_REDUCTION_H - -#include "reduced-matrix.h" - -namespace dionysus -{ - -// Mid-level interface -template, template class... Visitors> -class RowReduction -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef ReducedMatrix Persistence; - - public: - RowReduction(const Field& field): - persistence_(field) {} - - RowReduction(const Field& field, - const Comparison& cmp, - const Visitors&... visitors): - persistence_(field, cmp, visitors...) {} - - template - void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); - - template - void operator()(const Filtration& f, const ReportPair& report_pair); - - template - void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } - - static void no_report_pair(int, Index, Index) {} - static void no_progress() {} - - const Persistence& - persistence() const { return persistence_; } - Persistence& persistence() { return persistence_; } - - private: - Persistence persistence_; -}; - -} - -#include "row-reduction.hpp" - -#endif - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp deleted file mode 100644 index edb1652872..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/row-reduction.hpp +++ /dev/null @@ -1,103 +0,0 @@ -#include -namespace ba = boost::adaptors; - -template class... V> -template -void -dionysus::RowReduction:: -operator()(const Filtration& filtration, const ReportPair& report_pair) -{ - using Cell = typename Filtration::Cell; - (*this)(filtration, [](const Cell&) { return false; }, report_pair, &no_progress); -} - -template class... V> -template -void -dionysus::RowReduction:: -operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) -{ - persistence_.resize(filtration.size()); - - typedef typename Persistence::Index Index; - typedef typename Persistence::FieldElement Element; - typedef typename Persistence::Chain Chain; - typedef typename Filtration::Cell Cell; - typedef ChainEntry CellChainEntry; - typedef ChainEntry ChainEntry; - - std::vector rows(persistence_.size()); - - auto& field = persistence_.field(); - - // fill the matrix - Index i = 0; - for(auto& c : filtration) - { - progress(); - - if (relative(c)) - { - persistence_.set_skip(i); - ++i; - continue; - } - - persistence_.set(i, c.boundary(field) | - ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | - ba::transformed([this,&filtration](const CellChainEntry& e) - { return ChainEntry(e.element(), filtration.index(e.index())); })); - if (!persistence_[i].empty()) - { - auto& x = persistence_[i].back(); - rows[x.index()].emplace_back(x.element(),i); - } - ++i; - } - - auto entry_cmp = [this](const ChainEntry& e1, const ChainEntry& e2) { return this->persistence_.cmp()(e1.index(), e2.index()); }; - - // reduce the matrix from the bottom up - for (auto it = rows.rbegin(); it != rows.rend(); ++it) - { - auto& row = *it; - Index r = rows.rend() - it - 1; - - if (row.empty()) - continue; - - // add the first column to every other column - Index c = row.front().index(); - Element e = row.front().element(); - Chain& first = persistence_.column(c); - for (size_t i = 1; i < row.size(); ++i) - { - Index cur_idx = row[i].index(); - Element cur_elem = row[i].element(); - Chain& cur = persistence_.column(cur_idx); - if (cur.empty()) // zeroed out by the clearing optimization - continue; - - Element m = field.neg(field.div(cur_elem, e)); - // cur += m*first - ::dionysus::Chain::addto(cur, m, first, field, entry_cmp); - - // update row - if (!cur.empty()) - { - ChainEntry ce = cur.back(); - auto& new_row = rows[ce.index()]; - new_row.emplace_back(ce.element(), cur_idx); - if (entry_cmp(new_row.back(), new_row.front())) - std::swap(new_row.back(), new_row.front()); - } - } - - persistence_.set_pair(r,c); - report_pair(filtration[r].dimension(), r, c); - - // zero out the corresponding column (the clearing optimization) - persistence_.column(r).clear(); - } -} - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h deleted file mode 100644 index 4ac5cb6945..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/simplex.h +++ /dev/null @@ -1,280 +0,0 @@ -#ifndef DIONYSUS_SIMPLEX_H -#define DIONYSUS_SIMPLEX_H - -#include -#include - -//#include -#include -#include -#include - -#include "chain.h" - -namespace dionysus -{ - -struct Empty {}; - -template -class Simplex -{ - public: - typedef Vertex_ Vertex; - typedef T Data; - typedef std::unique_ptr Vertices; - - template - struct BoundaryChainIterator; - struct BoundaryIterator; - - template - using BoundaryChainRange = boost::iterator_range>; - using BoundaryRange = boost::iterator_range; - - template - using Entry = ChainEntry; - - public: - Simplex(const Data& d = Data()): - dim_(-1), data_(d) {} - - Simplex(const std::initializer_list& vertices, - Data&& d = Data()): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) - {} - - Simplex(const std::initializer_list& vertices, - const Data& d): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} - - Simplex(short unsigned dim, Vertices&& vertices, Data&& data = Data()): - dim_(dim), vertices_(std::move(vertices)), data_(std::move(data)) { std::sort(begin(), end()); } - - template - Simplex(const VertexRange& vertices, - Data&& d = Data()): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), std::move(d)) - {} - - template - Simplex(const VertexRange& vertices, - const Data& d): - Simplex(vertices.size() - 1, vertices.begin(), vertices.end(), d) {} - - Simplex(const Simplex& other): - Simplex(other.dim_, other.begin(), other.end(), other.data_) {} - Simplex& operator=(const Simplex& other) { dim_ = other.dim_; vertices_ = Vertices(new Vertex[dim_+1]); std::copy(other.begin(), other.end(), begin()); data_ = other.data_; return *this; } - - Simplex(Simplex&& other) noexcept: - dim_(other.dim_), - vertices_(std::move(other.vertices_)), - data_(std::move(other.data_)) {} - Simplex& operator=(Simplex&& other) = default; - - template - Simplex(short unsigned dim, - Iterator b, Iterator e, - Data&& d = Data()): - dim_(dim), - vertices_(new Vertex[dim_+1]), - data_(std::move(d)) { std::copy(b, e, begin()); std::sort(begin(), end()); } - - template - Simplex(short unsigned dim, - Iterator b, Iterator e, - const Data& d): - dim_(dim), - vertices_(new Vertex[dim_+1]), - data_(d) { std::copy(b, e, begin()); std::sort(begin(), end()); } - - short unsigned dimension() const { return dim_; } - - BoundaryRange boundary() const { return BoundaryRange(boundary_begin(), boundary_end()); } - BoundaryIterator boundary_begin() const; - BoundaryIterator boundary_end() const; - - template - BoundaryChainRange - boundary(const Field& field) const { return BoundaryChainRange(boundary_begin(field), boundary_end(field)); } - - template - BoundaryChainIterator - boundary_begin(const Field& field) const; - template - BoundaryChainIterator - boundary_end(const Field& field) const; - - const Vertex* begin() const { return vertices_.get(); } - const Vertex* end() const { return begin() + dim_ + 1; } - size_t size() const { return dim_ + 1; } - - std::pair - range() const { return std::make_pair(begin(), end()); } - - Simplex join(const Vertex& v) const { Vertices vertices(new Vertex[dim_+2]); std::copy(begin(), end(), vertices.get()); vertices[dim_+1] = v; return Simplex(dim_ + 1, std::move(vertices), Data(data_)); } - - bool operator==(const Simplex& other) const { return dim_ == other.dim_ && std::equal(begin(), end(), other.begin()); } - bool operator!=(const Simplex& other) const { return !operator==(other); } - bool operator<(const Simplex& other) const { return dim_ < other.dim_ || (dim_ == other.dim_ && std::lexicographical_compare(begin(), end(), other.begin(), other.end())); } - bool operator>(const Simplex& other) const { return other < (*this); } - - Vertex operator[](short unsigned i) const { return vertices_[i]; } - const Data& data() const { return data_; } - Data& data() { return data_; } - - friend - std::ostream& operator<<(std::ostream& out, const Simplex& s) - { out << '<' << *s.begin(); for (auto it = s.begin() + 1; it != s.end(); ++it) out << ',' << *it; out << '>'; return out; } - - private: - Vertex* begin() { return vertices_.get(); } - Vertex* end() { return begin() + dim_ + 1; } - - private: - short unsigned dim_; - //boost::compressed_pair vertices_data_; - Vertices vertices_; - Data data_; // TODO: optimize -}; - -template -size_t hash_value(const Simplex& s) { return boost::hash_range(s.begin(), s.end()); } - - -template -struct Simplex::BoundaryIterator: - public boost::iterator_adaptor, // Value - boost::use_default, - Simplex> // Reference -{ - public: - typedef const V* Iterator; - typedef Simplex Value; - - typedef boost::iterator_adaptor Parent; - - BoundaryIterator() {} - explicit BoundaryIterator(short unsigned dim, Iterator iter, Iterator bg, Iterator end): - Parent(iter), dim_(dim), bg_(bg), end_(end) {} - - Iterator begin() const { return bg_; } - - private: - friend class boost::iterator_core_access; - Value dereference() const - { - typedef std::not_equal_to NotEqualVertex; - - using std::placeholders::_1; - return Simplex(dim_ - 1, - boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), bg_, end_), - boost::make_filter_iterator(std::bind(NotEqualVertex(), _1, *(this->base())), end_, end_)); - } - - short unsigned dim_; - Iterator bg_; - Iterator end_; -}; - -template -template -struct Simplex::BoundaryChainIterator: - public boost::iterator_adaptor, // Derived - BoundaryIterator, - ChainEntry>, // Value - boost::use_default, - ChainEntry>> // Reference -{ - public: - typedef F Field; - typedef BoundaryIterator Iterator; - typedef ChainEntry> Value; - - typedef boost::iterator_adaptor Parent; - - BoundaryChainIterator() {} - explicit BoundaryChainIterator(const Field& field, Iterator iter): - Parent(iter), field_(&field) {} - - private: - friend class boost::iterator_core_access; - Value dereference() const - { - return Value(((this->base().base() - this->base().begin()) % 2 == 0)? field_->id() : field_->neg(field_->id()), - *(this->base())); - } - - const Field* field_ = nullptr; -}; - - -/* Simplex */ -template -typename Simplex::BoundaryIterator -Simplex:: -boundary_begin() const -{ - if (dimension() == 0) return boundary_end(); - return BoundaryIterator(dimension(), begin(), begin(), end()); -} - -template -typename Simplex::BoundaryIterator -Simplex:: -boundary_end() const -{ - return BoundaryIterator(dimension(), end(), begin(), end()); -} - -template -template -#if defined(_MSC_VER) -typename Simplex::BoundaryChainIterator -#else -typename Simplex::template BoundaryChainIterator -#endif -Simplex:: -boundary_begin(const F& field) const -{ - if (dimension() == 0) return boundary_end(field); - return BoundaryChainIterator(field, boundary_begin()); -} - -template -template -#if defined(_MSC_VER) -typename Simplex::BoundaryChainIterator -#else -typename Simplex::template BoundaryChainIterator -#endif -Simplex:: -boundary_end(const F& field) const -{ - return BoundaryChainIterator(field, boundary_end()); -} - -} // dionysus - -namespace std -{ - -template -struct hash> -{ - size_t operator()(const dionysus::Simplex& s) const { return hash_value(s); } -}; - -} // std - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h deleted file mode 100644 index fb1e929e02..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.h +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef DIONYSUS_SPARSE_ROW_MATRIX_H -#define DIONYSUS_SPARSE_ROW_MATRIX_H - -#include -#include -#include -#include // for debugging output - -#include - -#include "chain.h" -#include "reduction.h" - -namespace dionysus -{ - -namespace bi = boost::intrusive; - -namespace detail -{ - typedef bi::list_base_hook> auto_unlink_hook; - - template - struct SparseRowMatrixEntry: - public ChainEntry, auto_unlink_hook> - { - typedef I Index; - typedef typename F::Element FieldElement; - typedef std::tuple IndexPair; // (id, pair) - typedef ChainEntry Parent; - typedef SparseRowMatrixEntry Entry; - - SparseRowMatrixEntry(FieldElement e, const IndexPair& ip): - Parent(e,ip) {} - - SparseRowMatrixEntry(FieldElement e, const Index& r, const Index& c): - Parent(e,IndexPair(r,c)) {} - - SparseRowMatrixEntry(const Entry& other) = default; - SparseRowMatrixEntry(Entry&& other) = default; - Entry& operator=(Entry&& other) = default; - - void unlink() { auto_unlink_hook::unlink(); } - bool is_linked() const { return auto_unlink_hook::is_linked(); } - }; -} - -template, - template class Column_ = std::vector> -class SparseRowMatrix -{ - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef typename Field::Element FieldElement; - - typedef detail::SparseRowMatrixEntry Entry; - typedef Column_ Column; - typedef typename Entry::IndexPair IndexPair; - typedef bi::list> Row; - - typedef std::vector> IndexChain; - - typedef std::unordered_map Columns; - typedef std::unordered_map Rows; - typedef std::unordered_map LowMap; - - public: - SparseRowMatrix(const Field& field, - const Comparison& cmp = Comparison()): - field_(field), cmp_(cmp) {} - - SparseRowMatrix(SparseRowMatrix&& other) = default; - - - template - Column reduce(const ChainRange& chain, IndexChain& trail); - - Index set(Index i, Column&& chain); // returns previous column with this low - void fix(Index c, Column& column); - void fix(Index c) { fix(c, col(c)); } - - const Row& prepend_row(Index r, FieldElement m, const Row& chain); // could be horribly inefficient if Column is chosen poorly - - void drop_row(Index r) { rows_.erase(r); if (is_low(r)) lows_.erase(r); } - void drop_col(Index c) - { - auto cit = columns_.find(c); - Column& column = cit->second; - if (!column.empty()) - { - Index rlow = std::get<0>(column.back().index()); - auto it = lows_.find(rlow); - if (it != lows_.end() && it->second == c) - lows_.erase(it); - } - columns_.erase(cit); - } - void drop_low(Index r) { lows_.erase(r); } - - // accessors - Row& row(Index r) { return rows_[r]; } - Column& col(Index c) { assert(col_exists(c)); return columns_.find(c)->second; } - const Column& col(Index c) const { assert(col_exists(c)); return columns_.find(c)->second; } - Index low(Index r) const { return lows_.find(r)->second; } - bool is_low(Index r) const { return lows_.find(r) != lows_.end(); } - void update_low(Index c) { lows_[std::get<0>(col(c).back().index())] = c; } - - const Field& field() const { return field_; } - void reserve(size_t) {} // here for compatibility only - const Comparison& cmp() const { return cmp_; } - - // debug - bool col_exists(Index c) const { return columns_.find(c) != columns_.end(); } - const Columns& columns() const { return columns_; } - void check_columns() const - { - for (auto& x : columns_) - { - Index c = x.first; - if (x.second.empty()) - std::cout << "Warning: empty column " << c << std::endl; - Index rl = std::get<0>(x.second.back().index()); - if (!is_low(rl) || low(rl) != c) - { - std::cout << "Columns don't check out: lows don't match" << std::endl; - std::cout << " " << c << ' ' << rl << ' ' << ' ' << low(rl) << std::endl; - std::cout << "---\n"; - for (auto& x : col(c)) - std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; - std::cout << "---\n"; - for (auto& x : col(low(rl))) - std::cout << " " << x.element() << ' ' << std::get<0>(x.index()) << ' ' << std::get<1>(x.index()) << '\n'; - assert(0); - } - - for (auto& x : lows_) - { - if (!col_exists(x.second)) - { - std::cout << "Still keeping low of a removed column" << std::endl; - assert(0); - } - else if (std::get<0>(col(x.second).back().index()) != x.first) - { - std::cout << "Low mismatch: " << x.second << ' ' << std::get<0>(col(x.second).back().index()) << ' ' << x.first << '\n'; - assert(0); - } - } - } - } - - private: - Field field_; - Comparison cmp_; - - Columns columns_; - Rows rows_; - LowMap lows_; // column that has this low -}; - - -namespace detail -{ - -template -struct Unpaired> -{ - static - constexpr std::tuple - value() - { return std::make_tuple(std::numeric_limits::max(), - std::numeric_limits::max()); } -}; - -} - -} - -#include "sparse-row-matrix.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp deleted file mode 100644 index 10f4808c17..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/sparse-row-matrix.hpp +++ /dev/null @@ -1,103 +0,0 @@ -template class Col> -template -typename dionysus::SparseRowMatrix::Column -dionysus::SparseRowMatrix:: -reduce(const ChainRange& chain_, IndexChain& trail) -{ - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp_(std::get<0>(e1.index()), std::get<0>(e2.index())); }; - -#define __DIONYSUS_USE_VECTOR_CHAINS 1 - -#if !(__DIONYSUS_USE_VECTOR_CHAINS) - std::set chain(row_cmp); - for (auto x : chain_) - chain.insert(Entry(x.element(), IndexPair(x.index(), 0))); -#else - Column chain; - for (auto x : chain_) - chain.emplace_back(x.element(), IndexPair(x.index(), 0)); - std::sort(chain.begin(), chain.end(), row_cmp); -#endif - - typedef Reduction ReductionIP; - - auto chains = [this](const IndexPair& rc) -> const Column& { return this->col(std::get<1>(rc)); }; - auto lows = [this](const IndexPair& rc) -> IndexPair - { - Index r = std::get<0>(rc); - auto it = this->lows_.find(r); - if (it == this->lows_.end()) - return ReductionIP::unpaired; - else - { - Index rr = std::get<0>(col(it->second).back().index()); - if (rr != r) - std::cout << "Mismatch: " << rr << ' ' << r << std::endl; - return IndexPair(r, it->second); - } - }; - - auto addto = [&trail](FieldElement m, const IndexPair& rc) { trail.emplace_back(m, std::get<1>(rc)); }; - - ReductionIP::reduce(chain, - chains, lows, - field_, addto, row_cmp); - -#if !(__DIONYSUS_USE_VECTOR_CHAINS) - return Column(std::begin(chain), std::end(chain)); -#else - return chain; -#endif -} - -template class Col> -typename dionysus::SparseRowMatrix::Index -dionysus::SparseRowMatrix:: -set(Index col, Column&& chain) -{ - Column& column = columns_.emplace(col, std::move(chain)).first->second; - - fix(col, column); - - Index r = std::get<0>(column.back().index()); - Index res; - if (is_low(r)) - res = low(r); - else - res = col; - lows_[r] = col; - - return res; -} - -template class Col> -void -dionysus::SparseRowMatrix:: -fix(Index col, Column& column) -{ - for (auto& x : column) - { - std::get<1>(x.index()) = col; - Index r = std::get<0>(x.index()); - row(r).push_back(x); - } -} - -template class Col> -const typename dionysus::SparseRowMatrix::Row& -dionysus::SparseRowMatrix:: -prepend_row(Index r, FieldElement m, const Row& chain) -{ - Row& new_row = row(r); - - for (auto& x : chain) - { - Index c = std::get<1>(x.index()); - Column& column = col(c); - auto it = column.emplace(column.begin(), field().mul(x.element(), m), r, c); - new_row.push_back(*it); - } - - return new_row; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h deleted file mode 100644 index 0477d4683f..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef DIONYSUS_STANDARD_REDUCTION_H -#define DIONYSUS_STANDARD_REDUCTION_H - -namespace dionysus -{ - -// Mid-level interface -template -class StandardReduction -{ - public: - typedef Persistence_ Persistence; - typedef typename Persistence::Field Field; - typedef typename Persistence::Index Index; - - public: - StandardReduction(Persistence& persistence): - persistence_(persistence) {} - - template - void operator()(const Filtration& f, const Relative& relative, const ReportPair& report_pair, const Progress& progress); - - template - void operator()(const Filtration& f, const ReportPair& report_pair); - - template - void operator()(const Filtration& f) { return (*this)(f, &no_report_pair); } - - static void no_report_pair(int, Index, Index) {} - static void no_progress() {} - - const Persistence& - persistence() const { return persistence_; } - Persistence& persistence() { return persistence_; } - - private: - Persistence& persistence_; -}; - -} - -#include "standard-reduction.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp deleted file mode 100644 index 9aa3396a8c..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/standard-reduction.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -namespace ba = boost::adaptors; - -template -template -void -dionysus::StandardReduction

:: -operator()(const Filtration& filtration, const ReportPair& report_pair) -{ - using Cell = typename Filtration::Cell; - (*this)(filtration, [](const Cell&) { return false; }, report_pair, no_progress); -} - -template -template -void -dionysus::StandardReduction

:: -operator()(const Filtration& filtration, const Relative& relative, const ReportPair& report_pair, const Progress& progress) -{ - persistence_.reserve(filtration.size()); - - typedef typename Filtration::Cell Cell; - typedef ChainEntry CellChainEntry; - typedef ChainEntry ChainEntry; - - unsigned i = 0; - for(auto& c : filtration) - { - progress(); - - if (relative(c)) - { - ++i; - persistence_.add_skip(); - continue; - } - - //std::cout << "Adding: " << c << " : " << boost::distance(c.boundary(persistence_.field())) << std::endl; - Index pair = persistence_.add(c.boundary(persistence_.field()) | - ba::filtered([relative](const CellChainEntry& e) { return !relative(e.index()); }) | - ba::transformed([this,&filtration](const CellChainEntry& e) - { return ChainEntry(e.element(), filtration.index(e.index())); })); - if (pair != persistence_.unpaired()) - report_pair(c.dimension(), pair, i); - ++i; - } -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h deleted file mode 100644 index f18ff897e4..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/trails-chains.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef DIONYSUS_TRAILS_CHAINS_H -#define DIONYSUS_TRAILS_CHAINS_H - -#include "ordinary-persistence.h" - -template -struct ChainsVisitor: public EmptyVisitor -{ - template - void chain_initialized(Chain& c) { } - - void addto(typename Field::Element m, Index cl) {} - void reduction_finished() {} -}; - - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h deleted file mode 100644 index e9423099aa..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.h +++ /dev/null @@ -1,142 +0,0 @@ -#ifndef DIONYSUS_ZIGZAG_PERSISTENCE_H -#define DIONYSUS_ZIGZAG_PERSISTENCE_H - -#include -#include -#include - -#include -#include - -#include "sparse-row-matrix.h" - -namespace dionysus -{ - -namespace ba = boost::adaptors; - -template> -class ZigzagPersistence -{ - static_assert(std::is_signed::value, "Index type used in ZigzagPersistence must be a *signed* integer"); - - public: - typedef Field_ Field; - typedef Index_ Index; - typedef Comparison_ Comparison; - - typedef SparseRowMatrix RowMatrix; - typedef SparseRowMatrix DequeRowMatrix; - typedef typename RowMatrix::IndexPair IndexPair; - typedef typename RowMatrix::FieldElement FieldElement; - typedef typename RowMatrix::IndexChain IndexChain; - typedef typename RowMatrix::Column Column; - typedef typename RowMatrix::Row Row; - typedef typename DequeRowMatrix::Column DequeColumn; - typedef typename DequeRowMatrix::Row DequeRow; - - typedef std::unordered_map BirthIndexMap; - - - ZigzagPersistence(const Field& field, - const Comparison& cmp = Comparison()): - Z(field, cmp), C(field, cmp), B(field, cmp), - operations(0), - cell_indices(0), - z_indicies_last(0), - z_indicies_first(-1), - b_indices(0) {} - - template - Index add(const ChainRange& chain) // returns the id of the dying cycle (or unpaired) - { - Index res = add_impl(chain); -#ifdef DIONYSUS_ZIGZAG_DEBUG - check_sorted(); - check_b_cols(); - Z.check_columns(); -#endif - return res; - } - Index remove(Index cell) - { - Index res = remove_impl(cell); -#ifdef DIONYSUS_ZIGZAG_DEBUG - check_sorted(); - check_b_cols(); - Z.check_columns(); -#endif - return res; - } - - struct IsAlive - { - IsAlive(const ZigzagPersistence& zz_): zz(&zz_) {} - bool operator()(const std::pair& x) const { return zz->is_alive(x.first); } - const ZigzagPersistence* zz; - }; - - bool is_alive(Index x) const { return !B.is_low(x); } - - auto alive_ops() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_values) - { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_values; } - - auto alive_cycles() const -> decltype(BirthIndexMap() | ba::filtered(IsAlive(*this)) | ba::map_keys) - { return birth_index | ba::filtered(IsAlive(*this)) | ba::map_keys; } - - size_t alive_size() const { return Z.columns().size() - B.columns().size(); } - - void reserve(size_t) {} // here for compatibility only - const Field& field() const { return Z.field(); } - const Comparison& cmp() const { return Z.cmp(); } - - template - static Index row(const Entry& e) { return std::get<0>(e.index()); } - template - static Index col(const Entry& e) { return std::get<1>(e.index()); } - - static - const Index unpaired() { return Reduction::unpaired; } - - const Column& cycle(Index i) const { return Z.col(i); } - - // debug - void check_b_cols() const; - - template - void check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - template - void check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - - Column zb_dot(Index c) const; - - template - Column dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - - template - Column boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const; - - void check_sorted() const; - - private: - template - Index add_impl(const ChainRange& chain); - Index remove_impl(Index cell); - - private: - RowMatrix Z, C; - DequeRowMatrix B; - - BirthIndexMap birth_index; - Index operations; - Index cell_indices; - Index z_indicies_last, z_indicies_first; - Index b_indices; -}; - -} - -#include "zigzag-persistence.hpp" - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp b/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp deleted file mode 100644 index 96331a3b15..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/dionysus/zigzag-persistence.hpp +++ /dev/null @@ -1,541 +0,0 @@ -#include - -template -template -typename dionysus::ZigzagPersistence::Index -dionysus::ZigzagPersistence:: -add_impl(const ChainRange& chain_) -{ - // std::cout << "add(" << cell_indices << ")" << std::endl; - Index op = operations++; - - IndexChain cycles; // chain_ -> Z*cycles - Column z_remainder = Z.reduce(chain_, cycles); - // std::cout << "cycle: "; - // for (auto& v : cycles){ - // std::cout << v.index() << " "; - // } - // std::cout << "\n"; - assert(z_remainder.empty()); - - IndexChain boundaries; - DequeColumn b_remainder = B.reduce(cycles, boundaries); - - // add up columns of C indexed by boundaries - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column chain; - for (auto& x : boundaries) - Chain::addto(chain, x.element(), C.col(x.index()), field(), row_cmp); - chain.push_back(Entry(field().neg(field().id()), IndexPair(cell_indices++,0))); - - if (b_remainder.empty()) // birth - { - // std::cout << " birth" << std::endl; - Index z_col = z_indicies_last++; - Z.set(z_col, std::move(chain)); - birth_index[z_col] = op; - return unpaired(); - } - else // death - { - // std::cout << " death" << std::endl; - Index b_col = b_indices++; - Index pair = row(b_remainder.back()); - B.set(b_col, std::move(b_remainder)); - C.set(b_col, std::move(chain)); - return birth_index[pair]; - } -} - -template -typename dionysus::ZigzagPersistence::Index -dionysus::ZigzagPersistence:: -remove_impl(Index cell) -{ - //std::cout << "remove(" << cell << ")" << std::endl; - - Index op = operations++; - - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - typedef typename DequeColumn::value_type DequeEntry; - auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) - { return this->cmp()(row(e1), row(e2)); }; - - IndexChain z_row; - for (auto& x : Z.row(cell)) - z_row.emplace_back(x.element(), col(x)); - - if (z_row.empty()) // birth - { - //std::cout << " birth" << std::endl; - Row& c_row = C.row(cell); - // c_row.front() may not be the first column in order, but that doesn't really matter, does it? (TODO) - auto& c_front = c_row.front(); - - Index j = col(c_front); - Index l = row(B.col(j).back()); - - //std::cout << j << ' ' << l << std::endl; - - // cycle = ZB[j] = DC[j] - Column cycle; - for (auto& x : B.col(j)) - Chain::addto(cycle, x.element(), Z.col(row(x)), field(), row_cmp); - - //std::cout << "Cycle:" << std::endl; - //for (auto& x : cycle) - // std::cout << x.element() << ' ' << row(x) << std::endl; - - // 1: prepend the cycle - Index znew = z_indicies_first--; - Index oth = Z.set(znew, std::move(cycle)); // oth records our collision (used in step 6) - birth_index[znew] = op; - - //std::cout << "znew oth: " << znew << ' ' << oth << std::endl; - //std::cout << "oth column:" << std::endl; - //for (auto& x : Z.col(oth)) - // std::cout << x.element() << ' ' << row(x) << std::endl; - - // 2: prepend the row to B - FieldElement m = field().neg(field().inv(c_front.element())); // m = -1/c - const DequeRow& b_row = B.prepend_row(znew, m, c_row); - //std::cout << "Prepended row with multiplier: " << m << " (" << b_row.size() << ")" << std::endl; - - // 3: subtract C[j] from every C[k] - const Column& Cj = C.col(j); - - // use the copy of c_row in B, since c_row will be modified in the following loop - for (auto it = std::next(b_row.begin()); it != b_row.end(); ++it) - { - Index c = col(*it); - assert(c != j); - //std::cout << "adding to " << c << " in C" << std::endl; - Chain::addto(C.col(c), it->element(), Cj, field(), row_cmp); // using it->element() since b_row = m*c_row - C.fix(c); // old elements got removed via auto_unlink_hook - // we don't need lows in C, so not updating them - } - //std::cout << "Done with step 3" << std::endl; - - // 4: subtract B[j] from every B[k] that has l - // (we don't need to update C because ZB[j] = 0 after step 2) - DequeColumn& Bj = B.col(j); - FieldElement bm = field().neg(field().inv(Bj.back().element())); // bm = -1/B[l,j] - IndexChain Bl_row; // make a copy of Bl_row, since it will be changing - for (auto& x : B.row(l)) - { - if (col(x) == j) - continue; - Bl_row.emplace_back(x.element(), col(x)); - } - for (auto& x : Bl_row) - { - Index c = x.index(); - assert(c != j); - Chain::addto(B.col(c), field().mul(bm, x.element()), Bj, field(), b_row_cmp); - B.fix(c); // old elements got removed via auto_unlink_hook - // l cannot be the low in c, so no need to update lows - } - //std::cout << "Done with step 4" << std::endl; - - // 5: drop row l and column j from B; drop column l from Z; drop column j from C - B.drop_col(j); - assert(B.row(l).empty()); - B.drop_row(l); - Index Zl_low = row(Z.col(l).back()); - Z.drop_col(l); - birth_index.erase(l); - C.drop_col(j); - assert(Z.row(cell).empty()); - assert(C.row(cell).empty()); - C.drop_row(cell); - Z.drop_row(cell); - //std::cout << "Done with step 5" << std::endl; - if (oth == l) // we just dropped our collision in Z - oth = znew; - else - Z.drop_low(Zl_low); - - // 6: reduce Z - std::unordered_map b_changes; // the columns to add in B to apply row changes - Index cur = znew; - while (oth != cur) - { - Column& cur_col = Z.col(cur); - Column& oth_col = Z.col(oth); - assert(row(cur_col.back()) == row(oth_col.back())); - //std::cout << "--- " << cur << " (" << cur_col.size() << ") " << oth << " (" << oth_col.size() << ")" << std::endl; - FieldElement m1 = cur_col.back().element(); - FieldElement m2 = oth_col.back().element(); - FieldElement m2_div_m1 = field().div(m2, m1); - Chain::addto(oth_col, field().neg(m2_div_m1), cur_col, field(), row_cmp); - Z.fix(oth, oth_col); - - // record the changes we need to make in B; - // because there is only one collision in the matrix during the reduction, - // once we use a row as the source, we never revisit it. This means once the row is updated in B, - // we never touch it again, so below record is fine. - for (auto& x : this->B.row(oth)) - b_changes[col(x)].emplace_back(field().mul(x.element(), m2_div_m1), cur, col(x)); - - cur = oth; - Index low = row(oth_col.back()); - if (Z.is_low(low)) - oth = Z.low(low); - //std::cout << "--- -- new low: " << low << ' ' << cur << ' ' << oth << std::endl; - - if (cmp()(oth, cur)) - std::swap(oth, cur); - else - Z.update_low(cur); - } - - // apply changes in B (the complexity here could get ugly) - for (auto& bx : b_changes) - { - std::sort(bx.second.begin(), bx.second.end(), b_row_cmp); - Chain::addto(B.col(bx.first), field().id(), bx.second, field(), b_row_cmp); - B.fix(bx.first); - // no need to update low (additions from bottom up) - } - //std::cout << "Done with step 6" << std::endl; - - return unpaired(); - } - else // death - { - //std::cout << " death" << std::endl; - - auto index_chain_cmp = [this](const typename IndexChain::value_type& e1, const typename IndexChain::value_type& e2) - { return this->cmp()(e1.index(), e2.index()); }; - - // 1: change basis to clear z_row - std::sort(z_row.begin(), z_row.end(), index_chain_cmp); // this adds a log factor, but it makes life easier - Index j = z_row.front().index(); - FieldElement e = z_row.front().element(); - - if (z_row.size() > 1) - { - // figure out the columns we use for reduction - typedef typename IndexChain::const_iterator RowIterator; - std::vector reducers; - reducers.push_back(z_row.begin()); - for (RowIterator it = std::next(z_row.begin()); it != z_row.end(); ++it) - { - Index c = it->index(); - - assert(Z.col_exists(c)); - assert(Z.col_exists(reducers.back()->index())); - if (cmp()(row(Z.col(c).back()), - row(Z.col(reducers.back()->index()).back()))) - reducers.push_back(it); - } - reducers.push_back(z_row.end()); - //std::cout << "reducers.size(): " << reducers.size() << std::endl; - //std::cout << "z_row.size(): " << z_row.size() << std::endl; - - - std::map b_changes; // the rows to add to B - auto add_in_z = [this,&b_changes,&row_cmp,&index_chain_cmp](Index to, Index from, FieldElement m, FieldElement e) - { - //std::cout << " add_in_z: " << from << ' ' << to << std::endl; - - FieldElement mult = this->field().mul(m, e); - assert(Z.col_exists(to)); - assert(Z.col_exists(from)); - Chain::addto(Z.col(to), mult, Z.col(from), this->field(), row_cmp); - assert(!Z.col(to).empty()); - this->Z.fix(to); // NB: rows will be linked in the back, so the iterators are Ok - this->Z.update_low(to); - - // subtract B.row(to) from B.row(from) - IndexChain Bto_row; - for (auto& x : this->B.row(to)) - Bto_row.emplace_back(x.element(), col(x)); - std::sort(Bto_row.begin(), Bto_row.end(), index_chain_cmp); - -#if 0 - for (auto& x : this->B.row(to)) - std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; - - std::cout << "---\n"; - - for (auto& x : this->B.row(from)) - std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; -#endif - - Chain::addto(b_changes[from], this->field().neg(mult), Bto_row, this->field(), index_chain_cmp); - - // if there is b_changes[to] add it, too - auto it = b_changes.find(to); - if (it != b_changes.end()) - Chain::addto(b_changes[from], this->field().neg(mult), it->second, this->field(), index_chain_cmp); - }; - Index last_low = row(Z.col(reducers[reducers.size() - 2]->index()).back()); - for (int i = reducers.size() - 2; i >= 0; --i) - { - auto rit = reducers[i]; - FieldElement m = field().neg(field().inv(rit->element())); - - for (auto it = std::next(rit); it != reducers[i+1]; ++it) - add_in_z(it->index(), rit->index(), m, it->element()); - - if (static_cast(i + 1) != reducers.size() - 1) - { - auto it = reducers[i+1]; - add_in_z(it->index(), rit->index(), m, it->element()); - } - } - if (reducers.size() > 2) - Z.drop_low(last_low); - - // apply changes in b (the complexity here could get ugly) - // Specifically, transpose b_changes and add it in - std::unordered_map b_changes_transposed; - for (auto& b_row : b_changes) - for (auto& bx : b_row.second) - b_changes_transposed[bx.index()].emplace_back(bx.element(), b_row.first, bx.index()); - - for (auto& b_col : b_changes_transposed) - { -#if 0 - std::cout << "Adding:" << std::endl; - for (auto& x : b_col.second) - std::cout << x.element() << ' ' << row(x) << ' ' << col(x) << std::endl; -#endif - Chain::addto(B.col(b_col.first), field().id(), b_col.second, field(), b_row_cmp); - assert(!B.col(b_col.first).empty()); - B.fix(b_col.first); - // no need to update low (additions from bottom up) - } - } // z_row.size() > 1 - - // 2: subtract cycle from every chain in C - const Column& Zj = Z.col(j); - //std::cout << "Zj:" << std::endl; - //for (auto& x : Zj) - // std::cout << x.element() << " * " << row(x) << std::endl; - - IndexChain Ccols; // save the columns in C, we'll be modifying C.row(cell) - for (auto& x : C.row(cell)) - Ccols.emplace_back(x.element(), col(x)); - - for (auto& x : Ccols) - { - Index c = x.index(); - FieldElement m = field().neg(field().div(x.element(), e)); // m = -C[k][cell]/Z[j][cell] - //std::cout << "Adding to C: " << c << std::endl; - Chain::addto(C.col(c), m, Zj, field(), row_cmp); - C.fix(c); - // we don't care about lows in C, so don't update them - } - - // 3: drop - assert(Z.row(cell).size() == 1); - Z.drop_col(j); - assert(Z.row(cell).empty()); - assert(C.row(cell).empty()); - Z.drop_row(cell); - C.drop_row(cell); - assert(B.row(j).empty()); - B.drop_row(j); - - Index birth = birth_index[j]; - birth_index.erase(j); - - return birth; - } -} - - -/* debug routines */ -template -void -dionysus::ZigzagPersistence:: -check_b_cols() const -{ - // check that entries in B refer to existing Z columns - bool stop = false; - for (auto& b : B.columns()) - for (auto& x : b.second) - if (!Z.col_exists(row(x))) - { - std::cout << "B refers to a non-existent column in Z: " << row(x) << std::endl; - stop = true; - } - if (stop) - assert(0); -} - -template -template -void -dionysus::ZigzagPersistence:: -check_cycles(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - - for (auto& z : Z.columns()) - { - Column res; - for (auto& x : z.second) - { - Column bdry = boundary(row(x), s2i, i2s); - Chain::addto(res, x.element(), bdry, field(), row_cmp); - } - assert(res.empty()); - } -} - -template -template -void -dionysus::ZigzagPersistence:: -check_boundaries(const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - check_cycles(s2i, i2s); - - for (auto& x : B.columns()) - if (!C.col_exists(x.first)) - { - std::cout << x.first << " in B, but not in C" << std::endl; - assert(0); - } - - for (auto& x : C.columns()) - if (!B.col_exists(x.first)) - { - std::cout << x.first << " in B, but not in C" << std::endl; - assert(0); - } - - for (auto& x : B.columns()) - { - auto zb = zb_dot(x.first); - auto dc = dc_dot(x.first, s2i, i2s); - - auto it_zb = zb.begin(), - it_dc = dc.begin(); - for (; it_zb != zb.end(); ++it_zb, ++it_dc) - { - if (it_zb->element() != it_dc->element() || row(*it_zb) != row(*it_dc)) - { - std::cout << "Boundary mismatch: " << x.first << std::endl; - std::cout << "===" << std::endl; - for (auto& x : zb) - std::cout << " " << x.element() << ' ' << row(x) << std::endl; - for (auto& y : B.col(x.first)) - { - std::cout << " " << y.element() << " * " << row(y) << std::endl; - for (auto& z : Z.col(row(y))) - std::cout << " " << z.element() << ' ' << row(z) << std::endl; - std::cout << " ---" << std::endl; - } - std::cout << "===" << std::endl; - for (auto& x : dc) - std::cout << " " << x.element() << ' ' << row(x) << std::endl; - for (auto& y : C.col(x.first)) - { - std::cout << " " << y.element() << " * " << row(y) << std::endl; - for (auto& z : boundary(row(y), s2i, i2s)) - std::cout << " " << z.element() << ' ' << row(z) << std::endl; - std::cout << " ---" << std::endl; - } - assert(0); - } - } - if (it_zb != zb.end() || it_dc != dc.end()) - { - std::cout << "zb.end() doesn't match dc.end()" << std::endl; - assert(0); - } - } -} - -template -typename dionysus::ZigzagPersistence::Column -dionysus::ZigzagPersistence:: -zb_dot(Index c) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column res; - for (auto& x : B.col(c)) - Chain::addto(res, x.element(), Z.col(row(x)), field(), row_cmp); - - return res; -} - -template -template -typename dionysus::ZigzagPersistence::Column -dionysus::ZigzagPersistence:: -dc_dot(Index c, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column res; - for (auto& x : C.col(c)) - { - Column bdry = boundary(row(x), s2i, i2s); - Chain::addto(res, x.element(), bdry, field(), row_cmp); - } - return res; -} - -template -template -typename dionysus::ZigzagPersistence::Column -dionysus::ZigzagPersistence:: -boundary(Index i, const SimplexToIndex& s2i, const IndexToSimplex& i2s) const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - Column bdry; - auto s = i2s(i); - for (auto y : s.boundary(field())) - bdry.emplace_back(y.element(), s2i(y.index()), 0); - std::sort(bdry.begin(), bdry.end(), row_cmp); - return bdry; -} - -template -void -dionysus::ZigzagPersistence:: -check_sorted() const -{ - typedef typename Column::value_type Entry; - auto row_cmp = [this](const Entry& e1, const Entry& e2) - { return this->cmp()(row(e1), row(e2)); }; - typedef typename DequeColumn::value_type DequeEntry; - auto b_row_cmp = [this](const DequeEntry& e1, const DequeEntry& e2) - { return this->cmp()(row(e1), row(e2)); }; - - for (auto& x : Z.columns()) - if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) - { - std::cout << "Z column not sorted: " << x.first << std::endl; - assert(0); - } - for (auto& x : C.columns()) - if (!std::is_sorted(x.second.begin(), x.second.end(), row_cmp)) - { - std::cout << "C column not sorted: " << x.first << std::endl; - assert(0); - } - for (auto& x : B.columns()) - if (!std::is_sorted(x.second.begin(), x.second.end(), b_row_cmp)) - { - std::cout << "B column not sorted: " << x.first << std::endl; - assert(0); - } -} - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp deleted file mode 100644 index 5217a4f38c..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "fzz.h" - -#include - -// phat headers -// wrapper algorithm that computes the persistence pairs of a given boundary matrix using a specified algorithm -#include - -// main data structure (choice affects performance) -#include -#include - -// algorithm (choice affects performance) -#include -#include -#include -#include - -namespace FZZ { - -template -class VecHash { -public: - size_t operator()(const std::vector& v) const; -}; - -template -size_t VecHash - ::operator()(const std::vector& v) const { - - std::size_t seed = 0; - - for (auto e : v) { boost::hash_combine(seed, e); } - - return seed; -} - -template -class VecEqual { -public: - bool operator()(const std::vector& v1, - const std::vector& v2) const; -}; - -template -bool VecEqual - ::operator()(const std::vector& v1, - const std::vector& v2) const { - - if (v1.size() != v2.size()) { return false; } - - for (unsigned int i = 0; i < v1.size(); i ++) { - if (v1[i] != v2[i]) { - return false; - } - } - - return true; -} - -typedef std::unordered_map< Simplex, Integer, - VecHash, VecEqual > SimplexIdMap; - -void getBoundaryChainPhat(const std::vector &id_maps, - const Simplex &simp, std::vector &bound_c) { - - bound_c.clear(); - - if (simp.size() <= 1) { return; } - - bound_c.reserve(simp.size()); - - Simplex bound_simp(simp.begin()+1, simp.end()); - bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); - - for (unsigned int i = 0; i < simp.size()-1; ++i) { - bound_simp[i] = simp[i]; - bound_c.push_back(id_maps.at(bound_simp.size() - 1).at(bound_simp)); - } - - std::sort(bound_c.begin(), bound_c.end()); -} - -inline Integer getDim(const std::vector &bound_c) { - if (bound_c.size() == 0) { return 0; } - return bound_c.size() - 1; -} - -void FastZigzag::compute(const std::vector &filt_simp, - const std::vector &filt_op, - std::vector< std::tuple > *persistence) { - - orig_f_add_id.clear(); - orig_f_del_id.clear(); - persistence->clear(); - - simp_num = 0; - Integer max_dim = 0; - for (unsigned int i = 0; i < filt_op.size(); ++i) { - if (filt_op[i]) { - ++simp_num; - if (static_cast(filt_simp[i].size()) - 1 > max_dim) { max_dim = filt_simp[i].size() - 1; } - } - } - - std::vector bound_c; - // phat::boundary_matrix< phat::vector_vector > bound_chains; - phat::boundary_matrix< phat::bit_tree_pivot_column > bound_chains; - bound_chains.set_num_cols(simp_num * 2 + 1); - - // Add the Omega vertex for the coning - bound_chains.set_col(0, bound_c); - bound_chains.set_dim(0, 0); - - orig_f_add_id.reserve(simp_num); - orig_f_del_id.reserve(simp_num); - - std::vector del_ids; - del_ids.reserve(simp_num); - - std::vector *p_id_maps = new std::vector(max_dim+1); - std::vector &id_maps = *p_id_maps; - - Integer orig_f_id = 0; - Integer s_id = 1; - - for (unsigned int i = 0; i < filt_simp.size(); ++i) { - const Simplex &simp = filt_simp[i]; - - if (filt_op[i]) { - getBoundaryChainPhat(id_maps, simp, bound_c); - bound_chains.set_col(s_id, bound_c); - bound_chains.set_dim(s_id, getDim(bound_c)); - - // assert(s_id == bound_chains.size()-1); - id_maps.at(simp.size() - 1)[simp] = s_id; - orig_f_add_id.push_back(orig_f_id); - s_id ++; - } else { - del_ids.push_back(id_maps.at(simp.size() - 1)[simp]); - id_maps.at(simp.size() - 1).erase(simp); - orig_f_del_id.push_back(orig_f_id); - } - - orig_f_id ++; - } - - for (Integer i = id_maps.size() - 1; i >= 0; -- i) { - for (const auto &it : id_maps.at(i)) { - del_ids.push_back(it.second); - orig_f_del_id.push_back(orig_f_id); - orig_f_id ++; - } - } - - assert(del_ids.size() == s_id-1); - delete p_id_maps; - - assert(simp_num == del_ids.size()); - - std::vector cone_sid(simp_num+1); - - for (auto del_id_it = del_ids.rbegin(); del_id_it != del_ids.rend(); ++del_id_it) { - bound_c.clear(); - bound_c.push_back(*del_id_it); - - std::vector orig_bound_c; - bound_chains.get_col(*del_id_it, orig_bound_c); - - if (orig_bound_c.size() == 0) { - bound_c.push_back(0); - } else { - for (auto bsimp : orig_bound_c) { - // assert(cone_sid[bsimp] >= 0); - bound_c.push_back(cone_sid[bsimp]); - } - } - - std::sort(bound_c.begin(), bound_c.end()); - - bound_chains.set_col(s_id, bound_c); - bound_chains.set_dim(s_id, getDim(bound_c)); - - cone_sid[*del_id_it] = s_id; - - s_id ++; - } - - phat::persistence_pairs pairs; - phat::compute_persistence_pairs< phat::twist_reduction >( pairs, bound_chains ); - - for (phat::index idx = 0; idx < pairs.get_num_pairs(); idx++) { - Integer b = pairs.get_pair(idx).first; - Integer d = pairs.get_pair(idx).second - 1; - Integer p = bound_chains.get_dim(b); - - if (d < simp_num) { mapOrdIntv(b, d); } - else { mapRelExtIntv(p, b, d); } - - if (b > static_cast(filt_simp.size())) { continue; } - if (d > static_cast(filt_simp.size())) { d = filt_simp.size(); } - persistence->emplace_back(b, d, p); - } -} - -} // namespace FZZ { diff --git a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h b/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h deleted file mode 100644 index 8613bc3793..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/fzz/fzz.h +++ /dev/null @@ -1,87 +0,0 @@ -#ifndef _FZZ_H_ -#define _FZZ_H_ - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace FZZ { - -typedef int Integer; -typedef std::vector Simplex; - -class FastZigzag { -public: - /* - 'filt_simp' and 'filt_op' should have the same length which altogether - specify the input zigzag filtration. 'filt_simp' specifies the simplices - being added or deleted (following the order of the filtration) and - 'filt_op' specifies whether it's an addition (true) or deletion (false). - 'persistence' returns the barcode, with the first element of the tuple - being the birth, the second element being the death, and the third - being the dimension. - */ - void compute( - const std::vector &filt_simp, - const std::vector &filt_op, - std::vector< std::tuple > *persistence); - -private: - void mapOrdIntv(Integer &b, Integer &d) { - // assert(b-1 > 0); - // assert(d < orig_f_add_id.size()); - - // Up-down interval is same, - // so directly map to interval of input filtration - b = orig_f_add_id[b-1] + 1; - d = orig_f_add_id[d]; - } - - void mapRelExtIntv(Integer &p, Integer &b, Integer &d) { - // assert(d >= simp_num); - - if (b > simp_num) { // Open-closed - // Map to up-down interval - std::swap(b, d); - b = 3*simp_num - b; - d = 3*simp_num - d; - p --; - - // Map to interval of input filtration - b = orig_f_del_id[b-1-simp_num] + 1; - d = orig_f_del_id[d-simp_num]; - } else { // Closed-closed - // Map to up-down interval - d = 3*simp_num - d-1; - - // Map to interval of input filtration - b = orig_f_add_id[b-1]; - d = orig_f_del_id[d-simp_num]; - - if (b < d) { - b = b+1; - } else { - std::swap(b, d); - b = b+1; - p = p-1; - } - } - } - -private: - // 'orig_f_add_id' and 'orig_f_del_id' form a mapping - // from the up-down filtration to the original filtration - std::vector orig_f_add_id; - std::vector orig_f_del_id; - - Integer simp_num; -}; - -} - -#endif diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h deleted file mode 100644 index 179702312f..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/chunk_reduction.h +++ /dev/null @@ -1,223 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class chunk_reduction { - public: - enum column_type { GLOBAL - , LOCAL_POSITIVE - , LOCAL_NEGATIVE }; - - public: - template< typename Representation > - void operator() ( boundary_matrix< Representation >& boundary_matrix ) { - - - const index nr_columns = boundary_matrix.get_num_cols(); - if( omp_get_max_threads( ) > nr_columns ) - omp_set_num_threads( 1 ); - - const dimension max_dim = boundary_matrix.get_max_dim(); - - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - std::vector < column_type > column_type( nr_columns, GLOBAL ); - std::vector< char > is_active( nr_columns, false ); - - const index chunk_size = omp_get_max_threads() == 1 ? (index)sqrt( (double)nr_columns ) : nr_columns / omp_get_max_threads(); - - std::vector< index > chunk_boundaries; - for( index cur_boundary = 0; cur_boundary < nr_columns; cur_boundary += chunk_size ) - chunk_boundaries.push_back( cur_boundary ); - chunk_boundaries.push_back( nr_columns ); - - for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { - // Phase 1: Reduce chunks locally -- 1st pass - #pragma omp parallel for schedule( guided, 1 ) - for( index chunk_id = 0; chunk_id < (index)chunk_boundaries.size() - 1; chunk_id++ ) - _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, - chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id ] ); - boundary_matrix.sync(); - - // Phase 1: Reduce chunks locally -- 2nd pass - #pragma omp parallel for schedule( guided, 1 ) - for( index chunk_id = 1; chunk_id < (index)chunk_boundaries.size( ) - 1; chunk_id++ ) - _local_chunk_reduction( boundary_matrix, lowest_one_lookup, column_type, cur_dim, - chunk_boundaries[ chunk_id ], chunk_boundaries[ chunk_id + 1 ], chunk_boundaries[ chunk_id - 1 ] ); - boundary_matrix.sync( ); - } - - // get global columns - std::vector< index > global_columns; - for( index cur_col_idx = 0; cur_col_idx < nr_columns; cur_col_idx++ ) - if( column_type[ cur_col_idx ] == GLOBAL ) - global_columns.push_back( cur_col_idx ); - - // get active columns - #pragma omp parallel for - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) - is_active[ global_columns[ idx ] ] = true; - _get_active_columns( boundary_matrix, lowest_one_lookup, column_type, global_columns, is_active ); - - // Phase 2+3: Simplify columns and reduce them - for( dimension cur_dim = max_dim; cur_dim >= 1; cur_dim-- ) { - // Phase 2: Simplify columns - std::vector< index > temp_col; - #pragma omp parallel for schedule( guided, 1 ), private( temp_col ) - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) - if( boundary_matrix.get_dim( global_columns[ idx ] ) == cur_dim ) - _global_column_simplification( global_columns[ idx ], boundary_matrix, lowest_one_lookup, column_type, is_active, temp_col ); - boundary_matrix.sync(); - - // Phase 3: Reduce columns - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { - index cur_col = global_columns[ idx ]; - if( boundary_matrix.get_dim( cur_col ) == cur_dim && column_type[ cur_col ] == GLOBAL ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - lowest_one_lookup[ lowest_one ] = cur_col; - boundary_matrix.clear( lowest_one ); - } - boundary_matrix.finalize( cur_col ); - } - } - } - - boundary_matrix.sync(); - } - - protected: - template< typename Representation > - void _local_chunk_reduction( boundary_matrix< Representation >& boundary_matrix - , std::vector& lowest_one_lookup - , std::vector< column_type >& column_type - , const dimension cur_dim - , const index chunk_begin - , const index chunk_end - , const index row_begin ) { - - for( index cur_col = chunk_begin; cur_col < chunk_end; cur_col++ ) { - if( column_type[ cur_col ] == GLOBAL && boundary_matrix.get_dim( cur_col ) == cur_dim ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one >= row_begin && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one >= row_begin ) { - lowest_one_lookup[ lowest_one ] = cur_col; - column_type[ cur_col ] = LOCAL_NEGATIVE; - column_type[ lowest_one ] = LOCAL_POSITIVE; - boundary_matrix.clear( lowest_one ); - boundary_matrix.finalize( cur_col ); - } - } - } - } - - template< typename Representation > - void _get_active_columns( const boundary_matrix< Representation >& boundary_matrix - , const std::vector< index >& lowest_one_lookup - , const std::vector< column_type >& column_type - , const std::vector< index >& global_columns - , std::vector< char >& is_active ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< char > finished( nr_columns, false ); - - std::vector< std::pair < index, index > > stack; - std::vector< index > cur_col_values; - #pragma omp parallel for schedule( guided, 1 ), private( stack, cur_col_values ) - for( index idx = 0; idx < (index)global_columns.size(); idx++ ) { - bool pop_next = false; - index start_col = global_columns[ idx ]; - stack.push_back( std::pair< index, index >( start_col, -1 ) ); - while( !stack.empty() ) { - index cur_col = stack.back().first; - index prev_col = stack.back().second; - if( pop_next ) { - stack.pop_back(); - pop_next = false; - if( prev_col != -1 ) { - if( is_active[ cur_col ] ) { - is_active[ prev_col ] = true; - } - if( prev_col == stack.back().first ) { - finished[ prev_col ] = true; - pop_next = true; - } - } - } else { - pop_next = true; - boundary_matrix.get_col( cur_col, cur_col_values ); - for( index idx = 0; idx < (index) cur_col_values.size(); idx++ ) { - index cur_row = cur_col_values[ idx ]; - if( ( column_type[ cur_row ] == GLOBAL ) ) { - is_active[ cur_col ] = true; - } else if( column_type[ cur_row ] == LOCAL_POSITIVE ) { - index next_col = lowest_one_lookup[ cur_row ]; - if( next_col != cur_col && !finished[ cur_col ] ) { - stack.push_back( std::make_pair( next_col, cur_col ) ); - pop_next = false; - } - } - } - } - } - } - } - - template< typename Representation > - void _global_column_simplification( const index col_idx - , boundary_matrix< Representation >& boundary_matrix - , const std::vector< index >& lowest_one_lookup - , const std::vector< column_type >& column_type - , const std::vector< char >& is_active - , std::vector< index >& temp_col ) - { - temp_col.clear(); - while( !boundary_matrix.is_empty( col_idx ) ) { - index cur_row = boundary_matrix.get_max_index( col_idx ); - switch( column_type[ cur_row ] ) { - case GLOBAL: - temp_col.push_back( cur_row ); - boundary_matrix.remove_max( col_idx ); - break; - case LOCAL_NEGATIVE: - boundary_matrix.remove_max( col_idx ); - break; - case LOCAL_POSITIVE: - if( is_active[ lowest_one_lookup[ cur_row ] ] ) - boundary_matrix.add_to( lowest_one_lookup[ cur_row ], col_idx ); - else - boundary_matrix.remove_max( col_idx ); - break; - } - } - std::reverse( temp_col.begin(), temp_col.end() ); - boundary_matrix.set_col( col_idx, temp_col ); - } - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h deleted file mode 100644 index cdd1a8fd18..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/row_reduction.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class row_reduction { - public: - template< typename Representation > - void operator() ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< std::vector< index > > lowest_one_lookup( nr_columns ); - - for( index cur_col = nr_columns - 1; cur_col >= 0; cur_col-- ) { - if( !boundary_matrix.is_empty( cur_col ) ) - lowest_one_lookup[ boundary_matrix.get_max_index( cur_col ) ].push_back( cur_col ); - - if( !lowest_one_lookup[ cur_col ].empty() ) { - boundary_matrix.clear( cur_col ); - boundary_matrix.finalize( cur_col ); - std::vector< index >& cols_with_cur_lowest = lowest_one_lookup[ cur_col ]; - index source = *min_element( cols_with_cur_lowest.begin(), cols_with_cur_lowest.end() ); - for( index idx = 0; idx < (index)cols_with_cur_lowest.size(); idx++ ) { - index target = cols_with_cur_lowest[ idx ]; - if( target != source && !boundary_matrix.is_empty( target ) ) { - boundary_matrix.add_to( source, target ); - if( !boundary_matrix.is_empty( target ) ) { - index lowest_one_of_target = boundary_matrix.get_max_index( target ); - lowest_one_lookup[ lowest_one_of_target ].push_back( target ); - } - } - } - } - } - } - }; -} \ No newline at end of file diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h deleted file mode 100644 index bf442e6089..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/spectral_sequence_reduction.h +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class spectral_sequence_reduction { - public: - template< typename Representation > - void operator () ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - - //const index num_stripes = (index) sqrt( (double)nr_columns ); - const index num_stripes = omp_get_max_threads(); - - index block_size = ( nr_columns % num_stripes == 0 ) ? nr_columns / num_stripes : block_size = nr_columns / num_stripes + 1; - - std::vector< std::vector< index > > unreduced_cols_cur_pass( num_stripes ); - std::vector< std::vector< index > > unreduced_cols_next_pass( num_stripes ); - - for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { - #pragma omp parallel for schedule( guided, 1 ) - for( index cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { - index col_begin = cur_stripe * block_size; - index col_end = std::min( (cur_stripe+1) * block_size, nr_columns ); - for( index cur_col = col_begin; cur_col < col_end; cur_col++ ) - if( boundary_matrix.get_dim( cur_col ) == cur_dim && boundary_matrix.get_max_index( cur_col ) != -1 ) - unreduced_cols_cur_pass[ cur_stripe ].push_back( cur_col ); - } - for( index cur_pass = 0; cur_pass < num_stripes; cur_pass++ ) { - boundary_matrix.sync(); - #pragma omp parallel for schedule( guided, 1 ) - for( int cur_stripe = 0; cur_stripe < num_stripes; cur_stripe++ ) { - index row_begin = (cur_stripe - cur_pass) * block_size; - index row_end = row_begin + block_size; - unreduced_cols_next_pass[ cur_stripe ].clear(); - for( index idx = 0; idx < (index)unreduced_cols_cur_pass[ cur_stripe ].size(); idx++ ) { - index cur_col = unreduced_cols_cur_pass[ cur_stripe ][ idx ]; - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one >= row_begin && lowest_one < row_end && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - if( lowest_one >= row_begin && lowest_one < row_end ) { - lowest_one_lookup[ lowest_one ] = cur_col; - boundary_matrix.clear( lowest_one ); - boundary_matrix.finalize( cur_col ); - } else { - unreduced_cols_next_pass[ cur_stripe ].push_back( cur_col ); - } - } - } - unreduced_cols_next_pass[ cur_stripe ].swap( unreduced_cols_cur_pass[ cur_stripe ] ); - } - } - } - } - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h deleted file mode 100644 index e490a5e0d1..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/standard_reduction.h +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class standard_reduction { - public: - template< typename Representation > - void operator() ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - lowest_one_lookup[ lowest_one ] = cur_col; - } - boundary_matrix.finalize( cur_col ); - } - } - }; -} - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h deleted file mode 100644 index 2357df0256..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/algorithms/twist_reduction.h +++ /dev/null @@ -1,51 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class twist_reduction { - public: - template< typename Representation > - void operator () ( boundary_matrix< Representation >& boundary_matrix ) { - - const index nr_columns = boundary_matrix.get_num_cols(); - std::vector< index > lowest_one_lookup( nr_columns, -1 ); - - for( index cur_dim = boundary_matrix.get_max_dim(); cur_dim >= 1 ; cur_dim-- ) { - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - if( boundary_matrix.get_dim( cur_col ) == cur_dim ) { - index lowest_one = boundary_matrix.get_max_index( cur_col ); - while( lowest_one != -1 && lowest_one_lookup[ lowest_one ] != -1 ) { - boundary_matrix.add_to( lowest_one_lookup[ lowest_one ], cur_col ); - lowest_one = boundary_matrix.get_max_index( cur_col ); - } - if( lowest_one != -1 ) { - lowest_one_lookup[ lowest_one ] = cur_col; - boundary_matrix.clear( lowest_one ); - } - boundary_matrix.finalize( cur_col ); - } - } - } - } - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h deleted file mode 100644 index 10c66cca13..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/boundary_matrix.h +++ /dev/null @@ -1,343 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -// interface class for the main data structure -- implementations of the interface can be found in ./representations -namespace phat { - template< class Representation = bit_tree_pivot_column > - class boundary_matrix - { - - protected: - Representation rep; - - // interface functions -- actual implementation and complexity depends on chosen @Representation template - public: - // get overall number of columns in boundary_matrix - index get_num_cols() const { return rep._get_num_cols(); } - - // set overall number of columns in boundary_matrix - void set_num_cols( index nr_of_columns ) { rep._set_num_cols( nr_of_columns ); } - - // get dimension of given index - dimension get_dim( index idx ) const { return rep._get_dim( idx ); } - - // set dimension of given index - void set_dim( index idx, dimension dim ) { rep._set_dim( idx, dim ); } - - // replaces content of @col with boundary of given index - void get_col( index idx, column& col ) const { col.clear(); rep._get_col( idx, col ); } - - // set column @idx to the values contained in @col - void set_col( index idx, const column& col ) { rep._set_col( idx, col ); } - - // true iff boundary of given column is empty - bool is_empty( index idx ) const { return rep._is_empty( idx ); } - - // largest index of given column (new name for lowestOne()) -- NOT thread-safe - index get_max_index( index idx ) const { return rep._get_max_index( idx ); } - - // removes maximal index from given column - void remove_max( index idx ) { rep._remove_max( idx ); } - - // adds column @source to column @target' - void add_to( index source, index target ) { rep._add_to( source, target ); } - - // clears given column - void clear( index idx ) { rep._clear( idx ); } - - // finalizes given column - void finalize( index idx ) { rep._finalize( idx ); } - - // syncronizes all internal data structures -- has to be called before and after any multithreaded access! - void sync() { rep._sync(); } - - // info functions -- independent of chosen 'Representation' - public: - // maximal dimension - dimension get_max_dim() const { - dimension cur_max_dim = 0; - for( index idx = 0; idx < get_num_cols(); idx++ ) - cur_max_dim = get_dim( idx ) > cur_max_dim ? get_dim( idx ) : cur_max_dim; - return cur_max_dim; - } - - // number of nonzero rows for given column @idx - index get_num_rows( index idx ) const { - column cur_col; - get_col( idx, cur_col ); - return cur_col.size(); - } - - // maximal number of nonzero rows of all columns - index get_max_col_entries() const { - index max_col_entries = -1; - const index nr_of_columns = get_num_cols(); - for( index idx = 0; idx < nr_of_columns; idx++ ) - max_col_entries = get_num_rows( idx ) > max_col_entries ? get_num_rows( idx ) : max_col_entries; - return max_col_entries; - } - - // maximal number of nonzero cols of all rows - index get_max_row_entries() const { - size_t max_row_entries = 0; - const index nr_of_columns = get_num_cols(); - std::vector< std::vector< index > > transposed_matrix( nr_of_columns ); - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - get_col( cur_col, temp_col ); - for( index idx = 0; idx < (index)temp_col.size(); idx++) - transposed_matrix[ temp_col[ idx ] ].push_back( cur_col ); - } - for( index idx = 0; idx < nr_of_columns; idx++ ) - max_row_entries = transposed_matrix[ idx ].size() > max_row_entries ? transposed_matrix[ idx ].size() : max_row_entries; - return max_row_entries; - } - - // overall number of entries in the matrix - index get_num_entries() const { - index number_of_nonzero_entries = 0; - const index nr_of_columns = get_num_cols(); - for( index idx = 0; idx < nr_of_columns; idx++ ) - number_of_nonzero_entries += get_num_rows( idx ); - return number_of_nonzero_entries; - } - - // operators / constructors - public: - boundary_matrix() {}; - - template< class OtherRepresentation > - boundary_matrix( const boundary_matrix< OtherRepresentation >& other ) { - *this = other; - } - - template< typename OtherRepresentation > - bool operator==( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { - const index number_of_columns = this->get_num_cols(); - - if( number_of_columns != other_boundary_matrix.get_num_cols() ) - return false; - - column temp_col; - column other_temp_col; - for( index idx = 0; idx < number_of_columns; idx++ ) { - this->get_col( idx, temp_col ); - other_boundary_matrix.get_col( idx, other_temp_col ); - if( temp_col != other_temp_col || this->get_dim( idx ) != other_boundary_matrix.get_dim( idx ) ) - return false; - } - return true; - } - - template< typename OtherRepresentation > - bool operator!=( const boundary_matrix< OtherRepresentation >& other_boundary_matrix ) const { - return !( *this == other_boundary_matrix ); - } - - template< typename OtherRepresentation > - boundary_matrix< Representation >& operator=( const boundary_matrix< OtherRepresentation >& other ) - { - const index nr_of_columns = other.get_num_cols(); - this->set_num_cols( nr_of_columns ); - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - this->set_dim( cur_col, other.get_dim( cur_col ) ); - other.get_col( cur_col, temp_col ); - this->set_col( cur_col, temp_col ); - } - - // by convention, always return *this - return *this; - } - - // I/O -- independent of chosen 'Representation' - public: - - // initializes boundary_matrix from (vector, vector) pair -- untested - template< typename index_type, typename dimemsion_type > - void load_vector_vector( const std::vector< std::vector< index_type > >& input_matrix, const std::vector< dimemsion_type >& input_dims ) { - const index nr_of_columns = (index)input_matrix.size(); - this->set_num_cols( nr_of_columns ); - column temp_col; - #pragma omp parallel for private( temp_col ) - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - this->set_dim( cur_col, (dimension)input_dims[ cur_col ] ); - - index num_rows = input_matrix[ cur_col ].size(); - temp_col.resize( num_rows ); - for( index cur_row = 0; cur_row < num_rows; cur_row++ ) - temp_col[ cur_row ] = (index)input_matrix[ cur_col ][ cur_row ]; - this->set_col( cur_col, temp_col ); - } - } - - template< typename index_type, typename dimemsion_type > - void save_vector_vector( std::vector< std::vector< index_type > >& output_matrix, std::vector< dimemsion_type >& output_dims ) { - const index nr_of_columns = get_num_cols(); - output_matrix.resize( nr_of_columns ); - output_dims.resize( nr_of_columns ); - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - output_dims[ cur_col ] = (dimemsion_type)get_dim( cur_col ); - get_col( cur_col, temp_col ); - index num_rows = temp_col.size(); - output_matrix[ cur_col ].clear(); - output_matrix[ cur_col ].resize( num_rows ); - for( index cur_row = 0; cur_row < num_rows; cur_row++ ) - output_matrix[ cur_col ][ cur_row ] = (index_type)temp_col[ cur_row ]; - } - } - - - // Loads the boundary_matrix from given file in ascii format - // Format: each line represents a column, first number is dimension, other numbers are the content of the column. - // Ignores empty lines and lines starting with a '#'. - bool load_ascii( std::string filename ) { - // first count number of columns: - std::string cur_line; - std::ifstream dummy( filename .c_str() ); - if( dummy.fail() ) - return false; - - index number_of_columns = 0; - while( getline( dummy, cur_line ) ) { - cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); - if( cur_line != "" && cur_line[ 0 ] != '#' ) - number_of_columns++; - - } - this->set_num_cols( number_of_columns ); - dummy.close(); - - std::ifstream input_stream( filename.c_str() ); - if( input_stream.fail() ) - return false; - - column temp_col; - index cur_col = -1; - while( getline( input_stream, cur_line ) ) { - cur_line.erase(cur_line.find_last_not_of(" \t\n\r\f\v") + 1); - if( cur_line != "" && cur_line[ 0 ] != '#' ) { - cur_col++; - std::stringstream ss( cur_line ); - - int64_t temp_dim; - ss >> temp_dim; - this->set_dim( cur_col, (dimension) temp_dim ); - - int64_t temp_index; - temp_col.clear(); - while( ss.good() ) { - ss >> temp_index; - temp_col.push_back( (index)temp_index ); - } - std::sort( temp_col.begin(), temp_col.end() ); - this->set_col( cur_col, temp_col ); - } - } - - input_stream.close(); - return true; - } - - // Saves the boundary_matrix to given file in ascii format - // Format: each line represents a column, first number is dimension, other numbers are the content of the column - bool save_ascii( std::string filename ) { - std::ofstream output_stream( filename.c_str() ); - if( output_stream.fail() ) - return false; - - const index nr_columns = this->get_num_cols(); - column tempCol; - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - output_stream << (int64_t)this->get_dim( cur_col ); - this->get_col( cur_col, tempCol ); - for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size(); cur_row_idx++ ) - output_stream << " " << tempCol[ cur_row_idx ]; - output_stream << std::endl; - } - - output_stream.close(); - return true; - } - - // Loads boundary_matrix from given file - // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... - bool load_binary( std::string filename ) - { - std::ifstream input_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::in ); - if( input_stream.fail( ) ) - return false; - - int64_t nr_columns; - input_stream.read( (char*)&nr_columns, sizeof( int64_t ) ); - this->set_num_cols( (index)nr_columns ); - - column temp_col; - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - int64_t cur_dim; - input_stream.read( (char*)&cur_dim, sizeof( int64_t ) ); - this->set_dim( cur_col, (dimension)cur_dim ); - int64_t nr_rows; - input_stream.read( (char*)&nr_rows, sizeof( int64_t ) ); - temp_col.resize( ( std::size_t )nr_rows ); - for( index idx = 0; idx < nr_rows; idx++ ) { - int64_t cur_row; - input_stream.read( (char*)&cur_row, sizeof( int64_t ) ); - temp_col[ idx ] = (index)cur_row; - } - this->set_col( cur_col, temp_col ); - } - - input_stream.close( ); - return true; - } - - // Saves the boundary_matrix to given file in binary format - // Format: nr_columns % dim1 % N1 % row1 row2 % ...% rowN1 % dim2 % N2 % ... - bool save_binary( std::string filename ) - { - std::ofstream output_stream( filename.c_str( ), std::ios_base::binary | std::ios_base::out ); - if( output_stream.fail( ) ) - return false; - - const int64_t nr_columns = this->get_num_cols( ); - output_stream.write( (char*)&nr_columns, sizeof( int64_t ) ); - column tempCol; - for( index cur_col = 0; cur_col < nr_columns; cur_col++ ) { - int64_t cur_dim = this->get_dim( cur_col ); - output_stream.write( (char*)&cur_dim, sizeof( int64_t ) ); - this->get_col( cur_col, tempCol ); - int64_t cur_nr_rows = tempCol.size( ); - output_stream.write( (char*)&cur_nr_rows, sizeof( int64_t ) ); - for( index cur_row_idx = 0; cur_row_idx < (index)tempCol.size( ); cur_row_idx++ ) { - int64_t cur_row = tempCol[ cur_row_idx ]; - output_stream.write( (char*)&cur_row, sizeof( int64_t ) ); - } - } - - output_stream.close( ); - return true; - } - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h deleted file mode 100644 index 48be65c28e..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/compute_persistence_pairs.h +++ /dev/null @@ -1,128 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include -#include -#include - -namespace phat { - // Extracts persistence pairs in separate dimensions from a reduced - // boundary matrix representing ``double`` filtration. The pairs - // give persistent relative homology of the pair of filtrations. - // TODO: Use it with standard reduction algorithm (no template option). - template< typename ReductionAlgorithm, typename Representation > - void compute_relative_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix, const std::map& L) { - ReductionAlgorithm reduce; - reduce(boundary_matrix); - std::map free; - std::map invL; - for (std::map::const_iterator it = L.begin(); it != L.end(); ++it) { invL[it->second] = it->first; } - for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } - for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { - int dimension = boundary_matrix.get_dim(idx); - if (L.find(idx) != L.end()) { ++dimension; } - free[idx] = true; - if (!boundary_matrix.is_empty(idx)) { - index birth = boundary_matrix.get_max_index(idx); - index death = idx; - pairs[dimension-1].append_pair(birth, death); - free[birth] = false; - free[death] = false; - } else { - // This is an L-simplex and a (dimension+1)-dimensional cycle - if (L.find(idx) != L.end()) { - assert(dimension < pairs.size()); - pairs[dimension].append_pair(idx, -1); - } - } - } - for (std::map::iterator it = free.begin(); it != free.end(); ++it) { - if (it->second) { - int dimension = boundary_matrix.get_dim(it->first); - if (invL.find(it->first) == invL.end() && L.find(it->first) == L.end()) { - assert(dimension < pairs.size()); - pairs[dimension].append_pair(it->first, -1); - } - } - } - } - - // Extracts persistence pairs in separate dimensions; expects a d-dimensional vector of persistent_pairs - template< typename ReductionAlgorithm, typename Representation > - void compute_persistence_pairs(std::vector& pairs, boundary_matrix& boundary_matrix) { - ReductionAlgorithm reduce; - reduce(boundary_matrix); - std::map free; - for (std::vector::iterator it = pairs.begin(); it != pairs.end(); ++it) { it->clear(); } - for (index idx = 0; idx < boundary_matrix.get_num_cols(); ++idx) { - int dimension = boundary_matrix.get_dim(idx); - free[idx] = true; - if (!boundary_matrix.is_empty(idx)) { - index birth = boundary_matrix.get_max_index(idx); - index death = idx; - pairs[dimension-1].append_pair(birth, death); - // Cannot be of the form (a, infinity) - free[birth] = false; - free[death] = false; - } - } - for (std::map::iterator it = free.begin(); it != free.end(); ++it) { - if (it->second) { - int dimension = boundary_matrix.get_dim(it->first); - pairs[dimension].append_pair(it->first, -1); - } - } - } - - template< typename ReductionAlgorithm, typename Representation > - void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - ReductionAlgorithm reduce; - reduce( boundary_matrix ); - pairs.clear(); - for( index idx = 0; idx < boundary_matrix.get_num_cols(); idx++ ) { - if( !boundary_matrix.is_empty( idx ) ) { - index birth = boundary_matrix.get_max_index( idx ); - index death = idx; - pairs.append_pair( birth, death ); - } - } - } - - template< typename ReductionAlgorithm, typename Representation > - void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - - dualize( boundary_matrix ); - compute_persistence_pairs< ReductionAlgorithm >( pairs, boundary_matrix ); - dualize_persistence_pairs( pairs, boundary_matrix.get_num_cols() ); - } - - template< typename Representation > - void compute_persistence_pairs( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - phat::compute_persistence_pairs< twist_reduction >( pairs, boundary_matrix ); - } - - - template< typename Representation > - void compute_persistence_pairs_dualized( persistence_pairs& pairs, boundary_matrix< Representation >& boundary_matrix ) { - compute_persistence_pairs_dualized< twist_reduction >( pairs, boundary_matrix ); - } - -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h deleted file mode 100644 index 3ffedf875f..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/dualize.h +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include -#include - - -namespace phat { - template< typename Representation > - void dualize( boundary_matrix< Representation >& boundary_matrix ) { - - std::vector< dimension > dual_dims; - std::vector< std::vector< index > > dual_matrix; - - index nr_of_columns = boundary_matrix.get_num_cols(); - dual_matrix.resize( nr_of_columns ); - dual_dims.resize( nr_of_columns ); - - std::vector< index > dual_sizes( nr_of_columns, 0 ); - - column temp_col; - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - boundary_matrix.get_col( cur_col, temp_col ); - for( index idx = 0; idx < (index)temp_col.size(); idx++) - dual_sizes[ nr_of_columns - 1 - temp_col[ idx ] ]++; - } - - #pragma omp parallel for - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) - dual_matrix[cur_col].reserve(dual_sizes[cur_col]); - - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) { - boundary_matrix.get_col( cur_col, temp_col ); - for( index idx = 0; idx < (index)temp_col.size(); idx++) - dual_matrix[ nr_of_columns - 1 - temp_col[ idx ] ].push_back( nr_of_columns - 1 - cur_col ); - } - - const dimension max_dim = boundary_matrix.get_max_dim(); - #pragma omp parallel for - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) - dual_dims[ nr_of_columns - 1 - cur_col ] = max_dim - boundary_matrix.get_dim( cur_col ); - - #pragma omp parallel for - for( index cur_col = 0; cur_col < nr_of_columns; cur_col++ ) - std::reverse( dual_matrix[ cur_col ].begin(), dual_matrix[ cur_col ].end() ); - - boundary_matrix.load_vector_vector( dual_matrix, dual_dims ); - } - - void dualize_persistence_pairs( persistence_pairs& pairs, const index n ) { - for (index i = 0; i < pairs.get_num_pairs(); ++i) { - std::pair< index, index > pair = pairs.get_pair( i ); - pairs.set_pair( i , n - 1 - pair.second, n - 1 - pair.first); - } - } -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h deleted file mode 100644 index fb5c07acb0..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/misc.h +++ /dev/null @@ -1,75 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -// STL includes -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// VS2008 and below unfortunately do not support stdint.h -#if defined(_MSC_VER)&& _MSC_VER < 1600 - typedef __int8 int8_t; - typedef unsigned __int8 uint8_t; - typedef __int16 int16_t; - typedef unsigned __int16 uint16_t; - typedef __int32 int32_t; - typedef unsigned __int32 uint32_t; - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; -#else - #include -#endif - -// basic types. index can be changed to int32_t to save memory on small instances -namespace phat { - typedef int64_t index; - typedef int8_t dimension; - typedef std::vector< index > column; -} - -// OpenMP (proxy) functions -#if defined _OPENMP - #include -#else - #define omp_get_thread_num() 0 - #define omp_get_max_threads() 1 - #define omp_get_num_threads() 1 - void omp_set_num_threads( int ) {}; - #include - #define omp_get_wtime() (float)clock() / (float)CLOCKS_PER_SEC -#endif - -#include - - - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h deleted file mode 100644 index d0b5332bc1..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/helpers/thread_local_storage.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -// should ideally be equal to the cache line size of the CPU -#define PHAT_TLS_SPACING_FACTOR 64 - -// ThreadLocalStorage with some spacing to avoid "false sharing" (see wikipedia) -template< typename T > -class thread_local_storage -{ -public: - - thread_local_storage() : per_thread_storage( omp_get_max_threads() * PHAT_TLS_SPACING_FACTOR ) {}; - - T& operator()() { - return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; - } - - const T& operator()() const { - return per_thread_storage[ omp_get_thread_num() * PHAT_TLS_SPACING_FACTOR ]; - } - - T& operator[]( int tid ) { - return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; - } - - const T& operator[]( int tid ) const { - return per_thread_storage[ tid * PHAT_TLS_SPACING_FACTOR ]; - } - -protected: - std::vector< T > per_thread_storage; -}; diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h deleted file mode 100644 index eafc6389e2..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/persistence_pairs.h +++ /dev/null @@ -1,155 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class persistence_pairs { - - protected: - std::vector< std::pair< index, index > > pairs; - - public: - index get_num_pairs() const { - return (index)pairs.size(); - } - - void append_pair( index birth, index death ) { - pairs.push_back( std::make_pair( birth, death ) ); - } - - std::pair< index, index > get_pair( index idx ) const { - return pairs[ idx ]; - } - - void set_pair( index idx, index birth, index death ) { - pairs[ idx ] = std::make_pair( birth, death ); - } - - void clear() { - pairs.clear(); - } - - void sort() { - std::sort( pairs.begin(), pairs.end() ); - } - - // Loads the persistence pairs from given file in asci format - // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... - bool load_ascii( std::string filename ) { - std::ifstream input_stream( filename.c_str() ); - if( input_stream.fail() ) - return false; - - int64_t nr_pairs; - input_stream >> nr_pairs; - pairs.clear(); - for( index idx = 0; idx < nr_pairs; idx++ ) { - int64_t birth; - input_stream >> birth; - int64_t death; - input_stream >> death; - append_pair( (index)birth, (index)death ); - } - - input_stream.close(); - return true; - } - - // Saves the persistence pairs to given file in binary format - // Format: nr_pairs % newline % birth1 % death1 % newline % birth2 % death2 % newline ... - bool save_ascii( std::string filename ) { - std::ofstream output_stream( filename.c_str() ); - if( output_stream.fail() ) - return false; - - this->sort(); - output_stream << get_num_pairs() << std::endl; - for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { - output_stream << pairs[idx].first << " " << pairs[idx].second << std::endl; - } - - output_stream.close(); - return true; - } - - // Loads the persistence pairs from given file in binary format - // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... - bool load_binary( std::string filename ) { - std::ifstream input_stream( filename.c_str(), std::ios_base::binary | std::ios_base::in ); - if( input_stream.fail() ) - return false; - - int64_t nr_pairs; - input_stream.read( (char*)&nr_pairs, sizeof( int64_t ) ); - for( index idx = 0; idx < nr_pairs; idx++ ) { - int64_t birth; - input_stream.read( (char*)&birth, sizeof( int64_t ) ); - int64_t death; - input_stream.read( (char*)&death, sizeof( int64_t ) ); - append_pair( (index)birth, (index)death ); - } - - input_stream.close(); - return true; - } - - // Saves the persistence pairs to given file in binary format - // Format: nr_pairs % birth1 % death1 % birth2 % death2 ... - bool save_binary( std::string filename ) { - std::ofstream output_stream( filename.c_str(), std::ios_base::binary | std::ios_base::out ); - if( output_stream.fail() ) - return false; - - this->sort(); - int64_t nr_pairs = get_num_pairs(); - output_stream.write( (char*)&nr_pairs, sizeof( int64_t ) ); - for( std::size_t idx = 0; idx < pairs.size(); idx++ ) { - int64_t birth = pairs[ idx ].first; - output_stream.write( (char*)&birth, sizeof( int64_t ) ); - int64_t death = pairs[ idx ].second; - output_stream.write( (char*)&death, sizeof( int64_t ) ); - } - - output_stream.close(); - return true; - } - - bool operator==( persistence_pairs& other_pairs ) { - this->sort(); - other_pairs.sort(); - if( pairs.size() != (std::size_t)other_pairs.get_num_pairs() ) - return false; - - for( index idx = 0; idx < (index)pairs.size(); idx++ ) - if( get_pair( idx ) != other_pairs.get_pair( idx ) ) - return false; - - return true; - } - - bool operator!=( persistence_pairs& other_pairs ) { - return !( *this == other_pairs ); - } - }; - - - -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h deleted file mode 100644 index e16d7a5d13..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/abstract_pivot_column.h +++ /dev/null @@ -1,102 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - - // Note: We could even make the rep generic in the underlying Const representation - // But I cannot imagine that anything else than vector> would - // make sense - template< typename PivotColumn > - class abstract_pivot_column : public vector_vector { - - protected: - typedef vector_vector Base; - typedef PivotColumn pivot_col; - - // For parallization purposes, it could be more than one full column - mutable thread_local_storage< pivot_col > pivot_cols; - mutable thread_local_storage< index > idx_of_pivot_cols; - - pivot_col& get_pivot_col() const { - return pivot_cols(); - } - - bool is_pivot_col( index idx ) const { - return idx_of_pivot_cols() == idx; - } - - void release_pivot_col() { - index idx = idx_of_pivot_cols(); - if( idx != -1 ) { - this->matrix[ idx ].clear(); - pivot_cols().get_col_and_clear( this->matrix[ idx ] ); - } - idx_of_pivot_cols() = -1; - } - - void make_pivot_col( index idx ) { - release_pivot_col(); - idx_of_pivot_cols() = idx; - get_pivot_col().add_col( matrix[ idx ] ); - } - - public: - - void _set_num_cols( index nr_of_cols ) { - #pragma omp parallel for - for( int tid = 0; tid < omp_get_num_threads(); tid++ ) { - pivot_cols[ tid ].init( nr_of_cols ); - idx_of_pivot_cols[ tid ] = -1; - } - Base::_set_num_cols( nr_of_cols ); - } - - void _add_to( index source, index target ) { - if( !is_pivot_col( target ) ) - make_pivot_col( target ); - get_pivot_col().add_col( matrix[source] ); - } - - void _sync() { - #pragma omp parallel for - for( int tid = 0; tid < omp_get_num_threads(); tid++ ) - release_pivot_col(); - } - - void _get_col( index idx, column& col ) const { is_pivot_col( idx ) ? get_pivot_col().get_col( col ) : Base::_get_col( idx, col ); } - - bool _is_empty( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().is_empty() : Base::_is_empty( idx ); } - - index _get_max_index( index idx ) const { return is_pivot_col( idx ) ? get_pivot_col().get_max_index() : Base::_get_max_index( idx ); } - - void _clear( index idx ) { is_pivot_col( idx ) ? get_pivot_col().clear() : Base::_clear( idx ); } - - void _set_col( index idx, const column& col ) { is_pivot_col( idx ) ? get_pivot_col().set_col( col ) : Base::_set_col( idx, col ); } - - void _remove_max( index idx ) { is_pivot_col( idx ) ? get_pivot_col().remove_max() : Base::_remove_max( idx ); } - - void finalize( index idx ) { Base::_finalize( idx ); } - }; -} - - diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h deleted file mode 100644 index 4d48e8853d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/bit_tree_pivot_column.h +++ /dev/null @@ -1,165 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Hubert Wagner - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - - // This is a bitset indexed with a 64-ary tree. Each node in the index - // has 64 bits; i-th bit says that the i-th subtree is non-empty. - // Supports practically O(1), inplace, zero-allocation: insert, remove, max_element - // and clear in O(number of ones in the bitset). - // 'add_index' is still the real bottleneck in practice. - class bit_tree_column - { - protected: - - size_t offset; // data[i + offset] = ith block of the data-bitset - typedef uint64_t block_type; - std::vector< block_type > data; - - - size_t debrujin_magic_table[ 64 ]; - - enum { block_size_in_bits = 64 }; - enum { block_shift = 6 }; - - // Some magic: http://graphics.stanford.edu/~seander/bithacks.html - // Gets the position of the rightmost bit of 'x'. 0 means the most significant bit. - // (-x)&x isolates the rightmost bit. - // The whole method is much faster than calling log2i, and very comparable to using ScanBitForward/Reverse intrinsic, - // which should be one CPU instruction, but is not portable. - size_t rightmost_pos( const block_type value ) const { - return 64 - 1 - debrujin_magic_table[ ( (value & (-(int64_t)value) ) * 0x07EDD5E59A4E28C2 ) >> 58 ]; - } - - public: - - void init( index num_cols ) { - int64_t n = 1; // in case of overflow - int64_t bottom_blocks_needed = ( num_cols + block_size_in_bits - 1 ) / block_size_in_bits; - int64_t upper_blocks = 1; - - // How many blocks/nodes of index needed to index the whole bitset? - while( n * block_size_in_bits < bottom_blocks_needed ) { - n *= block_size_in_bits; - upper_blocks += n; - } - - offset = upper_blocks; - data.resize( upper_blocks + bottom_blocks_needed, 0 ); - - std::size_t temp_array[ 64 ] = { - 63, 0, 58, 1, 59, 47, 53, 2, - 60, 39, 48, 27, 54, 33, 42, 3, - 61, 51, 37, 40, 49, 18, 28, 20, - 55, 30, 34, 11, 43, 14, 22, 4, - 62, 57, 46, 52, 38, 26, 32, 41, - 50, 36, 17, 19, 29, 10, 13, 21, - 56, 45, 25, 31, 35, 16, 9, 12, - 44, 24, 15, 8, 23, 7, 6, 5 }; - - std::copy( &temp_array[ 0 ], &temp_array[ 64 ], &debrujin_magic_table[ 0 ] ); - } - - index get_max_index() const { - if( !data[ 0 ] ) - return -1; - - size_t n = 0; - size_t newn = 0; - size_t index = 0; - while( newn < data.size() ) { - n = newn; - index = rightmost_pos( data[ n ] ); - newn = ( n << block_shift ) + index + 1; - } - - return ( ( n - offset ) << block_shift ) + index; - } - - bool is_empty() const { - return data[ 0 ] == 0; - } - - void add_index( const size_t entry ) { - const block_type ONE = 1; - const block_type block_modulo_mask = ( ONE << block_shift ) - 1; - size_t index_in_level = entry >> block_shift; - size_t address = index_in_level + offset; - size_t index_in_block = entry & block_modulo_mask; - - block_type mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); - - data[ address ] ^= mask; - - // Check if we reached the root. Also, if anyone else was in this block, we don't need to update the path up. - while( address && !( data[ address ] & ~mask ) ) { - index_in_block = index_in_level & block_modulo_mask; - index_in_level >>= block_shift; - --address; - address >>= block_shift; - mask = ( ONE << ( block_size_in_bits - index_in_block - 1 ) ); - data[ address ] ^= mask; - } - } - - void get_col_and_clear( column &out ) { - index mx = this->get_max_index(); - while( mx != -1 ) { - out.push_back( mx ); - add_index( mx ); - mx = this->get_max_index(); - } - - std::reverse( out.begin(), out.end() ); - } - - void add_col(const column &col) { - for( size_t i = 0; i < col.size(); ++i ) - add_index(col[i]); - } - - void clear() { - index mx = this->get_max_index(); - while( mx != -1 ) { - add_index( mx ); - mx = this->get_max_index(); - } - } - - void remove_max() { - add_index( get_max_index() ); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column bit_tree_pivot_column; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h deleted file mode 100644 index c2e9e3c574..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/full_pivot_column.h +++ /dev/null @@ -1,100 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class full_column { - - protected: - std::priority_queue< index > history; - std::vector< char > is_in_history; - std::vector< char > col_bit_field; - - public: - void init( const index total_size ) { - col_bit_field.resize( total_size, false ); - is_in_history.resize( total_size, false ); - } - - void add_col( const column& col ) { - for( index idx = 0; idx < (index) col.size(); idx++ ) { - add_index( col[ idx ] ); - } - } - - void add_index( const index idx ) { - if( !is_in_history[ idx ] ) { - history.push( idx ); - is_in_history[ idx ] = true; - } - - col_bit_field[ idx ] = !col_bit_field[ idx ]; - } - - index get_max_index() { - while( history.size() > 0 ) { - index topIndex = history.top(); - if( col_bit_field[ topIndex ] ) { - return topIndex; - } else { - history.pop(); - is_in_history[ topIndex ] = false; - } - } - - return -1; - } - - void get_col_and_clear( column& col ) { - while( !is_empty() ) { - col.push_back( get_max_index() ); - add_index( get_max_index() ); - } - std::reverse( col.begin(), col.end() ); - } - - bool is_empty() { - return (get_max_index() == -1); - } - - void clear() { - while( !is_empty() ) - add_index( get_max_index() ); - } - - void remove_max() { - add_index( get_max_index() ); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column< full_column > full_pivot_column; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h deleted file mode 100644 index 33cd07b40d..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/heap_pivot_column.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class heap_column { - - protected: - std::priority_queue< index > data; - - column temp_col; - index inserts_since_last_prune; - - void prune() - { - temp_col.clear( ); - index max_index = pop_max_index( ); - while( max_index != -1 ) { - temp_col.push_back( max_index ); - max_index = pop_max_index( ); - } - - for( index idx = 0; idx < (index)temp_col.size( ); idx++ ) - data.push( temp_col[ idx ] ); - - inserts_since_last_prune = 0; - } - - index pop_max_index() - { - if( data.empty( ) ) - return -1; - else { - index max_element = data.top( ); - data.pop(); - while( !data.empty( ) && data.top( ) == max_element ) { - data.pop( ); - if( data.empty( ) ) - return -1; - else { - max_element = data.top( ); - data.pop( ); - } - } - return max_element; - } - } - - public: - void init( const index total_size ) { - inserts_since_last_prune = 0; - clear(); - } - - void add_col( const column& col ) { - for( index idx = 0; idx < (index) col.size(); idx++ ) - data.push( col[ idx ] ); - inserts_since_last_prune += col.size( ); - if( 2 * inserts_since_last_prune >( index ) data.size( ) ) - prune(); - } - - index get_max_index() { - index max_element = pop_max_index( ); - if( max_element == -1 ) - return -1; - else { - data.push( max_element ); - return max_element; - } - } - - void get_col_and_clear( column& col ) { - col.clear(); - index max_index = pop_max_index( ); - while( max_index != -1 ) { - col.push_back( max_index ); - max_index = pop_max_index( ); - } - std::reverse( col.begin(), col.end() ); - } - - bool is_empty() { - return get_max_index() == -1; - } - - void clear() { - data = std::priority_queue< index >(); - } - - void remove_max() { - pop_max_index(); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column< heap_column > heap_pivot_column; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h deleted file mode 100644 index 390fd91a99..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/sparse_pivot_column.h +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include -#include - -namespace phat { - class sparse_column { - - protected: - std::set< index > data; - - void add_index( const index idx ) { - std::pair< std::set< index >::iterator, bool > result = data.insert( idx ); - if( result.second == false ) - data.erase( result.first ); - } - - public: - void init( const index total_size ) { - data.clear(); - } - - void add_col( const column& col ) { - for( index idx = 0; idx < (index) col.size(); idx++ ) - add_index( col[ idx ] ); - } - - index get_max_index() { - return data.empty() ? -1 : *data.rbegin(); - } - - void get_col_and_clear( column& col ) { - col.assign( data.begin(), data.end() ); - data.clear(); - } - - bool is_empty() { - return data.empty(); - } - - void clear() { - data.clear(); - } - - void remove_max() { - add_index( get_max_index() ); - } - - void set_col( const column& col ) { - clear(); - add_col( col ); - } - - void get_col( column& col ) { - get_col_and_clear( col ); - add_col( col ); - } - }; - - typedef abstract_pivot_column< sparse_column > sparse_pivot_column; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h deleted file mode 100644 index db0420ff23..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_heap.h +++ /dev/null @@ -1,170 +0,0 @@ -/* Copyright 2013 IST Austria -Contributed by: Jan Reininghaus - -This file is part of PHAT. - -PHAT is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -PHAT 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 Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_heap { - - protected: - std::vector< dimension > dims; - std::vector< column > matrix; - - std::vector< index > inserts_since_last_prune; - - mutable thread_local_storage< column > temp_column_buffer; - - protected: - void _prune( index idx ) - { - column& col = matrix[ idx ]; - column& temp_col = temp_column_buffer(); - temp_col.clear(); - index max_index = _pop_max_index( col ); - while( max_index != -1 ) { - temp_col.push_back( max_index ); - max_index = _pop_max_index( col ); - } - col = temp_col; - std::reverse( col.begin( ), col.end( ) ); - std::make_heap( col.begin( ), col.end( ) ); - inserts_since_last_prune[ idx ] = 0; - } - - index _pop_max_index( index idx ) - { - return _pop_max_index( matrix[ idx ] ); - } - - index _pop_max_index( column& col ) const - { - if( col.empty( ) ) - return -1; - else { - index max_element = col.front( ); - std::pop_heap( col.begin( ), col.end( ) ); - col.pop_back( ); - while( !col.empty( ) && col.front( ) == max_element ) { - std::pop_heap( col.begin( ), col.end( ) ); - col.pop_back( ); - if( col.empty( ) ) - return -1; - else { - max_element = col.front( ); - std::pop_heap( col.begin( ), col.end( ) ); - col.pop_back( ); - } - } - return max_element; - } - } - - public: - // overall number of cells in boundary_matrix - index _get_num_cols( ) const - { - return (index)matrix.size( ); - } - void _set_num_cols( index nr_of_columns ) - { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - inserts_since_last_prune.assign( nr_of_columns, 0 ); - } - - // dimension of given index - dimension _get_dim( index idx ) const - { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) - { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const - { - temp_column_buffer( ) = matrix[ idx ]; - - index max_index = _pop_max_index( temp_column_buffer() ); - while( max_index != -1 ) { - col.push_back( max_index ); - max_index = _pop_max_index( temp_column_buffer( ) ); - } - std::reverse( col.begin( ), col.end( ) ); - } - void _set_col( index idx, const column& col ) - { - matrix[ idx ] = col; - std::make_heap( matrix[ idx ].begin( ), matrix[ idx ].end( ) ); - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const - { - return _get_max_index( idx ) == -1; - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const - { - column& col = const_cast< column& >( matrix[ idx ] ); - index max_element = _pop_max_index( col ); - col.push_back( max_element ); - std::push_heap( col.begin( ), col.end( ) ); - return max_element; - } - - // removes the maximal index of a column - void _remove_max( index idx ) - { - _pop_max_index( idx ); - } - - // clears given column - void _clear( index idx ) - { - matrix[ idx ].clear( ); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync( ) {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) - { - for( index idx = 0; idx < (index)matrix[ source ].size( ); idx++ ) { - matrix[ target ].push_back( matrix[ source ][ idx ] ); - std::push_heap( matrix[ target ].begin(), matrix[ target ].end() ); - } - inserts_since_last_prune[ target ] += matrix[ source ].size(); - - if( 2 * inserts_since_last_prune[ target ] > ( index )matrix[ target ].size() ) - _prune( target ); - } - - // finalizes given column - void _finalize( index idx ) { - _prune( idx ); - } - - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h deleted file mode 100644 index ca0b5b8e79..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_list.h +++ /dev/null @@ -1,101 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_list { - - protected: - std::vector< dimension > dims; - std::vector< std::list< index > > matrix; - - public: - // overall number of cells in boundary_matrix - index _get_num_cols() const { - return (index)matrix.size(); - } - void _set_num_cols( index nr_of_columns ) { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - } - - // dimension of given index - dimension _get_dim( index idx ) const { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const { - col.clear(); - col.reserve( matrix[idx].size() ); - std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); - } - - void _set_col( index idx, const column& col ) { - matrix[ idx ].clear(); - matrix[ idx ].resize( col.size() ); - std::copy (col.begin(), col.end(), matrix[ idx ].begin() ); - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const { - return matrix[ idx ].empty(); - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const { - return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); - } - - // removes the maximal index of a column - void _remove_max( index idx ) { - std::list< index >::iterator it = matrix[ idx ].end(); - it--; - matrix[ idx ].erase( it ); - } - - // clears given column - void _clear( index idx ) { - matrix[ idx ].clear(); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync() {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) { - std::list< index >& source_col = matrix[ source ]; - std::list< index >& target_col = matrix[ target ]; - std::list< index > temp_col; - target_col.swap( temp_col ); - std::set_symmetric_difference( temp_col.begin(), temp_col.end(), - source_col.begin(), source_col.end(), - std::back_inserter( target_col ) ); - } - - // finalizes given column - void _finalize( index idx ) { - } - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h deleted file mode 100644 index 6878a270c0..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_set.h +++ /dev/null @@ -1,99 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_set { - - protected: - std::vector< dimension > dims; - std::vector< std::set< index > > matrix; - - public: - // overall number of cells in boundary_matrix - index _get_num_cols() const { - return (index)matrix.size(); - } - void _set_num_cols( index nr_of_columns ) { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - } - - // dimension of given index - dimension _get_dim( index idx ) const { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const { - col.clear(); - col.reserve( matrix[idx].size() ); - std::copy (matrix[idx].begin(), matrix[idx].end(), std::back_inserter(col) ); - } - void _set_col( index idx, const column& col ) { - matrix[ idx ].clear(); - matrix[ idx ].insert( col.begin(), col.end() ); - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const { - return matrix[ idx ].empty(); - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const { - return matrix[ idx ].empty() ? -1 : *matrix[ idx ].rbegin(); - } - - // removes the maximal index of a column - void _remove_max( index idx ) { - std::set< index >::iterator it = matrix[ idx ].end(); - it--; - matrix[ idx ].erase( it ); - } - - // clears given column - void _clear( index idx ) { - matrix[ idx ].clear(); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync() {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) { - for( std::set< index >::iterator it = matrix[ source ].begin(); it != matrix[ source ].end(); it++ ) { - std::set< index >& col = matrix[ target ]; - std::pair< std::set< index >::iterator, bool > result = col.insert( *it ); - if( !result.second ) - col.erase( result.first ); - } - } - - // finalizes given column - void _finalize( index idx ) { - } - - }; -} diff --git a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h b/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h deleted file mode 100644 index f111d6b572..0000000000 --- a/src/Zigzag_persistence/benchmark/ext_zz/phat/representations/vector_vector.h +++ /dev/null @@ -1,107 +0,0 @@ -/* Copyright 2013 IST Austria - Contributed by: Ulrich Bauer, Michael Kerber, Jan Reininghaus - - This file is part of PHAT. - - PHAT is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - PHAT 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with PHAT. If not, see . */ - -#pragma once - -#include - -namespace phat { - class vector_vector { - - protected: - std::vector< dimension > dims; - std::vector< column > matrix; - - thread_local_storage< column > temp_column_buffer; - - public: - // overall number of cells in boundary_matrix - index _get_num_cols() const { - return (index)matrix.size(); - } - void _set_num_cols( index nr_of_columns ) { - dims.resize( nr_of_columns ); - matrix.resize( nr_of_columns ); - } - - // dimension of given index - dimension _get_dim( index idx ) const { - return dims[ idx ]; - } - void _set_dim( index idx, dimension dim ) { - dims[ idx ] = dim; - } - - // replaces(!) content of 'col' with boundary of given index - void _get_col( index idx, column& col ) const { - col = matrix[ idx ]; - } - void _set_col( index idx, const column& col ) { - matrix[ idx ] = col; - } - - // true iff boundary of given idx is empty - bool _is_empty( index idx ) const { - return matrix[ idx ].empty(); - } - - // largest row index of given column idx (new name for lowestOne()) - index _get_max_index( index idx ) const { - return matrix[ idx ].empty() ? -1 : matrix[ idx ].back(); - } - - // removes the maximal index of a column - void _remove_max( index idx ) { - matrix[ idx ].pop_back(); - } - - // clears given column - void _clear( index idx ) { - matrix[ idx ].clear(); - } - - // syncronizes all data structures (essential for openmp stuff) - void _sync() {} - - // adds column 'source' to column 'target' - void _add_to( index source, index target ) { - column& source_col = matrix[ source ]; - column& target_col = matrix[ target ]; - column& temp_col = temp_column_buffer(); - - - size_t new_size = source_col.size() + target_col.size(); - - if (new_size > temp_col.size()) temp_col.resize(new_size); - - std::vector::iterator col_end = std::set_symmetric_difference( target_col.begin(), target_col.end(), - source_col.begin(), source_col.end(), - temp_col.begin() ); - temp_col.erase(col_end, temp_col.end()); - - - target_col.swap(temp_col); - } - - // finalizes given column - void _finalize( index idx ) { - column& col = matrix[ idx ]; - column(col.begin(), col.end()).swap(col); - } - }; -} diff --git a/src/Zigzag_persistence/example/CMakeLists.txt b/src/Zigzag_persistence/example/CMakeLists.txt index 446456e0e5..5f50fbfdbe 100644 --- a/src/Zigzag_persistence/example/CMakeLists.txt +++ b/src/Zigzag_persistence/example/CMakeLists.txt @@ -19,10 +19,6 @@ if(TARGET TBB::tbb) endif() add_test(NAME Zigzag_persistence_example_oscillating_rips_persistence COMMAND $ "2" "3" "5" "10") -add_executable ( comp comparison_for_tests.cpp ) -if(TARGET TBB::tbb) - target_link_libraries(comp TBB::tbb) -endif() diff --git a/src/Zigzag_persistence/example/comparison_for_tests.cpp b/src/Zigzag_persistence/example/comparison_for_tests.cpp deleted file mode 100644 index 2b55bdd648..0000000000 --- a/src/Zigzag_persistence/example/comparison_for_tests.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - * Author(s): Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#include -#include -#include -#include -#include -#include // for pair -#include -#include - -#include - -#include -#include -#include -#include -#include - -using ST = Gudhi::Simplex_tree; -using Filtration_value = ST::Filtration_value; -using Simplex_handle = ST::Simplex_handle; -using Square = Gudhi::zigzag_persistence::Square_root_edge_modifier; -using ZE = Gudhi::zigzag_persistence::Zigzag_edge; -using ORE = Gudhi::zigzag_persistence::Oscillating_rips_edge_range; -using OR = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; -using ORv = Gudhi::zigzag_persistence::Oscillating_rips_simplex_range::iterator>; -using Point = std::vector; - -void print_points(const std::vector& points) { - std::cout << "Number of points: " << points.size() << "\n"; - for (const Point& p : points) { - std::cout << "(" << p[0] << ", " << p[1] << ")\n"; - } - std::cout << "\n"; -} - -void print_res(const std::tuple& t, ST& st){ - for (auto v : st.simplex_vertex_range(std::get<0>(t))) std::cout << v << " "; - std::cout << " -- " << std::get<1>(t) << ", " << std::get<2>(t) << "\n"; -} - -std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { - std::vector finalPoints; - std::set points; - std::random_device dev; - std::mt19937 rng(dev()); - if (seed > -1) rng.seed(seed); - std::uniform_real_distribution dist(0, 10); - - for (unsigned int i = 0; i < numberOfPoints; ++i) { - auto res = points.insert({dist(rng), dist(rng)}); - while (!res.second) { - res = points.insert({dist(rng), dist(rng)}); - } - finalPoints.push_back(*res.first); - } - - // print_points(finalPoints); - - return finalPoints; -} - -void canonical_sort_edge(std::vector& edges) { - // canonical sort of the edges: as much as possible, edges should be removed in - // the reverse order of their insertion. We decide to insert shorted edges first, - // with increasing lexicographical order, and remove larger edges first, with - // decreasing lexicographic order. - - // filtration then dimension, then lex order for insertion - auto edge_cmp = [](const ZE& e1, const ZE& e2) { - if (e1.get_filtration_value() != e2.get_filtration_value()) { - return e1.get_filtration_value() < e2.get_filtration_value(); - } // lower fil first - - if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first - if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); - } //-> vertex of lower label - else { - return true; - } //-> always vertices before edges - } else { // e1 is an edge - if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { - return false; - } // e2 vertex, -> put it first - else { // both are edges, lexigraphic compare - if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) { - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); - } // lex order - if (e1.get_biggest_vertex() != e2.get_biggest_vertex()) { - return e1.get_biggest_vertex() < e2.get_biggest_vertex(); - } - return false; // equality - } - } - }; - // the inverse ordering for deletions - auto inv_edge_cmp = [&](const ZE& e1, const ZE& e2) { - if (e1.get_smallest_vertex() == e2.get_smallest_vertex() && e1.get_biggest_vertex() == e2.get_biggest_vertex()) { - return false; - } //== => false - return !(edge_cmp(e1, e2)); // reverse order - }; - // sort sequences of inclusions of same filtration with edge_cmp - // sort sequences of removals of same filtration with inv_edge_cmp - auto beg = edges.begin(); - auto end = edges.begin(); - auto curr_fil = beg->get_filtration_value(); - auto curr_type = beg->get_direction(); - while (beg != edges.end()) { - while (end != edges.end() && end->get_filtration_value() == curr_fil && end->get_direction() == curr_type) { - ++end; - } - if (curr_type) { - sort(beg, end, edge_cmp); - } // sequence of insertions - else { - sort(beg, end, inv_edge_cmp); - } // sequence of removals - beg = end; - curr_fil = beg->get_filtration_value(); - curr_type = beg->get_direction(); - } -} - -void test_edges_comp(const std::vector& points, double nu, double mu, ORE::Order_policy p) -{ - std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - - unsigned int i = 0; - for (const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { - if (i < edges_v1.size()) { - if (!(edges_v1[i] == e)) { - std::cout << "[" << i << "] different:\n"; - std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " - << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; - std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() - << ", " << e.get_direction() << "\n"; - }/* else { - std::cout << "[" << i << "] same:\n"; - std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " - << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; - std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() - << ", " << e.get_direction() << "\n"; - } */ - } else { - std::cout << "[" << i << "] too long:\n"; - std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " - << e.get_direction() << "\n"; - } - ++i; - } -} - -void test_edges_canonical_sort(const std::vector& points, double nu, double mu, ORE::Order_policy p) { - std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - std::vector ordered_edges_v1(edges_v1); - canonical_sort_edge(ordered_edges_v1); - - for (unsigned int i = 0; i < edges_v1.size(); ++i) { - if (!(edges_v1[i] == ordered_edges_v1[i])) { - std::cout << "[" << i << "] different:\n"; - std::cout << edges_v1[i].get_smallest_vertex() << ", " << edges_v1[i].get_biggest_vertex() << ", " - << edges_v1[i].get_filtration_value() << ", " << edges_v1[i].get_direction() << "\n"; - std::cout << ordered_edges_v1[i].get_smallest_vertex() << ", " << ordered_edges_v1[i].get_biggest_vertex() << ", " - << ordered_edges_v1[i].get_filtration_value() << ", " << ordered_edges_v1[i].get_direction() << "\n"; - } - } -} - -void test_edges_asymetry(const std::vector& points, double nu, double mu, ORE::Order_policy p) { - auto comp = [](const ZE& e1, const ZE& e2) { - if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); - return e1.get_biggest_vertex() < e2.get_biggest_vertex(); - }; - std::set > curedges(comp); - for (const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { - if (e.get_direction()) - curedges.insert(e); - else - curedges.erase(e); - } - std::cout << "final state: " << curedges.size() << "\n"; - for (const auto& e : curedges) { - std::cout << e.get_smallest_vertex() << ", " << e.get_biggest_vertex() << ", " << e.get_filtration_value() << ", " - << e.get_direction() << "\n"; - } -} - -void test_edges_timings(const std::vector& points, double nu, double mu, ORE::Order_policy p) { -// { -// Gudhi::Clock time1("Vector version"); -// std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); -// std::cout << edges_v1.size() << "\n"; -// time1.end(); -// std::cout << time1; -// } - - { - Gudhi::Clock time2("Iterator version"); - unsigned int i = 0; - for ([[maybe_unused]] const auto& e : ORE::get_iterator_range(nu, mu, points, Gudhi::Euclidean_distance(), p)) { - ++i; - } - std::cout << i << "\n"; - time2.end(); - std::cout << time2; - } - - { - Gudhi::Clock time1("Vector version"); - std::vector edges_v1 = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - std::cout << edges_v1.size() << "\n"; - time1.end(); - std::cout << time1; - } -} - -void test_edges(const std::vector& points, double nu, double mu) { - ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; - // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; -// ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; - - // test_edges_comp(points, nu, mu, p); - // test_edges_canonical_sort(points, nu, mu, p); - // test_edges_asymetry(points, nu, mu, p); - test_edges_timings(points, nu, mu, p); -} - -void test_simplices_print(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { - ST st; - - auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto end = ORE::end(); - for (auto& t : OR::get_iterator_range(start, end, st, maxDim)) { - print_res(t, st); - } -} - -void test_simplices_comp(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { - ST stIt; - ST stVec; - - auto startEIt = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto endEIt = ORE::end(); - auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto startEVec = vec.begin(); - auto endEVec = vec.end(); - - auto startIt = OR::begin(startEIt, endEIt, stIt, maxDim); - auto endIt = OR::end(); - auto rangeVec = ORv::get_iterator_range(startEVec, endEVec, stVec, maxDim); - auto startVec = rangeVec.begin(); - auto endVec = rangeVec.end(); - unsigned int i = 0; - for (; startIt != endIt && startVec != endVec; ++startIt, ++startVec) { - const auto& tIt = *startIt; - const auto& tVec = *startVec; - if (std::get<1>(tIt) != std::get<1>(tVec) || std::get<2>(tIt) != std::get<2>(tVec)) { - std::cout << "[" << i << "] Different:\n"; - print_res(tIt, stIt); - print_res(tVec, stVec); - return; - } - auto verIt = stIt.simplex_vertex_range(std::get<0>(tIt)); - auto verVec = stVec.simplex_vertex_range(std::get<0>(tVec)); - auto itIt = verIt.begin(); - auto itVec = verVec.begin(); - while (itIt != verIt.end() && itVec != verVec.end()) { - if (*itIt != *itVec) { - std::cout << "[" << i << "] Different:\n"; - print_res(tIt, stIt); - print_res(tVec, stVec); - return; - } - ++itIt; - ++itVec; - } - if (itIt != verIt.end() || itVec != verVec.end()) { - std::cout << "[" << i << "] Different:\n"; - print_res(tIt, stIt); - print_res(tVec, stVec); - return; - } - ++i; - } -} - -void test_simplices_timings(const std::vector& points, double nu, double mu, int maxDim, ORE::Order_policy p) { -// bool dir = false; - { - Gudhi::Clock time1("Vector version"); - ST st; - unsigned int i = 0; - auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - // std::cout << vec.size() << "\n"; - auto start = vec.begin(); - auto end = vec.end(); - for ([[maybe_unused]] const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { - // if (dir != std::get<2>(t)){ - // dir = !dir; - // std::cout << st.num_simplices() << "\n"; - // } - ++i; - } - std::cout << "Number of iterations: " << i << "\n"; - time1.end(); - std::cout << time1; - } - - { - Gudhi::Clock time2("Iterator version"); - ST st; - unsigned int i = 0; - auto start = ORE::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto end = ORE::end(); - for ([[maybe_unused]] const auto& t : OR::get_iterator_range(start, end, st, maxDim)) { - // if (dir != std::get<2>(t)){ - // dir = !dir; - // std::cout << st.num_simplices() << "\n"; - // } - ++i; - } - std::cout << "Number of iterations: " << i << "\n"; - time2.end(); - std::cout << time2; - } - -// { -// Gudhi::Clock time1("Vector version"); -// ST st; -// unsigned int i = 0; -// auto vec = ORE::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); -// auto start = vec.begin(); -// auto end = vec.end(); -// for ([[maybe_unused]] const auto& t : ORv::get_iterator_range(start, end, st, maxDim)) { -// ++i; -// } -// std::cout << i << "\n"; -// time1.end(); -// std::cout << time1; -// } -} - -void test_simplices(const std::vector& points, double nu, double mu, int maxDim) { - ORE::Order_policy p = ORE::Order_policy::FARTHEST_POINT_ORDERING; - // ORE::Order_policy p = ORE::Order_policy::ALREADY_ORDERED; -// ORE::Order_policy p = ORE::Order_policy::RANDOM_POINT_ORDERING; - -// test_simplices_print(points, nu, mu, maxDim, p); -// test_simplices_comp(points, nu, mu, maxDim, p); - test_simplices_timings(points, nu, mu, maxDim, p); -} - -int main(int argc, char* const argv[]) { - if (argc != 5 && argc != 6) { - std::cout << "Usage: ./comp nu mu max_dim nomberOfPoints [seed]\n"; - return 0; - } - - double nu = std::stod(argv[1]); - double mu = std::stod(argv[2]); - int maxDim = std::stoi(argv[3]); - unsigned int numberOfPoints = std::stoi(argv[4]); - int seed = -1; - - if (argc == 6) seed = std::stoi(argv[5]); - - std::cout << "nu, mu: " << nu << ", " << mu << "\n"; - std::cout << "max dimension: " << maxDim << "\n"; - std::cout << "number of points: " << numberOfPoints << "\n"; - std::cout << "seed: " << seed << "\n"; - - std::vector points = build_point_cloud(numberOfPoints, seed); - -// test_edges(points, nu, mu); -// test_simplices(points, nu, mu, maxDim); - - Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); - - return 0; -} From d8a82735dbe698264c14505181370951a2759a3d Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 24 Sep 2024 15:56:22 +0200 Subject: [PATCH 08/21] name fix --- .../include/gudhi/Oscillating_rips_persistence.h | 4 ++-- .../gudhi/Zigzag_persistence/oscillating_rips_iterators.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index 37da3d69c1..01dadce6c4 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -134,7 +134,7 @@ void compute_oscillating_rips_persistence( using VectorEdgeRange = std::vector >; using EdgeRangeIterator = typename std::conditional::type; + typename VectorEdgeRange::const_iterator>::type; using OscillatingRipsSimplexRange = Oscillating_rips_simplex_range; StableFilteredComplex st; @@ -213,7 +213,7 @@ compute_oscillating_rips_persistence( using VectorEdgeRange = std::vector >; using EdgeRangeIterator = typename std::conditional::type; + typename VectorEdgeRange::const_iterator>::type; using OscillatingRipsSimplexRange = Oscillating_rips_simplex_range; StableFilteredComplex st; diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 2192030d21..60e0852503 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -894,7 +894,7 @@ class Oscillating_rips_edge_range for (size_t k = i + 1; k < n; ++k) { // set eps_range[k] <- d(p_k, P_i) == // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. - double dist_i_k = distance(points[i], points[k]); + double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); if (dist_i_k < eps_range[k]) { eps_range[k] = dist_i_k; } From da86ef5fa75cb998b91e881c5a2455cd2a06c4f9 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Wed, 25 Sep 2024 11:03:04 +0200 Subject: [PATCH 09/21] add std:: to size_t --- .../example_zzfiltration_from_file.cpp | 4 +- .../oscillating_rips_iterators.h | 56 +++++++++---------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index 333194be6f..4e04f2b00b 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -27,7 +27,7 @@ lineType read_operation(std::string& line, std::vector& faces, double faces.clear(); ID_handle num; - size_t current = line.find_first_not_of(' ', 0); + std::size_t current = line.find_first_not_of(' ', 0); if (current == std::string::npos) return COMMENT; if (line[current] == 'i') @@ -46,7 +46,7 @@ lineType read_operation(std::string& line, std::vector& faces, double std::clog << "(2) Syntaxe error in file." << std::endl; exit(0); } - size_t next = line.find_first_of(' ', current); + std::size_t next = line.find_first_of(' ', current); timestamp = std::stod(line.substr(current, next - current)); current = line.find_first_not_of(' ', next); diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 60e0852503..d37bab5f36 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -346,7 +346,7 @@ class Oscillating_rips_edge_range Filtration_value nu_; /**< Lower multiplier. */ Filtration_value mu_; /**< Upper multiplier. */ Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ - size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ + std::size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ''edge'' is a vertex. */ /** @@ -521,7 +521,7 @@ class Oscillating_rips_edge_range * @param i Epsilon value index. * @param direction Direction. */ - void _update_edge(size_t i, bool direction) + void _update_edge(std::size_t i, bool direction) { if constexpr (EdgeModifier::isActive_) currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, @@ -589,7 +589,7 @@ class Oscillating_rips_edge_range // only at the very last step of the oscillating Rips filtration. std::vector > > edgesAdded, edgesRemoved; - size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); + std::size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); // Now, sort edges according to lengths, and put everything in edgeFiltration edgeFiltration.clear(); @@ -602,7 +602,7 @@ class Oscillating_rips_edge_range true); // epsilonValues[0], true); - for (size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i + for (std::size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i if constexpr (EdgeModifier::isActive_) { edgeFiltration.emplace_back(i + 1, i + 1, @@ -815,7 +815,7 @@ class Oscillating_rips_edge_range { GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); - size_t n = points.size(); // number of points + std::size_t n = points.size(); // number of points PointRange sortedPoints; sortedPoints.reserve(n); @@ -873,16 +873,16 @@ class Oscillating_rips_edge_range static std::vector _compute_epsilon_values(const PointRange& sortedPoints, DistanceFunction&& distance) { - size_t n = sortedPoints.size(); + std::size_t n = sortedPoints.size(); std::vector eps_range(n, std::numeric_limits::infinity()); // compute all \f$\varepsilon_i\f$ values, such that eps_range[i] == // eps_i==d_H(P_i,P), for i=0 ... n-1: - for (size_t i = 0; i < n; ++i) { + for (std::size_t i = 0; i < n; ++i) { // entering step i, maintain eps_range[j] = eps_j for j= i. #ifdef GUDHI_USE_TBB - tbb::parallel_for(size_t(i + 1), n, [&](size_t k) { + tbb::parallel_for(std::size_t(i + 1), n, [&](std::size_t k) { // set eps_range[k] <- d(p_k, P_i) == // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); @@ -891,7 +891,7 @@ class Oscillating_rips_edge_range } }); #else - for (size_t k = i + 1; k < n; ++k) { + for (std::size_t k = i + 1; k < n; ++k) { // set eps_range[k] <- d(p_k, P_i) == // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); @@ -904,7 +904,7 @@ class Oscillating_rips_edge_range // to do: implement parallel version by dividing the vector // set eps_range[i] <- eps_i = d_H(P_i,P) = max_{k>i} d(p_k, P_i) double eps_i = 0.; - for (size_t k = i + 1; k < n; ++k) { + for (std::size_t k = i + 1; k < n; ++k) { if (eps_range[k] > eps_i) { eps_i = eps_range[k]; } @@ -936,10 +936,10 @@ class Oscillating_rips_edge_range { std::vector > > distanceMatrix(sortedPoints.size()); #ifdef GUDHI_USE_TBB - tbb::parallel_for(size_t(0), sortedPoints.size(), [&](size_t i) { + tbb::parallel_for(std::size_t(0), sortedPoints.size(), [&](std::size_t i) { // distanceMatrix[i] = std::vector< std::pair >(); distanceMatrix[i].resize(i); - for (size_t j = 0; j < i; ++j) { + for (std::size_t j = 0; j < i; ++j) { distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); } // distanceMatrix[i] is sorted by (j, d(p_i,p_j)) < (k, d(p_i,p_k)) iff @@ -947,10 +947,10 @@ class Oscillating_rips_edge_range std::stable_sort(distanceMatrix[i].begin(), distanceMatrix[i].end(), Point_distance_comp()); }); #else - for (size_t i = 0; i < sortedPoints.size(); ++i) { // for all vertices + for (std::size_t i = 0; i < sortedPoints.size(); ++i) { // for all vertices // distanceMatrix[i] = std::vector< std::pair >(); distanceMatrix[i].resize(i); - for (size_t j = 0; j < i; ++j) { + for (std::size_t j = 0; j < i; ++j) { distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); } std::stable_sort(distanceMatrix[i].begin(), distanceMatrix[i].end(), Point_distance_comp()); @@ -973,14 +973,14 @@ class Oscillating_rips_edge_range * * @return Total number of edges. */ - static size_t _compute_edges(Filtration_value nu, - Filtration_value mu, - const std::vector& epsilonValues, - std::vector > >& distanceMatrix, - std::vector > >& edgesAdded, - std::vector > >& edgesRemoved) + static std::size_t _compute_edges(Filtration_value nu, + Filtration_value mu, + const std::vector& epsilonValues, + std::vector > >& distanceMatrix, + std::vector > >& edgesAdded, + std::vector > >& edgesRemoved) { - size_t number_of_arrows = 0; + std::size_t number_of_arrows = 0; auto n = epsilonValues.size(); edgesAdded.resize(n); edgesRemoved.resize(n); @@ -994,7 +994,7 @@ class Oscillating_rips_edge_range // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) #ifdef GUDHI_USE_TBB // no need to consider the case i=n-1 in an oscillating Rips filtration - tbb::parallel_for(size_t(0), n - 1, [&](size_t i) { + tbb::parallel_for(std::size_t(0), n - 1, [&](std::size_t i) { typename std::vector >::iterator it; //----edgesAdded[i]: // consider first all edges added in inclusion: @@ -1002,7 +1002,7 @@ class Oscillating_rips_edge_range // i.e., all (p_j,p_k) with 0 <= k < j <= i with // nu eps_i < d(p_j,p_k) <= mu eps_i // these new edges get filtration value epsilonValues[i] - for (size_t j = 1; j <= i; ++j) { + for (std::size_t j = 1; j <= i; ++j) { // get very first edge (k,j), over all k >::iterator it; - for (size_t i = 0; i < n - 1; ++i) { + for (std::size_t i = 0; i < n - 1; ++i) { //----edgesAdded[i]: // consider first all edges added in inclusion: // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), // i.e., all (p_j,p_k) with 0 <= k < j <= i with // nu eps_i < d(p_j,p_k) <= mu eps_i // these new edges get filtration value epsilonValues[i] - for (size_t j = 1; j <= i; ++j) { + for (std::size_t j = 1; j <= i; ++j) { // get very first edge (k,j), over all k currentSimplices_; /**< Stores current simplex handles. */ StableFilteredComplex* complex_; /**< Pointer to the complex. */ - size_t currentSimplexIndex_; /**< Index to current position in currentSimplices_. */ + std::size_t currentSimplexIndex_; /**< Index to current position in currentSimplices_. */ EdgeRangeIterator currentEdgeIt_; /**< Iterator pointing to the next edge. */ EdgeRangeIterator endEdgeIt_; /**< End edge iterator. */ bool currentDirection_; /**< Current direction. */ From 0655678ef9fe9626757e6dfdd39e1443499446bc Mon Sep 17 00:00:00 2001 From: hschreiber Date: Wed, 8 Jan 2025 14:19:17 +0100 Subject: [PATCH 10/21] merge upstream part1 --- .../concept/ZigzagOptions.h | 2 +- ...mple_usage_filtered_zigzag_persistence.cpp | 16 +- ...ltered_zigzag_persistence_with_storage.cpp | 16 +- .../example_usage_zigzag_persistence.cpp | 16 +- ...xample_zigzag_filtration_as_input_loop.cpp | 10 +- .../example_zzfiltration_from_file.cpp | 8 +- .../gudhi/Oscillating_rips_persistence.h | 14 +- .../gudhi/filtered_zigzag_persistence.h | 184 +++++++++--------- .../include/gudhi/zigzag_persistence.h | 113 +++++------ .../test/test_filtered_zigzag_persistence.cpp | 64 +++--- .../test/test_zigzag_persistence.cpp | 25 ++- 11 files changed, 223 insertions(+), 245 deletions(-) diff --git a/src/Zigzag_persistence/concept/ZigzagOptions.h b/src/Zigzag_persistence/concept/ZigzagOptions.h index 79ee25995a..98f894364f 100644 --- a/src/Zigzag_persistence/concept/ZigzagOptions.h +++ b/src/Zigzag_persistence/concept/ZigzagOptions.h @@ -34,7 +34,7 @@ struct FilteredZigzagOptions { * @brief Type for the face IDs used at insertion and in the boundaries given as argument. * Has to be usable as key in a hashtable, so "hashable" and comparable. */ - using Face_key = unspecified; + using Cell_key = unspecified; /** * @brief Type for filtration values. diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp index 0419dd79f9..77141485cd 100644 --- a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp @@ -30,21 +30,21 @@ int main() { // filtration values are monotonous (ie., either only increasing or only decreasing). // inserts vertex 2 at filtration value 0.1 -> birth at 0.1 of 0-cycle - zp.insert_face(2, {}, 0, 0.1); + zp.insert_cell(2, {}, 0, 0.1); // inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle - zp.insert_face(4, {}, 0, 0.1); + zp.insert_cell(4, {}, 0, 0.1); // inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs (0, 0.1, 0.3) - zp.insert_face(5, {2, 4}, 1, 0.3); + zp.insert_cell(5, {2, 4}, 1, 0.3); // inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle - zp.insert_face(3, {}, 0, 0.4); + zp.insert_cell(3, {}, 0, 0.4); // inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs nothing - zp.insert_face(6, {2, 3}, 1, 0.4); + zp.insert_cell(6, {2, 3}, 1, 0.4); // inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle - zp.insert_face(9, {4, 3}, 1, 1.2); + zp.insert_cell(9, {4, 3}, 1, 1.2); // removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs (1, 1.2, 1.5) - zp.remove_face(6, 1.5); + zp.remove_cell(6, 1.5); // removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle - zp.remove_face(5, 2.0); + zp.remove_cell(5, 2.0); // Only the closed bars where output so far, so the open/infinite bars still need to be retrieved. diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp index 7cc0c866e9..2ce788714c 100644 --- a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp @@ -25,21 +25,21 @@ int main() { // filtration values are monotonous (ie., either only increasing or only decreasing). // inserts vertex 2 at filtration value 0.1 -> birth at 0.1 of 0-cycle - zp.insert_face(2, {}, 0, 0.1); + zp.insert_cell(2, {}, 0, 0.1); // inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle - zp.insert_face(4, {}, 0, 0.1); + zp.insert_cell(4, {}, 0, 0.1); // inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> stores (0, 0.1, 0.3) - zp.insert_face(5, {2, 4}, 1, 0.3); + zp.insert_cell(5, {2, 4}, 1, 0.3); // inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle - zp.insert_face(3, {}, 0, 0.4); + zp.insert_cell(3, {}, 0, 0.4); // inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> stores nothing - zp.insert_face(6, {2, 3}, 1, 0.4); + zp.insert_cell(6, {2, 3}, 1, 0.4); // inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle - zp.insert_face(9, {4, 3}, 1, 1.2); + zp.insert_cell(9, {4, 3}, 1, 1.2); // removes edge 6 at filtration value 1.5 -> death at 1.5 -> stores (1, 1.2, 1.5) - zp.remove_face(6, 1.5); + zp.remove_cell(6, 1.5); // removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle - zp.remove_face(5, 2.0); + zp.remove_cell(5, 2.0); // The bars are stored within the class and where not output at all for now. diff --git a/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp index e987b90ef4..47881a0ab7 100644 --- a/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp @@ -29,21 +29,21 @@ int main() { // A face has to be identified in the boundaries by the operation number the face was inserted with in the sequence. // inserts vertex 0 -> birth at 0 of 0-cycle - zp.insert_face({}, 0); + zp.insert_cell({}, 0); // inserts vertex 1 -> birth at 1 of 0-cycle - zp.insert_face({}, 0); + zp.insert_cell({}, 0); // inserts edge 2 = (0,1) -> death at 2 -> outputs (0, 1, 2) - zp.insert_face({0, 1}, 1); + zp.insert_cell({0, 1}, 1); // inserts vertex 3 -> birth at 3 of 0-cycle - zp.insert_face({}, 0); + zp.insert_cell({}, 0); // inserts edge 4 = (0,3) -> death at 4 -> outputs (0, 3, 4) - zp.insert_face({0, 3}, 1); + zp.insert_cell({0, 3}, 1); // inserts edge 5 = (1,3) -> birth at 5 of 1-cycle - zp.insert_face({1, 3}, 1); + zp.insert_cell({1, 3}, 1); // removes edge 4 -> death at 6 -> outputs (1, 5, 6) - zp.remove_face(4); + zp.remove_cell(4); // removes edge 2 -> birth at 7 of 0-cycle - zp.remove_face(2); + zp.remove_cell(2); // Only the closed bars were output so far, so the open/infinite bars still need to be retrieved. diff --git a/src/Zigzag_persistence/example/example_zigzag_filtration_as_input_loop.cpp b/src/Zigzag_persistence/example/example_zigzag_filtration_as_input_loop.cpp index 046e599b88..e98dae969e 100644 --- a/src/Zigzag_persistence/example/example_zigzag_filtration_as_input_loop.cpp +++ b/src/Zigzag_persistence/example/example_zigzag_filtration_as_input_loop.cpp @@ -14,7 +14,7 @@ #include using ZP = Gudhi::zigzag_persistence::Filtered_zigzag_persistence_with_storage<>; -using Face_handle = ZP::Face_key; +using Cell_handle = ZP::Cell_key; using Filtration_value = ZP::Filtration_value; using Interval_filtration = ZP::Filtration_value_interval; @@ -50,7 +50,7 @@ void print_indices(ZP& zp) { } } -std::vector > get_boundaries() { +std::vector > get_boundaries() { return {{}, {}, {}, @@ -114,7 +114,7 @@ int main(int argc, char* const argv[]) { ZP zp; - std::vector > simplices = get_boundaries(); + std::vector > simplices = get_boundaries(); std::vector fils = get_filtration_values(); std::vector dirs = get_directions(); @@ -125,10 +125,10 @@ int main(int argc, char* const argv[]) { } if (dirs[i]) { int dim = simplices[i].size() == 0 ? 0 : simplices[i].size() - 1; - zp.insert_face(i, simplices[i], dim, fils[i]); + zp.insert_cell(i, simplices[i], dim, fils[i]); } else { auto id = simplices[i][0]; - zp.remove_face(id, fils[i]); + zp.remove_cell(id, fils[i]); } } diff --git a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp index 4e04f2b00b..dc14363c5b 100644 --- a/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp +++ b/src/Zigzag_persistence/example/example_zzfiltration_from_file.cpp @@ -16,7 +16,7 @@ #include using ZP = Gudhi::zigzag_persistence::Filtered_zigzag_persistence<>; -using ID_handle = ZP::Face_key; +using ID_handle = ZP::Cell_key; using Filtration_value = ZP::Filtration_value; using Dimension = ZP::Dimension; @@ -89,7 +89,7 @@ int main(int argc, char* const argv[]) { while (getline(file, line, '\n') && read_operation(line, data, timestamp) == COMMENT); double lastTimestamp = timestamp; // first operation has to be an insertion. - zp.insert_face(id, data, 0, timestamp); + zp.insert_cell(id, data, 0, timestamp); while (getline(file, line, '\n')) { type = read_operation(line, data, timestamp); @@ -100,10 +100,10 @@ int main(int argc, char* const argv[]) { if (type == INCLUSION) { ++id; int dim = data.size() == 0 ? 0 : data.size() - 1; - zp.insert_face(id, data, dim, timestamp); + zp.insert_cell(id, data, dim, timestamp); } else if (type == REMOVAL) { ++id; - zp.remove_face(data[0], timestamp); + zp.remove_cell(data[0], timestamp); } } diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index 01dadce6c4..ba3169bf05 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -68,16 +68,16 @@ struct Simplex_tree_options_oscillating_rips { template struct Default_oscillating_rips_zigzag_options : Default_filtered_zigzag_options { - using Face_key = typename StableFilteredComplex::Simplex_handle; + using Cell_key = typename StableFilteredComplex::Simplex_handle; using Filtration_value = typename StableFilteredComplex::Filtration_value; using Dimension = int; // it is `int` in the simplex tree struct Hash { - std::size_t operator()(const Face_key& sh) const { + std::size_t operator()(const Cell_key& sh) const { return sh->second.key(); } }; struct KeyEqual { - bool operator()(const Face_key& sh1, const Face_key& sh2) const { + bool operator()(const Cell_key& sh1, const Cell_key& sh2) const { return sh1->second.key() == sh2->second.key(); } }; @@ -154,12 +154,12 @@ void compute_oscillating_rips_persistence( for (const auto& t : OscillatingRipsSimplexRange::get_iterator_range(start, end, st, maxDim)) { if (std::get<2>(t)) - zp.insert_face(std::get<0>(t), + zp.insert_cell(std::get<0>(t), st.boundary_simplex_range(std::get<0>(t)), st.dimension(std::get<0>(t)), std::get<1>(t)); else - zp.remove_face(std::get<0>(t), std::get<1>(t)); + zp.remove_cell(std::get<0>(t), std::get<1>(t)); } zp.get_current_infinite_intervals([&](Dimension dim, Filtration_value birth) { outStream(dim, birth, Bar::inf); }); @@ -233,12 +233,12 @@ compute_oscillating_rips_persistence( for (const auto& t : OscillatingRipsSimplexRange::get_iterator_range(start, end, st, maxDim)) { if (std::get<2>(t)) - zp.insert_face(std::get<0>(t), + zp.insert_cell(std::get<0>(t), st.boundary_simplex_range(std::get<0>(t)), st.dimension(std::get<0>(t)), std::get<1>(t)); else - zp.remove_face(std::get<0>(t), std::get<1>(t)); + zp.remove_cell(std::get<0>(t), std::get<1>(t)); } return zp.get_persistence_diagram(); diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index bf8d6e4ba6..d07cef5b97 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -41,18 +40,11 @@ namespace zigzag_persistence { * * @brief Default options for @ref Filtered_zigzag_persistence_with_storage and @ref Filtered_zigzag_persistence. */ -struct Default_filtered_zigzag_options { - using Internal_key = int; /**< Face ID used internally, must be signed. */ - using Face_key = int; /**< Face ID used in the given boundaries. */ - using Filtration_value = double; /**< Filtration value type. */ - using Dimension = int; /**< Dimension value type. */ - using Hash = std::hash; /**< Hash method for Face_key */ - using KeyEqual = std::equal_to; /**< Equality comparator for Face_key */ - /** - * @brief Column type use by the internal matrix. - */ - static const Gudhi::persistence_matrix::Column_types column_type = - Gudhi::persistence_matrix::Column_types::NAIVE_VECTOR; +struct Default_filtered_zigzag_options : Default_zigzag_options { + using Cell_key = int; /**< Cell ID used in the given boundaries. */ + using Filtration_value = double; /**< Filtration value type. */ + using Hash = std::hash; /**< Hash method for Cell_key */ + using KeyEqual = std::equal_to; /**< Equality comparator for Cell_key */ }; /** @@ -63,7 +55,7 @@ struct Default_filtered_zigzag_options { * stored during the whole process and not removed. It is therefore suited for smaller filtrations where the clean * ups produce a higher overhead than the memory consumption. * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., - * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * call @ref insert_cell, @ref remove_cell or @ref apply_identity for each step of the filtration in order of * the filtration. To retrieve the current persistence diagram at any moment of the filtration, * use @ref get_persistence_diagram or @ref get_index_persistence_diagram. * @@ -89,25 +81,25 @@ struct Default_filtered_zigzag_options { * // In all cases, it is important that the operations of insertions and removals are made **in the same order** * // as in the zigzag filtration ones wants to compute the barcode from. * - * // A face can be identified in the boundaries by any given numerical label, it is just important that the given + * // A cell can be identified in the boundaries by any given numerical label, it is just important that the given * // filtration values are monotonous (ie., either only increasing or only decreasing). * * //inserts vertex 2 at filtration value 0.1 -> birth at 0.1 of 0-cycle - * zp.insert_face(2, {}, 0, 0.1); + * zp.insert_cell(2, {}, 0, 0.1); * //inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle - * zp.insert_face(4, {}, 0, 0.1); + * zp.insert_cell(4, {}, 0, 0.1); * //inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs/stores (0, 0.1, 0.3) - * zp.insert_face(5, {2, 4}, 1, 0.3); + * zp.insert_cell(5, {2, 4}, 1, 0.3); * //inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle - * zp.insert_face(3, {}, 0, 0.4); + * zp.insert_cell(3, {}, 0, 0.4); * //inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs/stores nothing - * zp.insert_face(6, {2, 3}, 1, 0.4); + * zp.insert_cell(6, {2, 3}, 1, 0.4); * //inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle - * zp.insert_face(9, {4, 3}, 1, 1.2); + * zp.insert_cell(9, {4, 3}, 1, 1.2); * //removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) - * zp.remove_face(6, 1.5); + * zp.remove_cell(6, 1.5); * //removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle - * zp.remove_face(5, 2.0); + * zp.remove_cell(5, 2.0); * ``` * * #### Finalizations @@ -132,7 +124,7 @@ class Filtered_zigzag_persistence_with_storage public: using Options = FilteredZigzagOptions; /**< Zigzag options. */ using Internal_key = typename Options::Internal_key; /**< Key and index type, has to be signed. */ - using Face_key = typename Options::Face_key; /**< Face ID type from external inputs. */ + using Cell_key = typename Options::Cell_key; /**< Cell ID type from external inputs. */ using Filtration_value = typename Options::Filtration_value; /**< Type for filtration values. */ using Dimension = typename Options::Dimension; /**< Type for dimension values. */ @@ -148,13 +140,13 @@ class Filtered_zigzag_persistence_with_storage /** * @brief Constructor. * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., - * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * call @ref insert_cell, @ref remove_cell or @ref apply_identity for each step of the filtration in order of * the filtration. To retrieve the current persistence diagram at any moment of the filtration, * use @ref get_persistence_diagram or @ref get_index_persistence_diagram. * - * @param preallocationSize Reserves space for @p preallocationSize faces in the internal data structure. + * @param preallocationSize Reserves space for @p preallocationSize cells in the internal data structure. * This is optional and just helps skip a few reallocations. The optimal value (no reallocation, no wasted space) is - * the number of faces in the biggest complex of the filtration. + * the number of cells in the biggest complex of the filtration. * Default value: 0. * @param ignoreCyclesAboveDim Ignores cycles in dimension larger or equal in the final diagram. * If -1, no cycles are ignored. Default value: -1. @@ -173,21 +165,21 @@ class Filtered_zigzag_persistence_with_storage preallocationSize) {} /** - * @brief Updates the zigzag persistence diagram after the insertion of the given face. + * @brief Updates the zigzag persistence diagram after the insertion of the given cell. * * @tparam BoundaryRange Range type needing size, begin and end members. - * @param faceID ID representing the inserted face. - * @param boundary Boundary of the inserted face. The range should be composed of the IDs of all faces contained in - * the boundary (i.e. with non-zero coefficients), using the ID specified as `faceID` when the corresponding face - * was previously inserted (recall that the faces should be inserted in order of filtration). - * @param dimension Dimension of the inserted face. - * @param filtrationValue Filtration value associated to the face. + * @param cellID ID representing the inserted cell. + * @param boundary Boundary of the inserted cell. The range should be composed of the IDs of all cells contained in + * the boundary (i.e. with non-zero coefficients), using the ID specified as `cellID` when the corresponding cell + * was previously inserted (recall that the cells should be inserted in order of filtration). + * @param dimension Dimension of the inserted cell. + * @param filtrationValue Filtration value associated to the cell. * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous * values, ie. the changes are monotonous. * @return Number of the operation. */ - template > - Internal_key insert_face(Face_key faceID, + template > + Internal_key insert_cell(Cell_key cellID, const BoundaryRange& boundary, Dimension dimension, Filtration_value filtrationValue) @@ -200,34 +192,36 @@ class Filtered_zigzag_persistence_with_storage _store_filtration_value(filtrationValue); - [[maybe_unused]] auto res = handleToKey_.try_emplace(faceID, numArrow_); + [[maybe_unused]] auto res = handleToKey_.try_emplace(cellID, numArrow_); - GUDHI_CHECK(res.second, "Zigzag_persistence::insert_face - face already in the complex"); + GUDHI_CHECK(res.second, "Zigzag_persistence::insert_cell - cell already in the complex"); - // Compute the keys of the faces of the boundary. - std::set translatedBoundary; // set maintains the natural order on indices + // Compute the keys of the cells of the boundary. + std::vector translatedBoundary; + translatedBoundary.reserve(dimension * 2); // boundary does not have to have `size()` for (auto b : boundary) { - translatedBoundary.insert(handleToKey_.at(b)); // TODO: add possibilities of coefficients + translatedBoundary.push_back(handleToKey_.at(b)); // TODO: add possibilities of coefficients } + std::sort(translatedBoundary.begin(), translatedBoundary.end()); - pers_.insert_face(translatedBoundary, dimension); + pers_.insert_cell(translatedBoundary, dimension); return numArrow_; } /** - * @brief Updates the zigzag persistence diagram after the removal of the given face if the face was contained - * in the current complex (note that it will not contain faces of dimension > ignoreCyclesAboveDim if the latter was + * @brief Updates the zigzag persistence diagram after the removal of the given cell if the cell was contained + * in the current complex (note that it will not contain cells of dimension > ignoreCyclesAboveDim if the latter was * non negative at construction of the class). Otherwise, just increases the operation count by one. * - * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. + * @param cellID ID representing the cell to remove. Should be the same than the one used to insert it. * @param filtrationValue Filtration value associated to the removal. * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous * values, ie. the changes are monotonous. * @return Number of the operation. */ - Internal_key remove_face(Face_key faceID, Filtration_value filtrationValue) { - auto it = handleToKey_.find(faceID); + Internal_key remove_cell(Cell_key cellID, Filtration_value filtrationValue) { + auto it = handleToKey_.find(cellID); if (it == handleToKey_.end()) { return apply_identity(); @@ -237,14 +231,14 @@ class Filtered_zigzag_persistence_with_storage _store_filtration_value(filtrationValue); - pers_.remove_face(it->second); + pers_.remove_cell(it->second); handleToKey_.erase(it); return numArrow_; } /** - * @brief To use when a face is neither inserted nor removed, but the filtration moves along the identity operator + * @brief To use when a cell is neither inserted nor removed, but the filtration moves along the identity operator * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposely skipped * to avoid useless computation. * @return Number of the operation. @@ -306,18 +300,18 @@ class Filtered_zigzag_persistence_with_storage /** * @brief Map from input keys to internal keys. */ - std::unordered_map handleToKey_; + std::unordered_map handleToKey_; Dimension dimMax_; /**< Maximal dimension of a bar to record. */ std::vector persistenceDiagram_; /**< Stores current closed persistence intervals. */ Internal_key numArrow_; /**< Current arrow number. */ Filtration_value previousFiltrationValue_; /**< Filtration value of the previous arrow. */ /** * @brief filtrationValues_ stores consecutive pairs (i,f) , (j,f') with f != f', - * meaning that all inserted faces with key in [i;j-1] have filtration value f, - * i is the smallest face index whose face has filtration value f. + * meaning that all inserted cells with key in [i;j-1] have filtration value f, + * i is the smallest cell index whose cell has filtration value f. */ std::vector > filtrationValues_; - Zigzag_persistence pers_; /**< Class computing the pairs. */ + Zigzag_persistence pers_; /**< Class computing the pairs. */ /** * @brief Stores the filtration value if the value is new. Assumes that the given value is either greater (or equal) @@ -328,7 +322,7 @@ class Filtered_zigzag_persistence_with_storage void _store_filtration_value(Filtration_value filtrationValue) { if (filtrationValue != previousFiltrationValue_) // check whether the filt value has changed { - // consecutive pairs (i,f), (j,f') mean faces of index k in [i,j-1] have filtration value f + // consecutive pairs (i,f), (j,f') mean cells of index k in [i,j-1] have filtration value f previousFiltrationValue_ = filtrationValue; filtrationValues_.emplace_back(numArrow_, previousFiltrationValue_); } @@ -384,7 +378,7 @@ class Filtered_zigzag_persistence_with_storage * * @brief Class computing the zigzag persistent homology of a zigzag filtration. Algorithm based on \cite zigzag. * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., - * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * call @ref insert_cell, @ref remove_cell or @ref apply_identity for each step of the filtration in order of * the filtration. The bars of the diagram are retrieved via the given callback method every time * a pair with non-zero length is closed. To retrieve the open/infinite bars, use @ref get_current_infinite_intervals. * @@ -415,25 +409,25 @@ class Filtered_zigzag_persistence_with_storage * // In all cases, it is important that the operations of insertions and removals are made **in the same order** * // as in the zigzag filtration ones wants to compute the barcode from. * - * // A face can be identified in the boundaries by any given numerical label, it is just important that the given + * // A cell can be identified in the boundaries by any given numerical label, it is just important that the given * // filtration values are monotonous (ie., either only increasing or only decreasing). * * //inserts vertex 2 at filtration value 0.1 -> birth at 0.1 of 0-cycle - * zp.insert_face(2, {}, 0, 0.1); + * zp.insert_cell(2, {}, 0, 0.1); * //inserts vertex 4 at filtration value 0.1 -> birth at 0.1 of 0-cycle - * zp.insert_face(4, {}, 0, 0.1); + * zp.insert_cell(4, {}, 0, 0.1); * //inserts edge 5 = (2,4) at filtration value 0.3 -> death at 0.3 -> outputs/stores (0, 0.1, 0.3) - * zp.insert_face(5, {2, 4}, 1, 0.3); + * zp.insert_cell(5, {2, 4}, 1, 0.3); * //inserts vertex 3 at filtration value 0.4 -> birth at 0.4 of 0-cycle - * zp.insert_face(3, {}, 0, 0.4); + * zp.insert_cell(3, {}, 0, 0.4); * //inserts edge 6 = (2,3) at filtration value 0.4 -> death at 0.4 of the cycle born at 0.4 -> outputs/stores nothing - * zp.insert_face(6, {2, 3}, 1, 0.4); + * zp.insert_cell(6, {2, 3}, 1, 0.4); * //inserts edge 9 = (3,4) at filtration value 1.2 -> birth at 1.2 of 1-cycle - * zp.insert_face(9, {4, 3}, 1, 1.2); + * zp.insert_cell(9, {4, 3}, 1, 1.2); * //removes edge 6 at filtration value 1.5 -> death at 1.5 -> outputs/stores (1, 1.2, 1.5) - * zp.remove_face(6, 1.5); + * zp.remove_cell(6, 1.5); * //removes edge 5 at filtration value 2.0 -> birth at 2.0 of 0-cycle - * zp.remove_face(5, 2.0); + * zp.remove_cell(5, 2.0); * ``` * * #### Finalizations @@ -454,14 +448,14 @@ class Filtered_zigzag_persistence { public: using Options = FilteredZigzagOptions; /**< Zigzag options. */ using Internal_key = typename Options::Internal_key; /**< Key and index type, has to be signed. */ - using Face_key = typename Options::Face_key; /**< Face ID type from external inputs. */ + using Cell_key = typename Options::Cell_key; /**< Cell ID type from external inputs. */ using Filtration_value = typename Options::Filtration_value; /**< Type for filtration values. */ using Dimension = typename Options::Dimension; /**< Type for dimension values. */ /** * @brief Constructor. * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., - * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * call @ref insert_cell, @ref remove_cell or @ref apply_identity for each step of the filtration in order of * the filtration. The bars of the diagram are retrieved via the given callback method every time * a pair with non-zero length is closed. To retrieve the open/infinite bars, use @ref get_current_infinite_intervals. * @@ -469,9 +463,9 @@ class Filtered_zigzag_persistence { * Has to take three arguments as input: first the dimension of the cycle, then the birth value of the cycle * and third the death value of the cycle. The values corresponds to the filtration values which were given at * insertions or removals. Note that bars of length 0 will not be token into account. - * @param preallocationSize Reserves space for @p preallocationSize faces in the internal data structure. + * @param preallocationSize Reserves space for @p preallocationSize cells in the internal data structure. * This is optional and just helps skip a few reallocations. The optimal value (no reallocation, no wasted space) is - * the number of faces in the biggest complex of the filtration. + * the number of cells in the biggest complex of the filtration. * Default value: 0. * @tparam F Type of callback method. */ @@ -493,67 +487,69 @@ class Filtered_zigzag_persistence { preallocationSize) {} /** - * @brief Updates the zigzag persistence diagram after the insertion of the given face. + * @brief Updates the zigzag persistence diagram after the insertion of the given cell. * * @tparam BoundaryRange Range type needing size, begin and end members. - * @param faceID ID representing the inserted face. - * @param boundary Boundary of the inserted face. The range should be composed of the IDs of all faces contained in - * the boundary (i.e. with non-zero coefficients), using the ID specified as `faceID` when the corresponding face - * was previously inserted (recall that the faces should be inserted in order of filtration). - * @param dimension Dimension of the inserted face. - * @param filtrationValue Filtration value associated to the face. + * @param cellID ID representing the inserted cell. + * @param boundary Boundary of the inserted cell. The range should be composed of the IDs of all cells contained in + * the boundary (i.e. with non-zero coefficients), using the ID specified as `cellID` when the corresponding cell + * was previously inserted (recall that the cells should be inserted in order of filtration). + * @param dimension Dimension of the inserted cell. + * @param filtrationValue Filtration value associated to the cell. * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous * values, ie. the changes are monotonous. */ - template > - Internal_key insert_face(Face_key faceID, + template > + Internal_key insert_cell(Cell_key cellID, const BoundaryRange& boundary, Dimension dimension, Filtration_value filtrationValue) { ++numArrow_; - [[maybe_unused]] auto res = handleToKey_.try_emplace(faceID, numArrow_); + [[maybe_unused]] auto res = handleToKey_.try_emplace(cellID, numArrow_); - GUDHI_CHECK(res.second, "Zigzag_persistence::insert_face - face already in the complex"); + GUDHI_CHECK(res.second, "Zigzag_persistence::insert_cell - cell already in the complex"); keyToFiltrationValue_.try_emplace(numArrow_, filtrationValue); - // Compute the keys of the faces of the boundary. - std::set translatedBoundary; // set maintains the natural order on indices + // Compute the keys of the cells of the boundary. + std::vector translatedBoundary; + translatedBoundary.reserve(dimension * 2); // boundary does not have to have `size()` for (auto b : boundary) { - translatedBoundary.insert(handleToKey_.at(b)); // TODO: add possibilities of coefficients + translatedBoundary.push_back(handleToKey_.at(b)); // TODO: add possibilities of coefficients } + std::sort(translatedBoundary.begin(), translatedBoundary.end()); - pers_.insert_face(translatedBoundary, dimension); + pers_.insert_cell(translatedBoundary, dimension); return numArrow_; } /** - * @brief Updates the zigzag persistence diagram after the removal of the given face. + * @brief Updates the zigzag persistence diagram after the removal of the given cell. *preallocationSize - * @param faceID ID representing the face to remove. Should be the same than the one used to insert it. + * @param cellID ID representing the cell to remove. Should be the same than the one used to insert it. * @param filtrationValue Filtration value associated to the removal. * Assumed to be always larger or equal to previously used filtration values or always smaller or equal than previous * values, ie. the changes are monotonous. */ - Internal_key remove_face(Face_key faceID, Filtration_value filtrationValue) { + Internal_key remove_cell(Cell_key cellID, Filtration_value filtrationValue) { ++numArrow_; - auto it = handleToKey_.find(faceID); - GUDHI_CHECK(it != handleToKey_.end(), "Zigzag_persistence::remove_face - face not in the complex"); + auto it = handleToKey_.find(cellID); + GUDHI_CHECK(it != handleToKey_.end(), "Zigzag_persistence::remove_cell - cell not in the complex"); keyToFiltrationValue_.try_emplace(numArrow_, filtrationValue); - pers_.remove_face(it->second); + pers_.remove_cell(it->second); handleToKey_.erase(it); return numArrow_; } /** - * @brief To use when a face is neither inserted nor removed, but the filtration moves along the identity operator + * @brief To use when a cell is neither inserted nor removed, but the filtration moves along the identity operator * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposely skipped * to avoid useless computation. */ @@ -581,10 +577,10 @@ class Filtered_zigzag_persistence { /** * @brief Map from input keys to internal keys. */ - std::unordered_map handleToKey_; - Internal_key numArrow_; /**< Current arrow number. */ - std::unordered_map keyToFiltrationValue_; /**< Face Key to filtration value map. */ - Zigzag_persistence pers_; /**< Class computing the pairs. */ + std::unordered_map handleToKey_; + Internal_key numArrow_; /**< Current arrow number. */ + std::unordered_map keyToFiltrationValue_; /**< Cell Key to filtration value map. */ + Zigzag_persistence pers_; /**< Class computing the pairs. */ }; // end class Filtered_zigzag_persistence } // namespace zigzag_persistence diff --git a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h index cdb32a6fe1..e704587dee 100644 --- a/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/zigzag_persistence.h @@ -62,24 +62,22 @@ struct Zigzag_matrix_options : Gudhi::persistence_matrix::Default_options birth at 0 of 0-cycle - * zp.insert_face({}, 0); + * zp.insert_cell({}, 0); * //inserts vertex 1 -> birth at 1 of 0-cycle - * zp.insert_face({}, 0); + * zp.insert_cell({}, 0); * //inserts edge 2 = (0,1) -> death at 2 -> outputs (0, 1, 2) - * zp.insert_face({0, 1}, 1); + * zp.insert_cell({0, 1}, 1); * //inserts vertex 3 -> birth at 3 of 0-cycle - * zp.insert_face({}, 0); + * zp.insert_cell({}, 0); * //inserts edge 4 = (0,3) -> death at 4 -> outputs (0, 3, 4) - * zp.insert_face({0, 3}, 1); + * zp.insert_cell({0, 3}, 1); * //inserts edge 5 = (1,3) -> birth at 5 of 1-cycle - * zp.insert_face({1, 3}, 1); + * zp.insert_cell({1, 3}, 1); * //removes edge 4 -> death at 6 -> outputs (1, 5, 6) - * zp.remove_face(4); + * zp.remove_cell(4); * //removes edge 2 -> birth at 7 of 0-cycle - * zp.remove_face(2); + * zp.remove_cell(2); * ``` * * #### Finalizations @@ -146,7 +144,7 @@ struct Default_zigzag_options { * * @tparam ZigzagOptions Structure following the @ref ZigzagOptions concept. Default value: @ref Default_zigzag_options. */ -template +template class Zigzag_persistence { public: @@ -248,18 +246,18 @@ class Zigzag_persistence /** * @brief Constructor of the Zigzag_persistence class. * @details After construction of the class, the zigzag filtration should be given in a streaming like way, i.e., - * call @ref insert_face, @ref remove_face or @ref apply_identity for each step of the filtration in order of + * call @ref insert_cell, @ref remove_cell or @ref apply_identity for each step of the filtration in order of * the filtration. The pairs of birth and death indices are retrieved via the given callback method every time * a pair is closed. To retrieve the open pairs (corresponding to infinite bars), * use @ref get_current_infinite_intervals. * * @param stream_interval Callback method to process the birth and death index pairs. Has to take three arguments * as input: first the dimension of the cycle, then the birth index of the cycle and third the death index of the - * cycle. An index always corresponds to the arrow number the event occurred (one call to @ref insert_face, - * @ref remove_face or @ref apply_identity is equal to one arrow and increases the arrow count by one). - * @param preallocationSize Reserves space for @p preallocationSize faces in the internal data structure. + * cycle. An index always corresponds to the arrow number the event occurred (one call to @ref insert_cell, + * @ref remove_cell or @ref apply_identity is equal to one arrow and increases the arrow count by one). + * @param preallocationSize Reserves space for @p preallocationSize cells in the internal data structure. * This is optional and just helps skip a few reallocations. The optimal value (no reallocation, no wasted space) is - * the number of faces in the biggest complex of the filtration. + * the number of cells in the biggest complex of the filtration. * Default value: 0. */ Zigzag_persistence(std::function stream_interval, @@ -276,37 +274,37 @@ class Zigzag_persistence numArrow_(-1), stream_interval_(std::move(stream_interval)) {} /** - * @brief Updates the zigzag persistence diagram after the insertion of the given face. + * @brief Updates the zigzag persistence diagram after the insertion of the given cell. * * @tparam BoundaryRange Range type needing size, begin and end members. - * @param boundary Boundary of the inserted face. The boundary should be represented by all the faces with - * non-zero coefficients generating it. A face should be represented by the arrow number when the face appeared for - * the first time in the filtration (if a face was inserted and then removed and reinserted etc., only the last - * insertion counts). The face range should be ordered by increasing arrow numbers. - * @param dimension Dimension of the inserted face. + * @param boundary Boundary of the inserted cell. The boundary should be represented by all the cells with + * non-zero coefficients generating it. A cell should be represented by the arrow number when the cell appeared for + * the first time in the filtration (if a cell was inserted and then removed and reinserted etc., only the last + * insertion counts). The cell range should be ordered by increasing arrow numbers. + * @param dimension Dimension of the inserted cell. * @return Number of the operation. */ template > - Index insert_face(const BoundaryRange& boundary, Dimension dimension) { + Index insert_cell(const BoundaryRange& boundary, Dimension dimension) { ++numArrow_; _process_forward_arrow(boundary, dimension); return numArrow_; } /** - * @brief Updates the zigzag persistence diagram after the removal of the given face. + * @brief Updates the zigzag persistence diagram after the removal of the given cell. * - * @param arrowNumber Arrow number of when the face to remove was inserted for the last time. + * @param arrowNumber Arrow number of when the cell to remove was inserted for the last time. * @return Number of the operation. */ - Index remove_face(Index arrowNumber) { + Index remove_cell(Index arrowNumber) { ++numArrow_; _process_backward_arrow(arrowNumber); return numArrow_; } /** - * @brief To use when a face is neither inserted nor removed, but the filtration moves along the identity operator + * @brief To use when a cell is neither inserted nor removed, but the filtration moves along the identity operator * on homology level. Useful to keep the birth/death indices aligned when insertions/removals are purposely skipped * to avoid useless computation. Increases the arrow number by one. * @return Number of the operation. @@ -324,30 +322,19 @@ class Zigzag_persistence template void get_current_infinite_intervals(F&& stream_infinite_interval) { for (auto& p : births_) { - if constexpr (erase_birth_history) { - auto& col = matrix_.get_column(p.first); - stream_infinite_interval(col.get_dimension(), p.second); - } else { - try { - auto& col = matrix_.get_column(p.first); - if (!col.is_paired()) { - stream_infinite_interval(col.get_dimension(), p.second); - } - } catch (const std::out_of_range&) { - continue; - } - } + auto& col = matrix_.get_column(p.first); + stream_infinite_interval(col.get_dimension(), p.second); } } private: /** - * @brief Express the boundary cycle of the new face as a sum of cycles in a matrix. + * @brief Express the boundary cycle of the new cell as a sum of cycles in a matrix. * If some cycles are not boundary cycles, i.e., columns with F-index * in the matrix, it applies a surjective diamond to the zigzag module. * - * @param boundary Boundary of the inserted face. - * @param dim Dimension of the inserted face. + * @param boundary Boundary of the inserted cell. + * @param dim Dimension of the inserted cell. */ template void _process_forward_arrow(const BoundaryRange& boundary, Dimension dim) { @@ -369,7 +356,7 @@ class Zigzag_persistence * the boundary in _process_forward_arrow(...). It is equivalent to decreasing death index * order w.r.t. the & chainsInF) { @@ -438,27 +425,25 @@ class Zigzag_persistence } // birth not available anymore, do not } // modify *chain_f_it. - if constexpr (erase_birth_history) { - birthOrdering_.remove_birth(maxb); - births_.erase(chainFp); - } + birthOrdering_.remove_birth(maxb); + births_.erase(chainFp); // Update persistence diagram with left interval [fil(b_max) ; fil(m)) stream_interval_(dim - 1, maxb, numArrow_); } /** - * @brief Removes the given face by pushing up the matrix the corresponding column and erasing it. + * @brief Removes the given cell by pushing up the matrix the corresponding column and erasing it. * - * @param faceID Internal ID of the face to remove. + * @param cellID Internal ID of the cell to remove. */ - void _process_backward_arrow(Index faceID) { - // column whose key is the one of the removed face - Matrix_index currCol = matrix_.get_column_with_pivot(faceID); + void _process_backward_arrow(Index cellID) { + // column whose key is the one of the removed cell + Matrix_index currCol = matrix_.get_column_with_pivot(cellID); // Record all columns that get affected by the transpositions, i.e., have a coeff std::vector modifiedColumns; - const auto& row = matrix_.get_row(faceID); + const auto& row = matrix_.get_row(cellID); modifiedColumns.reserve(row.size()); std::transform(row.begin(), row.end(), std::back_inserter(modifiedColumns), [](const auto& cell) { return cell.get_column_index(); }); @@ -477,23 +462,21 @@ class Zigzag_persistence if (!col.is_paired()) { // in F auto it = births_.find(currCol); stream_interval_(col.get_dimension(), it->second, numArrow_); - if constexpr (erase_birth_history) { - birthOrdering_.remove_birth(it->second); - births_.erase(it); - } + birthOrdering_.remove_birth(it->second); + births_.erase(it); } else { // in H -> paired with c_g, that now belongs to F now // maintain the <=b order birthOrdering_.add_birth_backward(numArrow_); births_[col.get_paired_chain_index()] = numArrow_; } - // cannot be in G as the removed face is maximal - matrix_.remove_maximal_face(faceID, {}); // also un-pairs c_g if in H + // cannot be in G as the removed cell is maximal + matrix_.remove_maximal_cell(cellID, {}); // also un-pairs c_g if in H } private: Matrix matrix_; /**< Matrix storing a base of the current chain complex. */ - Birth_dictionary births_; /**< Map face index in F to corresponding birth. */ + Birth_dictionary births_; /**< Map cell index in F to corresponding birth. */ Birth_ordering birthOrdering_; /**< Maintains stream_interval_; /**< Callback method for closed pairs. */ diff --git a/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp index 4e20047a51..6d03c0cc9e 100644 --- a/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/test/test_filtered_zigzag_persistence.cpp @@ -128,7 +128,7 @@ std::vector get_filtration_values() { template void test_filtered_zigzag_with_storage() { - using face_handle = typename ZP::Face_key; + using cell_handle = typename ZP::Cell_key; using Filtration_value = typename ZP::Filtration_value; using Interval_index = typename ZP::Index_interval; using Interval_filtration = typename ZP::Filtration_value_interval; @@ -139,11 +139,11 @@ void test_filtered_zigzag_with_storage() { realIndices.reserve(13); realBarcode.reserve(9); - std::vector > simplices = get_boundaries(); + std::vector > simplices = get_boundaries(); std::vector filValues = get_filtration_values(); for (unsigned int i = 0; i < 14; ++i) { - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + zp.insert_cell(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } realIndices.emplace_back(1, 3, 0); @@ -160,11 +160,11 @@ void test_filtered_zigzag_with_storage() { for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, filValues[i]); + zp.remove_cell(id, filValues[i]); } for (unsigned int i = 16; i < 24; ++i) { - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + zp.insert_cell(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } realIndices.emplace_back(5, 16, 0); @@ -179,19 +179,19 @@ void test_filtered_zigzag_with_storage() { for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, filValues[i]); + zp.remove_cell(id, filValues[i]); } realIndices.emplace_back(24, 25, 1); realBarcode.emplace_back(8, 9, 1); - zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + zp.insert_cell(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); realIndices.emplace_back(23, 27, 2); realBarcode.emplace_back(7, 9, 2); auto id = simplices[28][0]; - zp.remove_face(id, filValues[28]); + zp.remove_cell(id, filValues[28]); realBarcode.emplace_back(0, Interval_filtration::inf, 0); realBarcode.emplace_back(9, Interval_filtration::inf, 0); @@ -203,7 +203,7 @@ void test_filtered_zigzag_with_storage() { template void test_filtered_zigzag_with_storage_max1() { - using face_handle = typename ZP::Face_key; + using cell_handle = typename ZP::Cell_key; using Filtration_value = typename ZP::Filtration_value; using Interval_index = typename ZP::Index_interval; using Interval_filtration = typename ZP::Filtration_value_interval; @@ -214,11 +214,11 @@ void test_filtered_zigzag_with_storage_max1() { realIndices.reserve(5); realBarcode.reserve(3); - std::vector > simplices = get_boundaries(); + std::vector > simplices = get_boundaries(); std::vector filValues = get_filtration_values(); for (unsigned int i = 0; i < 14; ++i) { - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + zp.insert_cell(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } realIndices.emplace_back(1, 3, 0); @@ -231,11 +231,11 @@ void test_filtered_zigzag_with_storage_max1() { for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, filValues[i]); + zp.remove_cell(id, filValues[i]); } for (unsigned int i = 16; i < 24; ++i) { - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + zp.insert_cell(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } realIndices.emplace_back(5, 16, 0); @@ -243,12 +243,12 @@ void test_filtered_zigzag_with_storage_max1() { for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; - zp.remove_face(id, filValues[i]); + zp.remove_cell(id, filValues[i]); } - zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + zp.insert_cell(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); auto id = simplices[28][0]; - zp.remove_face(id, filValues[28]); + zp.remove_cell(id, filValues[28]); realBarcode.emplace_back(0, Interval_filtration::inf, 0); realBarcode.emplace_back(9, Interval_filtration::inf, 0); @@ -264,7 +264,7 @@ BOOST_AUTO_TEST_CASE(filtered_zigzag_persistence_with_storage) { template void test_filtered_zigzag() { - using face_handle = typename ZP::Face_key; + using cell_handle = typename ZP::Cell_key; using Filtration_value = typename ZP::Filtration_value; using Dimension = typename ZP::Dimension; using Interval = std::tuple; @@ -308,37 +308,37 @@ void test_filtered_zigzag() { realBarcode.emplace_back(2, 7, 9); //23-27 realBarcode.emplace_back(3, 0, 28); //dummy - std::vector > simplices = get_boundaries(); + std::vector > simplices = get_boundaries(); std::vector filValues = get_filtration_values(); for (unsigned int i = 0; i < 14; ++i) { interval = realBarcode[i]; - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + zp.insert_cell(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } for (unsigned int i = 14; i < 16; ++i) { interval = realBarcode[i]; auto id = simplices[i][0]; - zp.remove_face(id, filValues[i]); + zp.remove_cell(id, filValues[i]); } for (unsigned int i = 16; i < 24; ++i) { interval = realBarcode[i]; - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + zp.insert_cell(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } for (unsigned int i = 24; i < 27; ++i) { interval = realBarcode[i]; auto id = simplices[i][0]; - zp.remove_face(id, filValues[i]); + zp.remove_cell(id, filValues[i]); } interval = realBarcode[27]; - zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + zp.insert_cell(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); interval = realBarcode[28]; auto id = simplices[28][0]; - zp.remove_face(id, filValues[28]); + zp.remove_cell(id, filValues[28]); //there is no real guarantee on the order of the infinite bars std::vector infiniteBars; @@ -364,7 +364,7 @@ void test_filtered_zigzag() { template void test_filtered_zigzag_max1() { - using face_handle = typename ZP::Face_key; + using cell_handle = typename ZP::Cell_key; using Filtration_value = typename ZP::Filtration_value; using Dimension = typename ZP::Dimension; using Interval = std::tuple; @@ -412,37 +412,37 @@ void test_filtered_zigzag_max1() { realBarcode.emplace_back(2, 7, 9); //23-27 realBarcode.emplace_back(1, 0, 28); //dummy - std::vector > simplices = get_boundaries(); + std::vector > simplices = get_boundaries(); std::vector filValues = get_filtration_values(); for (unsigned int i = 0; i < 14; ++i) { interval = realBarcode[i]; - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + zp.insert_cell(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } for (unsigned int i = 14; i < 16; ++i) { interval = realBarcode[i]; auto id = simplices[i][0]; - zp.remove_face(id, filValues[i]); + zp.remove_cell(id, filValues[i]); } for (unsigned int i = 16; i < 24; ++i) { interval = realBarcode[i]; - zp.insert_face(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); + zp.insert_cell(i, simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1, filValues[i]); } for (unsigned int i = 24; i < 27; ++i) { interval = realBarcode[i]; auto id = simplices[i][0]; - zp.remove_face(id, filValues[i]); + zp.remove_cell(id, filValues[i]); } interval = realBarcode[27]; - zp.insert_face(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); + zp.insert_cell(27, simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1, filValues[27]); interval = realBarcode[28]; auto id = simplices[28][0]; - zp.remove_face(id, filValues[28]); + zp.remove_cell(id, filValues[28]); //there is no real guarantee on the order of the infinite bars std::vector infiniteBars; diff --git a/src/Zigzag_persistence/test/test_zigzag_persistence.cpp b/src/Zigzag_persistence/test/test_zigzag_persistence.cpp index c589fa57b6..0f1edb9a2b 100644 --- a/src/Zigzag_persistence/test/test_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/test/test_zigzag_persistence.cpp @@ -17,7 +17,6 @@ #include using ZP = Gudhi::zigzag_persistence::Zigzag_persistence<>; -// using ZP = Gudhi::zigzag_persistence::Zigzag_persistence; struct Interval { Interval() {} @@ -97,7 +96,7 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { std::vector > simplices = get_boundaries(); for (unsigned int i = 0; i < 14; ++i) { - zp.insert_face(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); + zp.insert_cell(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); } realIndices.emplace_back(0, 1, 3); @@ -109,11 +108,11 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; - zp.remove_face(id); + zp.remove_cell(id); } for (unsigned int i = 16; i < 24; ++i) { - zp.insert_face(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); + zp.insert_cell(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); } realIndices.emplace_back(0, 5, 16); @@ -124,17 +123,17 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single) { for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; - zp.remove_face(id); + zp.remove_cell(id); } realIndices.emplace_back(1, 24, 25); - zp.insert_face(simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1); + zp.insert_cell(simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1); realIndices.emplace_back(2, 23, 27); auto id = simplices[28][0]; - zp.remove_face(id); + zp.remove_cell(id); realIndices.emplace_back(0, 0, -1); realIndices.emplace_back(0, 26, -1); @@ -165,7 +164,7 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { std::vector > simplices = get_boundaries(); for (unsigned int i = 0; i < 14; ++i) { - zp.insert_face(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); + zp.insert_cell(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); } realIndices.emplace_back(0, 1, 3); @@ -175,23 +174,23 @@ BOOST_AUTO_TEST_CASE(zigzag_persistence_single_max1) { for (unsigned int i = 14; i < 16; ++i) { auto id = simplices[i][0]; - zp.remove_face(id); + zp.remove_cell(id); } for (unsigned int i = 16; i < 24; ++i) { - zp.insert_face(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); + zp.insert_cell(simplices[i], simplices[i].size() == 0 ? 0 : simplices[i].size() - 1); } realIndices.emplace_back(0, 5, 16); for (unsigned int i = 24; i < 27; ++i) { auto id = simplices[i][0]; - zp.remove_face(id); + zp.remove_cell(id); } - zp.insert_face(simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1); + zp.insert_cell(simplices[27], simplices[27].size() == 0 ? 0 : simplices[27].size() - 1); auto id = simplices[28][0]; - zp.remove_face(id); + zp.remove_cell(id); realIndices.emplace_back(0, 0, -1); realIndices.emplace_back(0, 26, -1); From 656435ec1a876895f063048407d88511c3f27170 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 9 Jan 2025 18:42:12 +0100 Subject: [PATCH 11/21] oscillating rips unitary tests part 1 --- .../oscillating_rips_iterators.h | 164 ++++++++---- src/Zigzag_persistence/test/CMakeLists.txt | 3 + .../test/test_oscillating_rips.cpp | 252 ++++++++++++++++++ 3 files changed, 369 insertions(+), 50 deletions(-) create mode 100644 src/Zigzag_persistence/test/test_oscillating_rips.cpp diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index d37bab5f36..027a7b5d03 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -23,9 +23,11 @@ #include #include +#include #include #include #include +#include #include @@ -122,6 +124,18 @@ class Zigzag_edge return ((e.u_ == u_) && (e.v_ == v_) && (e.fil_ == fil_) && (e.direction_ == direction_)); } + friend std::ostream &operator<<(std::ostream &stream, const Zigzag_edge &ze) { + stream << std::setprecision(6); + // stream << std::setprecision(std::numeric_limits::digits); + stream << "(" << ze.u_ << ", " << ze.v_<< ") "; + if (ze.direction_){ + stream << "-- " << ze.fil_ << " -->"; + } else { + stream << "<-- " << ze.fil_ << " --"; + } + return stream; + } + private: int u_; /**< Smaller vertex. */ int v_; /**< Bigger vertex. */ @@ -307,6 +321,7 @@ class Oscillating_rips_edge_range "The number of points and the number of epsilon values should match."); GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); + //TODO: remove this if, the two method could just do nothing for the identity modifier. if constexpr (EdgeModifier::isActive_) { nu_ = EdgeModifier::apply_inverse_modifier(nu); mu_ = EdgeModifier::apply_inverse_modifier(mu); @@ -577,56 +592,31 @@ class Oscillating_rips_edge_range DistanceFunction&& distance, Oscillating_rips_edge_order_policy orderPolicy = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) { - std::vector > edgeFiltration; std::vector epsilonValues; std::vector > > distanceMatrix; - auto n = points.size(); _initialize(nu, mu, epsilonValues, distanceMatrix, points, distance, orderPolicy); - // edgesAdded[i] (resp. edgesRemoved[i]) == list of edges (i,j), with j > > edgesAdded, edgesRemoved; - - std::size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); - - // Now, sort edges according to lengths, and put everything in edgeFiltration - edgeFiltration.clear(); - edgeFiltration.reserve(number_of_arrows + n); // count edges + vertices additions - - // initialise R({p_0}, +infinity) - edgeFiltration.emplace_back(0, - 0, // add vertex p_0,+infty - std::numeric_limits::infinity(), - true); - // epsilonValues[0], true); + return _compute_vector_range(nu, mu, distanceMatrix, epsilonValues); + } - for (std::size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i - if constexpr (EdgeModifier::isActive_) { - edgeFiltration.emplace_back(i + 1, - i + 1, - EdgeModifier::apply_modifier(epsilonValues[i]), - true); // add p_{i+1},eps_i - } else { - edgeFiltration.emplace_back(i + 1, i + 1, epsilonValues[i], true); // add p_{i+1},eps_i - } - for (auto edgeIt = edgesAdded[i].begin(); edgeIt != edgesAdded[i].end(); ++edgeIt) { - edgeFiltration.push_back(*edgeIt); - } - for (auto edgeIt = edgesRemoved[i].rbegin(); // longest first - edgeIt != edgesRemoved[i].rend(); - ++edgeIt) { - edgeFiltration.push_back(*edgeIt); - } - } - for (int i = n - 1; i >= 0; --i) { - edgeFiltration.emplace_back(i, i, -std::numeric_limits::infinity(), false); - } + template + static std::vector > compute_vector_range( + Filtration_value nu, + Filtration_value mu, + const PointRange& orderedPoints, + DistanceFunction&& distance, + const std::vector& epsilonValues) + { + GUDHI_CHECK( + orderedPoints.size() == epsilonValues.size(), + std::invalid_argument("Epsilon values should be initialized and have the same size than the point container.")); + + std::vector > > distanceMatrix; - _canonically_sort_edges(edgeFiltration); + _initialize(nu, mu, epsilonValues, distanceMatrix, orderedPoints, distance); - return edgeFiltration; + return _compute_vector_range(nu, mu, distanceMatrix, epsilonValues); } /** @@ -825,17 +815,18 @@ class Oscillating_rips_edge_range } // compute epsilon values + if (orderPolicy == Oscillating_rips_edge_order_policy::ALREADY_ORDERED) { sortedPoints.assign(points.begin(), points.end()); epsilonValues = _compute_epsilon_values(sortedPoints, distance); } else if (orderPolicy == Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) { epsilonValues.reserve(n); Gudhi::subsampling::choose_n_farthest_points(distance, - points, - n, // final size - 0, // starting point - std::back_inserter(sortedPoints), - std::back_inserter(epsilonValues)); + points, + n, // final size + 0, // starting point + std::back_inserter(sortedPoints), + std::back_inserter(epsilonValues)); // need to shift values output by subsampling: for (unsigned int i = 1; i < n; ++i) { epsilonValues[i - 1] = epsilonValues[i]; @@ -850,6 +841,79 @@ class Oscillating_rips_edge_range distanceMatrix = _compute_distance_matrix(sortedPoints, distance); } + template + static void _initialize(Filtration_value& nu, + Filtration_value& mu, + const std::vector& epsilonValues, + std::vector > >& distanceMatrix, + const PointRange& points, + DistanceFunction&& distance) + { + GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); + + if constexpr (EdgeModifier::isActive_) { + nu = EdgeModifier::apply_inverse_modifier(nu); + mu = EdgeModifier::apply_inverse_modifier(mu); + } + + // compute the distance matrix + distanceMatrix = _compute_distance_matrix(points, distance); + } + + static std::vector > _compute_vector_range( + Filtration_value nu, + Filtration_value mu, + const std::vector > >& distanceMatrix, + const std::vector& epsilonValues) + { + std::vector > edgeFiltration; + auto n = epsilonValues.size(); + + // edgesAdded[i] (resp. edgesRemoved[i]) == list of edges (i,j), with j > > edgesAdded, edgesRemoved; + + std::size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); + + // Now, sort edges according to lengths, and put everything in edgeFiltration + edgeFiltration.clear(); + edgeFiltration.reserve(number_of_arrows + n); // count edges + vertices additions + + // initialise R({p_0}, +infinity) + edgeFiltration.emplace_back(0, + 0, // add vertex p_0,+infty + std::numeric_limits::infinity(), + true); + // epsilonValues[0], true); + + for (std::size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i + if constexpr (EdgeModifier::isActive_) { + edgeFiltration.emplace_back(i + 1, + i + 1, + EdgeModifier::apply_modifier(epsilonValues[i]), + true); // add p_{i+1},eps_i + } else { + edgeFiltration.emplace_back(i + 1, i + 1, epsilonValues[i], true); // add p_{i+1},eps_i + } + for (auto edgeIt = edgesAdded[i].begin(); edgeIt != edgesAdded[i].end(); ++edgeIt) { + edgeFiltration.push_back(*edgeIt); + } + for (auto edgeIt = edgesRemoved[i].rbegin(); // longest first + edgeIt != edgesRemoved[i].rend(); + ++edgeIt) { + edgeFiltration.push_back(*edgeIt); + } + } + for (int i = n - 1; i >= 0; --i) { + edgeFiltration.emplace_back(i, i, -std::numeric_limits::infinity(), false); + } + + _canonically_sort_edges(edgeFiltration); + + return edgeFiltration; + } + /** * @brief Compute the epsilon values for an ordered set of points, measuring the * sparsity of the ordering. @@ -976,7 +1040,7 @@ class Oscillating_rips_edge_range static std::size_t _compute_edges(Filtration_value nu, Filtration_value mu, const std::vector& epsilonValues, - std::vector > >& distanceMatrix, + const std::vector > >& distanceMatrix, std::vector > >& edgesAdded, std::vector > >& edgesRemoved) { @@ -995,7 +1059,7 @@ class Oscillating_rips_edge_range #ifdef GUDHI_USE_TBB // no need to consider the case i=n-1 in an oscillating Rips filtration tbb::parallel_for(std::size_t(0), n - 1, [&](std::size_t i) { - typename std::vector >::iterator it; + typename std::vector >::const_iterator it; //----edgesAdded[i]: // consider first all edges added in inclusion: // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), @@ -1075,7 +1139,7 @@ class Oscillating_rips_edge_range }); #else // GUDHI_USE_TBB not defined - typename std::vector >::iterator it; + typename std::vector >::const_iterator it; for (std::size_t i = 0; i < n - 1; ++i) { //----edgesAdded[i]: diff --git a/src/Zigzag_persistence/test/CMakeLists.txt b/src/Zigzag_persistence/test/CMakeLists.txt index ee0fe2c4e9..f83956bb61 100644 --- a/src/Zigzag_persistence/test/CMakeLists.txt +++ b/src/Zigzag_persistence/test/CMakeLists.txt @@ -8,5 +8,8 @@ gudhi_add_boost_test(Zigzag_persistence_test_zigzag_persistence) add_executable_with_targets(Zigzag_persistence_test_filtered_zigzag_persistence test_filtered_zigzag_persistence.cpp TBB::tbb) gudhi_add_boost_test(Zigzag_persistence_test_filtered_zigzag_persistence) +add_executable_with_targets(Zigzag_persistence_test_oscillating_rips test_oscillating_rips.cpp TBB::tbb) +gudhi_add_boost_test(Zigzag_persistence_test_oscillating_rips) + diff --git a/src/Zigzag_persistence/test/test_oscillating_rips.cpp b/src/Zigzag_persistence/test/test_oscillating_rips.cpp new file mode 100644 index 0000000000..70c89340e8 --- /dev/null +++ b/src/Zigzag_persistence/test/test_oscillating_rips.cpp @@ -0,0 +1,252 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +#include + +#define BOOST_TEST_DYN_LINK +#define BOOST_TEST_MODULE "zigzag_persistence" +#include + +#include +#include +#include + +struct Simplex_tree_options_oscillating_rips { + typedef Gudhi::linear_indexing_tag Indexing_tag; + typedef int Vertex_handle; + typedef double Filtration_value; + typedef std::int64_t Simplex_key; + static const bool store_key = true; + static const bool store_filtration = true; + static const bool contiguous_vertices = true; + static const bool link_nodes_by_label = true; + static const bool stable_simplex_handles = true; +}; + +using Gudhi::zigzag_persistence::Oscillating_rips_edge_order_policy; +using Gudhi::zigzag_persistence::Oscillating_rips_edge_range; +using Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; +using Gudhi::zigzag_persistence::Zigzag_edge; + +using Point = std::vector; +using StableFilteredComplex = Gudhi::Simplex_tree; +using Simplex_handle = typename StableFilteredComplex::Simplex_handle; +using Vertex_handle = typename StableFilteredComplex::Vertex_handle; +using Filtration_value = typename StableFilteredComplex::Filtration_value; +using Edge = Zigzag_edge; +using Face = std::vector; +using Arrow = std::tuple; +using VArrow = std::tuple; +using EdgeRange = Oscillating_rips_edge_range; + +// using OscillatingRipsSimplexRange = Oscillating_rips_simplex_range; + +std::vector get_point_cloud() { return {{0, 0}, {1.2, 1.2}, {0, 1.1}, {1, 0}, {0.51, 0.49}}; } + +std::vector get_epsilons(const std::vector& points) +{ + auto size = points.size(); + std::vector sortedPoints; + sortedPoints.reserve(size); + std::vector epsilonValues; + epsilonValues.reserve(size); + + Gudhi::subsampling::choose_n_farthest_points(Gudhi::Euclidean_distance(), + points, + size, // final size + 0, // starting point + std::back_inserter(sortedPoints), + std::back_inserter(epsilonValues)); + // need to shift values output by subsampling: + for (unsigned int i = 1; i < size; ++i) { + epsilonValues[i - 1] = epsilonValues[i]; + } + epsilonValues[size - 1] = 0; + + BOOST_CHECK(sortedPoints == points); + + return epsilonValues; +} + +std::vector get_filtration(const std::vector& eps) +{ + Filtration_value inf = std::numeric_limits::infinity(); + std::vector arrows; + arrows.reserve(46); + + arrows.emplace_back(Face{0}, inf, true); + + arrows.emplace_back(Face{1}, eps[0], true); + arrows.emplace_back(Face{0, 1}, eps[0], true); + + arrows.emplace_back(Face{2}, eps[1], true); + arrows.emplace_back(Face{1, 2}, eps[1], true); + arrows.emplace_back(Face{0, 2}, eps[1], true); + arrows.emplace_back(Face{0, 1, 2}, eps[1], true); + + arrows.emplace_back(Face{3}, eps[2], true); + arrows.emplace_back(Face{2, 3}, eps[2], true); + arrows.emplace_back(Face{1, 3}, eps[2], true); + arrows.emplace_back(Face{1, 2, 3}, eps[2], true); + arrows.emplace_back(Face{0, 3}, eps[2], true); + arrows.emplace_back(Face{0, 1, 3}, eps[2], true); + arrows.emplace_back(Face{0, 2, 3}, eps[2], true); + + arrows.emplace_back(Face{0, 2, 3}, eps[2], false); + arrows.emplace_back(Face{0, 1, 3}, eps[2], false); + arrows.emplace_back(Face{1, 2, 3}, eps[2], false); + arrows.emplace_back(Face{2, 3}, eps[2], false); + arrows.emplace_back(Face{0, 1, 2}, eps[2], false); + arrows.emplace_back(Face{0, 1}, eps[2], false); + + arrows.emplace_back(Face{4}, eps[3], true); + arrows.emplace_back(Face{1, 4}, eps[3], true); + arrows.emplace_back(Face{2, 4}, eps[3], true); + arrows.emplace_back(Face{1, 2, 4}, eps[3], true); + arrows.emplace_back(Face{0, 4}, eps[3], true); + arrows.emplace_back(Face{0, 2, 4}, eps[3], true); + arrows.emplace_back(Face{3, 4}, eps[3], true); + arrows.emplace_back(Face{1, 3, 4}, eps[3], true); + arrows.emplace_back(Face{0, 3, 4}, eps[3], true); + + arrows.emplace_back(Face{0, 3, 4}, eps[3], false); + arrows.emplace_back(Face{1, 3, 4}, eps[3], false); + arrows.emplace_back(Face{3, 4}, eps[3], false); + arrows.emplace_back(Face{0, 2, 4}, eps[3], false); + arrows.emplace_back(Face{0, 4}, eps[3], false); + arrows.emplace_back(Face{1, 2, 4}, eps[3], false); + arrows.emplace_back(Face{2, 4}, eps[3], false); + arrows.emplace_back(Face{1, 4}, eps[3], false); + arrows.emplace_back(Face{0, 3}, eps[3], false); + arrows.emplace_back(Face{1, 3}, eps[3], false); + arrows.emplace_back(Face{0, 2}, eps[3], false); + arrows.emplace_back(Face{1, 2}, eps[3], false); + + arrows.emplace_back(Face{4}, -inf, false); + arrows.emplace_back(Face{3}, -inf, false); + arrows.emplace_back(Face{2}, -inf, false); + arrows.emplace_back(Face{1}, -inf, false); + arrows.emplace_back(Face{0}, -inf, false); + + return arrows; +} + +template +void test_edges(EdgeRangeIterator& start, const EdgeRangeIterator& end, const std::vector& eps) +{ + auto comp = [](const Edge& e1, const Edge& e2) { + if (e2.get_filtration_value() == e1.get_filtration_value()) { + if (e1.get_direction() == e2.get_direction()) { + if (e1.get_smallest_vertex() == e2.get_smallest_vertex()) { + if (e1.get_biggest_vertex() == e2.get_biggest_vertex()) return false; + return e1.get_biggest_vertex() < e2.get_biggest_vertex(); + } else { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); + } + } else { + return e1.get_direction(); + } + } else { + return e1.get_filtration_value() > e2.get_filtration_value(); + } + }; + + std::vector realEdges; + realEdges.reserve(30); + for (const VArrow& a : get_filtration(eps)) { + const Face& f = std::get<0>(a); + if (f.size() < 3) { + if (f.size() == 1) { + realEdges.emplace_back(f[0], f[0], std::get<1>(a), std::get<2>(a)); + } else if (f.size() == 2) { + realEdges.emplace_back(f[0], f[1], std::get<1>(a), std::get<2>(a)); + } + } + } + // for same filtration value, the order can change for the different iterator types. + std::sort(realEdges.begin(), realEdges.end(), comp); + + std::vector testEdges; + testEdges.reserve(30); + unsigned int i = 0; + while (start != end && i < realEdges.size()) { // to avoid infinite loop when something is wrong + testEdges.push_back(*start); + ++start; + } + BOOST_CHECK(start == end); + // for same filtration value, the order can change for the different iterator types. + std::sort(testEdges.begin(), testEdges.end(), comp); + + // instead of comparing directly testEdges == realEdges, because it makes debuging easier with more details + i = 0; + BOOST_CHECK(testEdges.size() == realEdges.size()); + for (const auto& e : testEdges) { + // std::cout << "test: " << e << "\n"; + // std::cout << "real: " << realEdges[i] << "\n"; + BOOST_CHECK(e == realEdges[i]); + ++i; + } +} + +void test_arrow(const Arrow& a, const VArrow& va, const StableFilteredComplex& st) +{ + std::vector vertices; + for (Vertex_handle v : st.simplex_vertex_range(std::get<0>(a))) { + vertices.push_back(v); + } + std::sort(vertices.begin(), vertices.end()); + BOOST_CHECK(vertices == std::get<0>(va)); + BOOST_CHECK(std::get<1>(a) == std::get<1>(va)); + BOOST_CHECK(std::get<2>(a) == std::get<2>(va)); +} + +template +void test_filtration(SimplexRangeIterator& start, + const SimplexRangeIterator& end, + const StableFilteredComplex& st, + const std::vector& eps) +{ + for (const VArrow& a : get_filtration(eps)) { + BOOST_CHECK(start != end); + test_arrow(*start, a, st); + ++start; + } + + BOOST_CHECK(start == end); +} + +BOOST_AUTO_TEST_CASE(oscillating_rips_edge_range1) +{ + double nu = 1.73, mu = 2; + std::vector points = get_point_cloud(); + std::vector eps = {1.697, 1.1, 1, 0.707, 0}; + + auto start1 = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), eps); + test_edges(start1, EdgeRange::end(), eps); + + auto vec1 = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), eps); + auto start3 = vec1.begin(); + test_edges(start3, vec1.end(), eps); +} + +BOOST_AUTO_TEST_CASE(oscillating_rips_edge_range2) +{ + double nu = 1.76, mu = 2; + std::vector points = get_point_cloud(); + Oscillating_rips_edge_order_policy p = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING; + std::vector eps = get_epsilons(points); + + auto start2 = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); + test_edges(start2, EdgeRange::end(), eps); + + auto vec2 = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto start4 = vec2.begin(); + test_edges(start4, vec2.end(), eps); +} From 8e1512a7e1f8c7f6fb5460437b79ef07e7056529 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 10 Jan 2025 16:17:47 +0100 Subject: [PATCH 12/21] oscillating rips unitary tests part 2 --- .../oscillating_rips_iterators.h | 3 + .../test/test_oscillating_rips.cpp | 125 +++++++++++++++--- 2 files changed, 110 insertions(+), 18 deletions(-) diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 027a7b5d03..43ef5c464b 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -210,6 +210,9 @@ enum Oscillating_rips_edge_order_policy { RANDOM_POINT_ORDERING /**< The points are shuffled randomly. */ }; +// TODO: make all the iterators more "copyable" by giving some additional pointer to a container they can fill +// and which won't be copied together with the pointer + /** * @class Oscillating_rips_edge_range oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h * @brief Gives access and computes different edge range types for an oscillating Rips filtration. diff --git a/src/Zigzag_persistence/test/test_oscillating_rips.cpp b/src/Zigzag_persistence/test/test_oscillating_rips.cpp index 70c89340e8..f2da519969 100644 --- a/src/Zigzag_persistence/test/test_oscillating_rips.cpp +++ b/src/Zigzag_persistence/test/test_oscillating_rips.cpp @@ -9,6 +9,7 @@ */ #include +#include #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE "zigzag_persistence" @@ -45,8 +46,8 @@ using Face = std::vector; using Arrow = std::tuple; using VArrow = std::tuple; using EdgeRange = Oscillating_rips_edge_range; - -// using OscillatingRipsSimplexRange = Oscillating_rips_simplex_range; +template +using OscillatingRipsSimplexRange = Oscillating_rips_simplex_range; std::vector get_point_cloud() { return {{0, 0}, {1.2, 1.2}, {0, 1.1}, {1, 0}, {0.51, 0.49}}; } @@ -179,6 +180,7 @@ void test_edges(EdgeRangeIterator& start, const EdgeRangeIterator& end, const st while (start != end && i < realEdges.size()) { // to avoid infinite loop when something is wrong testEdges.push_back(*start); ++start; + ++i; } BOOST_CHECK(start == end); // for same filtration value, the order can change for the different iterator types. @@ -195,31 +197,67 @@ void test_edges(EdgeRangeIterator& start, const EdgeRangeIterator& end, const st } } -void test_arrow(const Arrow& a, const VArrow& va, const StableFilteredComplex& st) -{ - std::vector vertices; - for (Vertex_handle v : st.simplex_vertex_range(std::get<0>(a))) { - vertices.push_back(v); - } - std::sort(vertices.begin(), vertices.end()); - BOOST_CHECK(vertices == std::get<0>(va)); - BOOST_CHECK(std::get<1>(a) == std::get<1>(va)); - BOOST_CHECK(std::get<2>(a) == std::get<2>(va)); -} - template void test_filtration(SimplexRangeIterator& start, const SimplexRangeIterator& end, const StableFilteredComplex& st, const std::vector& eps) { - for (const VArrow& a : get_filtration(eps)) { - BOOST_CHECK(start != end); - test_arrow(*start, a, st); + auto comp = [](const VArrow& e1, const VArrow& e2) { + if (std::get<1>(e1) == std::get<1>(e2)) { + if (std::get<2>(e1) == std::get<2>(e2)) { + const auto& v1 = std::get<0>(e1); + const auto& v2 = std::get<0>(e2); + return std::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end()); + } else { + return std::get<2>(e1); + } + } else { + return std::get<1>(e1) > std::get<1>(e2); + } + }; + + std::vector realSimplices = get_filtration(eps); + // for same filtration value, the order can change for the different iterator types. + std::sort(realSimplices.begin(), realSimplices.end(), comp); + + std::vector testSimplices; + testSimplices.reserve(46); + unsigned int i = 0; + while (start != end && i < realSimplices.size()) { // to avoid infinite loop when something is wrong + testSimplices.emplace_back(); + auto& vertices = std::get<0>(testSimplices.back()); + for (Vertex_handle v : st.simplex_vertex_range(std::get<0>(*start))) { + vertices.push_back(v); + } + std::sort(vertices.begin(), vertices.end()); + std::get<1>(testSimplices.back()) = std::get<1>(*start); + std::get<2>(testSimplices.back()) = std::get<2>(*start); ++start; + ++i; } - BOOST_CHECK(start == end); + // for same filtration value, the order can change for the different iterator types. + std::sort(testSimplices.begin(), testSimplices.end(), comp); + + i = 0; + BOOST_CHECK(testSimplices.size() == realSimplices.size()); + for (const auto& e : testSimplices) { + // std::cout << "test: "; + // for (const auto& v : std::get<0>(e)) { + // std::cout << v << " "; + // } + // std::cout << "\n"; + // std::cout << "real: "; + // for (const auto& v : std::get<0>(realSimplices[i])) { + // std::cout << v << " "; + // } + // std::cout << "\n"; + BOOST_CHECK(std::get<0>(e) == std::get<0>(realSimplices[i])); + BOOST_CHECK(std::get<1>(e) == std::get<1>(realSimplices[i])); + BOOST_CHECK(std::get<2>(e) == std::get<2>(realSimplices[i])); + ++i; + } } BOOST_AUTO_TEST_CASE(oscillating_rips_edge_range1) @@ -250,3 +288,54 @@ BOOST_AUTO_TEST_CASE(oscillating_rips_edge_range2) auto start4 = vec2.begin(); test_edges(start4, vec2.end(), eps); } + +BOOST_AUTO_TEST_CASE(oscillating_rips_simplex_range1) +{ + double nu = 1.73, mu = 2; + int maxDim = 2; + std::vector points = get_point_cloud(); + std::vector eps = {1.697, 1.1, 1, 0.707, 0}; + StableFilteredComplex st; + + auto edgeStart1 = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), eps); + auto edgeEnd1 = EdgeRange::end(); + auto start1 = OscillatingRipsSimplexRange::begin( + edgeStart1, edgeEnd1, st, maxDim); + auto end1 = OscillatingRipsSimplexRange::end(); + test_filtration(start1, end1, st, eps); + + BOOST_CHECK(st.is_empty()); + + auto vec1 = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), eps); + auto edgeStart3 = vec1.cbegin(); + auto edgeEnd3 = vec1.cend(); + auto start3 = OscillatingRipsSimplexRange::const_iterator>::begin(edgeStart3, edgeEnd3, st, maxDim); + auto end3 = OscillatingRipsSimplexRange::const_iterator>::end(); + test_filtration(start3, end3, st, eps); +} + +BOOST_AUTO_TEST_CASE(oscillating_rips_simplex_range2) +{ + double nu = 1.76, mu = 2; + int maxDim = 2; + std::vector points = get_point_cloud(); + Oscillating_rips_edge_order_policy p = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING; + std::vector eps = get_epsilons(points); + StableFilteredComplex st; + + auto edgeStart1 = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto edgeEnd1 = EdgeRange::end(); + auto start1 = OscillatingRipsSimplexRange::begin( + edgeStart1, edgeEnd1, st, maxDim); + auto end1 = OscillatingRipsSimplexRange::end(); + test_filtration(start1, end1, st, eps); + + BOOST_CHECK(st.is_empty()); + + auto vec1 = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + auto edgeStart3 = vec1.cbegin(); + auto edgeEnd3 = vec1.cend(); + auto start3 = OscillatingRipsSimplexRange::const_iterator>::begin(edgeStart3, edgeEnd3, st, maxDim); + auto end3 = OscillatingRipsSimplexRange::const_iterator>::end(); + test_filtration(start3, end3, st, eps); +} From f76a6824b064beda19131635c7775e09d4309044 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 14 Jan 2025 18:37:42 +0100 Subject: [PATCH 13/21] doc --- src/Zigzag_persistence/concept/EdgeModifier.h | 2 +- .../concept/OscillatingRipsSimplexRange.h | 69 --- src/Zigzag_persistence/concept/PointRange.h | 8 +- .../concept/StableFilteredComplex.h | 25 +- .../concept/ZigzagOptions.h | 26 +- .../doc/Intro_zigzag_persistence.h | 35 +- src/Zigzag_persistence/doc/osc_rips.svg | 475 ++++++++++++++++++ .../gudhi/Oscillating_rips_persistence.h | 42 +- .../oscillating_rips_iterators.h | 19 +- .../gudhi/filtered_zigzag_persistence.h | 20 +- src/common/doc/main_page.md | 23 + 11 files changed, 605 insertions(+), 139 deletions(-) delete mode 100644 src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h create mode 100644 src/Zigzag_persistence/doc/osc_rips.svg diff --git a/src/Zigzag_persistence/concept/EdgeModifier.h b/src/Zigzag_persistence/concept/EdgeModifier.h index 180460d3d6..c3bdaca3a8 100644 --- a/src/Zigzag_persistence/concept/EdgeModifier.h +++ b/src/Zigzag_persistence/concept/EdgeModifier.h @@ -22,7 +22,7 @@ namespace zigzag_persistence { * @brief Methods whose purposes are to modify the filtration value of a given edge following a rule. * The concept is for example realized by @ref Square_root_edge_modifier. */ - template +template class EdgeModifier { public: /** diff --git a/src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h b/src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h deleted file mode 100644 index 7b3c724815..0000000000 --- a/src/Zigzag_persistence/concept/OscillatingRipsSimplexRange.h +++ /dev/null @@ -1,69 +0,0 @@ -/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - * Author(s): Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -#ifndef CONCEPT_ZZ_OSCI_RIPS_RANGE_H_ -#define CONCEPT_ZZ_OSCI_RIPS_RANGE_H_ - -/** @file OscillatingRipsSimplexRange.h - * @brief Contains @ref Gudhi::zigzag_persistence::OscillatingRipsSimplexRange concept. - */ - -#include -#include - -namespace Gudhi { -namespace zigzag_persistence { - -/** - * @brief Class giving access to a range over the simplices in an oscillating Rips filtration - * in order of the filtration. - * - * A simplex has to be represented by a tuple of three elements: - * the first is the simplex handle of the simplex in the given complex, - * the second is the filtration value of the corresponding arrow, - * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. - */ -class OscillatingRipsSimplexRange { - public: - /** - * @brief Returns a range over the simplices in an oscillating Rips filtration - * in order of the filtration. - * - * @param edgeStartIterator Begin iterator of the edge range. - * @param edgeEndIterator End iterator of the edge range. - * @param complex Structure storing the complex at each step. - * @param maxDimension Maximal dimension of the expansion. - * @return A range with begin() and end() methods. - */ - static auto get_iterator_range(Oscillating_rips_edge_range::Oscillating_rips_edge_iterator& edgeStartIterator, - Oscillating_rips_edge_range::Oscillating_rips_edge_iterator& edgeEndIterator, - StableFilteredComplex& complex, - int maxDimension); - - /** - * @brief Returns a range over the simplices in an oscillating Rips filtration - * in order of the filtration. - * - * @param edgeStartIterator Begin iterator of the edge range. - * @param edgeEndIterator End iterator of the edge range. - * @param complex Structure storing the complex at each step. - * @param maxDimension Maximal dimension of the expansion. - * @return A range with begin() and end() methods. - */ - static auto get_iterator_range(std::vector >::iterator& edgeStartIterator, - std::vector >::iterator& edgeEndIterator, - StableFilteredComplex& complex, - int maxDimension); -}; - -} // namespace zigzag_persistence -} // namespace Gudhi - -#endif // CONCEPT_ZZ_OSCI_RIPS_RANGE_H_ diff --git a/src/Zigzag_persistence/concept/PointRange.h b/src/Zigzag_persistence/concept/PointRange.h index b09a9123e7..5f77ba498c 100644 --- a/src/Zigzag_persistence/concept/PointRange.h +++ b/src/Zigzag_persistence/concept/PointRange.h @@ -25,8 +25,8 @@ namespace zigzag_persistence { class Point{}; /** - * @brief Range of @ref Point. Used with @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING order policy, - * it has to be a random access range. + * @brief Range of @ref Point. If used with @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING + * order policy, it has to be a random access range. */ class PointRange { public: @@ -52,8 +52,8 @@ class PointRange { std::size_t size(); /** - * @brief Necessary only if used with @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING. Returns the element - * at the given index. + * @brief Necessary only if used with @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING. + * Returns the element at the given index. * * @param index Index of the element to return. * @return Point at index @p index. diff --git a/src/Zigzag_persistence/concept/StableFilteredComplex.h b/src/Zigzag_persistence/concept/StableFilteredComplex.h index 6d430ec52a..7f638ad811 100644 --- a/src/Zigzag_persistence/concept/StableFilteredComplex.h +++ b/src/Zigzag_persistence/concept/StableFilteredComplex.h @@ -20,13 +20,8 @@ namespace zigzag_persistence { /** * @brief Data structure storing the simplices and their filtration values in the current complex. - * The concept is realized for example by @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_oscillating_rips >. - * - * This concept is not incompatible with the @ref ZigzagComplex concept, so it is possible to use the same complex - * for the @ref Oscillating_rips_iterator class and the @ref ZigzagPersistence class, as long as it realizes the two - * concepts (which is the case for @ref Gudhi::Simplex_tree < Gudhi::Simplex_tree_options_oscillating_rips >). - * This avoids having two complexes in memory and give the possibility to directly use the simplex handle instead - * of the vector of vertices to communicate between the two classes. + * The concept is realized for example by + * @ref Gudhi::Simplex_tree < Gudhi::zigzag_persistence::Simplex_tree_options_oscillating_rips >. */ class StableFilteredComplex { public: @@ -36,9 +31,8 @@ class StableFilteredComplex { typename Simplex_key; /** - * @brief Handle to specify a simplex. Different from the @ref ZigzagComplex concept, - * the simplex handles have to be stable, that is, they do not invalidate when a simplex is - * added or removed from the complex (except for the removed simplices them selves of course). + * @brief Handle to specify a simplex. The simplex handles have to be stable, that is, they do not invalidate when + * a simplex is added or removed from the complex (except for the removed simplices them selves of course). */ typename Simplex_handle; @@ -74,17 +68,11 @@ class StableFilteredComplex { /** * @brief Returns the key associated to the given simplex. - * - * @param sh Simplex handle representing the simplex. - * @return The key. */ Simplex_key key(Simplex_handle sh); /** * @brief Assignes the given value to the given simplex as a key. - * - * @param sh Simplex handle representing the simplex. - * @param key Values to associate as key. */ void assign_key(Simplex_handle sh, Simplex_key key); @@ -93,16 +81,13 @@ class StableFilteredComplex { * * @tparam VertexRange Range over the vertices of a simplex. * @param simplex Simplex to find represented by its vertices. - * @return The simplex handle associated to @p simplex if the simplex is found, @ref null_simplex() otherwise. + * @return The simplex handle associated to @p simplex if the simplex is found. */ template Simplex_handle find(const VertexRange& simplex); /** * @brief Returns the filtration value of the given simplex. - * - * @param sh - * @return Filtration_value */ Filtration_value filtration(Simplex_handle sh); diff --git a/src/Zigzag_persistence/concept/ZigzagOptions.h b/src/Zigzag_persistence/concept/ZigzagOptions.h index 35f421ac11..acdb047b82 100644 --- a/src/Zigzag_persistence/concept/ZigzagOptions.h +++ b/src/Zigzag_persistence/concept/ZigzagOptions.h @@ -22,24 +22,35 @@ namespace zigzag_persistence { /** * @ingroup zigzag_persistence * - * @brief List of options used for the filtered zigzag persistence computation. + * @brief List of options used for the filtered zigzag persistence computation. An implementation of this concept is + * @ref Default_filtered_zigzag_options "", which inherits from @ref Default_zigzag_options for the common types. */ struct FilteredZigzagOptions { /** - * @brief Numerical type for the cell IDs used internally and other indexations. It must be signed. + * @brief Type for filtration values. */ - using Internal_key = unspecified; + using Filtration_value = unspecified; /** * @brief Type for the cell IDs used at insertion and in the boundaries given as argument. - * Has to be usable as key in a hashtable, so "hashable" and comparable. */ using Cell_key = unspecified; /** - * @brief Type for filtration values. + * @brief Hash method for @ref Cell_key type. Could simply be 'std::hash' if the specialization exists; */ - using Filtration_value = unspecified; + using Cell_key_hash = unspecified; + + /** + * @brief Equality comparator for @ref Cell_key type. Could simply be 'std::equal_to' if the specialization + * exists; + */ + using Cell_key_equal = unspecified; + + /** + * @brief Numerical type for the cell IDs used internally and other indexations. It must be signed. + */ + using Internal_key = unspecified; /** * @brief Type for the dimension values. @@ -55,7 +66,8 @@ struct FilteredZigzagOptions { /** * @ingroup zigzag_persistence * - * @brief List of options used for the zigzag persistence computation. + * @brief List of options used for the zigzag persistence computation. An implementation of this concept is + * @ref Default_zigzag_options "". */ struct ZigzagOptions { /** diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 444fb14d68..7a46196eee 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -55,9 +55,38 @@ namespace zigzag_persistence { * * \subsection zigzagrips Oscillating Rips * - * A typical example of zigzag filtrations are oscillating rips filtrations. Similar to standard Rips filtrations, they - * completely depend on their edges. But here we look at neighborhoods "oscillating" in size around the points, so - * edges are added but also removed. We refer for example to \cite osc_zz. + * A typical example of zigzag filtrations are oscillating Rips filtrations. Similar to standard + * @ref ripsdefinition "Rips filtrations", they completely depend on their edges. But here we look at neighborhoods + * "oscillating" in size around the points, so edges are added but also removed. We refer for example to \cite osc_zz + * for more theoretical details. + * + * This module implements the construction of such an oscillating Rips filtration as follows: + * \image html "osc_rips.svg" width=70% + * + * If \f$ P \f$ is the set of points generating the rips filtration, \f$ P_i \f$ corresponds to the subset containing + * the \f$ i \f$ first points. So, at each forward inclusion, one vertex is added. + * + * The superscript of \f$ \mathcal{R} \f$ corresponds to the filtration values which will be associated to the cycles. + * The sequence of \f$ \varepsilon \f$ should be decreasing and end with \f$ 0 \f$. The sequence can either be specified + * by the user or automatically generated by choosing the distance of the added point from the other already added + * points from the set. In the second case, the user has to specify how the order of the points should be decided. + * + * The subscript of \f$ \mathcal{R} \f$ corresponds like usual to the radius of the Rips complex. It is generated from + * the current epsilon / filtration value by multiplying it alternatively by two coefficients: \f$ \nu \leqslant \mu \f$. + * Both multipliers have to be specified by the user. + * + * The construction is based on two classes: + * - @ref Oscillating_rips_edge_range computes the range of inserted and removed vertices and edges in the filtration + * based on the elements descipted above. + * - @ref Oscillating_rips_simplex_range infers from @ref Oscillating_rips_edge_range all simplices from higher + * dimensions (i.e. > 1) of the filtration if necessary. For this purpose, a data structure able to represent a flag + * complex is additionally needed (such as @ref Gudhi::Simplex_tree). Note that @ref Oscillating_rips_edge_range is + * passed by template, so the user can potentially pass any other type of edge range as long as the dereferenced format + * corresponds and the sequence makes sense as a zigzag filtration. + * + * If only the barcode of the filtration is of interest and not the filtration it-self, the helper method + * @ref compute_oscillating_rips_persistence can be used. It will directly feed to the filtration constructed by the + * two classes above into @ref Zigzag_persistence "". * * \section zigzagexamples Examples * diff --git a/src/Zigzag_persistence/doc/osc_rips.svg b/src/Zigzag_persistence/doc/osc_rips.svg new file mode 100644 index 0000000000..e71cc06650 --- /dev/null +++ b/src/Zigzag_persistence/doc/osc_rips.svg @@ -0,0 +1,475 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index ba3169bf05..a353357595 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -85,7 +85,8 @@ struct Default_oscillating_rips_zigzag_options : Default_filtered_zigzag_options /** * @brief Computes the oscillating Rips filtration based on the given parameters and computes the - * corresponding zigzag barcode. + * corresponding zigzag barcode. The bars are not stored during the computation and returned via the given callback + * method instead. * * @ingroup zigzag_persistence * @@ -96,22 +97,23 @@ struct Default_oscillating_rips_zigzag_options : Default_filtered_zigzag_options * more information). One can easily create their own method based on this one. * * @tparam PointRange Range containing the point cloud. - * @tparam edge_range_type Either Edge_range_type::BOOST_RANGE or Edge_range_type::VECTOR. - * Default value: Edge_range_type::BOOST_RANGE. - * @tparam OscillatingRipsSimplexRange Type of the oscillating Rips simplex range. - * Default value: Oscillating_rips_simplex_range with templates depending on @p edge_range_type. - * @tparam ZigzagPersistenceOptions Options for the matrix used by the zigzag persistence algorithm. - * Default value: @ref Gudhi::persistence_matrix::Zigzag_options<>. + * @tparam F Callback method type for the zigzag barcode output. + * @tparam edge_range_type Either @ref Edge_range_type::BOOST_RANGE or @ref Edge_range_type::VECTOR. + * Default value: @ref Edge_range_type::BOOST_RANGE. + * @tparam StableFilteredComplex Data structure to store and build the computed complex at each step. * @param points Point cloud. * @param nu Lower multiplier. * @param mu Upper multiplier. * @param maxDim Maximum dimension to which to expand the Rips complex. If set to -1, there is no limit. + * @param outStream Callback method to process the birth and death values of a persistence bar. + * Has to take three arguments as input: first the dimension of the cycle, then the birth value of the cycle + * and third the death value of the cycle. The values corresponds to the filtration values which were given at + * insertions or removals. Note that bars of length 0 will not be token into account. * @param p Order policy for the points. - * Can be either @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_range::Order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_range::Order_policy::RANDOM_POINT_ORDERING. - * Default value: @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING. - * @return The persistence diagram of the oscillating Rips filtration. + * Can be either @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + * Default value: @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING. */ template . + * @tparam StableFilteredComplex Data structure to store and build the computed complex at each step. * @param points Point cloud. * @param nu Lower multiplier. * @param mu Upper multiplier. * @param maxDim Maximum dimension to which to expand the Rips complex. If set to -1, there is no limit. * @param p Order policy for the points. - * Can be either @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_range::Order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_range::Order_policy::RANDOM_POINT_ORDERING. - * Default value: @ref Oscillating_rips_edge_range::Order_policy::FARTHEST_POINT_ORDERING. - * @return The persistence diagram of the oscillating Rips filtration. + * Can be either @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + * Default value: @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING. + * @return The persistence diagram of the oscillating Rips filtration as vector of + * @ref Gudhi::persistence_matrix::Persistence_interval "". */ template class Zigzag_edge @@ -143,6 +143,8 @@ class Zigzag_edge bool direction_; /**< Direction. True = forward, false = backward. */ }; +//TODO: remove the isActive_ members from the edge modifiers. + /** * @class Identity_edge_modifier oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h * @brief Identity modifier, i.e., does nothing. @@ -170,7 +172,7 @@ class Identity_edge_modifier * @details Useful in particular when geometric computations (edge length, etc) are * run with squared Euclidean distance for performance. * - * @tparam Filtration_value Filtration value type. + * @tparam Filtration_value Filtration value type. Should be compatible with `std::sqrt` and `operator*`. */ template class Square_root_edge_modifier @@ -223,7 +225,7 @@ enum Oscillating_rips_edge_order_policy { * a Boost range made from a custom iterator computing an edge on the fly at each increment. * The custom iterator is therefore only a forward iterator and can only be incremented. * - * @tparam Filtration_value Filtration value type + * @tparam Filtration_value Filtration value type. Should be compatible with the edge modifier. * @tparam EdgeModifier Modifier for the edge filtration values. If no modifications are wanted, * use @ref Identity_edge_modifier. Default value: @ref Identity_edge_modifier. * @@ -263,8 +265,10 @@ class Oscillating_rips_edge_range * distance function. * @param distance Distance function. Has to take two points as it from the range @p points as input parameters * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING "", + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING "". */ template Oscillating_rips_edge_iterator(Filtration_value nu, @@ -573,6 +577,7 @@ class Oscillating_rips_edge_range /** * @brief Computes and return a vector with all edges in order of the oscillating Rips filtration. + * See the @ref zigzagrips "introduction page" for more details about the arguments. * * @tparam PointRange Point range type. * @tparam DistanceFunction Type of the distance function. @@ -625,6 +630,7 @@ class Oscillating_rips_edge_range /** * @brief Returns a boost::iterator_range from @ref Oscillating_rips_edge_iterator. The edges are computed * on the fly at each increment. The iterator is a forward iterator only. + * See the @ref zigzagrips "introduction page" for more details about the arguments. * * @tparam PointRange Point range type. * @tparam DistanceFunction Type of the distance function. @@ -659,6 +665,7 @@ class Oscillating_rips_edge_range * on the fly at each increment. The iterator is a forward iterator only. * Takes already computed epsilon values as input, but assumes that the points are already ordered accordingly. * Assumes also that the last epsilon value is 0. + * See the @ref zigzagrips "introduction page" for more details about the arguments. * * @tparam PointRange Point range type. * @tparam DistanceFunction Type of the distance function. @@ -692,6 +699,7 @@ class Oscillating_rips_edge_range /** * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. + * See the @ref zigzagrips "introduction page" for more details about the arguments. * * @tparam PointRange Point range type. * @tparam DistanceFunction Type of the distance function. @@ -722,6 +730,7 @@ class Oscillating_rips_edge_range * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. * Takes already computed epsilon values as input, but assumes that the points are already ordered accordingly. * Assumes also that the last epsilon value is 0. + * See the @ref zigzagrips "introduction page" for more details about the arguments. * * @tparam PointRange Point range type. * @tparam DistanceFunction Type of the distance function. diff --git a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h index d07cef5b97..824934781a 100644 --- a/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/filtered_zigzag_persistence.h @@ -41,10 +41,10 @@ namespace zigzag_persistence { * @brief Default options for @ref Filtered_zigzag_persistence_with_storage and @ref Filtered_zigzag_persistence. */ struct Default_filtered_zigzag_options : Default_zigzag_options { - using Cell_key = int; /**< Cell ID used in the given boundaries. */ - using Filtration_value = double; /**< Filtration value type. */ - using Hash = std::hash; /**< Hash method for Cell_key */ - using KeyEqual = std::equal_to; /**< Equality comparator for Cell_key */ + using Cell_key = int; /**< Cell ID used in the given boundaries. */ + using Filtration_value = double; /**< Filtration value type. */ + using Cell_key_hash = std::hash; /**< Hash method for Cell_key */ + using Cell_key_equal = std::equal_to; /**< Equality comparator for Cell_key */ }; /** @@ -300,7 +300,8 @@ class Filtered_zigzag_persistence_with_storage /** * @brief Map from input keys to internal keys. */ - std::unordered_map handleToKey_; + std::unordered_map + handleToKey_; Dimension dimMax_; /**< Maximal dimension of a bar to record. */ std::vector persistenceDiagram_; /**< Stores current closed persistence intervals. */ Internal_key numArrow_; /**< Current arrow number. */ @@ -450,7 +451,7 @@ class Filtered_zigzag_persistence { using Internal_key = typename Options::Internal_key; /**< Key and index type, has to be signed. */ using Cell_key = typename Options::Cell_key; /**< Cell ID type from external inputs. */ using Filtration_value = typename Options::Filtration_value; /**< Type for filtration values. */ - using Dimension = typename Options::Dimension; /**< Type for dimension values. */ + using Dimension = typename Options::Dimension; /**< Type for dimension values. */ /** * @brief Constructor. @@ -577,10 +578,11 @@ class Filtered_zigzag_persistence { /** * @brief Map from input keys to internal keys. */ - std::unordered_map handleToKey_; - Internal_key numArrow_; /**< Current arrow number. */ + std::unordered_map + handleToKey_; + Internal_key numArrow_; /**< Current arrow number. */ std::unordered_map keyToFiltrationValue_; /**< Cell Key to filtration value map. */ - Zigzag_persistence pers_; /**< Class computing the pairs. */ + Zigzag_persistence pers_; /**< Class computing the pairs. */ }; // end class Filtered_zigzag_persistence } // namespace zigzag_persistence diff --git a/src/common/doc/main_page.md b/src/common/doc/main_page.md index 334746b319..dfc3840b1d 100644 --- a/src/common/doc/main_page.md +++ b/src/common/doc/main_page.md @@ -294,6 +294,29 @@ +### Oscillating Rips Filtrations + + + + + + + + + +
+ \image html "osc_rips.svg" width=100% + + Variant of the Rips filtration in which the diameter not only grows but "oscillates". The resulting filtration + is therefore a zigzag filtration.
+
+ Author: Clément Maria, Hannah Schreiber
+ Introduced in: GUDHI 3.11.x
+ License: MIT
+
+ User manual: \ref zigzagrips +
+ ## Manifold reconstructions ### Coxeter triangulation From 9a526f6f9ee3644617137aae3071542979381355 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 14 Jan 2025 18:44:19 +0100 Subject: [PATCH 14/21] biblio misplaced --- biblio/bibliography.bib | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/biblio/bibliography.bib b/biblio/bibliography.bib index 5e693c064a..4f8b290913 100644 --- a/biblio/bibliography.bib +++ b/biblio/bibliography.bib @@ -267,18 +267,6 @@ @book{Munkres-elementsalgtop1984 bibsource = {DBLP, http://dblp.uni-trier.de} } -@inproceedings{osc_zz, -author = {Oudot, Steve and Sheehy, Donald}, -year = {2013}, -month = {06}, -pages = {387-396}, -title = {{Zigzag Zoology: Rips Zigzags for Homology Inference}}, -volume = {15}, - booktitle = {Foundations of Computational Mathematics}, -url = {https://doi.org/10.1145/2462356.2462371}, -doi = {10.1145/2462356.2462371} -} - @article{Cohen-Steiner2009, author = {Cohen-Steiner, David and Edelsbrunner, Herbert and Harer, John}, journal = {Foundations of Computational Mathematics}, @@ -817,6 +805,18 @@ @inproceedings{zigzag doi = {10.1137/1.9781611973730.14} } +@inproceedings{osc_zz, + author = {Oudot, Steve and Sheehy, Donald}, + year = {2013}, + month = {06}, + pages = {387-396}, + title = {{Zigzag Zoology: Rips Zigzags for Homology Inference}}, + volume = {15}, + booktitle = {Foundations of Computational Mathematics}, + url = {https://doi.org/10.1145/2462356.2462371}, + doi = {10.1145/2462356.2462371} +} + %----------------------------- % UNUSED %----------------------------- From aac903d623d210afdd2647e86d050bf1da87964c Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 16 Jan 2025 15:46:06 +0100 Subject: [PATCH 15/21] doc + removal of unnecessary flag in edge modifier --- src/Zigzag_persistence/concept/EdgeModifier.h | 7 +- src/Zigzag_persistence/concept/PointRange.h | 6 - .../concept/StableFilteredComplex.h | 8 +- .../concept/ZigzagOptions.h | 2 + .../example_oscillating_rips_persistence.cpp | 6 + .../gudhi/Oscillating_rips_persistence.h | 29 ++- .../oscillating_rips_iterators.h | 198 +++++++++--------- 7 files changed, 142 insertions(+), 114 deletions(-) diff --git a/src/Zigzag_persistence/concept/EdgeModifier.h b/src/Zigzag_persistence/concept/EdgeModifier.h index c3bdaca3a8..c37cf6ee0c 100644 --- a/src/Zigzag_persistence/concept/EdgeModifier.h +++ b/src/Zigzag_persistence/concept/EdgeModifier.h @@ -20,16 +20,11 @@ namespace zigzag_persistence { /** * @brief Methods whose purposes are to modify the filtration value of a given edge following a rule. - * The concept is for example realized by @ref Square_root_edge_modifier. + * The concept is for example realized by @ref Identity_edge_modifier or @ref Square_root_edge_modifier "". */ template class EdgeModifier { public: - /** - * @brief Indicates that the modifier should not be ignored. - */ - static constexpr bool isActive_ = true; - /** * @brief Applies the modifier to the given value and returns it. * diff --git a/src/Zigzag_persistence/concept/PointRange.h b/src/Zigzag_persistence/concept/PointRange.h index 5f77ba498c..b488b97bf1 100644 --- a/src/Zigzag_persistence/concept/PointRange.h +++ b/src/Zigzag_persistence/concept/PointRange.h @@ -32,22 +32,16 @@ class PointRange { public: /** * @brief Returns begin iterator. - * - * @return Begin iterator. */ auto begin(); /** * @brief Returns end iterator. - * - * @return End iterator. */ auto end(); /** * @brief Returns size of the range. - * - * @return Size of the range. */ std::size_t size(); diff --git a/src/Zigzag_persistence/concept/StableFilteredComplex.h b/src/Zigzag_persistence/concept/StableFilteredComplex.h index 7f638ad811..ed92d02859 100644 --- a/src/Zigzag_persistence/concept/StableFilteredComplex.h +++ b/src/Zigzag_persistence/concept/StableFilteredComplex.h @@ -32,7 +32,7 @@ class StableFilteredComplex { /** * @brief Handle to specify a simplex. The simplex handles have to be stable, that is, they do not invalidate when - * a simplex is added or removed from the complex (except for the removed simplices them selves of course). + * a simplex is added or removed from the complex (except for the removed simplices them-selves of course). */ typename Simplex_handle; @@ -98,6 +98,12 @@ class StableFilteredComplex { * @return A iterable range over the star of @p simplex. */ auto star_simplex_range(const Simplex_handle simplex); + + /** + * @brief Returns a range over the vertices of a simplex. The vertices have to be ordered monotonously by their + * labels. + */ + auto simplex_vertex_range(Simplex_handle sh) const; }; } // namespace zigzag_persistence diff --git a/src/Zigzag_persistence/concept/ZigzagOptions.h b/src/Zigzag_persistence/concept/ZigzagOptions.h index acdb047b82..ac71f5b46a 100644 --- a/src/Zigzag_persistence/concept/ZigzagOptions.h +++ b/src/Zigzag_persistence/concept/ZigzagOptions.h @@ -24,6 +24,8 @@ namespace zigzag_persistence { * * @brief List of options used for the filtered zigzag persistence computation. An implementation of this concept is * @ref Default_filtered_zigzag_options "", which inherits from @ref Default_zigzag_options for the common types. + * Another implementation is + * @ref Gudhi::zigzag_persistence::Default_oscillating_rips_zigzag_options "Default_oscillating_rips_zigzag_options". */ struct FilteredZigzagOptions { /** diff --git a/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp b/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp index cfb9724915..8ff95ae0ba 100644 --- a/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp +++ b/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp @@ -19,6 +19,7 @@ using Filtration_value = double; using Point = std::vector; using Barcode = std::vector >; +//prints computed zigzag barcode void print_barcode(const Barcode& bars) { std::clog << "Resulting barcode:" << std::endl; for (auto& bar : bars) { @@ -26,6 +27,7 @@ void print_barcode(const Barcode& bars) { } } +//prints initial points void print_points(const std::vector& points) { std::clog << "Number of points: " << points.size() << std::endl; for (const Point& p : points) { @@ -34,6 +36,7 @@ void print_points(const std::vector& points) { std::clog << std::endl; } +//computes some random 2-dimensional point cloud std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { std::vector finalPoints; std::set points; @@ -56,6 +59,9 @@ std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { } int main(int argc, char* const argv[]) { + //nu, mu: lower and upper multiplier, see introduction page. + //max_dim: maximal dimension to which the complex should be extended. If -1: no limits. + //seed (optional): fixes seed for the randomly computed point cloud. if (argc != 5 && argc != 6) { std::clog << "Usage: ./comp nu mu max_dim numberOfPoints [seed]" << std::endl; return 0; diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index a353357595..2dd5ec1059 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -66,17 +66,30 @@ struct Simplex_tree_options_oscillating_rips { static const bool stable_simplex_handles = true; }; +/** + * @ingroup zigzag_persistence + * + * @brief Filtered zigzag options such that the class directly uses the simplex handles of the complex of the + * oscillating Rips iterator as cell IDs. More or less assumes that the complex is a @ref Gudhi::Simplex_tree on + * the way a handle accesses its key. + */ template struct Default_oscillating_rips_zigzag_options : Default_filtered_zigzag_options { - using Cell_key = typename StableFilteredComplex::Simplex_handle; - using Filtration_value = typename StableFilteredComplex::Filtration_value; - using Dimension = int; // it is `int` in the simplex tree - struct Hash { + using Cell_key = typename StableFilteredComplex::Simplex_handle; /**< Cell IDs are simplex handles. */ + using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ + using Dimension = int; /**< As in the @ref Simplex_tree "". */ + /** + * @brief Hash method for simplex handles from the @ref Simplex_tree "". + */ + struct Cell_key_hash { std::size_t operator()(const Cell_key& sh) const { return sh->second.key(); } }; - struct KeyEqual { + /** + * @brief Equality method for simplex handles from the @ref Simplex_tree "". + */ + struct Cell_key_equal { bool operator()(const Cell_key& sh1, const Cell_key& sh2) const { return sh1->second.key() == sh2->second.key(); } @@ -100,7 +113,8 @@ struct Default_oscillating_rips_zigzag_options : Default_filtered_zigzag_options * @tparam F Callback method type for the zigzag barcode output. * @tparam edge_range_type Either @ref Edge_range_type::BOOST_RANGE or @ref Edge_range_type::VECTOR. * Default value: @ref Edge_range_type::BOOST_RANGE. - * @tparam StableFilteredComplex Data structure to store and build the computed complex at each step. + * @tparam StableFilteredComplex A version of the @ref Simplex_tree "". Used to store and build the computed complex + * at each step. * @param points Point cloud. * @param nu Lower multiplier. * @param mu Upper multiplier. @@ -182,7 +196,8 @@ void compute_oscillating_rips_persistence( * @tparam PointRange Range containing the point cloud. * @tparam edge_range_type Either Edge_range_type::BOOST_RANGE or Edge_range_type::VECTOR. * Default value: Edge_range_type::BOOST_RANGE. - * @tparam StableFilteredComplex Data structure to store and build the computed complex at each step. + * @tparam StableFilteredComplex A version of the @ref Simplex_tree "". Used to store and build the computed complex + * at each step. * @param points Point cloud. * @param nu Lower multiplier. * @param mu Upper multiplier. diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h index 30edfc4c31..f0e947739e 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h @@ -125,7 +125,7 @@ class Zigzag_edge } friend std::ostream &operator<<(std::ostream &stream, const Zigzag_edge &ze) { - stream << std::setprecision(6); + // stream << std::setprecision(6); // stream << std::setprecision(std::numeric_limits::digits); stream << "(" << ze.u_ << ", " << ze.v_<< ") "; if (ze.direction_){ @@ -143,8 +143,6 @@ class Zigzag_edge bool direction_; /**< Direction. True = forward, false = backward. */ }; -//TODO: remove the isActive_ members from the edge modifiers. - /** * @class Identity_edge_modifier oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h * @brief Identity modifier, i.e., does nothing. @@ -154,7 +152,17 @@ class Zigzag_edge class Identity_edge_modifier { public: - static constexpr bool isActive_ = false; /**< Indicates that the modifier should be ignored. */ + /** + * @brief Returns the given value. + */ + template + static Filtration_value apply_modifier(Filtration_value f) { return f; } + + /** + * @brief Returns the given value. + */ + template + static Filtration_value apply_inverse_modifier(Filtration_value f) { return f; } private: /** @@ -194,8 +202,6 @@ class Square_root_edge_modifier */ static Filtration_value apply_inverse_modifier(Filtration_value f) { return f * f; } - static constexpr bool isActive_ = true; /**< Indicates that the modifier should not be ignored. */ - private: /** * @brief Default constructor. Should not be called and therefore private. @@ -204,8 +210,10 @@ class Square_root_edge_modifier }; /** - * @brief Order policy for the points. - */ + * @brief Order policy for the points. + * + * @ingroup zigzag_persistence + */ enum Oscillating_rips_edge_order_policy { ALREADY_ORDERED, /**< The given range of points is considered ordered. */ FARTHEST_POINT_ORDERING, /**< The points are reordered using @ref Gudhi::subsampling::choose_n_farthest_points.*/ @@ -238,7 +246,7 @@ class Oscillating_rips_edge_range { public: /** - * @class Oscillating_rips_edge_iterator oscillating_rips_iterators.h + * @class Oscillating_rips_edge_iterator oscillating_rips_iterators.h \ * gudhi/Zigzag_persistence/oscillating_rips_iterators.h * @brief Custom iterator over the edges of an oscillating rips filtration. * @@ -328,11 +336,8 @@ class Oscillating_rips_edge_range "The number of points and the number of epsilon values should match."); GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); - //TODO: remove this if, the two method could just do nothing for the identity modifier. - if constexpr (EdgeModifier::isActive_) { - nu_ = EdgeModifier::apply_inverse_modifier(nu); - mu_ = EdgeModifier::apply_inverse_modifier(mu); - } + nu_ = EdgeModifier::apply_inverse_modifier(nu); + mu_ = EdgeModifier::apply_inverse_modifier(mu); // compute the distance matrix distanceMatrix_ = _compute_distance_matrix(orderedPoints, distance); @@ -367,9 +372,9 @@ class Oscillating_rips_edge_range std::vector > > distanceMatrix_; /**< Distance matrix. */ Filtration_value nu_; /**< Lower multiplier. */ Filtration_value mu_; /**< Upper multiplier. */ - Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ + Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ std::size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ - bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ''edge'' is a vertex. */ + bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ''edge'' is a vertex. */ /** * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if to iterators are equal. @@ -545,13 +550,10 @@ class Oscillating_rips_edge_range */ void _update_edge(std::size_t i, bool direction) { - if constexpr (EdgeModifier::isActive_) - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, - rowIndex_, - EdgeModifier::apply_modifier(epsilonValues_[i]), - direction); - else - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, rowIndex_, epsilonValues_[i], direction); + currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, + rowIndex_, + EdgeModifier::apply_modifier(epsilonValues_[i]), + direction); } /** @@ -559,11 +561,8 @@ class Oscillating_rips_edge_range */ void _update_edge_as_positive_vertex() { - if constexpr (EdgeModifier::isActive_) - currentEdge_.set( - epsilonIndex_ + 1, epsilonIndex_ + 1, EdgeModifier::apply_modifier(epsilonValues_[epsilonIndex_]), true); - else - currentEdge_.set(epsilonIndex_ + 1, epsilonIndex_ + 1, epsilonValues_[epsilonIndex_], true); + currentEdge_.set( + epsilonIndex_ + 1, epsilonIndex_ + 1, EdgeModifier::apply_modifier(epsilonValues_[epsilonIndex_]), true); } /** @@ -587,8 +586,10 @@ class Oscillating_rips_edge_range * distance function. * @param distance Distance function. Has to take two points as it from the range @p points as input parameters * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. * * @return Vector of @ref Zigzag_edge. */ @@ -608,6 +609,24 @@ class Oscillating_rips_edge_range return _compute_vector_range(nu, mu, distanceMatrix, epsilonValues); } + /** + * @brief Computes and return a vector with all edges in order of the oscillating Rips filtration. + * See the @ref zigzagrips "introduction page" for more details about the arguments. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. The order of the points should be in correspondence with the order of + * the epsilon values. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. + * And the last value should be 0. + * + * @return Vector of @ref Zigzag_edge. + */ template static std::vector > compute_vector_range( Filtration_value nu, @@ -640,8 +659,10 @@ class Oscillating_rips_edge_range * distance function. * @param distance Distance function. Has to take two points as it from the range @p points as input parameters * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. * * @return boost::iterator_range of @ref Oscillating_rips_edge_iterator. * @@ -709,19 +730,22 @@ class Oscillating_rips_edge_range * distance function. * @param distance Distance function. Has to take two points as it from the range @p points as input parameters * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. * * @return Instantiation of @ref Oscillating_rips_edge_iterator. * * @warning Avoid copying the iterator as it is heavier than usual iterators. */ template - static Oscillating_rips_edge_iterator begin(Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Oscillating_rips_edge_order_policy orderPolicy = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) + static Oscillating_rips_edge_iterator begin( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Oscillating_rips_edge_order_policy orderPolicy = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) { return Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); } @@ -795,16 +819,18 @@ class Oscillating_rips_edge_range * * @tparam PointRange Point range type. * @tparam DistanceFunction Type of the distance function. - * @param[out] nu Lower multiplier. - * @param[out] mu Upper multiplier. - * @param[out] epsilonValues Container for the epsilon values. - * @param[out] distanceMatrix Container for the distance matrices. - * @param[in] points Point cloud as a range.The format of a point has to correspond to the input format of the + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param epsilonValues Container for the epsilon values. + * @param distanceMatrix Container for the distance matrices. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the * distance function. - * @param[in] distance Distance function. Has to take two points as it from the range @p points as input parameters + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters * and return the distance between those points. - * @param[in] orderPolicy Order policy for the points. Can be either @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. */ template static void _initialize(Filtration_value& nu, @@ -821,10 +847,8 @@ class Oscillating_rips_edge_range PointRange sortedPoints; sortedPoints.reserve(n); - if constexpr (EdgeModifier::isActive_) { - nu = EdgeModifier::apply_inverse_modifier(nu); - mu = EdgeModifier::apply_inverse_modifier(mu); - } + nu = EdgeModifier::apply_inverse_modifier(nu); + mu = EdgeModifier::apply_inverse_modifier(mu); // compute epsilon values @@ -853,6 +877,21 @@ class Oscillating_rips_edge_range distanceMatrix = _compute_distance_matrix(sortedPoints, distance); } + /** + * @brief Initialize the distance function with already given epsilon values. Updates also the multipliers if + * the edge modifier is active. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param epsilonValues Container for the epsilon values. + * @param distanceMatrix Container for the distance matrices. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + */ template static void _initialize(Filtration_value& nu, Filtration_value& mu, @@ -863,15 +902,16 @@ class Oscillating_rips_edge_range { GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); - if constexpr (EdgeModifier::isActive_) { - nu = EdgeModifier::apply_inverse_modifier(nu); - mu = EdgeModifier::apply_inverse_modifier(mu); - } + nu = EdgeModifier::apply_inverse_modifier(nu); + mu = EdgeModifier::apply_inverse_modifier(mu); // compute the distance matrix distanceMatrix = _compute_distance_matrix(points, distance); } + /** + * @brief Computes and return a vector with all edges in order of the oscillating Rips filtration. + */ static std::vector > _compute_vector_range( Filtration_value nu, Filtration_value mu, @@ -900,14 +940,8 @@ class Oscillating_rips_edge_range // epsilonValues[0], true); for (std::size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i - if constexpr (EdgeModifier::isActive_) { - edgeFiltration.emplace_back(i + 1, - i + 1, - EdgeModifier::apply_modifier(epsilonValues[i]), - true); // add p_{i+1},eps_i - } else { - edgeFiltration.emplace_back(i + 1, i + 1, epsilonValues[i], true); // add p_{i+1},eps_i - } + // add p_{i+1},eps_i + edgeFiltration.emplace_back(i + 1, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); for (auto edgeIt = edgesAdded[i].begin(); edgeIt != edgesAdded[i].end(); ++edgeIt) { edgeFiltration.push_back(*edgeIt); } @@ -1092,11 +1126,7 @@ class Oscillating_rips_edge_range if (it->second <= nu * epsilonValues[i]) { break; } - if constexpr (EdgeModifier::isActive_) { - edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); - } else { - edgesAdded[i].emplace_back(it->first, j, epsilonValues[i], true); - } + edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); ++number_of_arrows; } } @@ -1112,11 +1142,7 @@ class Oscillating_rips_edge_range while (it != distanceMatrix[i + 1].begin()) { --it; - if constexpr (EdgeModifier::isActive_) { - edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); - } else { - edgesAdded[i].emplace_back(it->first, i + 1, epsilonValues[i], true); - } + edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); ++number_of_arrows; } @@ -1140,11 +1166,7 @@ class Oscillating_rips_edge_range if (it->second <= nu * epsilonValues[i + 1]) { break; } - if constexpr (EdgeModifier::isActive_) { - edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); - } else { - edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i], false); - } + edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); ++number_of_arrows; } } @@ -1174,11 +1196,7 @@ class Oscillating_rips_edge_range if (it->second <= nu * epsilonValues[i]) { break; } - if constexpr (EdgeModifier::isActive_) { - edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); - } else { - edgesAdded[i].emplace_back(it->first, j, epsilonValues[i], true); - } + edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); ++number_of_arrows; } } @@ -1193,11 +1211,7 @@ class Oscillating_rips_edge_range Point_distance_comp()); while (it != distanceMatrix[i + 1].begin()) { --it; - if constexpr (EdgeModifier::isActive_) { - edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); - } else { - edgesAdded[i].emplace_back(it->first, i + 1, epsilonValues[i], true); - } + edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); ++number_of_arrows; } @@ -1221,11 +1235,7 @@ class Oscillating_rips_edge_range if (it->second <= nu * epsilonValues[i + 1]) { break; } - if constexpr (EdgeModifier::isActive_) { - edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); - } else { - edgesRemoved[i].emplace_back(it->first, j, epsilonValues[i], false); - } + edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); ++number_of_arrows; } } @@ -1311,7 +1321,7 @@ class Oscillating_rips_edge_range }; /** - * @class Oscillating_rips_simplex_range oscillating_rips_iterators.h + * @class Oscillating_rips_simplex_range oscillating_rips_iterators.h \ * gudhi/Zigzag_persistence/oscillating_rips_iterators.h * @brief Gives access to a range of the ordered simplices in an oscillating Rips filtration. * From b69b2888a99f42eadb4e2cbb4823d2c53571bd16 Mon Sep 17 00:00:00 2001 From: hschreiber Date: Tue, 21 Jan 2025 17:08:12 +0100 Subject: [PATCH 16/21] oscillating rips: more intuitive organization + light iterators --- .../example_oscillating_rips_persistence.cpp | 29 +- .../gudhi/Oscillating_rips_persistence.h | 73 +- .../gudhi/Zigzag_persistence/Zigzag_edge.h | 136 ++ .../gudhi/Zigzag_persistence/edge_modifiers.h | 103 + .../oscillating_rips_edge_ranges.h | 1099 +++++++++++ .../oscillating_rips_iterators.h | 1673 ----------------- .../oscillating_rips_simplex_ranges.h | 575 ++++++ .../test/test_oscillating_rips.cpp | 171 +- 8 files changed, 2087 insertions(+), 1772 deletions(-) create mode 100644 src/Zigzag_persistence/include/gudhi/Zigzag_persistence/Zigzag_edge.h create mode 100644 src/Zigzag_persistence/include/gudhi/Zigzag_persistence/edge_modifiers.h create mode 100644 src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h delete mode 100644 src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h create mode 100644 src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h diff --git a/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp b/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp index 8ff95ae0ba..d53b404e17 100644 --- a/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp +++ b/src/Zigzag_persistence/example/example_oscillating_rips_persistence.cpp @@ -19,16 +19,18 @@ using Filtration_value = double; using Point = std::vector; using Barcode = std::vector >; -//prints computed zigzag barcode -void print_barcode(const Barcode& bars) { +// prints computed zigzag barcode +void print_barcode(const Barcode& bars) +{ std::clog << "Resulting barcode:" << std::endl; for (auto& bar : bars) { std::clog << bar << std::endl; } } -//prints initial points -void print_points(const std::vector& points) { +// prints initial points +void print_points(const std::vector& points) +{ std::clog << "Number of points: " << points.size() << std::endl; for (const Point& p : points) { std::clog << "(" << p[0] << ", " << p[1] << ")" << std::endl; @@ -36,8 +38,9 @@ void print_points(const std::vector& points) { std::clog << std::endl; } -//computes some random 2-dimensional point cloud -std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { +// computes some random 2-dimensional point cloud +std::vector build_point_cloud(unsigned int numberOfPoints, int seed) +{ std::vector finalPoints; std::set points; std::random_device dev; @@ -58,12 +61,14 @@ std::vector build_point_cloud(unsigned int numberOfPoints, int seed) { return finalPoints; } -int main(int argc, char* const argv[]) { - //nu, mu: lower and upper multiplier, see introduction page. - //max_dim: maximal dimension to which the complex should be extended. If -1: no limits. - //seed (optional): fixes seed for the randomly computed point cloud. +int main(int argc, char* const argv[]) +{ + // nu, mu: lower and upper multiplier, see introduction page. + // max_dim: maximal dimension to which the complex should be extended. If -1: no limits. + // seed (optional): fixes seed for the randomly computed point cloud. if (argc != 5 && argc != 6) { - std::clog << "Usage: ./comp nu mu max_dim numberOfPoints [seed]" << std::endl; + std::clog << "Usage: ./Zigzag_persistence_example_oscillating_rips_persistence nu mu max_dim numberOfPoints [seed]" + << std::endl; return 0; } @@ -84,7 +89,7 @@ int main(int argc, char* const argv[]) { std::vector points = build_point_cloud(numberOfPoints, seed); std::clog << "********** Computing oscillating rips filtration and persistence" << std::endl; - //with default templates and parameters. See documentation for more information. + // with default templates and parameters. See documentation for more information. Barcode res = Gudhi::zigzag_persistence::compute_oscillating_rips_persistence(points, nu, mu, maxDim); print_barcode(res); diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index 2dd5ec1059..d975bcf7b5 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -20,7 +20,8 @@ #include -#include +#include +#include #include #include #include @@ -75,24 +76,22 @@ struct Simplex_tree_options_oscillating_rips { */ template struct Default_oscillating_rips_zigzag_options : Default_filtered_zigzag_options { - using Cell_key = typename StableFilteredComplex::Simplex_handle; /**< Cell IDs are simplex handles. */ - using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ - using Dimension = int; /**< As in the @ref Simplex_tree "". */ + using Cell_key = typename StableFilteredComplex::Simplex_handle; /**< Cell IDs are simplex handles. */ + using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ + using Dimension = int; /**< As in the @ref Simplex_tree "". */ + /** * @brief Hash method for simplex handles from the @ref Simplex_tree "". */ struct Cell_key_hash { - std::size_t operator()(const Cell_key& sh) const { - return sh->second.key(); - } + std::size_t operator()(const Cell_key& sh) const { return sh->second.key(); } }; + /** * @brief Equality method for simplex handles from the @ref Simplex_tree "". */ struct Cell_key_equal { - bool operator()(const Cell_key& sh1, const Cell_key& sh2) const { - return sh1->second.key() == sh2->second.key(); - } + bool operator()(const Cell_key& sh1, const Cell_key& sh2) const { return sh1->second.key() == sh2->second.key(); } }; }; @@ -146,34 +145,31 @@ void compute_oscillating_rips_persistence( using Filtration_value = typename ZPOptions::Filtration_value; using Dimension = typename ZPOptions::Dimension; // always `int` for now using Bar = Gudhi::persistence_matrix::Persistence_interval; - using EdgeRange = Oscillating_rips_edge_range; + using ItEdgeRange = Oscillating_rips_edge_iterator_range; using VectorEdgeRange = std::vector >; + using EdgeRange = + typename std::conditional::type; using EdgeRangeIterator = typename std::conditional::type; - using OscillatingRipsSimplexRange = Oscillating_rips_simplex_range; + using OscillatingRipsSimplexRange = Oscillating_rips_simplex_iterator_range; StableFilteredComplex st; ZP zp(outStream); - EdgeRangeIterator start, end; - VectorEdgeRange vec; + EdgeRange edges; if constexpr (edge_range_type == Edge_range_type::BOOST_RANGE) { - start = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); - end = EdgeRange::end(); + edges = ItEdgeRange(nu, mu, points, Gudhi::Euclidean_distance(), p); } else { - vec = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - start = vec.begin(); - end = vec.end(); + edges = Oscillating_rips_edge_vector_range_constructor::make_range( + nu, mu, points, Gudhi::Euclidean_distance(), p); } - for (const auto& t : OscillatingRipsSimplexRange::get_iterator_range(start, end, st, maxDim)) { + for (const auto& t : OscillatingRipsSimplexRange(edges.begin(), edges.end(), st, maxDim)) { if (std::get<2>(t)) - zp.insert_cell(std::get<0>(t), - st.boundary_simplex_range(std::get<0>(t)), - st.dimension(std::get<0>(t)), - std::get<1>(t)); + zp.insert_cell( + std::get<0>(t), st.boundary_simplex_range(std::get<0>(t)), st.dimension(std::get<0>(t)), std::get<1>(t)); else zp.remove_cell(std::get<0>(t), std::get<1>(t)); } @@ -224,34 +220,31 @@ compute_oscillating_rips_persistence( using ZPOptions = Default_oscillating_rips_zigzag_options; using ZP = Filtered_zigzag_persistence_with_storage; using Filtration_value = typename ZPOptions::Filtration_value; - using EdgeRange = Oscillating_rips_edge_range; + using ItEdgeRange = Oscillating_rips_edge_iterator_range; using VectorEdgeRange = std::vector >; + using EdgeRange = + typename std::conditional::type; using EdgeRangeIterator = typename std::conditional::type; - using OscillatingRipsSimplexRange = Oscillating_rips_simplex_range; + using OscillatingRipsSimplexRange = Oscillating_rips_simplex_iterator_range; StableFilteredComplex st; ZP zp; - EdgeRangeIterator start, end; - VectorEdgeRange vec; + EdgeRange edges; if constexpr (edge_range_type == Edge_range_type::BOOST_RANGE) { - start = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); - end = EdgeRange::end(); + edges = ItEdgeRange(nu, mu, points, Gudhi::Euclidean_distance(), p); } else { - vec = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - start = vec.begin(); - end = vec.end(); + edges = Oscillating_rips_edge_vector_range_constructor::make_range( + nu, mu, points, Gudhi::Euclidean_distance(), p); } - for (const auto& t : OscillatingRipsSimplexRange::get_iterator_range(start, end, st, maxDim)) { + for (const auto& t : OscillatingRipsSimplexRange(edges.begin(), edges.end(), st, maxDim)) { if (std::get<2>(t)) - zp.insert_cell(std::get<0>(t), - st.boundary_simplex_range(std::get<0>(t)), - st.dimension(std::get<0>(t)), - std::get<1>(t)); + zp.insert_cell( + std::get<0>(t), st.boundary_simplex_range(std::get<0>(t)), st.dimension(std::get<0>(t)), std::get<1>(t)); else zp.remove_cell(std::get<0>(t), std::get<1>(t)); } diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/Zigzag_edge.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/Zigzag_edge.h new file mode 100644 index 0000000000..ce40a20c96 --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/Zigzag_edge.h @@ -0,0 +1,136 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Clément Maria and Hannah Schreiber + * + * Copyright (C) 2024 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +/** + * @file Zigzag_edge.h + * @author Clément Maria, Hannah Schreiber + * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Zigzag_edge class. + */ + +#ifndef ZIGZAG_ZZ_EDGE_H_ +#define ZIGZAG_ZZ_EDGE_H_ + +#include +#include +#include +#include +// #include + +// #include + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @class Zigzag_edge Zigzag_edge.h gudhi/Zigzag_persistence/Zigzag_edge.h + * @brief Edge structure for the oscillating Rips filtration. + * + * @ingroup zigzag_persistence + * + * @details The edges of the filtration are computed first and the remaining simplices are deduced from them. + * Is also used to represent the vertices for technical reasons, by giving both vertices the same value. + * + * @tparam Filtration_value Type of the filtration value. Is recommended to be easy to copy, like an arithmetic type. + */ +template +class Zigzag_edge +{ + public: + /** + * @brief Constructor. + * + * @param u First boundary vertex ID + * @param v Second boundary vertex ID. If same than @p u, the edge is considered a vertex. + * @param fil Filtration value + * @param direction If true, forwards. If false, backwards. + */ + Zigzag_edge(int u, int v, Filtration_value fil, bool direction) : u_(u), v_(v), fil_(fil), direction_(direction) + { + if (u > v) std::swap(u_, v_); + } + + /** + * @brief Default constructor. Initialize everything to zero and the direction to true. + */ + Zigzag_edge() : u_(0), v_(0), fil_(0), direction_(true) {} + + /** + * @brief Returns vertex with smaller ID. + * + * @return Smallest ID among the boundary vertices. + */ + int get_smallest_vertex() const { return u_; } + + /** + * @brief Returns vertex with bigger ID. + * + * @return Biggest ID among the boundary vertices. + */ + int get_biggest_vertex() const { return v_; } + + /** + * @brief Returns the filtration value of the edge. + * + * @return Filtration value of the edge. + */ + Filtration_value get_filtration_value() const { return fil_; } + + /** + * @brief Gives the direction of the arrow corresponding to the edge. + * + * @return True, if forward, i.e., an insertion. + * @return False, if backward, i.e., a removal. + */ + bool get_direction() const { return direction_; } + + void set(int u, int v, Filtration_value fil, bool direction) + { + u_ = u; + v_ = v; + fil_ = fil; + direction_ = direction; + } + + /** + * @brief Equality test + * + * @param e Edge to compare. + * @return True, if both edges are equal. + * @return False, if both edges are not equal. + */ + bool operator==(const Zigzag_edge& e) const + { + return ((e.u_ == u_) && (e.v_ == v_) && (e.fil_ == fil_) && (e.direction_ == direction_)); + } + + friend std::ostream& operator<<(std::ostream& stream, const Zigzag_edge& ze) + { + // stream << std::setprecision(6); + // stream << std::setprecision(std::numeric_limits::digits); + stream << "(" << ze.u_ << ", " << ze.v_ << ") "; + if (ze.direction_) { + stream << "-- " << ze.fil_ << " -->"; + } else { + stream << "<-- " << ze.fil_ << " --"; + } + return stream; + } + + private: + int u_; /**< Smaller vertex. */ + int v_; /**< Bigger vertex. */ + Filtration_value fil_; /**< Filtration value. */ + bool direction_; /**< Direction. True = forward, false = backward. */ +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // ZIGZAG_ZZ_EDGE_H_ diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/edge_modifiers.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/edge_modifiers.h new file mode 100644 index 0000000000..c815fe5dbb --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/edge_modifiers.h @@ -0,0 +1,103 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Clément Maria and Hannah Schreiber + * + * Copyright (C) 2024 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +/** + * @file edge_modifiers.h + * @author Clément Maria, Hannah Schreiber + * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Identity_edge_modifier class and + * @ref Gudhi::zigzag_persistence::Square_root_edge_modifier class. + */ + +#ifndef ZIGZAG_EDGE_MODIFIERS_H_ +#define ZIGZAG_EDGE_MODIFIERS_H_ + +#include + +#include + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @class Identity_edge_modifier edge_modifiers.h gudhi/Zigzag_persistence/edge_modifiers.h + * @brief Identity modifier, i.e., does nothing. + * + * @ingroup zigzag_persistence + */ +class Identity_edge_modifier +{ + public: + /** + * @brief Returns the given value. + */ + template + static Filtration_value apply_modifier(Filtration_value f) + { + return f; + } + + /** + * @brief Returns the given value. + */ + template + static Filtration_value apply_inverse_modifier(Filtration_value f) + { + return f; + } + + private: + /** + * @brief Default constructor. Should not be called and therefore private. + */ + Identity_edge_modifier() {} +}; + +/** + * @class Square_root_edge_modifier edge_modifiers.h gudhi/Zigzag_persistence/edge_modifiers.h + * @brief Modifier that square roots the filtration value of an edge. + * + * @ingroup zigzag_persistence + * + * @details Useful in particular when geometric computations (edge length, etc) are + * run with squared Euclidean distance for performance. + * + * @tparam Filtration_value Filtration value type. Should be compatible with `std::sqrt` and `operator*`. + */ +template +class Square_root_edge_modifier +{ + public: + /** + * @brief Returns the square root of the given value. The parameter it-self is not modified. + * + * @param f Value to modify. + * @return The modified value of @p f. + */ + static Filtration_value apply_modifier(Filtration_value f) { return std::sqrt(f); } + + /** + * @brief Returns the square of the given value. The parameter it-self is not modified. + * + * @param f Value to modify. + * @return The modified value of @p f. + */ + static Filtration_value apply_inverse_modifier(Filtration_value f) { return f * f; } + + private: + /** + * @brief Default constructor. Should not be called and therefore private. + */ + Square_root_edge_modifier() {} +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // ZIGZAG_EDGE_MODIFIERS_H_ diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h new file mode 100644 index 0000000000..b096703dd3 --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h @@ -0,0 +1,1099 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Clément Maria and Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +/** + * @file oscillating_rips_edge_ranges.h + * @author Clément Maria, Hannah Schreiber + * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Oscillating_rips_edge_order_policy enum, + * @ref Gudhi::zigzag_persistence::Oscillating_rips_edge_iterator_base class, + * @ref Gudhi::zigzag_persistence::Oscillating_rips_edge_iterator_range class and + * @ref Gudhi::zigzag_persistence::Oscillating_rips_edge_vector_range_constructor class. + */ + +#ifndef ZIGZAG_OSCILLATING_RIPS_EDGE_RANGES_H_ +#define ZIGZAG_OSCILLATING_RIPS_EDGE_RANGES_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef GUDHI_USE_TBB +#include +#endif + +#include +#include +#include +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @brief Order policy for the points. + * + * @ingroup zigzag_persistence + */ +enum Oscillating_rips_edge_order_policy { + ALREADY_ORDERED, /**< The given range of points is considered ordered. */ + FARTHEST_POINT_ORDERING, /**< The points are reordered using @ref Gudhi::subsampling::choose_n_farthest_points.*/ + RANDOM_POINT_ORDERING /**< The points are shuffled randomly. */ +}; + +/** + * @private + * @brief + * + * @tparam Filtration_value + */ +template +class Oscillating_rips_initializer +{ + public: + /** + * @brief Initialize the distance function and epsilon values. Updates also the multipliers if + * the edge modifier is active. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param epsilonValues Container for the epsilon values. + * @param distanceMatrix Container for the distance matrices. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + */ + template + static void initialize(std::vector& epsilonValues, + std::vector > >& distanceMatrix, + const PointRange& points, + DistanceFunction&& distance, + Oscillating_rips_edge_order_policy orderPolicy) + { + std::size_t n = points.size(); // number of points + PointRange sortedPoints; + sortedPoints.reserve(n); + + // compute epsilon values + if (orderPolicy == Oscillating_rips_edge_order_policy::ALREADY_ORDERED) { + sortedPoints.assign(points.begin(), points.end()); + epsilonValues = _compute_epsilon_values(sortedPoints, distance); + } else if (orderPolicy == Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) { + epsilonValues.reserve(n); + Gudhi::subsampling::choose_n_farthest_points(distance, + points, + n, // final size + 0, // starting point + std::back_inserter(sortedPoints), + std::back_inserter(epsilonValues)); + // need to shift values output by subsampling: + for (unsigned int i = 1; i < n; ++i) { + epsilonValues[i - 1] = epsilonValues[i]; + } + epsilonValues[n - 1] = 0; + } else { + Gudhi::subsampling::pick_n_random_points(points, n, std::back_inserter(sortedPoints)); + epsilonValues = _compute_epsilon_values(sortedPoints, distance); + } + + // compute the distance matrix + distanceMatrix = _compute_distance_matrix(sortedPoints, distance); + } + + /** + * @brief Initialize the distance function and epsilon values. Updates also the multipliers if + * the edge modifier is active. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param epsilonValues Container for the epsilon values. + * @param distanceMatrix Container for the distance matrices. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + */ + template + static void initialize(std::vector > >& distanceMatrix, + const PointRange& sortedPoints, + DistanceFunction&& distance) + { + distanceMatrix = _compute_distance_matrix(sortedPoints, distance); + } + + /** + * @brief The two input types std::pair encode pairs + * @f$(j, d(p_i,p_j))@f$ and @f$(k, d(p_i,p_k))@f$ for some fixed point @f$p_i@f$. + * The operator() orders edges by length. By convention, if lengths are equal, + * it orders pairs by taking the smaller vertex label between @f$j@f$ and @f$k@f$. + */ + struct Point_distance_comp { + bool operator()(const std::pair& p1, const std::pair& p2) const + { + { + if (p1.second != p2.second) { + return p1.second < p2.second; // shorter first + } + return p1.first < p2.first; + } + } + }; + + private: + /** + * @brief Compute the epsilon values for an ordered set of points, measuring the + * sparsity of the ordering. + * + * @details Let \f$P = \{p_0, \ldots, p_{n-1}\f$ be the ordered set of points. Then + * the method sets eps_range[i]<\CODE> with the value \$f\varepsilon_i\$f, + * defined as \f$\varepsilon_i = d_H(P_i,P)\f$, the Hausdorff between the points + * \f$P_i= \{p_0, \ldots, p_{i}\}\f$ and the entire point cloud + * \f$P = \{p_0, \ldots, p_{n-1}\}\f$. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param sortedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * + * @return Vector of decreasing epsilon values ending with 0. + */ + template + static std::vector _compute_epsilon_values(const PointRange& sortedPoints, + DistanceFunction&& distance) + { + std::size_t n = sortedPoints.size(); + std::vector eps_range(n, std::numeric_limits::infinity()); + + // compute all \f$\varepsilon_i\f$ values, such that eps_range[i] == + // eps_i==d_H(P_i,P), for i=0 ... n-1: + for (std::size_t i = 0; i < n; ++i) { + // entering step i, maintain eps_range[j] = eps_j for j= i. +#ifdef GUDHI_USE_TBB + tbb::parallel_for(std::size_t(i + 1), n, [&](std::size_t k) { + // set eps_range[k] <- d(p_k, P_i) == + // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. + double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); + if (dist_i_k < eps_range[k]) { + eps_range[k] = dist_i_k; + } + }); +#else + for (std::size_t k = i + 1; k < n; ++k) { + // set eps_range[k] <- d(p_k, P_i) == + // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. + double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); + if (dist_i_k < eps_range[k]) { + eps_range[k] = dist_i_k; + } + } +#endif + // we have now eps_range[k] = d(p_k, P_i) for k > i. + // to do: implement parallel version by dividing the vector + // set eps_range[i] <- eps_i = d_H(P_i,P) = max_{k>i} d(p_k, P_i) + double eps_i = 0.; + for (std::size_t k = i + 1; k < n; ++k) { + if (eps_range[k] > eps_i) { + eps_i = eps_range[k]; + } + } + eps_range[i] = eps_i; + } + + return eps_range; + } + + /** + * @brief Returns the sparse distance matrix. A cell is represented as a pair of its row index and its value, + * i.e., @f$(j, d(p_i,p_j))@f$ is stored in column @f$i@f$ to indicate that the distance of the @f$i^{th}@f$ + * point has distance @f$d(p_i,p_j)@f$ from the @f$j^{th}@f$ point. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param sortedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * + * @return A vector of vectors of pairs of row indices and distances. + */ + template + static std::vector > > _compute_distance_matrix( + const PointRange& sortedPoints, + DistanceFunction&& distance) + { + std::vector > > distanceMatrix(sortedPoints.size()); +#ifdef GUDHI_USE_TBB + tbb::parallel_for(std::size_t(0), sortedPoints.size(), [&](std::size_t i) { + // distanceMatrix[i] = std::vector< std::pair >(); + distanceMatrix[i].resize(i); + for (std::size_t j = 0; j < i; ++j) { + distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); + } + // distanceMatrix[i] is sorted by (j, d(p_i,p_j)) < (k, d(p_i,p_k)) iff + // d(p_i,p_j) < d(p_i,p_k) or (j >(); + distanceMatrix[i].resize(i); + for (std::size_t j = 0; j < i; ++j) { + distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); + } + std::stable_sort(distanceMatrix[i].begin(), distanceMatrix[i].end(), Point_distance_comp()); + } +#endif + + return distanceMatrix; + } +}; + +/** + * @class Oscillating_rips_edge_iterator oscillating_rips_edge_ranges.h \ + * gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h + * @brief Custom iterator over the edges of an oscillating rips filtration. + * + * It inherits from boost::iterator_facade. + * + * The iterator stores the distance matrix and epsilon values to compute the current edge on the fly + * at each increment. + * + * @warning As the iterator stores possibly large ranges, avoid copying it. + */ +template +class Oscillating_rips_edge_iterator_base +{ + public: + /** + * @brief Constructor. Computes the distance matrix and the epsilon values it-self from the given parameters. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING "", + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING "". + */ + Oscillating_rips_edge_iterator_base( + Filtration_value nu, + Filtration_value mu, + const std::vector* epsilonValues, + const std::vector > >* distanceMatrix) + : epsilonValues_(epsilonValues), + distanceMatrix_(distanceMatrix), + nu_(EdgeModifier::apply_inverse_modifier(nu)), + mu_(EdgeModifier::apply_inverse_modifier(mu)), + currentEdge_(0, 0, std::numeric_limits::infinity(), true), + epsilonIndex_(0), + rowIndex_(1), + inPositiveDirection_(true), + insertVertex_(true) + { + const auto& row = (*distanceMatrix_)[1]; + auto it = std::upper_bound( + row.begin(), + row.end(), + std::pair(distanceMatrix_->size(), mu_ * (*epsilonValues_)[epsilonIndex_]), + typename Oscillating_rips_initializer::Point_distance_comp()); + columnIndex_ = it - row.begin(); + } + + /** + * @brief Default constructor. Corresponds to the end iterator. + */ + Oscillating_rips_edge_iterator_base() + : epsilonValues_(nullptr), + distanceMatrix_(nullptr), + nu_(0), + mu_(0), + currentEdge_(0, 0, 0, true), + epsilonIndex_(0), + rowIndex_(0), + columnIndex_(0), + inPositiveDirection_(true), + insertVertex_(true) + { + } + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if to iterators are equal. + * + * @param other Iterator to compare. + * @return True, the iterators are pointing to the same position. + * @return False, otherwise. + */ + bool equal(Oscillating_rips_edge_iterator_base const& other) const + { + return rowIndex_ == other.rowIndex_ && currentEdge_ == other.currentEdge_; + } + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Returns the value of the dereferenced iterator. + * + * @return Current edge. + */ + const Zigzag_edge& dereference() const { return currentEdge_; } + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. + * + */ + void increment() + { + auto size = distanceMatrix_->size(); + if (epsilonIndex_ < size - 1) { + if (insertVertex_) { + _update_edge_as_positive_vertex(); + insertVertex_ = false; + return; + } + + if (inPositiveDirection_ && _positive_col_index_is_not_valid()) { + while (_positive_col_index_is_not_valid() && rowIndex_ <= epsilonIndex_) { + ++rowIndex_; + _initialize_positive_col_index(); + } + if (_positive_col_index_is_not_valid()) { + _initialize_negative_col_index(); + inPositiveDirection_ = false; + } + } + + if (!inPositiveDirection_ && _negative_col_index_is_not_valid()) { + while (_negative_col_index_is_not_valid() && rowIndex_ > 1) { + --rowIndex_; + _initialize_negative_col_index(); + } + if (_negative_col_index_is_not_valid()) { + _initialize_positive_col_index(); + ++epsilonIndex_; + inPositiveDirection_ = true; + if (epsilonIndex_ == size - 1) { + // _set_end(); + rowIndex_ = size; + _update_edge_as_negative_vertex(); + return; + } + _update_edge_as_positive_vertex(); + return; + } + } + + if (inPositiveDirection_) { + --columnIndex_; + _update_edge(epsilonIndex_, true); + while (_positive_col_index_is_not_valid() && rowIndex_ <= epsilonIndex_) { + ++rowIndex_; + _initialize_positive_col_index(); + } + if (_positive_col_index_is_not_valid()) { + ++rowIndex_; + } + if (rowIndex_ == epsilonIndex_ + 2) { + inPositiveDirection_ = false; + --rowIndex_; + _initialize_negative_col_index(); + } + return; + } + + _update_edge(epsilonIndex_, false); + ++columnIndex_; + while (_negative_col_index_is_not_valid() && rowIndex_ > 1) { + --rowIndex_; + _initialize_negative_col_index(); + } + if (_negative_col_index_is_not_valid()) { + --rowIndex_; + } + if (rowIndex_ == 0) { + ++epsilonIndex_; + if (epsilonIndex_ == size - 1) { + rowIndex_ = size + 1; + return; + } + insertVertex_ = true; + inPositiveDirection_ = true; + ++rowIndex_; + _initialize_positive_col_index(); + } + return; + } + if (rowIndex_ > 1) { + --rowIndex_; + _update_edge_as_negative_vertex(); + return; + } + + _set_end(); + } + + private: + const std::vector* epsilonValues_; /**< Epsilon values. */ + const std::vector > >* distanceMatrix_; /**< Distance matrix. */ + const Filtration_value nu_; /**< Lower multiplier. */ + const Filtration_value mu_; /**< Upper multiplier. */ + Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ + std::size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ + bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ''edge'' is a vertex. */ + + /** + * @brief Set the iterator as the end iterator. + */ + void _set_end() + { + rowIndex_ = 0; + currentEdge_ = Zigzag_edge(); + } + + /** + * @brief Initialize the column index for a new sequence of positive edges. + */ + void _initialize_positive_col_index() + { + const auto& row = (*distanceMatrix_)[rowIndex_]; + auto it = std::upper_bound( + row.begin(), + row.end(), + std::pair(distanceMatrix_->size(), mu_ * (*epsilonValues_)[epsilonIndex_]), + typename Oscillating_rips_initializer::Point_distance_comp()); + columnIndex_ = it - row.begin(); + } + + /** + * @brief Initialize the column index for a new sequence of negative edges. + */ + void _initialize_negative_col_index() + { + const auto& row = (*distanceMatrix_)[rowIndex_]; + const auto eps = (*epsilonValues_)[epsilonIndex_ + 1]; + auto it = std::lower_bound(row.begin(), + row.end(), + std::pair(0, nu_ * eps), + typename Oscillating_rips_initializer::Point_distance_comp()); + while (it != row.end() && it->second == nu_ * eps) ++it; + columnIndex_ = it - row.begin(); + } + + /** + * @brief Indicates if the column index needs to be initialized or not. + */ + bool _positive_col_index_is_not_valid() + { + return columnIndex_ == 0 || + (rowIndex_ != (epsilonIndex_ + 1) && + (*distanceMatrix_)[rowIndex_][columnIndex_ - 1].second <= nu_ * (*epsilonValues_)[epsilonIndex_]); + } + + /** + * @brief Indicates if the column index needs to be initialized or not. + */ + bool _negative_col_index_is_not_valid() + { + const auto& row = (*distanceMatrix_)[rowIndex_]; + return columnIndex_ == row.size() || row[columnIndex_].second > mu_ * (*epsilonValues_)[epsilonIndex_]; + } + + /** + * @brief Updates the current edge. + * + * @param i Epsilon value index. + * @param direction Direction. + */ + void _update_edge(std::size_t i, bool direction) + { + currentEdge_.set((*distanceMatrix_)[rowIndex_][columnIndex_].first, + rowIndex_, + EdgeModifier::apply_modifier((*epsilonValues_)[i]), + direction); + } + + /** + * @brief Update the current edge as a vertex to insert. + */ + void _update_edge_as_positive_vertex() + { + currentEdge_.set( + epsilonIndex_ + 1, epsilonIndex_ + 1, EdgeModifier::apply_modifier((*epsilonValues_)[epsilonIndex_]), true); + } + + /** + * @brief Update the current edge as a vertex to remove. + */ + void _update_edge_as_negative_vertex() + { + currentEdge_.set(rowIndex_ - 1, rowIndex_ - 1, -std::numeric_limits::infinity(), false); + } +}; + +/** + * @class Oscillating_rips_edge_iterator_range oscillating_rips_edge_ranges.h + * gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h + * @brief Gives access and computes different edge range types for an oscillating Rips filtration. + * + * @ingroup zigzag_persistence + * + * @details There are two different kind of ranges: a vector range containing all computed edges and + * a Boost range made from a custom iterator computing an edge on the fly at each increment. + * The custom iterator is therefore only a forward iterator and can only be incremented. + * + * @tparam Filtration_value Filtration value type. Should be compatible with the edge modifier. + * @tparam EdgeModifier Modifier for the edge filtration values. If no modifications are wanted, + * use @ref Identity_edge_modifier. Default value: @ref Identity_edge_modifier. + * + * @warning As the custom iterator used for the Boost ranges stores possibly large ranges, avoid copying it. + * Use @ref get_iterator_range, @ref begin and @ref end wisely. If one needs to do something else than to simply + * iterate over the edges in order, then the vector range is for sure the better option. + */ +template +class Oscillating_rips_edge_iterator_range +{ + public: + /** + * @class Oscillating_rips_edge_iterator oscillating_rips_edge_ranges.h \ + * gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h + * @brief Custom iterator over the edges of an oscillating rips filtration. + * + * It inherits from boost::iterator_facade. + * + * The iterator stores the distance matrix and epsilon values to compute the current edge on the fly + * at each increment. + * + * @warning As the iterator stores possibly large ranges, avoid copying it. + */ + class Oscillating_rips_edge_iterator : public boost::iterator_facade&, + boost::forward_traversal_tag> + { + public: + /** + * @brief Constructor. Takes already computed epsilon values as input, but assumes that the points are + * already ordered accordingly. Assumes also that the last epsilon value is 0. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. The order of the points should be in correspondence with the order of + * the epsilon values. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. + * And the last value should be 0. + */ + Oscillating_rips_edge_iterator(Oscillating_rips_edge_iterator_base* base) + : base_iterator_(base) + { + } + + Oscillating_rips_edge_iterator() : base_iterator_(nullptr) {} + + private: + // mandatory for the boost::iterator_facade inheritance. + friend class boost::iterator_core_access; + + /** + * @brief Pointer to heavy iterator, to avoid copying it. + */ + std::shared_ptr > base_iterator_; + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if to iterators are equal. + * + * @param other Iterator to compare. + * @return True, the iterators are pointing to the same position. + * @return False, otherwise. + */ + bool equal(Oscillating_rips_edge_iterator const& other) const + { + if (base_iterator_ == nullptr) { + if (other.base_iterator_ == nullptr) return true; + return other.base_iterator_->equal(Oscillating_rips_edge_iterator_base()); + } + return base_iterator_->equal(*other.base_iterator_); + } + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Returns the value of the dereferenced iterator. + * + * @return Current edge. + */ + const Zigzag_edge& dereference() const { return base_iterator_->dereference(); } + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. + */ + void increment() { base_iterator_->increment(); } + }; + + Oscillating_rips_edge_iterator_range() + : nu_(0), mu_(0) + {} + + template + Oscillating_rips_edge_iterator_range( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Oscillating_rips_edge_order_policy orderPolicy = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) + : nu_(nu), mu_(mu) + { + GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); + Oscillating_rips_initializer::initialize( + epsilonValues_, distanceMatrix_, points, distance, orderPolicy); + } + + template + Oscillating_rips_edge_iterator_range(Filtration_value nu, + Filtration_value mu, + const PointRange& orderedPoints, + DistanceFunction&& distance, + const std::vector& epsilonValues) + : epsilonValues_(epsilonValues), nu_(nu), mu_(mu) + { + GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); + Oscillating_rips_initializer::initialize(distanceMatrix_, orderedPoints, distance); + } + + /** + * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. + * See the @ref zigzagrips "introduction page" for more details about the arguments. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + * + * @return Instantiation of @ref Oscillating_rips_edge_iterator. + * + * @warning Avoid copying the iterator as it is heavier than usual iterators. + */ + Oscillating_rips_edge_iterator begin() + { + // shared pointer on the other side will take ownership + // enables begin() to be called several times without invalidating precedent iterators + // still have the inconvenience that all copies of a same iterator (sharing the same base) increment simultaneously + return Oscillating_rips_edge_iterator(new Oscillating_rips_edge_iterator_base( + nu_, mu_, &epsilonValues_, &distanceMatrix_)); + } + + /** + * @brief Returns the end iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. + * + * @return Default instantiation of @ref Oscillating_rips_edge_iterator. + */ + Oscillating_rips_edge_iterator end() { return endIt_; } + + void set_multipliers(Filtration_value nu, Filtration_value mu) + { + nu_ = nu; + mu_ = mu; + } + + private: + std::vector epsilonValues_; /**< Epsilon values. */ + std::vector > > distanceMatrix_; /**< Distance matrix. */ + Filtration_value nu_; /**< Lower multiplier. */ + Filtration_value mu_; /**< Upper multiplier. */ + inline static const Oscillating_rips_edge_iterator endIt_ = + Oscillating_rips_edge_iterator(new Oscillating_rips_edge_iterator_base()); +}; + +template +class Oscillating_rips_edge_vector_range_constructor +{ + public: + template + static std::vector > make_range( + Filtration_value nu, + Filtration_value mu, + const PointRange& points, + DistanceFunction&& distance, + Oscillating_rips_edge_order_policy orderPolicy = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) + { + GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); + + std::vector epsilonValues; + std::vector > > distanceMatrix; + + Oscillating_rips_initializer::initialize( + epsilonValues, distanceMatrix, points, distance, orderPolicy); + + return _compute_vector_range(EdgeModifier::apply_inverse_modifier(nu), + EdgeModifier::apply_inverse_modifier(mu), + distanceMatrix, + epsilonValues); + } + + template + static std::vector > make_range(Filtration_value nu, + Filtration_value mu, + const PointRange& orderedPoints, + DistanceFunction&& distance, + const std::vector& epsilonValues) + { + GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); + GUDHI_CHECK( + orderedPoints.size() == epsilonValues.size(), + std::invalid_argument("Epsilon values should be initialized and have the same size than the point container.")); + + std::vector > > distanceMatrix; + + Oscillating_rips_initializer::initialize(distanceMatrix, orderedPoints, distance); + + return _compute_vector_range(EdgeModifier::apply_inverse_modifier(nu), + EdgeModifier::apply_inverse_modifier(mu), + distanceMatrix, + epsilonValues); + } + + private: + /** + * @brief Computes and return a vector with all edges in order of the oscillating Rips filtration. + */ + static std::vector > _compute_vector_range( + Filtration_value nu, + Filtration_value mu, + const std::vector > >& distanceMatrix, + const std::vector& epsilonValues) + { + std::vector > edgeFiltration; + auto n = epsilonValues.size(); + + // edgesAdded[i] (resp. edgesRemoved[i]) == list of edges (i,j), with j > > edgesAdded, edgesRemoved; + + std::size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); + + // Now, sort edges according to lengths, and put everything in edgeFiltration + edgeFiltration.clear(); + edgeFiltration.reserve(number_of_arrows + n); // count edges + vertices additions + + // initialise R({p_0}, +infinity) + edgeFiltration.emplace_back(0, + 0, // add vertex p_0,+infty + std::numeric_limits::infinity(), + true); + // epsilonValues[0], true); + + for (std::size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i + // add p_{i+1},eps_i + edgeFiltration.emplace_back(i + 1, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); + for (auto edgeIt = edgesAdded[i].begin(); edgeIt != edgesAdded[i].end(); ++edgeIt) { + edgeFiltration.push_back(*edgeIt); + } + for (auto edgeIt = edgesRemoved[i].rbegin(); // longest first + edgeIt != edgesRemoved[i].rend(); + ++edgeIt) { + edgeFiltration.push_back(*edgeIt); + } + } + for (int i = n - 1; i >= 0; --i) { + edgeFiltration.emplace_back(i, i, -std::numeric_limits::infinity(), false); + } + + _canonically_sort_edges(edgeFiltration); + + return edgeFiltration; + } + + /** + * @brief Computes the edges that are added and the edges that are removed and stores them in two separate containers. + * The edge container @f$e@f$ stores the @f$i^{th}@f$ edge induced by vertex @f$j@f$ at @f$e[j][i]@f$. + * + * @param[in] nu Lower multiplier. + * @param[in] mu Upper multiplier. + * @param[in] epsilonValues Epsilon values in decreasing order finishing with 0. + * @param[in] distanceMatrix Spare distance matrix. + * @param[out] edgesAdded Container for positive edges. + * @param[out] edgesRemoved Container for negative edges. + * + * @return Total number of edges. + */ + static std::size_t _compute_edges(Filtration_value nu, + Filtration_value mu, + const std::vector& epsilonValues, + const std::vector > >& distanceMatrix, + std::vector > >& edgesAdded, + std::vector > >& edgesRemoved) + { + std::size_t number_of_arrows = 0; + auto n = epsilonValues.size(); + edgesAdded.resize(n); + edgesRemoved.resize(n); + + // edgesAdded[i] must contain all new edges (k,j), 0 <= k < j <= i+1, + // inserted in inclusion: + // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i, p_i+1 }, mu * eps_i) + // and + // edgesRemoved[i] must contain all edges (k,j), 0 <= k < j <= i+1, + // removed in backward inclusion: + // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) +#ifdef GUDHI_USE_TBB + // no need to consider the case i=n-1 in an oscillating Rips filtration + tbb::parallel_for(std::size_t(0), n - 1, [&](std::size_t i) { + typename std::vector >::const_iterator it; + //----edgesAdded[i]: + // consider first all edges added in inclusion: + // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), + // i.e., all (p_j,p_k) with 0 <= k < j <= i with + // nu eps_i < d(p_j,p_k) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + for (std::size_t j = 1; j <= i; ++j) { + // get very first edge (k,j), over all k(n, mu * epsilonValues[i]), + typename Oscillating_rips_initializer::Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // if edge already in R({p_0, ... , p_i}, nu * eps_i), stop + if (it->second <= nu * epsilonValues[i]) { + break; + } + edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); + ++number_of_arrows; + } + } + // now consider all edges added in inclusion: + // R({p_0, ... , p_i}, mu * eps_i) -> R({p_0, ... , p_i, p_i+1}, mu * eps_i) + // i.e., all (p_j,p_i+1) with 0 <= j <= i with d(p_j,p_i+1) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + // first strictly longer edge + it = std::upper_bound(distanceMatrix[i + 1].begin(), + distanceMatrix[i + 1].end(), + std::pair(n, mu * epsilonValues[i]), + typename Oscillating_rips_initializer::Point_distance_comp()); + + while (it != distanceMatrix[i + 1].begin()) { + --it; + edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); + ++number_of_arrows; + } + + //----edgesRemoved[i]: + // consider all edges removed in + // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) + // i.e., all edges (p_k,p_j), 0<=k(n, mu * epsilonValues[i]), + typename Oscillating_rips_initializer::Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // when reading an edge in R({p_0, ... , p_{i+1}}, nu * eps_{i+1}), stop + if (it->second <= nu * epsilonValues[i + 1]) { + break; + } + edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); + ++number_of_arrows; + } + } + }); +#else // GUDHI_USE_TBB not defined + + typename std::vector >::const_iterator it; + + for (std::size_t i = 0; i < n - 1; ++i) { + //----edgesAdded[i]: + // consider first all edges added in inclusion: + // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), + // i.e., all (p_j,p_k) with 0 <= k < j <= i with + // nu eps_i < d(p_j,p_k) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + for (std::size_t j = 1; j <= i; ++j) { + // get very first edge (k,j), over all k(n, mu * epsilonValues[i]), + typename Oscillating_rips_initializer::Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // if edge already in R({p_0, ... , p_i}, nu * eps_i), stop + if (it->second <= nu * epsilonValues[i]) { + break; + } + edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); + ++number_of_arrows; + } + } + // now consider all edges added in inclusion: + // R({p_0, ... , p_i}, mu * eps_i) -> R({p_0, ... , p_i, p_i+1}, mu * eps_i) + // i.e., all (p_j,p_i+1) with 0 <= j <= i with d(p_j,p_i+1) <= mu eps_i + // these new edges get filtration value epsilonValues[i] + // first strictly longer edge + it = std::upper_bound(distanceMatrix[i + 1].begin(), + distanceMatrix[i + 1].end(), + std::pair(n, mu * epsilonValues[i]), + typename Oscillating_rips_initializer::Point_distance_comp()); + while (it != distanceMatrix[i + 1].begin()) { + --it; + edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); + ++number_of_arrows; + } + + //----edgesRemoved[i]: + // consider all edges removed in + // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) + // i.e., all edges (p_k,p_j), 0<=k(n, mu * epsilonValues[i]), + typename Oscillating_rips_initializer::Point_distance_comp()); + + while (it != distanceMatrix[j].begin()) { + --it; + // when reading an edge in R({p_0, ... , p_{i+1}}, nu * eps_{i+1}), stop + if (it->second <= nu * epsilonValues[i + 1]) { + break; + } + edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); + ++number_of_arrows; + } + } + } +#endif + + return number_of_arrows; + } + + /** + * @brief Sorts canonically the edges: as much as possible, edges should be removed in + * the reverse order of their insertion. We decide to insert shortest edges first, + * with increasing lexicographical order, and remove larger edges first, with + * decreasing lexicographic order. + * + * @param edges Edges to sort. + */ + static void _canonically_sort_edges(std::vector >& edges) + { + // filtration then dimension, then lex order for insertion + auto edge_cmp = [](const Zigzag_edge& e1, const Zigzag_edge& e2) { + if (e1.get_filtration_value() != e2.get_filtration_value()) { + return e1.get_filtration_value() < e2.get_filtration_value(); // lower fil first + } + + if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first + if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); //-> vertex of lower label + } else { + return true; //-> always vertices before edges + } + } + // e1 is an edge + if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { + return false; // e2 vertex, -> put it first + } + // both are edges, lexicographic compare + if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) { + return e1.get_smallest_vertex() < e2.get_smallest_vertex(); // lex order + } + if (e1.get_biggest_vertex() != e2.get_biggest_vertex()) { + return e1.get_biggest_vertex() < e2.get_biggest_vertex(); + } + return false; // equality + }; + + // the inverse ordering for deletions + auto inv_edge_cmp = [&](const Zigzag_edge& e1, const Zigzag_edge& e2) { + if (e1.get_smallest_vertex() == e2.get_smallest_vertex() && e1.get_biggest_vertex() == e2.get_biggest_vertex()) { + return false; // equality => false + } + return !(edge_cmp(e1, e2)); // reverse order + }; + + // sort sequences of inclusions of same filtration with edge_cmp + // sort sequences of removals of same filtration with inv_edge_cmp + auto beg = edges.begin(); + auto end = edges.begin(); + auto curr_fil = beg->get_filtration_value(); + auto curr_type = beg->get_direction(); + while (beg != edges.end()) { + while (end != edges.end() && end->get_filtration_value() == curr_fil && end->get_direction() == curr_type) { + ++end; + } + if (curr_type) { // sequence of insertions +#ifdef GUDHI_USE_TBB + tbb::parallel_sort(beg, end, edge_cmp); +#else + std::sort(beg, end, edge_cmp); +#endif + } else { // sequence of removals +#ifdef GUDHI_USE_TBB + tbb::parallel_sort(beg, end, inv_edge_cmp); +#else + std::sort(beg, end, inv_edge_cmp); +#endif + } + beg = end; + curr_fil = beg->get_filtration_value(); + curr_type = beg->get_direction(); + } + } +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // ZIGZAG_OSCILLATING_RIPS_EDGE_RANGES_H_ diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h deleted file mode 100644 index f0e947739e..0000000000 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_iterators.h +++ /dev/null @@ -1,1673 +0,0 @@ -/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. - * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. - * Author(s): Clément Maria and Hannah Schreiber - * - * Copyright (C) 2023 Inria - * - * Modification(s): - * - YYYY/MM Author: Description of the modification - */ - -/** - * @file oscillating_rips_iterators.h - * @author Clément Maria, Hannah Schreiber - * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Zigzag_edge class, - * @ref Gudhi::zigzag_persistence::Identity_edge_modifier class, - * @ref Gudhi::zigzag_persistence::Square_root_edge_modifier class, - * @ref Gudhi::zigzag_persistence::Oscillating_rips_edge_range class and - * @ref Gudhi::zigzag_persistence::Oscillating_rips_simplex_range class. - */ - -#ifndef ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ -#define ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef GUDHI_USE_TBB -#include -#endif - -#include -#include -#include - -namespace Gudhi { -namespace zigzag_persistence { - -/** - * @class Zigzag_edge oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h - * @brief Edge structure for the oscillating Rips filtration. - * - * @ingroup zigzag_persistence - * - * @details The edges of the filtration are computed first and the remaining simplices are deduced from them. - * Is also used to represent the vertices for technical reasons, by giving both vertices the same value. - * - * @tparam Filtration_value Type of the filtration value. Is recommended to be easy to copy, like an arithmetic type. - */ -template -class Zigzag_edge -{ - public: - /** - * @brief Constructor. - * - * @param u First boundary vertex ID - * @param v Second boundary vertex ID. If same than @p u, the edge is considered a vertex. - * @param fil Filtration value - * @param direction If true, forwards. If false, backwards. - */ - Zigzag_edge(int u, int v, Filtration_value fil, bool direction) : u_(u), v_(v), fil_(fil), direction_(direction) - { - if (u > v) std::swap(u_, v_); - } - - /** - * @brief Default constructor. Initialize everything to zero and the direction to true. - */ - Zigzag_edge() : u_(0), v_(0), fil_(0), direction_(true) {} - - /** - * @brief Returns vertex with smaller ID. - * - * @return Smallest ID among the boundary vertices. - */ - int get_smallest_vertex() const { return u_; } - - /** - * @brief Returns vertex with bigger ID. - * - * @return Biggest ID among the boundary vertices. - */ - int get_biggest_vertex() const { return v_; } - - /** - * @brief Returns the filtration value of the edge. - * - * @return Filtration value of the edge. - */ - Filtration_value get_filtration_value() const { return fil_; } - - /** - * @brief Gives the direction of the arrow corresponding to the edge. - * - * @return True, if forward, i.e., an insertion. - * @return False, if backward, i.e., a removal. - */ - bool get_direction() const { return direction_; } - - void set(int u, int v, Filtration_value fil, bool direction) - { - u_ = u; - v_ = v; - fil_ = fil; - direction_ = direction; - } - - /** - * @brief Equality test - * - * @param e Edge to compare. - * @return True, if both edges are equal. - * @return False, if both edges are not equal. - */ - bool operator==(const Zigzag_edge& e) const - { - return ((e.u_ == u_) && (e.v_ == v_) && (e.fil_ == fil_) && (e.direction_ == direction_)); - } - - friend std::ostream &operator<<(std::ostream &stream, const Zigzag_edge &ze) { - // stream << std::setprecision(6); - // stream << std::setprecision(std::numeric_limits::digits); - stream << "(" << ze.u_ << ", " << ze.v_<< ") "; - if (ze.direction_){ - stream << "-- " << ze.fil_ << " -->"; - } else { - stream << "<-- " << ze.fil_ << " --"; - } - return stream; - } - - private: - int u_; /**< Smaller vertex. */ - int v_; /**< Bigger vertex. */ - Filtration_value fil_; /**< Filtration value. */ - bool direction_; /**< Direction. True = forward, false = backward. */ -}; - -/** - * @class Identity_edge_modifier oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h - * @brief Identity modifier, i.e., does nothing. - * - * @ingroup zigzag_persistence - */ -class Identity_edge_modifier -{ - public: - /** - * @brief Returns the given value. - */ - template - static Filtration_value apply_modifier(Filtration_value f) { return f; } - - /** - * @brief Returns the given value. - */ - template - static Filtration_value apply_inverse_modifier(Filtration_value f) { return f; } - - private: - /** - * @brief Default constructor. Should not be called and therefore private. - */ - Identity_edge_modifier() {} -}; - -/** - * @class Square_root_edge_modifier oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h - * @brief Modifier that square roots the filtration value of an edge. - * - * @ingroup zigzag_persistence - * - * @details Useful in particular when geometric computations (edge length, etc) are - * run with squared Euclidean distance for performance. - * - * @tparam Filtration_value Filtration value type. Should be compatible with `std::sqrt` and `operator*`. - */ -template -class Square_root_edge_modifier -{ - public: - /** - * @brief Returns the square root of the given value. The parameter it-self is not modified. - * - * @param f Value to modify. - * @return The modified value of @p f. - */ - static Filtration_value apply_modifier(Filtration_value f) { return std::sqrt(f); } - - /** - * @brief Returns the square of the given value. The parameter it-self is not modified. - * - * @param f Value to modify. - * @return The modified value of @p f. - */ - static Filtration_value apply_inverse_modifier(Filtration_value f) { return f * f; } - - private: - /** - * @brief Default constructor. Should not be called and therefore private. - */ - Square_root_edge_modifier() {} -}; - -/** - * @brief Order policy for the points. - * - * @ingroup zigzag_persistence - */ -enum Oscillating_rips_edge_order_policy { - ALREADY_ORDERED, /**< The given range of points is considered ordered. */ - FARTHEST_POINT_ORDERING, /**< The points are reordered using @ref Gudhi::subsampling::choose_n_farthest_points.*/ - RANDOM_POINT_ORDERING /**< The points are shuffled randomly. */ -}; - -// TODO: make all the iterators more "copyable" by giving some additional pointer to a container they can fill -// and which won't be copied together with the pointer - -/** - * @class Oscillating_rips_edge_range oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h - * @brief Gives access and computes different edge range types for an oscillating Rips filtration. - * - * @ingroup zigzag_persistence - * - * @details There are two different kind of ranges: a vector range containing all computed edges and - * a Boost range made from a custom iterator computing an edge on the fly at each increment. - * The custom iterator is therefore only a forward iterator and can only be incremented. - * - * @tparam Filtration_value Filtration value type. Should be compatible with the edge modifier. - * @tparam EdgeModifier Modifier for the edge filtration values. If no modifications are wanted, - * use @ref Identity_edge_modifier. Default value: @ref Identity_edge_modifier. - * - * @warning As the custom iterator used for the Boost ranges stores possibly large ranges, avoid copying it. - * Use @ref get_iterator_range, @ref begin and @ref end wisely. If one needs to do something else than to simply - * iterate over the edges in order, then the vector range is for sure the better option. - */ -template -class Oscillating_rips_edge_range -{ - public: - /** - * @class Oscillating_rips_edge_iterator oscillating_rips_iterators.h \ - * gudhi/Zigzag_persistence/oscillating_rips_iterators.h - * @brief Custom iterator over the edges of an oscillating rips filtration. - * - * It inherits from boost::iterator_facade. - * - * The iterator stores the distance matrix and epsilon values to compute the current edge on the fly - * at each increment. - * - * @warning As the iterator stores possibly large ranges, avoid copying it. - */ - class Oscillating_rips_edge_iterator : public boost::iterator_facade&, - boost::forward_traversal_tag> - { - public: - /** - * @brief Constructor. Computes the distance matrix and the epsilon values it-self from the given parameters. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param points Point cloud as a range.The format of a point has to correspond to the input format of the - * distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either - * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING "", - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING "". - */ - template - Oscillating_rips_edge_iterator(Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Oscillating_rips_edge_order_policy orderPolicy) - : nu_(nu), - mu_(mu), - currentEdge_(0, 0, std::numeric_limits::infinity(), true), - epsilonIndex_(0), - rowIndex_(1), - inPositiveDirection_(true), - insertVertex_(true) - { - _initialize(nu_, mu_, epsilonValues_, distanceMatrix_, points, distance, orderPolicy); - auto it = std::upper_bound( - distanceMatrix_[1].begin(), - distanceMatrix_[1].end(), - std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsilonIndex_]), - Point_distance_comp()); - columnIndex_ = it - distanceMatrix_[1].begin(); - } - - /** - * @brief Constructor. Takes already computed epsilon values as input, but assumes that the points are - * already ordered accordingly. Assumes also that the last epsilon value is 0. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input - * format of the distance function. The order of the points should be in correspondence with the order of - * the epsilon values. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. - * And the last value should be 0. - */ - template - Oscillating_rips_edge_iterator(Filtration_value nu, - Filtration_value mu, - const PointRange& orderedPoints, - DistanceFunction&& distance, - const std::vector& epsilonValues) - : epsilonValues_(epsilonValues), - nu_(nu), - mu_(mu), - currentEdge_(0, 0, std::numeric_limits::infinity(), true), - epsilonIndex_(0), - rowIndex_(1), - inPositiveDirection_(true), - insertVertex_(true) - { - GUDHI_CHECK(orderedPoints.size() == epsilonValues.size(), - "The number of points and the number of epsilon values should match."); - GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); - - nu_ = EdgeModifier::apply_inverse_modifier(nu); - mu_ = EdgeModifier::apply_inverse_modifier(mu); - - // compute the distance matrix - distanceMatrix_ = _compute_distance_matrix(orderedPoints, distance); - - auto it = std::upper_bound( - distanceMatrix_[1].begin(), - distanceMatrix_[1].end(), - std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsilonIndex_]), - Point_distance_comp()); - columnIndex_ = it - distanceMatrix_[1].begin(); - } - - /** - * @brief Default constructor. Corresponds to the end iterator. - */ - Oscillating_rips_edge_iterator() - : nu_(0), - mu_(0), - currentEdge_(0, 0, 0, true), - epsilonIndex_(0), - rowIndex_(0), - columnIndex_(0), - inPositiveDirection_(true), - insertVertex_(true) - {} - - private: - // mandatory for the boost::iterator_facade inheritance. - friend class boost::iterator_core_access; - - std::vector epsilonValues_; /**< Epsilon values. */ - std::vector > > distanceMatrix_; /**< Distance matrix. */ - Filtration_value nu_; /**< Lower multiplier. */ - Filtration_value mu_; /**< Upper multiplier. */ - Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ - std::size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ - bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ''edge'' is a vertex. */ - - /** - * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if to iterators are equal. - * - * @param other Iterator to compare. - * @return True, the iterators are pointing to the same position. - * @return False, otherwise. - */ - bool equal(Oscillating_rips_edge_iterator const& other) const - { - return rowIndex_ == other.rowIndex_ && currentEdge_ == other.currentEdge_; - } - - /** - * @brief Mandatory for the boost::iterator_facade inheritance. Returns the value of the dereferenced iterator. - * - * @return Current edge. - */ - const Zigzag_edge& dereference() const { return currentEdge_; } - - /** - * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. - * - */ - void increment() - { - if (epsilonIndex_ < distanceMatrix_.size() - 1) { - if (insertVertex_) { - _update_edge_as_positive_vertex(); - insertVertex_ = false; - return; - } - - if (inPositiveDirection_ && _positive_col_index_is_not_valid()) { - while (_positive_col_index_is_not_valid() && rowIndex_ <= epsilonIndex_) { - ++rowIndex_; - _initialize_positive_col_index(); - } - if (_positive_col_index_is_not_valid()) { - _initialize_negative_col_index(); - inPositiveDirection_ = false; - } - } - - if (!inPositiveDirection_ && _negative_col_index_is_not_valid()) { - while (_negative_col_index_is_not_valid() && rowIndex_ > 1) { - --rowIndex_; - _initialize_negative_col_index(); - } - if (_negative_col_index_is_not_valid()) { - _initialize_positive_col_index(); - ++epsilonIndex_; - inPositiveDirection_ = true; - if (epsilonIndex_ == distanceMatrix_.size() - 1) { - // _set_end(); - rowIndex_ = distanceMatrix_.size(); - _update_edge_as_negative_vertex(); - return; - } - _update_edge_as_positive_vertex(); - return; - } - } - - if (inPositiveDirection_) { - --columnIndex_; - _update_edge(epsilonIndex_, true); - while (_positive_col_index_is_not_valid() && rowIndex_ <= epsilonIndex_) { - ++rowIndex_; - _initialize_positive_col_index(); - } - if (_positive_col_index_is_not_valid()) { - ++rowIndex_; - } - if (rowIndex_ == epsilonIndex_ + 2) { - inPositiveDirection_ = false; - --rowIndex_; - _initialize_negative_col_index(); - } - return; - } - - _update_edge(epsilonIndex_, false); - ++columnIndex_; - while (_negative_col_index_is_not_valid() && rowIndex_ > 1) { - --rowIndex_; - _initialize_negative_col_index(); - } - if (_negative_col_index_is_not_valid()) { - --rowIndex_; - } - if (rowIndex_ == 0) { - ++epsilonIndex_; - if (epsilonIndex_ == distanceMatrix_.size() - 1) { - rowIndex_ = distanceMatrix_.size() + 1; - return; - } - insertVertex_ = true; - inPositiveDirection_ = true; - ++rowIndex_; - _initialize_positive_col_index(); - } - return; - } - if (rowIndex_ > 1) { - --rowIndex_; - _update_edge_as_negative_vertex(); - return; - } - - _set_end(); - } - - /** - * @brief Set the iterator as the end iterator. - */ - void _set_end() - { - rowIndex_ = 0; - currentEdge_ = Zigzag_edge(); - } - - /** - * @brief Initialize the column index for a new sequence of positive edges. - */ - void _initialize_positive_col_index() - { - auto it = std::upper_bound( - distanceMatrix_[rowIndex_].begin(), - distanceMatrix_[rowIndex_].end(), - std::pair(distanceMatrix_.size(), mu_ * epsilonValues_[epsilonIndex_]), - Point_distance_comp()); - columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); - } - - /** - * @brief Initialize the column index for a new sequence of negative edges. - */ - void _initialize_negative_col_index() - { - auto it = std::lower_bound(distanceMatrix_[rowIndex_].begin(), - distanceMatrix_[rowIndex_].end(), - std::pair(0, nu_ * epsilonValues_[epsilonIndex_ + 1]), - Point_distance_comp()); - while (it != distanceMatrix_[rowIndex_].end() && it->second == nu_ * epsilonValues_[epsilonIndex_ + 1]) ++it; - columnIndex_ = it - distanceMatrix_[rowIndex_].begin(); - } - - /** - * @brief Indicates if the column index needs to be initialized or not. - */ - bool _positive_col_index_is_not_valid() - { - return columnIndex_ == 0 || - (rowIndex_ != (epsilonIndex_ + 1) && - distanceMatrix_[rowIndex_][columnIndex_ - 1].second <= nu_ * epsilonValues_[epsilonIndex_]); - } - - /** - * @brief Indicates if the column index needs to be initialized or not. - */ - bool _negative_col_index_is_not_valid() - { - return columnIndex_ == distanceMatrix_[rowIndex_].size() || - distanceMatrix_[rowIndex_][columnIndex_].second > mu_ * epsilonValues_[epsilonIndex_]; - } - - /** - * @brief Updates the current edge. - * - * @param i Epsilon value index. - * @param direction Direction. - */ - void _update_edge(std::size_t i, bool direction) - { - currentEdge_.set(distanceMatrix_[rowIndex_][columnIndex_].first, - rowIndex_, - EdgeModifier::apply_modifier(epsilonValues_[i]), - direction); - } - - /** - * @brief Update the current edge as a vertex to insert. - */ - void _update_edge_as_positive_vertex() - { - currentEdge_.set( - epsilonIndex_ + 1, epsilonIndex_ + 1, EdgeModifier::apply_modifier(epsilonValues_[epsilonIndex_]), true); - } - - /** - * @brief Update the current edge as a vertex to remove. - */ - void _update_edge_as_negative_vertex() - { - currentEdge_.set(rowIndex_ - 1, rowIndex_ - 1, -std::numeric_limits::infinity(), false); - } - }; - - /** - * @brief Computes and return a vector with all edges in order of the oscillating Rips filtration. - * See the @ref zigzagrips "introduction page" for more details about the arguments. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param points Point cloud as a range.The format of a point has to correspond to the input format of the - * distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either - * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. - * - * @return Vector of @ref Zigzag_edge. - */ - template - static std::vector > compute_vector_range( - Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Oscillating_rips_edge_order_policy orderPolicy = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) - { - std::vector epsilonValues; - std::vector > > distanceMatrix; - - _initialize(nu, mu, epsilonValues, distanceMatrix, points, distance, orderPolicy); - - return _compute_vector_range(nu, mu, distanceMatrix, epsilonValues); - } - - /** - * @brief Computes and return a vector with all edges in order of the oscillating Rips filtration. - * See the @ref zigzagrips "introduction page" for more details about the arguments. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input - * format of the distance function. The order of the points should be in correspondence with the order of - * the epsilon values. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. - * And the last value should be 0. - * - * @return Vector of @ref Zigzag_edge. - */ - template - static std::vector > compute_vector_range( - Filtration_value nu, - Filtration_value mu, - const PointRange& orderedPoints, - DistanceFunction&& distance, - const std::vector& epsilonValues) - { - GUDHI_CHECK( - orderedPoints.size() == epsilonValues.size(), - std::invalid_argument("Epsilon values should be initialized and have the same size than the point container.")); - - std::vector > > distanceMatrix; - - _initialize(nu, mu, epsilonValues, distanceMatrix, orderedPoints, distance); - - return _compute_vector_range(nu, mu, distanceMatrix, epsilonValues); - } - - /** - * @brief Returns a boost::iterator_range from @ref Oscillating_rips_edge_iterator. The edges are computed - * on the fly at each increment. The iterator is a forward iterator only. - * See the @ref zigzagrips "introduction page" for more details about the arguments. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param points Point cloud as a range.The format of a point has to correspond to the input format of the - * distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either - * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. - * - * @return boost::iterator_range of @ref Oscillating_rips_edge_iterator. - * - * @warning Avoid copying the iterators as they are heavier than usual iterators. If begin and end iterators - * are needed but not the structure of the range, use @ref begin and @ref end instead. - */ - template - static boost::iterator_range get_iterator_range( - Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Oscillating_rips_edge_order_policy orderPolicy = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) - { - return boost::iterator_range( - Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy), Oscillating_rips_edge_iterator()); - } - - /** - * @brief Returns a boost::iterator_range from @ref Oscillating_rips_edge_iterator. The edges are computed - * on the fly at each increment. The iterator is a forward iterator only. - * Takes already computed epsilon values as input, but assumes that the points are already ordered accordingly. - * Assumes also that the last epsilon value is 0. - * See the @ref zigzagrips "introduction page" for more details about the arguments. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input - * format of the distance function. The order of the points should be in correspondence with the order of - * the epsilon values. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. - * And the last value should be 0. - * - * @return boost::iterator_range of @ref Oscillating_rips_edge_iterator. - * - * @warning Avoid copying the iterators as they are heavier than usual iterators. If begin and end iterators - * are needed but not the structure of the range, use @ref begin and @ref end instead. - */ - template - static boost::iterator_range get_iterator_range( - Filtration_value nu, - Filtration_value mu, - const PointRange& orderedPoints, - DistanceFunction&& distance, - const std::vector& epsilonValues) - { - return boost::iterator_range( - Oscillating_rips_edge_iterator(nu, mu, orderedPoints, distance, epsilonValues), - Oscillating_rips_edge_iterator()); - } - - /** - * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. - * See the @ref zigzagrips "introduction page" for more details about the arguments. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param points Point cloud as a range.The format of a point has to correspond to the input format of the - * distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either - * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. - * - * @return Instantiation of @ref Oscillating_rips_edge_iterator. - * - * @warning Avoid copying the iterator as it is heavier than usual iterators. - */ - template - static Oscillating_rips_edge_iterator begin( - Filtration_value nu, - Filtration_value mu, - const PointRange& points, - DistanceFunction&& distance, - Oscillating_rips_edge_order_policy orderPolicy = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) - { - return Oscillating_rips_edge_iterator(nu, mu, points, distance, orderPolicy); - } - - /** - * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. - * Takes already computed epsilon values as input, but assumes that the points are already ordered accordingly. - * Assumes also that the last epsilon value is 0. - * See the @ref zigzagrips "introduction page" for more details about the arguments. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input - * format of the distance function. The order of the points should be in correspondence with the order of - * the epsilon values. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. - * And the last value should be 0. - * - * @return Instantiation of @ref Oscillating_rips_edge_iterator. - * - * @warning Avoid copying the iterator as it is heavier than usual iterators. - */ - template - static Oscillating_rips_edge_iterator begin(Filtration_value nu, - Filtration_value mu, - const PointRange& orderedPoints, - DistanceFunction&& distance, - const std::vector& epsilonValues) - { - return Oscillating_rips_edge_iterator(nu, mu, orderedPoints, distance, epsilonValues); - } - - /** - * @brief Returns the end iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. - * - * @return Default instantiation of @ref Oscillating_rips_edge_iterator. - */ - static Oscillating_rips_edge_iterator end() { return Oscillating_rips_edge_iterator(); } - - private: - /** - * @brief Default constructor. Should not be called and therfore private. Use as a ''static'' class only. - */ - Oscillating_rips_edge_range() {}; - - /** - * @brief The two input types std::pair encode pairs - * @f$(j, d(p_i,p_j))@f$ and @f$(k, d(p_i,p_k))@f$ for some fixed point @f$p_i@f$. - * The operator() orders edges by length. By convention, if lengths are equal, - * it orders pairs by taking the smaller vertex label between @f$j@f$ and @f$k@f$. - */ - struct Point_distance_comp { - bool operator()(const std::pair& p1, const std::pair& p2) const - { - { - if (p1.second != p2.second) { - return p1.second < p2.second; // shorter first - } - return p1.first < p2.first; - } - } - }; - - /** - * @brief Initialize the distance function and epsilon values. Updates also the multipliers if - * the edge modifier is active. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param epsilonValues Container for the epsilon values. - * @param distanceMatrix Container for the distance matrices. - * @param points Point cloud as a range.The format of a point has to correspond to the input format of the - * distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either - * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. - */ - template - static void _initialize(Filtration_value& nu, - Filtration_value& mu, - std::vector& epsilonValues, - std::vector > >& distanceMatrix, - const PointRange& points, - DistanceFunction&& distance, - Oscillating_rips_edge_order_policy orderPolicy) - { - GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); - - std::size_t n = points.size(); // number of points - PointRange sortedPoints; - sortedPoints.reserve(n); - - nu = EdgeModifier::apply_inverse_modifier(nu); - mu = EdgeModifier::apply_inverse_modifier(mu); - - // compute epsilon values - - if (orderPolicy == Oscillating_rips_edge_order_policy::ALREADY_ORDERED) { - sortedPoints.assign(points.begin(), points.end()); - epsilonValues = _compute_epsilon_values(sortedPoints, distance); - } else if (orderPolicy == Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING) { - epsilonValues.reserve(n); - Gudhi::subsampling::choose_n_farthest_points(distance, - points, - n, // final size - 0, // starting point - std::back_inserter(sortedPoints), - std::back_inserter(epsilonValues)); - // need to shift values output by subsampling: - for (unsigned int i = 1; i < n; ++i) { - epsilonValues[i - 1] = epsilonValues[i]; - } - epsilonValues[n - 1] = 0; - } else { - Gudhi::subsampling::pick_n_random_points(points, n, std::back_inserter(sortedPoints)); - epsilonValues = _compute_epsilon_values(sortedPoints, distance); - } - - // compute the distance matrix - distanceMatrix = _compute_distance_matrix(sortedPoints, distance); - } - - /** - * @brief Initialize the distance function with already given epsilon values. Updates also the multipliers if - * the edge modifier is active. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param epsilonValues Container for the epsilon values. - * @param distanceMatrix Container for the distance matrices. - * @param points Point cloud as a range.The format of a point has to correspond to the input format of the - * distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - */ - template - static void _initialize(Filtration_value& nu, - Filtration_value& mu, - const std::vector& epsilonValues, - std::vector > >& distanceMatrix, - const PointRange& points, - DistanceFunction&& distance) - { - GUDHI_CHECK((nu <= mu) && (nu >= 0), "Invalid parameters mu and nu"); - - nu = EdgeModifier::apply_inverse_modifier(nu); - mu = EdgeModifier::apply_inverse_modifier(mu); - - // compute the distance matrix - distanceMatrix = _compute_distance_matrix(points, distance); - } - - /** - * @brief Computes and return a vector with all edges in order of the oscillating Rips filtration. - */ - static std::vector > _compute_vector_range( - Filtration_value nu, - Filtration_value mu, - const std::vector > >& distanceMatrix, - const std::vector& epsilonValues) - { - std::vector > edgeFiltration; - auto n = epsilonValues.size(); - - // edgesAdded[i] (resp. edgesRemoved[i]) == list of edges (i,j), with j > > edgesAdded, edgesRemoved; - - std::size_t number_of_arrows = _compute_edges(nu, mu, epsilonValues, distanceMatrix, edgesAdded, edgesRemoved); - - // Now, sort edges according to lengths, and put everything in edgeFiltration - edgeFiltration.clear(); - edgeFiltration.reserve(number_of_arrows + n); // count edges + vertices additions - - // initialise R({p_0}, +infinity) - edgeFiltration.emplace_back(0, - 0, // add vertex p_0,+infty - std::numeric_limits::infinity(), - true); - // epsilonValues[0], true); - - for (std::size_t i = 0; i < n - 1; ++i) { // all ascending arrows eps_i - // add p_{i+1},eps_i - edgeFiltration.emplace_back(i + 1, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); - for (auto edgeIt = edgesAdded[i].begin(); edgeIt != edgesAdded[i].end(); ++edgeIt) { - edgeFiltration.push_back(*edgeIt); - } - for (auto edgeIt = edgesRemoved[i].rbegin(); // longest first - edgeIt != edgesRemoved[i].rend(); - ++edgeIt) { - edgeFiltration.push_back(*edgeIt); - } - } - for (int i = n - 1; i >= 0; --i) { - edgeFiltration.emplace_back(i, i, -std::numeric_limits::infinity(), false); - } - - _canonically_sort_edges(edgeFiltration); - - return edgeFiltration; - } - - /** - * @brief Compute the epsilon values for an ordered set of points, measuring the - * sparsity of the ordering. - * - * @details Let \f$P = \{p_0, \ldots, p_{n-1}\f$ be the ordered set of points. Then - * the method sets eps_range[i]<\CODE> with the value \$f\varepsilon_i\$f, - * defined as \f$\varepsilon_i = d_H(P_i,P)\f$, the Hausdorff between the points - * \f$P_i= \{p_0, \ldots, p_{i}\}\f$ and the entire point cloud - * \f$P = \{p_0, \ldots, p_{n-1}\}\f$. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param sortedPoints Point cloud as an ordered range. The format of a point has to correspond to the input - * format of the distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * - * @return Vector of decreasing epsilon values ending with 0. - */ - template - static std::vector _compute_epsilon_values(const PointRange& sortedPoints, - DistanceFunction&& distance) - { - std::size_t n = sortedPoints.size(); - std::vector eps_range(n, std::numeric_limits::infinity()); - - // compute all \f$\varepsilon_i\f$ values, such that eps_range[i] == - // eps_i==d_H(P_i,P), for i=0 ... n-1: - for (std::size_t i = 0; i < n; ++i) { - // entering step i, maintain eps_range[j] = eps_j for j= i. -#ifdef GUDHI_USE_TBB - tbb::parallel_for(std::size_t(i + 1), n, [&](std::size_t k) { - // set eps_range[k] <- d(p_k, P_i) == - // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. - double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); - if (dist_i_k < eps_range[k]) { - eps_range[k] = dist_i_k; - } - }); -#else - for (std::size_t k = i + 1; k < n; ++k) { - // set eps_range[k] <- d(p_k, P_i) == - // min{ d(p_k, P_{i-1}), d(p_k, p_i) } for k >= i. - double dist_i_k = distance(sortedPoints[i], sortedPoints[k]); - if (dist_i_k < eps_range[k]) { - eps_range[k] = dist_i_k; - } - } -#endif - // we have now eps_range[k] = d(p_k, P_i) for k > i. - // to do: implement parallel version by dividing the vector - // set eps_range[i] <- eps_i = d_H(P_i,P) = max_{k>i} d(p_k, P_i) - double eps_i = 0.; - for (std::size_t k = i + 1; k < n; ++k) { - if (eps_range[k] > eps_i) { - eps_i = eps_range[k]; - } - } - eps_range[i] = eps_i; - } - - return eps_range; - } - - /** - * @brief Returns the sparse distance matrix. A cell is represented as a pair of its row index and its value, - * i.e., @f$(j, d(p_i,p_j))@f$ is stored in column @f$i@f$ to indicate that the distance of the @f$i^{th}@f$ - * point has distance @f$d(p_i,p_j)@f$ from the @f$j^{th}@f$ point. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param sortedPoints Point cloud as an ordered range. The format of a point has to correspond to the input - * format of the distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * - * @return A vector of vectors of pairs of row indices and distances. - */ - template - static std::vector > > _compute_distance_matrix( - const PointRange& sortedPoints, - DistanceFunction&& distance) - { - std::vector > > distanceMatrix(sortedPoints.size()); -#ifdef GUDHI_USE_TBB - tbb::parallel_for(std::size_t(0), sortedPoints.size(), [&](std::size_t i) { - // distanceMatrix[i] = std::vector< std::pair >(); - distanceMatrix[i].resize(i); - for (std::size_t j = 0; j < i; ++j) { - distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); - } - // distanceMatrix[i] is sorted by (j, d(p_i,p_j)) < (k, d(p_i,p_k)) iff - // d(p_i,p_j) < d(p_i,p_k) or (j >(); - distanceMatrix[i].resize(i); - for (std::size_t j = 0; j < i; ++j) { - distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); - } - std::stable_sort(distanceMatrix[i].begin(), distanceMatrix[i].end(), Point_distance_comp()); - } -#endif - - return distanceMatrix; - } - - /** - * @brief Computes the edges that are added and the edges that are removed and stores them in two separate containers. - * The edge container @f$e@f$ stores the @f$i^{th}@f$ edge induced by vertex @f$j@f$ at @f$e[j][i]@f$. - * - * @param[in] nu Lower multiplier. - * @param[in] mu Upper multiplier. - * @param[in] epsilonValues Epsilon values in decreasing order finishing with 0. - * @param[in] distanceMatrix Spare distance matrix. - * @param[out] edgesAdded Container for positive edges. - * @param[out] edgesRemoved Container for negative edges. - * - * @return Total number of edges. - */ - static std::size_t _compute_edges(Filtration_value nu, - Filtration_value mu, - const std::vector& epsilonValues, - const std::vector > >& distanceMatrix, - std::vector > >& edgesAdded, - std::vector > >& edgesRemoved) - { - std::size_t number_of_arrows = 0; - auto n = epsilonValues.size(); - edgesAdded.resize(n); - edgesRemoved.resize(n); - - // edgesAdded[i] must contain all new edges (k,j), 0 <= k < j <= i+1, - // inserted in inclusion: - // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i, p_i+1 }, mu * eps_i) - // and - // edgesRemoved[i] must contain all edges (k,j), 0 <= k < j <= i+1, - // removed in backward inclusion: - // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) -#ifdef GUDHI_USE_TBB - // no need to consider the case i=n-1 in an oscillating Rips filtration - tbb::parallel_for(std::size_t(0), n - 1, [&](std::size_t i) { - typename std::vector >::const_iterator it; - //----edgesAdded[i]: - // consider first all edges added in inclusion: - // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), - // i.e., all (p_j,p_k) with 0 <= k < j <= i with - // nu eps_i < d(p_j,p_k) <= mu eps_i - // these new edges get filtration value epsilonValues[i] - for (std::size_t j = 1; j <= i; ++j) { - // get very first edge (k,j), over all k(n, mu * epsilonValues[i]), - Point_distance_comp()); - - while (it != distanceMatrix[j].begin()) { - --it; - // if edge already in R({p_0, ... , p_i}, nu * eps_i), stop - if (it->second <= nu * epsilonValues[i]) { - break; - } - edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); - ++number_of_arrows; - } - } - // now consider all edges added in inclusion: - // R({p_0, ... , p_i}, mu * eps_i) -> R({p_0, ... , p_i, p_i+1}, mu * eps_i) - // i.e., all (p_j,p_i+1) with 0 <= j <= i with d(p_j,p_i+1) <= mu eps_i - // these new edges get filtration value epsilonValues[i] - // first strictly longer edge - it = std::upper_bound(distanceMatrix[i + 1].begin(), - distanceMatrix[i + 1].end(), - std::pair(n, mu * epsilonValues[i]), - Point_distance_comp()); - - while (it != distanceMatrix[i + 1].begin()) { - --it; - edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); - ++number_of_arrows; - } - - //----edgesRemoved[i]: - // consider all edges removed in - // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) - // i.e., all edges (p_k,p_j), 0<=k(n, mu * epsilonValues[i]), - Point_distance_comp()); - - while (it != distanceMatrix[j].begin()) { - --it; - // when reading an edge in R({p_0, ... , p_{i+1}}, nu * eps_{i+1}), stop - if (it->second <= nu * epsilonValues[i + 1]) { - break; - } - edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); - ++number_of_arrows; - } - } - }); -#else // GUDHI_USE_TBB not defined - - typename std::vector >::const_iterator it; - - for (std::size_t i = 0; i < n - 1; ++i) { - //----edgesAdded[i]: - // consider first all edges added in inclusion: - // R({p_0, ... , p_i}, nu * eps_i) -> R({p_0, ... , p_i}, mu * eps_i), - // i.e., all (p_j,p_k) with 0 <= k < j <= i with - // nu eps_i < d(p_j,p_k) <= mu eps_i - // these new edges get filtration value epsilonValues[i] - for (std::size_t j = 1; j <= i; ++j) { - // get very first edge (k,j), over all k(n, mu * epsilonValues[i]), - Point_distance_comp()); - - while (it != distanceMatrix[j].begin()) { - --it; - // if edge already in R({p_0, ... , p_i}, nu * eps_i), stop - if (it->second <= nu * epsilonValues[i]) { - break; - } - edgesAdded[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), true); - ++number_of_arrows; - } - } - // now consider all edges added in inclusion: - // R({p_0, ... , p_i}, mu * eps_i) -> R({p_0, ... , p_i, p_i+1}, mu * eps_i) - // i.e., all (p_j,p_i+1) with 0 <= j <= i with d(p_j,p_i+1) <= mu eps_i - // these new edges get filtration value epsilonValues[i] - // first strictly longer edge - it = std::upper_bound(distanceMatrix[i + 1].begin(), - distanceMatrix[i + 1].end(), - std::pair(n, mu * epsilonValues[i]), - Point_distance_comp()); - while (it != distanceMatrix[i + 1].begin()) { - --it; - edgesAdded[i].emplace_back(it->first, i + 1, EdgeModifier::apply_modifier(epsilonValues[i]), true); - ++number_of_arrows; - } - - //----edgesRemoved[i]: - // consider all edges removed in - // R({p_0, ... , p_{i+1}}, mu * eps_i) <- R({p_0, ... , p_{i+1}}, nu * eps_{i+1}) - // i.e., all edges (p_k,p_j), 0<=k(n, mu * epsilonValues[i]), - Point_distance_comp()); - - while (it != distanceMatrix[j].begin()) { - --it; - // when reading an edge in R({p_0, ... , p_{i+1}}, nu * eps_{i+1}), stop - if (it->second <= nu * epsilonValues[i + 1]) { - break; - } - edgesRemoved[i].emplace_back(it->first, j, EdgeModifier::apply_modifier(epsilonValues[i]), false); - ++number_of_arrows; - } - } - } -#endif - - return number_of_arrows; - } - - /** - * @brief Sorts canonically the edges: as much as possible, edges should be removed in - * the reverse order of their insertion. We decide to insert shortest edges first, - * with increasing lexicographical order, and remove larger edges first, with - * decreasing lexicographic order. - * - * @param edges Edges to sort. - */ - static void _canonically_sort_edges(std::vector >& edges) - { - // filtration then dimension, then lex order for insertion - auto edge_cmp = [](const Zigzag_edge& e1, const Zigzag_edge& e2) { - if (e1.get_filtration_value() != e2.get_filtration_value()) { - return e1.get_filtration_value() < e2.get_filtration_value(); // lower fil first - } - - if (e1.get_smallest_vertex() == e1.get_biggest_vertex()) { // e1 is a vertex, -> put vertices first - if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); //-> vertex of lower label - } else { - return true; //-> always vertices before edges - } - } - // e1 is an edge - if (e2.get_smallest_vertex() == e2.get_biggest_vertex()) { - return false; // e2 vertex, -> put it first - } - // both are edges, lexicographic compare - if (e1.get_smallest_vertex() != e2.get_smallest_vertex()) { - return e1.get_smallest_vertex() < e2.get_smallest_vertex(); // lex order - } - if (e1.get_biggest_vertex() != e2.get_biggest_vertex()) { - return e1.get_biggest_vertex() < e2.get_biggest_vertex(); - } - return false; // equality - }; - - // the inverse ordering for deletions - auto inv_edge_cmp = [&](const Zigzag_edge& e1, const Zigzag_edge& e2) { - if (e1.get_smallest_vertex() == e2.get_smallest_vertex() && e1.get_biggest_vertex() == e2.get_biggest_vertex()) { - return false; // equality => false - } - return !(edge_cmp(e1, e2)); // reverse order - }; - - // sort sequences of inclusions of same filtration with edge_cmp - // sort sequences of removals of same filtration with inv_edge_cmp - auto beg = edges.begin(); - auto end = edges.begin(); - auto curr_fil = beg->get_filtration_value(); - auto curr_type = beg->get_direction(); - while (beg != edges.end()) { - while (end != edges.end() && end->get_filtration_value() == curr_fil && end->get_direction() == curr_type) { - ++end; - } - if (curr_type) { // sequence of insertions -#ifdef GUDHI_USE_TBB - tbb::parallel_sort(beg, end, edge_cmp); -#else - std::sort(beg, end, edge_cmp); -#endif - } else { // sequence of removals -#ifdef GUDHI_USE_TBB - tbb::parallel_sort(beg, end, inv_edge_cmp); -#else - std::sort(beg, end, inv_edge_cmp); -#endif - } - beg = end; - curr_fil = beg->get_filtration_value(); - curr_type = beg->get_direction(); - } - } -}; - -/** - * @class Oscillating_rips_simplex_range oscillating_rips_iterators.h \ - * gudhi/Zigzag_persistence/oscillating_rips_iterators.h - * @brief Gives access to a range of the ordered simplices in an oscillating Rips filtration. - * - * @ingroup zigzag_persistence - * - * @details The simplices are returned as tuples of three elements: - * the first is the simplex handle of the simplex in the given complex, - * the second is the filtration value of the corresponding arrow, - * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. - * For more information, see @ref Oscillating_rips_iterator. - * - * @tparam StableFilteredComplex Filtered complex structure that has stable simplex handles, - * that is, they do not invalidates after an insertion or a removal (except for the removed simplices). - * @tparam EdgeRangeIterator Type of the edge range iterator. Each element of the range is assumed to have - * type @ref Zigzag_edge or at least to be a class with the same public methods than @ref Zigzag_edge. - * - * @warning As the custom iterator stores a possibly large range, avoid copying it. - * Use @ref get_iterator_range, @ref begin and @ref end wisely. - */ -template -class Oscillating_rips_simplex_range -{ - public: - /** - * @class Oscillating_rips_iterator oscillating_rips_iterators.h gudhi/Zigzag_persistence/oscillating_rips_iterators.h - * @brief Custom iterator over the simplices of an oscillating rips filtration. Is a forward iterator only. - * - * It inherits from boost::iterator_facade. - * - * For each positive edge given by the edge range iterator, the given complex computes and adds all possible - * cofaces and they respective simplex handle are stored. For each negative edge, the given complex computes - * the simplex handles of all cofaces and they are stored. - * The simplex handles induced by an edge are stored only the time needed and are removed when reaching the next edge. - * That is, we start by storing the possible cofaces of an edge, then at each increment, - * the next element within the stored simplex handles is retrieved and once the end is reached, - * the simplex handles are erased and replaced by the possible cofaces of the next edge and so on... - * If the edge is negative, then a simplex is removed from the complex at the next increment. - * So, the maximum size of the complex corresponds to the maximum size of a complex in the zigzag filtration. - * - * The simplices are returned by the iterator as tuples of three elements: - * the first is the simplex handle of the simplex in the given complex, - * the second is the filtration value of the corresponding arrow, - * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. - * - * @warning As the iterator stores possibly a large range, avoid copying it. - */ - class Oscillating_rips_iterator - : public boost::iterator_facade&, - boost::forward_traversal_tag> - { - public: - using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ - using Simplex_handle = typename StableFilteredComplex::Simplex_handle; /**< Simplex handle type. */ - using Simplex_key = typename StableFilteredComplex::Simplex_key; /**< Key type. */ - - /** - * @brief Constructor. The edge iterators and the complex are not copied, so do not modify or invalidate - * them outside as long as this iterator is different from the end iterator. - * - * @param edgeStartIterator Begin iterator of the oscillating Rips edge range. - * Is moved, so the original iterator will probably be invalidated. - * @param edgeEndIterator End iterator of the oscillating Rips edge range. - * Is moved, so the original iterator will probably be invalidated. - * @param complex Empty complex which will be used to store current simplices. - * Only the address of the complex is stored, so the state of the complex can be - * consulted between each increment. - * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. - * Default value: -1. - */ - Oscillating_rips_iterator(EdgeRangeIterator& edgeStartIterator, - EdgeRangeIterator& edgeEndIterator, - StableFilteredComplex& complex, - int maxDimension = -1) - : complex_(&complex), - currentSimplexIndex_(0), - currentEdgeIt_(std::move(edgeStartIterator)), // TODO: move constructor for custom iterator? - endEdgeIt_(std::move(edgeEndIterator)), - currentDirection_(true), - maxDimension_(maxDimension), - currentArrowNumber_(0) - { - if (currentEdgeIt_ == endEdgeIt_) { - _set_end(); - return; - } - - // first simplex is the vertex (0,0) which is the only one with its filtration value. - complex_->insert_edge_as_flag(currentEdgeIt_->get_smallest_vertex(), - currentEdgeIt_->get_biggest_vertex(), - currentEdgeIt_->get_filtration_value(), - maxDimension_, - currentSimplices_); - ++currentEdgeIt_; - - std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; - std::get<1>(currentArrow_) = complex_->filtration(currentSimplices_[currentSimplexIndex_]); - std::get<2>(currentArrow_) = currentDirection_; - - complex_->assign_key(currentSimplices_[currentSimplexIndex_], 0); - } - - /** - * @brief Default constructor. Corresponds to the end iterator. - */ - Oscillating_rips_iterator() - : complex_(nullptr), currentSimplexIndex_(0), currentDirection_(true), maxDimension_(0), currentArrowNumber_(0) - {} - - private: - // mandatory for the boost::iterator_facade inheritance. - friend class boost::iterator_core_access; - - /** - * @brief Reverse lexicographical order for the simplex handles. - */ - struct reverse_lexicographic_order { - explicit reverse_lexicographic_order(StableFilteredComplex* st) : st_(st) {} - - bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const - { - auto rg1 = st_->simplex_vertex_range(sh1); - auto rg2 = st_->simplex_vertex_range(sh2); - auto it1 = rg1.begin(); - auto it2 = rg2.begin(); - while (it1 != rg1.end() && it2 != rg2.end()) { - if (*it1 == *it2) { - ++it1; - ++it2; - } else { - return *it1 < *it2; - } - } - return ((it1 == rg1.end()) && (it2 != rg2.end())); - } - - StableFilteredComplex* st_; - }; - - std::vector currentSimplices_; /**< Stores current simplex handles. */ - StableFilteredComplex* complex_; /**< Pointer to the complex. */ - std::size_t currentSimplexIndex_; /**< Index to current position in currentSimplices_. */ - EdgeRangeIterator currentEdgeIt_; /**< Iterator pointing to the next edge. */ - EdgeRangeIterator endEdgeIt_; /**< End edge iterator. */ - bool currentDirection_; /**< Current direction. */ - const int maxDimension_; /**< Maximal dimension of expansion. */ - std::tuple currentArrow_; /**< Current return element. */ - Simplex_key currentArrowNumber_; /**< Number of increments. */ - - /** - * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if two iterators are equal. - * - * @param other Iterator to compare. - * @return True, if the two iterators point to the same position. - * @return False, otherwise. - */ - bool equal(Oscillating_rips_iterator const& other) const - { - if (complex_ == nullptr) return other.complex_ == nullptr; - - return complex_ == other.complex_ && currentEdgeIt_ == other.currentEdgeIt_ && - currentSimplexIndex_ == other.currentSimplexIndex_; - } - - /** - * @brief Mandatory for the boost::iterator_facade inheritance. Dereference the iterator. - * - * @return Value of the current return element. - */ - const std::tuple& dereference() const { return currentArrow_; } - - /** - * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. - */ - void increment() - { - if (!currentDirection_) complex_->remove_maximal_simplex(currentSimplices_[currentSimplexIndex_]); - - ++currentSimplexIndex_; - ++currentArrowNumber_; - - if (currentSimplexIndex_ == currentSimplices_.size()) { - if (currentEdgeIt_ == endEdgeIt_) { - _set_end(); - return; - } - - currentSimplices_.clear(); - - auto fil = currentEdgeIt_->get_filtration_value(); - currentDirection_ = currentEdgeIt_->get_direction(); - - std::get<1>(currentArrow_) = fil; - std::get<2>(currentArrow_) = currentDirection_; - - if (currentDirection_) { - _update_positive_current_simplices(fil); - } else { - _update_negative_current_simplices(fil); - } - currentSimplexIndex_ = 0; - } - - std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; - if (currentDirection_) complex_->assign_key(currentSimplices_[currentSimplexIndex_], currentArrowNumber_); - } - - /** - * @brief Sets the iterator as the end iterator. - */ - void _set_end() { complex_ = nullptr; } - - /** - * @brief Updates @p currentSimplices_ for insertions. - * - * @param fil Current filtration value. - */ - void _update_positive_current_simplices(Filtration_value fil) - { - while (currentEdgeIt_ != endEdgeIt_ && currentEdgeIt_->get_direction() && - currentEdgeIt_->get_filtration_value() == fil) { - complex_->insert_edge_as_flag(currentEdgeIt_->get_smallest_vertex(), - currentEdgeIt_->get_biggest_vertex(), - currentEdgeIt_->get_filtration_value(), - maxDimension_, - currentSimplices_); - ++currentEdgeIt_; - } -#ifdef GUDHI_USE_TBB - tbb::parallel_sort(currentSimplices_.begin(), currentSimplices_.end(), reverse_lexicographic_order(complex_)); -#else - std::sort(currentSimplices_.begin(), currentSimplices_.end(), reverse_lexicographic_order(complex_)); -#endif - } - - /** - * @brief Updates @p currentSimplices_ for removals. - * - * @param fil Current filtration value. - */ - void _update_negative_current_simplices(Filtration_value fil) - { - unsigned int count = 0; - while (currentEdgeIt_ != endEdgeIt_ && !currentEdgeIt_->get_direction() && - currentEdgeIt_->get_filtration_value() == fil) { - Simplex_handle sh = complex_->find({currentEdgeIt_->get_smallest_vertex()}); - if (currentEdgeIt_->get_smallest_vertex() != currentEdgeIt_->get_biggest_vertex()) { - sh = sh->second.children()->members().find(currentEdgeIt_->get_biggest_vertex()); - } - auto toRemove = complex_->star_simplex_range(sh); - currentSimplices_.insert(currentSimplices_.end(), toRemove.begin(), toRemove.end()); - ++currentEdgeIt_; - ++count; - } -#ifdef GUDHI_USE_TBB - tbb::parallel_sort( - currentSimplices_.begin(), currentSimplices_.end(), [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { - return complex_->key(sh1) > complex_->key(sh2); - }); -#else - std::sort( - currentSimplices_.begin(), currentSimplices_.end(), [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { - return complex_->key(sh1) > complex_->key(sh2); - }); -#endif - if (count > 1) { // more than 1 edge inserted: cofaces can be duplicated - auto last = std::unique( - currentSimplices_.begin(), currentSimplices_.end(), [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { - return complex_->key(sh1) == complex_->key(sh2); - }); // equal simplex handles means equal key - currentSimplices_.erase(last, currentSimplices_.end()); // remove duplicated cofaces - } - } - }; - - /** - * @brief Returns a boost::iterator_range from @ref Oscillating_rips_iterator. - * The iterator is a forward iterator only. - * - * @param edgeStartIterator Begin iterator of the oscillating Rips edge range. - * Is moved, so the original iterator will probably be invalidated. - * @param edgeEndIterator End iterator of the oscillating Rips edge range. - * Is moved, so the original iterator will probably be invalidated. - * @param complex Empty complex which will be used to store current simplices. - * Only the address of the complex is stored, so the state of the complex can be - * consulted between each increment. - * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. - * Default value: -1. - * - * @return boost::iterator_range of @ref Oscillating_rips_iterator. - * - * @warning Avoid copying the iterators as they are heavier than usual iterators. If begin and end iterators - * are needed but not the structure of the range, use @ref begin and @ref end instead. - */ - static boost::iterator_range get_iterator_range(EdgeRangeIterator& edgeStartIterator, - EdgeRangeIterator& edgeEndIterator, - StableFilteredComplex& complex, - int maxDimension = -1) - { - return boost::iterator_range( - Oscillating_rips_iterator(edgeStartIterator, edgeEndIterator, complex, maxDimension), - Oscillating_rips_iterator()); - } - - /** - * @brief Returns the begin iterator of a the range of simplices based on @ref Oscillating_rips_iterator. - * - * @param edgeStartIterator Begin iterator of the oscillating Rips edge range. - * Is moved, so the original iterator will probably be invalidated. - * @param edgeEndIterator End iterator of the oscillating Rips edge range. - * Is moved, so the original iterator will probably be invalidated. - * @param complex Empty complex which will be used to store current simplices. - * Only the address of the complex is stored, so the state of the complex can be - * consulted between each increment. - * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. - * Default value: -1. - * - * @return Instantiation of @ref Oscillating_rips_iterator. - * - * @warning Avoid copying the iterator as it is heavier than usual iterators. - */ - static Oscillating_rips_iterator begin(EdgeRangeIterator& edgeStartIterator, - EdgeRangeIterator& edgeEndIterator, - StableFilteredComplex& complex, - int maxDimension = -1) - { - return Oscillating_rips_iterator(edgeStartIterator, edgeEndIterator, complex, maxDimension); - } - - /** - * @brief Returns the end iterator of a the range of simplices based on @ref Oscillating_rips_iterator. - * - * @return Default instantiation of @ref Oscillating_rips_iterator. - */ - static Oscillating_rips_iterator end() { return Oscillating_rips_iterator(); } - - private: - /** - * @brief Default constructor. Should not be called and therfore private. Use as a ''static'' class only. - */ - Oscillating_rips_simplex_range() {}; -}; - -} // namespace zigzag_persistence -} // namespace Gudhi - -#endif // ZIGZAG_OSCILLATING_RIPS_ITERATORS_H_ diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h new file mode 100644 index 0000000000..fed8908bba --- /dev/null +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h @@ -0,0 +1,575 @@ +/* This file is part of the Gudhi Library - https://gudhi.inria.fr/ - which is released under MIT. + * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. + * Author(s): Clément Maria and Hannah Schreiber + * + * Copyright (C) 2023 Inria + * + * Modification(s): + * - YYYY/MM Author: Description of the modification + */ + +/** + * @file oscillating_rips_simplex_ranges.h + * @author Clément Maria, Hannah Schreiber + * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Oscillating_rips_edge_order_policy enum, + * @ref Gudhi::zigzag_persistence::Oscillating_rips_simplex_iterator_base class, + * @ref Gudhi::zigzag_persistence::Oscillating_rips_simplex_iterator_range class and + * @ref Gudhi::zigzag_persistence::Oscillating_rips_simplex_vector_range_constructor class. + */ + +#ifndef ZIGZAG_OSCILLATING_RIPS_SIMPLEX_RANGES_H_ +#define ZIGZAG_OSCILLATING_RIPS_SIMPLEX_RANGES_H_ + +#include +#include +#include +#include +#include + +#include + +#ifdef GUDHI_USE_TBB +#include +#endif + +#include +#include + +namespace Gudhi { +namespace zigzag_persistence { + +/** + * @private + * @brief + * + * @tparam Filtration_value + */ +class Oscillating_rips_edge_expander +{ + public: + /** + * @brief Reverse lexicographical order for the simplex handles. + */ + template + struct reverse_lexicographic_order { + using Simplex_handle = typename StableFilteredComplex::Simplex_handle; + + explicit reverse_lexicographic_order(StableFilteredComplex* st) : st_(st) {} + + bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const + { + auto rg1 = st_->simplex_vertex_range(sh1); + auto rg2 = st_->simplex_vertex_range(sh2); + auto it1 = rg1.begin(); + auto it2 = rg2.begin(); + while (it1 != rg1.end() && it2 != rg2.end()) { + if (*it1 == *it2) { + ++it1; + ++it2; + } else { + return *it1 < *it2; + } + } + return ((it1 == rg1.end()) && (it2 != rg2.end())); + } + + StableFilteredComplex* st_; + }; + + template + static void expand_edges_of_same_filtration_value( + EdgeRangeIterator& currentEdgeIt, + const EdgeRangeIterator& edgeEndIterator, + int maxDimension, + StableFilteredComplex& complex, + std::vector& currentSimplices) + { + if (currentEdgeIt == edgeEndIterator) return; + + auto fil = currentEdgeIt->get_filtration_value(); + while (currentEdgeIt != edgeEndIterator && currentEdgeIt->get_direction() && + currentEdgeIt->get_filtration_value() == fil) { + complex.insert_edge_as_flag(currentEdgeIt->get_smallest_vertex(), + currentEdgeIt->get_biggest_vertex(), + fil, + maxDimension, + currentSimplices); + ++currentEdgeIt; + } +#ifdef GUDHI_USE_TBB + tbb::parallel_sort(currentSimplices.begin(), currentSimplices.end(), reverse_lexicographic_order(&complex)); +#else + std::sort(currentSimplices.begin(), currentSimplices.end(), reverse_lexicographic_order(&complex)); +#endif + } + + /** + * @brief Updates @p currentSimplices_ for removals. + * + * @param fil Current filtration value. + */ + template + static void collapse_edges_of_same_filtration_value( + EdgeRangeIterator& currentEdgeIt, + const EdgeRangeIterator& edgeEndIterator, + StableFilteredComplex& complex, + std::vector& currentSimplices) + { + using Filtration_value = typename StableFilteredComplex::Filtration_value; + using Simplex_handle = typename StableFilteredComplex::Simplex_handle; + + if (currentEdgeIt == edgeEndIterator) return; + + unsigned int count = 0; + Filtration_value fil = currentEdgeIt->get_filtration_value(); + while (currentEdgeIt != edgeEndIterator && !currentEdgeIt->get_direction() && + currentEdgeIt->get_filtration_value() == fil) { + Simplex_handle sh = complex.find({currentEdgeIt->get_smallest_vertex()}); + if (currentEdgeIt->get_smallest_vertex() != currentEdgeIt->get_biggest_vertex()) { + sh = sh->second.children()->members().find(currentEdgeIt->get_biggest_vertex()); + } + auto toRemove = complex.star_simplex_range(sh); + currentSimplices.insert(currentSimplices.end(), toRemove.begin(), toRemove.end()); + ++currentEdgeIt; + ++count; + } +#ifdef GUDHI_USE_TBB + tbb::parallel_sort( + currentSimplices.begin(), currentSimplices.end(), [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { + return complex.key(sh1) > complex.key(sh2); + }); +#else + std::sort(currentSimplices.begin(), currentSimplices.end(), [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { + return complex.key(sh1) > complex.key(sh2); + }); +#endif + if (count > 1) { // more than 1 edge inserted: cofaces can be duplicated + auto last = std::unique( + currentSimplices.begin(), currentSimplices.end(), [&](Simplex_handle sh1, Simplex_handle sh2) -> bool { + return complex.key(sh1) == complex.key(sh2); + }); // equal simplex handles means equal key + currentSimplices.erase(last, currentSimplices.end()); // remove duplicated cofaces + } + } +}; + +/** + * @class Oscillating_rips_simplex_iterator oscillating_rips_simplex_ranges.h \ + * gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h + * @brief Custom iterator over the simplices of an oscillating rips filtration. Is a forward iterator only. + * + * It inherits from boost::iterator_facade. + * + * For each positive edge given by the edge range iterator, the given complex computes and adds all possible + * cofaces and they respective simplex handle are stored. For each negative edge, the given complex computes + * the simplex handles of all cofaces and they are stored. + * The simplex handles induced by an edge are stored only the time needed and are removed when reaching the next edge. + * That is, we start by storing the possible cofaces of an edge, then at each increment, + * the next element within the stored simplex handles is retrieved and once the end is reached, + * the simplex handles are erased and replaced by the possible cofaces of the next edge and so on... + * If the edge is negative, then a simplex is removed from the complex at the next increment. + * So, the maximum size of the complex corresponds to the maximum size of a complex in the zigzag filtration. + * + * The simplices are returned by the iterator as tuples of three elements: + * the first is the simplex handle of the simplex in the given complex, + * the second is the filtration value of the corresponding arrow, + * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. + * + * @warning As the iterator stores possibly large ranges, avoid copying it. + */ +template +class Oscillating_rips_simplex_iterator_base +{ + public: + public: + using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ + using Simplex_handle = typename StableFilteredComplex::Simplex_handle; /**< Simplex handle type. */ + using Simplex_key = typename StableFilteredComplex::Simplex_key; /**< Key type. */ + + /** + * @brief Constructor. The edge iterators and the complex are not copied, so do not modify or invalidate + * them outside as long as this iterator is different from the end iterator. + * + * @param edgeStartIterator Begin iterator of the oscillating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param edgeEndIterator End iterator of the oscillating Rips edge range. + * Is moved, so the original iterator will probably be invalidated. + * @param complex Empty complex which will be used to store current simplices. + * Only the address of the complex is stored, so the state of the complex can be + * consulted between each increment. + * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. + * Default value: -1. + */ + Oscillating_rips_simplex_iterator_base(const EdgeRangeIterator& edgeStartIterator, + const EdgeRangeIterator& edgeEndIterator, + StableFilteredComplex* complex, + int maxDimension = -1) + : complex_(complex), + currentSimplexIndex_(0), + currentEdgeIt_(edgeStartIterator), + endEdgeIt_(edgeEndIterator), + currentDirection_(true), + maxDimension_(maxDimension), + currentArrowNumber_(0) + { + if (currentEdgeIt_ == endEdgeIt_) { + _set_end(); + return; + } + + // first simplex is the vertex (0,0) which is the only one with its filtration value. + complex_->insert_edge_as_flag(currentEdgeIt_->get_smallest_vertex(), + currentEdgeIt_->get_biggest_vertex(), + currentEdgeIt_->get_filtration_value(), + maxDimension_, + currentSimplices_); + ++currentEdgeIt_; + + std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; + std::get<1>(currentArrow_) = complex_->filtration(currentSimplices_[currentSimplexIndex_]); + std::get<2>(currentArrow_) = currentDirection_; + + complex_->assign_key(currentSimplices_[currentSimplexIndex_], 0); + } + + /** + * @brief Default constructor. Corresponds to the end iterator. + */ + Oscillating_rips_simplex_iterator_base() + : complex_(nullptr), currentSimplexIndex_(0), currentDirection_(true), maxDimension_(0), currentArrowNumber_(0) + { + } + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if two iterators are equal. + * + * @param other Iterator to compare. + * @return True, if the two iterators point to the same position. + * @return False, otherwise. + */ + bool equal(Oscillating_rips_simplex_iterator_base const& other) const + { + if (complex_ == nullptr) return other.complex_ == nullptr; + + return complex_ == other.complex_ && currentEdgeIt_ == other.currentEdgeIt_ && + currentSimplexIndex_ == other.currentSimplexIndex_; + } + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Dereference the iterator. + * + * @return Value of the current return element. + */ + const std::tuple& dereference() const { return currentArrow_; } + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. + */ + void increment() + { + if (!currentDirection_) complex_->remove_maximal_simplex(currentSimplices_[currentSimplexIndex_]); + + ++currentSimplexIndex_; + ++currentArrowNumber_; + + if (currentSimplexIndex_ == currentSimplices_.size()) { + if (currentEdgeIt_ == endEdgeIt_) { + _set_end(); + return; + } + + currentSimplices_.clear(); + + auto fil = currentEdgeIt_->get_filtration_value(); + currentDirection_ = currentEdgeIt_->get_direction(); + + std::get<1>(currentArrow_) = fil; + std::get<2>(currentArrow_) = currentDirection_; + + if (currentDirection_) { + Oscillating_rips_edge_expander::expand_edges_of_same_filtration_value( + currentEdgeIt_, endEdgeIt_, maxDimension_, *complex_, currentSimplices_); + } else { + Oscillating_rips_edge_expander::collapse_edges_of_same_filtration_value( + currentEdgeIt_, endEdgeIt_, *complex_, currentSimplices_); + } + currentSimplexIndex_ = 0; + } + + std::get<0>(currentArrow_) = currentSimplices_[currentSimplexIndex_]; + if (currentDirection_) complex_->assign_key(currentSimplices_[currentSimplexIndex_], currentArrowNumber_); + } + + private: + /** + * @brief Reverse lexicographical order for the simplex handles. + */ + struct reverse_lexicographic_order { + explicit reverse_lexicographic_order(StableFilteredComplex* st) : st_(st) {} + + bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const + { + auto rg1 = st_->simplex_vertex_range(sh1); + auto rg2 = st_->simplex_vertex_range(sh2); + auto it1 = rg1.begin(); + auto it2 = rg2.begin(); + while (it1 != rg1.end() && it2 != rg2.end()) { + if (*it1 == *it2) { + ++it1; + ++it2; + } else { + return *it1 < *it2; + } + } + return ((it1 == rg1.end()) && (it2 != rg2.end())); + } + + StableFilteredComplex* st_; + }; + + std::vector currentSimplices_; /**< Stores current simplex handles. */ + StableFilteredComplex* complex_; /**< Pointer to the complex. */ + std::size_t currentSimplexIndex_; /**< Index to current position in currentSimplices_. */ + EdgeRangeIterator currentEdgeIt_; /**< Iterator pointing to the next edge. */ + EdgeRangeIterator endEdgeIt_; /**< End edge iterator. */ + bool currentDirection_; /**< Current direction. */ + const int maxDimension_; /**< Maximal dimension of expansion. */ + std::tuple currentArrow_; /**< Current return element. */ + Simplex_key currentArrowNumber_; /**< Number of increments. */ + + /** + * @brief Sets the iterator as the end iterator. + */ + void _set_end() { complex_ = nullptr; } +}; + +/** + * @class Oscillating_rips_simplex_iterator_range oscillating_rips_simplex_ranges.h + * gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h + * @brief Gives access to a range of the ordered simplices in an oscillating Rips filtration. + * + * @ingroup zigzag_persistence + * + * @details The simplices are returned as tuples of three elements: + * the first is the simplex handle of the simplex in the given complex, + * the second is the filtration value of the corresponding arrow, + * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. + * For more information, see @ref Oscillating_rips_iterator. + * + * @tparam StableFilteredComplex Filtered complex structure that has stable simplex handles, + * that is, they do not invalidates after an insertion or a removal (except for the removed simplices). + * @tparam EdgeRangeIterator Type of the edge range iterator. Each element of the range is assumed to have + * type @ref Zigzag_edge or at least to be a class with the same public methods than @ref Zigzag_edge. + * + * @warning As the custom iterator stores a possibly large range, avoid copying it. + * Use @ref get_iterator_range, @ref begin and @ref end wisely. + */ +template +class Oscillating_rips_simplex_iterator_range +{ + public: + /** + * @class Oscillating_rips_simplex_iterator oscillating_rips_simplex_ranges.h \ + * gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h + * @brief Custom iterator over the edges of an oscillating rips filtration. + * + * It inherits from boost::iterator_facade. + * + * The iterator stores the distance matrix and epsilon values to compute the current edge on the fly + * at each increment. + * + * @warning As the iterator stores possibly large ranges, avoid copying it. + */ + class Oscillating_rips_simplex_iterator + : public boost::iterator_facade&, + boost::forward_traversal_tag> + { + public: + using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ + using Simplex_handle = typename StableFilteredComplex::Simplex_handle; /**< Simplex handle type. */ + + /** + * @brief Constructor. Takes already computed epsilon values as input, but assumes that the points are + * already ordered accordingly. Assumes also that the last epsilon value is 0. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input + * format of the distance function. The order of the points should be in correspondence with the order of + * the epsilon values. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. + * And the last value should be 0. + */ + Oscillating_rips_simplex_iterator( + Oscillating_rips_simplex_iterator_base* base) + : base_iterator_(base) + { + } + + private: + // mandatory for the boost::iterator_facade inheritance. + friend class boost::iterator_core_access; + + /** + * @brief Pointer to heavy iterator, to avoid copying it. + */ + std::shared_ptr > base_iterator_; + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if to iterators are equal. + * + * @param other Iterator to compare. + * @return True, the iterators are pointing to the same position. + * @return False, otherwise. + */ + bool equal(Oscillating_rips_simplex_iterator const& other) const + { + return base_iterator_->equal(*other.base_iterator_); + } + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Returns the value of the dereferenced iterator. + * + * @return Current edge. + */ + const std::tuple& dereference() const + { + return base_iterator_->dereference(); + } + + /** + * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. + */ + void increment() { base_iterator_->increment(); } + }; + + Oscillating_rips_simplex_iterator_range(const EdgeRangeIterator& edgeStartIterator, + const EdgeRangeIterator& edgeEndIterator, + StableFilteredComplex& complex, + int maxDimension = -1) + : edgeStartIterator_(edgeStartIterator), + edgeEndIterator_(edgeEndIterator), + complex_(&complex), + maxDimension_(maxDimension) + { + } + + /** + * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_simplex_iterator. + * See the @ref zigzagrips "introduction page" for more details about the arguments. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + * + * @return Instantiation of @ref Oscillating_rips_simplex_iterator. + * + * @warning Avoid copying the iterator as it is heavier than usual iterators. + */ + Oscillating_rips_simplex_iterator begin() + { + // shared pointer on the other side will take ownership + // enables begin() to be called several times without invalidating precedent iterators + // still have the inconvenience that all copies of a same iterator (sharing the same base) increment simultaneously + return Oscillating_rips_simplex_iterator( + new Oscillating_rips_simplex_iterator_base( + edgeStartIterator_, edgeEndIterator_, complex_, maxDimension_)); + } + + /** + * @brief Returns the end iterator of a the range of edges based on @ref Oscillating_rips_simplex_iterator. + * + * @return Default instantiation of @ref Oscillating_rips_simplex_iterator. + */ + Oscillating_rips_simplex_iterator end() { return endIt_; } + + private: + EdgeRangeIterator edgeStartIterator_; + EdgeRangeIterator edgeEndIterator_; + StableFilteredComplex* complex_; + int maxDimension_; + inline static const Oscillating_rips_simplex_iterator endIt_ = Oscillating_rips_simplex_iterator( + new Oscillating_rips_simplex_iterator_base()); +}; + +template +class Oscillating_rips_simplex_vector_range_constructor +{ + public: + using Vertex_handle = typename StableFilteredComplex::Vertex_handle; + using Simplex_handle = typename StableFilteredComplex::Simplex_handle; + using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ + + struct Simplex { + std::vector vertices; + Filtration_value filtration_value; + bool direction; + }; + + template + static std::vector make_range(EdgeRangeIterator edgeStartIterator, + EdgeRangeIterator edgeEndIterator, + int maxDimension = -1) + { + StableFilteredComplex complex; + std::vector simplices; + std::vector currentSimplices; + unsigned int currentArrowNumber = 0; + + while (edgeStartIterator != edgeEndIterator) { + const auto& edge = *edgeStartIterator; + currentSimplices.clear(); + Filtration_value fil = edge.get_filtration_value(); + if (edge.get_direction()) { + Oscillating_rips_edge_expander::expand_edges_of_same_filtration_value( + edgeStartIterator, edgeEndIterator, maxDimension, complex, currentSimplices); + unsigned int i = simplices.size(); + simplices.resize(i + currentSimplices.size()); + for (Simplex_handle sh : currentSimplices) { + Simplex& s = simplices[i++]; + for (auto v : complex.simplex_vertex_range(sh)) s.vertices.push_back(v); + std::reverse(s.vertices.begin(), s.vertices.end()); + s.filtration_value = fil; + s.direction = true; + complex.assign_key(sh, currentArrowNumber++); + } + } else { + Oscillating_rips_edge_expander::collapse_edges_of_same_filtration_value( + edgeStartIterator, edgeEndIterator, complex, currentSimplices); + unsigned int i = simplices.size(); + simplices.resize(i + currentSimplices.size()); + for (Simplex_handle sh : currentSimplices) { + Simplex& s = simplices[i++]; + for (auto v : complex.simplex_vertex_range(sh)) s.vertices.push_back(v); + std::reverse(s.vertices.begin(), s.vertices.end()); + s.filtration_value = fil; + s.direction = false; + complex.remove_maximal_simplex(sh); + } + } + } + + return simplices; + } +}; + +} // namespace zigzag_persistence +} // namespace Gudhi + +#endif // ZIGZAG_OSCILLATING_RIPS_SIMPLEX_RANGES_H_ diff --git a/src/Zigzag_persistence/test/test_oscillating_rips.cpp b/src/Zigzag_persistence/test/test_oscillating_rips.cpp index f2da519969..73970b09bc 100644 --- a/src/Zigzag_persistence/test/test_oscillating_rips.cpp +++ b/src/Zigzag_persistence/test/test_oscillating_rips.cpp @@ -17,7 +17,8 @@ #include #include -#include +#include +#include struct Simplex_tree_options_oscillating_rips { typedef Gudhi::linear_indexing_tag Indexing_tag; @@ -32,8 +33,10 @@ struct Simplex_tree_options_oscillating_rips { }; using Gudhi::zigzag_persistence::Oscillating_rips_edge_order_policy; -using Gudhi::zigzag_persistence::Oscillating_rips_edge_range; -using Gudhi::zigzag_persistence::Oscillating_rips_simplex_range; +using Gudhi::zigzag_persistence::Oscillating_rips_edge_iterator_range; +using Gudhi::zigzag_persistence::Oscillating_rips_edge_vector_range_constructor; +using Gudhi::zigzag_persistence::Oscillating_rips_simplex_iterator_range; +using Gudhi::zigzag_persistence::Oscillating_rips_simplex_vector_range_constructor; using Gudhi::zigzag_persistence::Zigzag_edge; using Point = std::vector; @@ -45,9 +48,16 @@ using Edge = Zigzag_edge; using Face = std::vector; using Arrow = std::tuple; using VArrow = std::tuple; -using EdgeRange = Oscillating_rips_edge_range; +using ItEdgeRange = Oscillating_rips_edge_iterator_range; template -using OscillatingRipsSimplexRange = Oscillating_rips_simplex_range; +using ItSimplexRange = Oscillating_rips_simplex_iterator_range; +using VecEdgeConstr = Oscillating_rips_edge_vector_range_constructor; +using VecSimplexConstr = Oscillating_rips_simplex_vector_range_constructor; +using ItEdgeRangeIt = ItEdgeRange::Oscillating_rips_edge_iterator; +template +using ItSimplexRangeIt = typename ItSimplexRange::Oscillating_rips_simplex_iterator; +using VecEdgeIt = std::vector::const_iterator; +using VecSimplexIt = std::vector::const_iterator; std::vector get_point_cloud() { return {{0, 0}, {1.2, 1.2}, {0, 1.1}, {1, 0}, {0.51, 0.49}}; } @@ -140,7 +150,7 @@ std::vector get_filtration(const std::vector& eps) } template -void test_edges(EdgeRangeIterator& start, const EdgeRangeIterator& end, const std::vector& eps) +void test_edges(EdgeRangeIterator start, EdgeRangeIterator end, const std::vector& eps) { auto comp = [](const Edge& e1, const Edge& e2) { if (e2.get_filtration_value() == e1.get_filtration_value()) { @@ -197,9 +207,9 @@ void test_edges(EdgeRangeIterator& start, const EdgeRangeIterator& end, const st } } -template -void test_filtration(SimplexRangeIterator& start, - const SimplexRangeIterator& end, +template +void test_filtration(ItSimplexRangeIt start, + ItSimplexRangeIt end, const StableFilteredComplex& st, const std::vector& eps) { @@ -260,36 +270,91 @@ void test_filtration(SimplexRangeIterator& start, } } -BOOST_AUTO_TEST_CASE(oscillating_rips_edge_range1) +void test_filtration(VecSimplexIt start, + VecSimplexIt end, + const std::vector& eps) +{ + auto comp = [](const VArrow& e1, const VArrow& e2) { + if (std::get<1>(e1) == std::get<1>(e2)) { + if (std::get<2>(e1) == std::get<2>(e2)) { + const auto& v1 = std::get<0>(e1); + const auto& v2 = std::get<0>(e2); + return std::lexicographical_compare(v1.begin(), v1.end(), v2.begin(), v2.end()); + } else { + return std::get<2>(e1); + } + } else { + return std::get<1>(e1) > std::get<1>(e2); + } + }; + + std::vector realSimplices = get_filtration(eps); + // for same filtration value, the order can change for the different iterator types. + std::sort(realSimplices.begin(), realSimplices.end(), comp); + + std::vector testSimplices; + testSimplices.reserve(46); + unsigned int i = 0; + while (start != end && i < realSimplices.size()) { // to avoid infinite loop when something is wrong + testSimplices.emplace_back(); + std::get<0>(testSimplices.back()) = start->vertices; + std::get<1>(testSimplices.back()) = start->filtration_value; + std::get<2>(testSimplices.back()) = start->direction; + ++start; + ++i; + } + BOOST_CHECK(start == end); + // for same filtration value, the order can change for the different iterator types. + std::sort(testSimplices.begin(), testSimplices.end(), comp); + + i = 0; + BOOST_CHECK(testSimplices.size() == realSimplices.size()); + for (const auto& e : testSimplices) { + // std::cout << "test: "; + // for (const auto& v : std::get<0>(e)) { + // std::cout << v << " "; + // } + // std::cout << "\n"; + // std::cout << "real: "; + // for (const auto& v : std::get<0>(realSimplices[i])) { + // std::cout << v << " "; + // } + // std::cout << "\n"; + BOOST_CHECK(std::get<0>(e) == std::get<0>(realSimplices[i])); + BOOST_CHECK(std::get<1>(e) == std::get<1>(realSimplices[i])); + BOOST_CHECK(std::get<2>(e) == std::get<2>(realSimplices[i])); + ++i; + } +} + +BOOST_AUTO_TEST_CASE(oscillating_rips_edge_range12) { double nu = 1.73, mu = 2; std::vector points = get_point_cloud(); std::vector eps = {1.697, 1.1, 1, 0.707, 0}; - auto start1 = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), eps); - test_edges(start1, EdgeRange::end(), eps); + ItEdgeRange r1(nu, mu, points, Gudhi::Euclidean_distance(), eps); + test_edges(r1.begin(), r1.end(), eps); - auto vec1 = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), eps); - auto start3 = vec1.begin(); - test_edges(start3, vec1.end(), eps); + auto r2 = VecEdgeConstr::make_range(nu, mu, points, Gudhi::Euclidean_distance(), eps); + test_edges(r2.begin(), r2.end(), eps); } -BOOST_AUTO_TEST_CASE(oscillating_rips_edge_range2) +BOOST_AUTO_TEST_CASE(oscillating_rips_edge_range22) { double nu = 1.76, mu = 2; std::vector points = get_point_cloud(); Oscillating_rips_edge_order_policy p = Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING; std::vector eps = get_epsilons(points); - auto start2 = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); - test_edges(start2, EdgeRange::end(), eps); + ItEdgeRange r1(nu, mu, points, Gudhi::Euclidean_distance(), p); + test_edges(r1.begin(), r1.end(), eps); - auto vec2 = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto start4 = vec2.begin(); - test_edges(start4, vec2.end(), eps); + auto r2 = VecEdgeConstr::make_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + test_edges(r2.begin(), r2.end(), eps); } -BOOST_AUTO_TEST_CASE(oscillating_rips_simplex_range1) +BOOST_AUTO_TEST_CASE(oscillating_rips_simplex_range12) { double nu = 1.73, mu = 2; int maxDim = 2; @@ -297,24 +362,30 @@ BOOST_AUTO_TEST_CASE(oscillating_rips_simplex_range1) std::vector eps = {1.697, 1.1, 1, 0.707, 0}; StableFilteredComplex st; - auto edgeStart1 = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), eps); - auto edgeEnd1 = EdgeRange::end(); - auto start1 = OscillatingRipsSimplexRange::begin( - edgeStart1, edgeEnd1, st, maxDim); - auto end1 = OscillatingRipsSimplexRange::end(); - test_filtration(start1, end1, st, eps); + ItEdgeRange er1(nu, mu, points, Gudhi::Euclidean_distance(), eps); + ItSimplexRange sr1(er1.begin(), er1.end(), st, maxDim); + test_filtration(sr1.begin(), sr1.end(), st, eps); BOOST_CHECK(st.is_empty()); - auto vec1 = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), eps); - auto edgeStart3 = vec1.cbegin(); - auto edgeEnd3 = vec1.cend(); - auto start3 = OscillatingRipsSimplexRange::const_iterator>::begin(edgeStart3, edgeEnd3, st, maxDim); - auto end3 = OscillatingRipsSimplexRange::const_iterator>::end(); - test_filtration(start3, end3, st, eps); + auto sr3 = VecSimplexConstr::make_range(er1.begin(), er1.end(), maxDim); + test_filtration(sr3.begin(), sr3.end(), eps); + + BOOST_CHECK(st.is_empty()); + + auto er2 = VecEdgeConstr::make_range(nu, mu, points, Gudhi::Euclidean_distance(), eps); + ItSimplexRange sr2(er2.begin(), er2.end(), st, maxDim); + test_filtration(sr2.begin(), sr2.end(), st, eps); + + BOOST_CHECK(st.is_empty()); + + auto sr4 = VecSimplexConstr::make_range(er2.begin(), er2.end(), maxDim); + test_filtration(sr4.begin(), sr4.end(), eps); + + BOOST_CHECK(st.is_empty()); } -BOOST_AUTO_TEST_CASE(oscillating_rips_simplex_range2) +BOOST_AUTO_TEST_CASE(oscillating_rips_simplex_range22) { double nu = 1.76, mu = 2; int maxDim = 2; @@ -323,19 +394,25 @@ BOOST_AUTO_TEST_CASE(oscillating_rips_simplex_range2) std::vector eps = get_epsilons(points); StableFilteredComplex st; - auto edgeStart1 = EdgeRange::begin(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto edgeEnd1 = EdgeRange::end(); - auto start1 = OscillatingRipsSimplexRange::begin( - edgeStart1, edgeEnd1, st, maxDim); - auto end1 = OscillatingRipsSimplexRange::end(); - test_filtration(start1, end1, st, eps); + ItEdgeRange er1(nu, mu, points, Gudhi::Euclidean_distance(), p); + ItSimplexRange sr1(er1.begin(), er1.end(), st, maxDim); + test_filtration(sr1.begin(), sr1.end(), st, eps); BOOST_CHECK(st.is_empty()); - auto vec1 = EdgeRange::compute_vector_range(nu, mu, points, Gudhi::Euclidean_distance(), p); - auto edgeStart3 = vec1.cbegin(); - auto edgeEnd3 = vec1.cend(); - auto start3 = OscillatingRipsSimplexRange::const_iterator>::begin(edgeStart3, edgeEnd3, st, maxDim); - auto end3 = OscillatingRipsSimplexRange::const_iterator>::end(); - test_filtration(start3, end3, st, eps); + auto sr3 = VecSimplexConstr::make_range(er1.begin(), er1.end(), maxDim); + test_filtration(sr3.begin(), sr3.end(), eps); + + BOOST_CHECK(st.is_empty()); + + auto er2 = VecEdgeConstr::make_range(nu, mu, points, Gudhi::Euclidean_distance(), p); + ItSimplexRange sr2(er2.begin(), er2.end(), st, maxDim); + test_filtration(sr2.begin(), sr2.end(), st, eps); + + BOOST_CHECK(st.is_empty()); + + auto sr4 = VecSimplexConstr::make_range(er2.begin(), er2.end(), maxDim); + test_filtration(sr4.begin(), sr4.end(), eps); + + BOOST_CHECK(st.is_empty()); } From 2e973f11cb8cb6068c0fae7df2959e9ba091a12d Mon Sep 17 00:00:00 2001 From: hschreiber Date: Fri, 24 Jan 2025 19:41:49 +0100 Subject: [PATCH 17/21] doc --- .../doc/Intro_zigzag_persistence.h | 18 +- .../gudhi/Oscillating_rips_persistence.h | 7 +- .../gudhi/Zigzag_persistence/Zigzag_edge.h | 2 - .../oscillating_rips_edge_ranges.h | 289 ++++++++++------- .../oscillating_rips_simplex_ranges.h | 300 ++++++++++-------- 5 files changed, 354 insertions(+), 262 deletions(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 7a46196eee..4b6929b6d5 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -75,14 +75,16 @@ namespace zigzag_persistence { * the current epsilon / filtration value by multiplying it alternatively by two coefficients: \f$ \nu \leqslant \mu \f$. * Both multipliers have to be specified by the user. * - * The construction is based on two classes: - * - @ref Oscillating_rips_edge_range computes the range of inserted and removed vertices and edges in the filtration - * based on the elements descipted above. - * - @ref Oscillating_rips_simplex_range infers from @ref Oscillating_rips_edge_range all simplices from higher - * dimensions (i.e. > 1) of the filtration if necessary. For this purpose, a data structure able to represent a flag - * complex is additionally needed (such as @ref Gudhi::Simplex_tree). Note that @ref Oscillating_rips_edge_range is - * passed by template, so the user can potentially pass any other type of edge range as long as the dereferenced format - * corresponds and the sequence makes sense as a zigzag filtration. + * The construction is based on two types of classes: + * - @ref Oscillating_rips_edge_iterator_range and @ref Oscillating_rips_edge_vector_range_constructor computes the + * range of inserted and removed vertices and edges in the filtration based on the elements descipted above. + * - @ref Oscillating_rips_simplex_iterator_range and @ref Oscillating_rips_simplex_vector_range_constructor infers + * from @ref Oscillating_rips_edge_iterator_range or @ref Oscillating_rips_edge_vector_range_constructor all simplices + * from higher dimensions (i.e. > 1) of the filtration if necessary. + * For this purpose, a data structure able to represent a flag complex is additionally needed + * (such as @ref Gudhi::Simplex_tree). Note that the edge range is passed by template, so the user + * can potentially pass any other type of edge range as long as the dereferenced format corresponds and the sequence + * makes sense as a zigzag filtration. * * If only the barcode of the filtration is of interest and not the filtration it-self, the helper method * @ref compute_oscillating_rips_persistence can be used. It will directly feed to the filtration constructed by the diff --git a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h index d975bcf7b5..11371f3524 100644 --- a/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h +++ b/src/Zigzag_persistence/include/gudhi/Oscillating_rips_persistence.h @@ -46,7 +46,8 @@ enum Edge_range_type { /** * @ingroup zigzag_persistence * - * @brief Model of @ref SimplexTreeOptions, as expected from @ref Oscillating_rips_simplex_range. + * @brief Model of @ref SimplexTreeOptions, as expected from @ref Oscillating_rips_simplex_iterator_range and + * @ref Oscillating_rips_simplex_vector_range_constructor. * The values of `stable_simplex_handles`, `store_key` and `store_filtration` are mandatory. * `Simplex_key` has to be a signed integer type if @ref Zigzag_persistence is used * for the range. Otherwise, the options can be readapted. @@ -105,7 +106,7 @@ struct Default_oscillating_rips_zigzag_options : Default_filtered_zigzag_options * @details It is thought to be a helper function to easily compute oscillating Rips persistence with the * possibility to switch out a few common options. But additional options exists for the iterators * (e.g. replacing the Euclidean distance by another one, or using your own epsilon values; see - * the documentation of @ref Oscillating_rips_edge_range and @ref Oscillating_rips_simplex_range for + * the documentation of @ref Oscillating_rips_edge_iterator_range and @ref Oscillating_rips_simplex_iterator_range for * more information). One can easily create their own method based on this one. * * @tparam PointRange Range containing the point cloud. @@ -186,7 +187,7 @@ void compute_oscillating_rips_persistence( * @details It is thought to be a helper function to easily compute oscillating Rips persistence with the * possibility to switch out a few common options. But additional options exists for the iterators * (e.g. replacing the Euclidean distance by another one, or using your own epsilon values; see - * the documentation of @ref Oscillating_rips_edge_range and @ref Oscillating_rips_simplex_range for + * the documentation of @ref Oscillating_rips_edge_iterator_range and @ref Oscillating_rips_simplex_iterator_range for * more information). One can easily create their own method based on this one. * * @tparam PointRange Range containing the point cloud. diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/Zigzag_edge.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/Zigzag_edge.h index ce40a20c96..de67a72a62 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/Zigzag_edge.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/Zigzag_edge.h @@ -23,8 +23,6 @@ #include // #include -// #include - namespace Gudhi { namespace zigzag_persistence { diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h index b096703dd3..9dc117a939 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h @@ -2,7 +2,7 @@ * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. * Author(s): Clément Maria and Hannah Schreiber * - * Copyright (C) 2023 Inria + * Copyright (C) 2023-25 Inria * * Modification(s): * - YYYY/MM Author: Description of the modification @@ -55,24 +55,22 @@ enum Oscillating_rips_edge_order_policy { /** * @private - * @brief + * @ingroup zigzag_persistence + * @brief Initialize epsilon values and distance matrix from the given parameters and point cloud. * - * @tparam Filtration_value + * @tparam Filtration_value Filtration value type. */ template class Oscillating_rips_initializer { public: /** - * @brief Initialize the distance function and epsilon values. Updates also the multipliers if - * the edge modifier is active. + * @brief Initializes the distance matrix and epsilon values. * * @tparam PointRange Point range type. * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. * @param epsilonValues Container for the epsilon values. - * @param distanceMatrix Container for the distance matrices. + * @param distanceMatrix Container for the distance matrix. * @param points Point cloud as a range.The format of a point has to correspond to the input format of the * distance function. * @param distance Distance function. Has to take two points as it from the range @p points as input parameters @@ -120,23 +118,16 @@ class Oscillating_rips_initializer } /** - * @brief Initialize the distance function and epsilon values. Updates also the multipliers if - * the edge modifier is active. - * + * @brief Initializes the distance matrix. + * * @tparam PointRange Point range type. * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param epsilonValues Container for the epsilon values. - * @param distanceMatrix Container for the distance matrices. - * @param points Point cloud as a range.The format of a point has to correspond to the input format of the - * distance function. + * @param distanceMatrix Container for the distance matrix. The order will correspond to the order of the given + * points. + * @param sortedPoints Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. The order of the points will not change. * @param distance Distance function. Has to take two points as it from the range @p points as input parameters * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either - * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. */ template static void initialize(std::vector > >& distanceMatrix, @@ -174,14 +165,13 @@ class Oscillating_rips_initializer * defined as \f$\varepsilon_i = d_H(P_i,P)\f$, the Hausdorff between the points * \f$P_i= \{p_0, \ldots, p_{i}\}\f$ and the entire point cloud * \f$P = \{p_0, \ldots, p_{n-1}\}\f$. - * + * * @tparam PointRange Point range type. * @tparam DistanceFunction Type of the distance function. * @param sortedPoints Point cloud as an ordered range. The format of a point has to correspond to the input * format of the distance function. * @param distance Distance function. Has to take two points as it from the range @p points as input parameters * and return the distance between those points. - * * @return Vector of decreasing epsilon values ending with 0. */ template @@ -252,7 +242,6 @@ class Oscillating_rips_initializer std::vector > > distanceMatrix(sortedPoints.size()); #ifdef GUDHI_USE_TBB tbb::parallel_for(std::size_t(0), sortedPoints.size(), [&](std::size_t i) { - // distanceMatrix[i] = std::vector< std::pair >(); distanceMatrix[i].resize(i); for (std::size_t j = 0; j < i; ++j) { distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); @@ -263,7 +252,6 @@ class Oscillating_rips_initializer }); #else for (std::size_t i = 0; i < sortedPoints.size(); ++i) { // for all vertices - // distanceMatrix[i] = std::vector< std::pair >(); distanceMatrix[i].resize(i); for (std::size_t j = 0; j < i; ++j) { distanceMatrix[i][j] = std::make_pair(j, distance(sortedPoints[i], sortedPoints[j])); @@ -277,36 +265,27 @@ class Oscillating_rips_initializer }; /** - * @class Oscillating_rips_edge_iterator oscillating_rips_edge_ranges.h \ + * @class Oscillating_rips_edge_iterator_base oscillating_rips_edge_ranges.h \ * gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h - * @brief Custom iterator over the edges of an oscillating rips filtration. - * - * It inherits from boost::iterator_facade. - * - * The iterator stores the distance matrix and epsilon values to compute the current edge on the fly - * at each increment. + * @ingroup zigzag_persistence * - * @warning As the iterator stores possibly large ranges, avoid copying it. + * @brief Heavy base for a custom iterator over the edges of an oscillating rips filtration. + * + * @tparam Filtration_value Filtration value type. Should be compatible with the edge modifier. + * @tparam EdgeModifier Modifier for the edge filtration values. If no modifications are wanted, + * use @ref Identity_edge_modifier. Default value: @ref Identity_edge_modifier. */ template class Oscillating_rips_edge_iterator_base { public: /** - * @brief Constructor. Computes the distance matrix and the epsilon values it-self from the given parameters. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. + * @brief Construct. + * * @param nu Lower multiplier. * @param mu Upper multiplier. - * @param points Point cloud as a range.The format of a point has to correspond to the input format of the - * distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either - * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING "", - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING "". + * @param epsilonValues Pointer to the epsilon values. + * @param distanceMatrix Pointer to the distance matrix. */ Oscillating_rips_edge_iterator_base( Filtration_value nu, @@ -323,6 +302,14 @@ class Oscillating_rips_edge_iterator_base inPositiveDirection_(true), insertVertex_(true) { + GUDHI_CHECK(epsilonValues->size() == distanceMatrix->size(), + "Epsilon values and distance matrix are not compatible."); + + if (epsilonValues->empty()) { + _set_end(); + return; + } + const auto& row = (*distanceMatrix_)[1]; auto it = std::upper_bound( row.begin(), @@ -346,15 +333,10 @@ class Oscillating_rips_edge_iterator_base columnIndex_(0), inPositiveDirection_(true), insertVertex_(true) - { - } + {} /** - * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if to iterators are equal. - * - * @param other Iterator to compare. - * @return True, the iterators are pointing to the same position. - * @return False, otherwise. + * @brief Indicates if two iterators are equal. */ bool equal(Oscillating_rips_edge_iterator_base const& other) const { @@ -362,15 +344,14 @@ class Oscillating_rips_edge_iterator_base } /** - * @brief Mandatory for the boost::iterator_facade inheritance. Returns the value of the dereferenced iterator. + * @brief Returns the value of the dereferenced iterator. * - * @return Current edge. + * @return Current @ref Zigzag_edge edge. */ const Zigzag_edge& dereference() const { return currentEdge_; } /** - * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. - * + * @brief Increments the iterator. */ void increment() { @@ -469,7 +450,7 @@ class Oscillating_rips_edge_iterator_base const Filtration_value mu_; /**< Upper multiplier. */ Zigzag_edge currentEdge_; /**< Stores the current edge in the range. */ std::size_t epsilonIndex_, rowIndex_, columnIndex_; /**< Indices indicating the next position in the range. */ - bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next ''edge'' is a vertex. */ + bool inPositiveDirection_, insertVertex_; /**< Next direction and indicates if next "edge" is a vertex. */ /** * @brief Set the iterator as the end iterator. @@ -561,23 +542,36 @@ class Oscillating_rips_edge_iterator_base }; /** - * @class Oscillating_rips_edge_iterator_range oscillating_rips_edge_ranges.h + * @class Oscillating_rips_edge_iterator_range oscillating_rips_edge_ranges.h \ * gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h - * @brief Gives access and computes different edge range types for an oscillating Rips filtration. - * * @ingroup zigzag_persistence * - * @details There are two different kind of ranges: a vector range containing all computed edges and - * a Boost range made from a custom iterator computing an edge on the fly at each increment. - * The custom iterator is therefore only a forward iterator and can only be incremented. + * @brief Ordered range of @ref Zigzag_edge in the oscillating Rips filtration generated by the given parameters. + * Even though it is called "edge", the simplex can also be a vertex if the two ends in @ref Zigzag_edge have the + * same value. That means, that the range corresponds in fact to the filtration with maximal dimension 1. * + * @details The range is forward traversal only, as the value of the edge is computed on the fly at each increment + * and information of other edges are not stored. This is useful when the filtration is very long. Note that the + * distance matrix and the filtration values are stored however. Note also that each copy of the same iterator will + * increment simultaneously. That is, for example: + * ``` + * Oscillating_rips_edge_iterator_range r(nu, mu, ...); + * auto it1 = r.begin(); + * auto it2 = r.begin(); + * auto it3 = it1; + * ++it1; + * ++it2; ++it2; + * // it1 and it2 are independent, so both have different values now: it1 points to the second edge, where it2 points + * // to the third edge. + * // but it3 is the copy of it1 and therefore, even if it3 was not explicitly incremented, it will still point to + * // the second edge and not the first anymore. + * ``` + * If a more flexible range is needed, use @ref Oscillating_rips_edge_vector_range_constructor::make_range instead. + * It will construct a std::vector of @ref Zigzag_edge "". + * * @tparam Filtration_value Filtration value type. Should be compatible with the edge modifier. * @tparam EdgeModifier Modifier for the edge filtration values. If no modifications are wanted, * use @ref Identity_edge_modifier. Default value: @ref Identity_edge_modifier. - * - * @warning As the custom iterator used for the Boost ranges stores possibly large ranges, avoid copying it. - * Use @ref get_iterator_range, @ref begin and @ref end wisely. If one needs to do something else than to simply - * iterate over the edges in order, then the vector range is for sure the better option. */ template class Oscillating_rips_edge_iterator_range @@ -590,10 +584,10 @@ class Oscillating_rips_edge_iterator_range * * It inherits from boost::iterator_facade. * - * The iterator stores the distance matrix and epsilon values to compute the current edge on the fly - * at each increment. - * - * @warning As the iterator stores possibly large ranges, avoid copying it. + * @warning Each **copy** of the same iterator is pointing to the same base and will therefore update + * **simultaneously**. This is to make the iterators copyable in the first place. If each copy would have its own + * base, a copy would be too heavy to build without caution. Note that the `begin()` method of + * @ref Oscillating_rips_edge_iterator_range does **not** return copies of the same iterator. */ class Oscillating_rips_edge_iterator : public boost::iterator_facade&, @@ -601,26 +595,18 @@ class Oscillating_rips_edge_iterator_range { public: /** - * @brief Constructor. Takes already computed epsilon values as input, but assumes that the points are - * already ordered accordingly. Assumes also that the last epsilon value is 0. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param orderedPoints Point cloud as an ordered range. The format of a point has to correspond to the input - * format of the distance function. The order of the points should be in correspondence with the order of - * the epsilon values. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param epsilonValues Epsilon values for the oscillating rips filtration. Should be in decreasing order. - * And the last value should be 0. + * @brief Constructor. + * + * @param base Pointer to an @ref Oscillating_rips_edge_iterator_base instantiation. */ Oscillating_rips_edge_iterator(Oscillating_rips_edge_iterator_base* base) : base_iterator_(base) - { - } + {} + /** + * @brief Default constructor. Equivalent to the end iterator, but has a high chance to seg fault if incremented + * or dereferenced. + */ Oscillating_rips_edge_iterator() : base_iterator_(nullptr) {} private: @@ -661,10 +647,28 @@ class Oscillating_rips_edge_iterator_range void increment() { base_iterator_->increment(); } }; - Oscillating_rips_edge_iterator_range() - : nu_(0), mu_(0) - {} + /** + * @brief Default constructor. The range will be empty. + */ + Oscillating_rips_edge_iterator_range() : nu_(0), mu_(0) {} + /** + * @brief Constructor. Initializes all necessary data to deduce the vertices and edges of the filtration. + * See the @ref zigzagrips "introduction page" for more details about the arguments. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + */ template Oscillating_rips_edge_iterator_range( Filtration_value nu, @@ -679,6 +683,20 @@ class Oscillating_rips_edge_iterator_range epsilonValues_, distanceMatrix_, points, distance, orderPolicy); } + /** + * @brief Constructor. Initializes all necessary data to deduce the vertices and edges of the filtration. + * See the @ref zigzagrips "introduction page" for more details about the arguments. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param orderedPoints Point cloud already ordered in filtration order. The format of a point has to correspond to + * the input format of the distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Wanted epsilon values. Should be decreasing and end with 0. + */ template Oscillating_rips_edge_iterator_range(Filtration_value nu, Filtration_value mu, @@ -692,25 +710,21 @@ class Oscillating_rips_edge_iterator_range } /** - * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. - * See the @ref zigzagrips "introduction page" for more details about the arguments. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param points Point cloud as a range.The format of a point has to correspond to the input format of the - * distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either - * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. - * - * @return Instantiation of @ref Oscillating_rips_edge_iterator. + * @brief Returns the begin iterator of a the range. * - * @warning Avoid copying the iterator as it is heavier than usual iterators. + * @warning Forward traversal only. And each copy of an iterator will increment simultaneously. That is, for example: + * ``` + * Oscillating_rips_edge_iterator_range r(nu, mu, ...); + * auto it1 = r.begin(); + * auto it2 = r.begin(); + * auto it3 = it1; + * ++it1; + * ++it2; ++it2; + * // it1 and it2 are independent, so both have different values now: it1 points to the second edge, where it2 points + * // to the third edge. + * // but it3 is the copy of it1 and therefore, even if it3 was not explicitly incremented, it will still point to + * // the second edge and not the first anymore. + * ``` */ Oscillating_rips_edge_iterator begin() { @@ -722,12 +736,15 @@ class Oscillating_rips_edge_iterator_range } /** - * @brief Returns the end iterator of a the range of edges based on @ref Oscillating_rips_edge_iterator. - * - * @return Default instantiation of @ref Oscillating_rips_edge_iterator. + * @brief Returns the end iterator of a the range. */ Oscillating_rips_edge_iterator end() { return endIt_; } + /** + * @brief Sets the multipliers of the range. Old iterators are not invalidated and will still point to the old + * filtration. Only new iterators will take into account the changes. Epsilon values and the distance matrix will + * remain unchanged. + */ void set_multipliers(Filtration_value nu, Filtration_value mu) { nu_ = nu; @@ -739,14 +756,49 @@ class Oscillating_rips_edge_iterator_range std::vector > > distanceMatrix_; /**< Distance matrix. */ Filtration_value nu_; /**< Lower multiplier. */ Filtration_value mu_; /**< Upper multiplier. */ + /** + * @brief End iterator. Does not depend on any parameter and can therefore be shared. + */ inline static const Oscillating_rips_edge_iterator endIt_ = Oscillating_rips_edge_iterator(new Oscillating_rips_edge_iterator_base()); }; +/** + * @class Oscillating_rips_edge_vector_range_constructor oscillating_rips_edge_ranges.h \ + * gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h + * @ingroup zigzag_persistence + * + * @brief Constructor class for standard vectors of @ref Zigzag_edge corresponding to the oscillating Rips filtration + * generated by the given parameters. Even though it is called "edge", the simplex can also be a vertex if the two ends + * in @ref Zigzag_edge have the same value. That means, that the range corresponds in fact to the filtration with + * maximal dimension 1. + * + * @tparam Filtration_value Filtration value type. Should be compatible with the edge modifier. + * @tparam EdgeModifier Modifier for the edge filtration values. If no modifications are wanted, + * use @ref Identity_edge_modifier. Default value: @ref Identity_edge_modifier. + */ template class Oscillating_rips_edge_vector_range_constructor { public: + /** + * @brief Builds the oscillating Rips filtration generated by the given parameters with maximal dimension 1 as a + * standard vector of @ref Zigzag_edge "". A vertex is represented by an edge whose two ends have the same value. + * See the @ref zigzagrips "introduction page" for more details about the arguments. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param points Point cloud as a range.The format of a point has to correspond to the input format of the + * distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param orderPolicy Order policy for the points. Can be either + * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, + * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or + * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. + */ template static std::vector > make_range( Filtration_value nu, @@ -769,6 +821,21 @@ class Oscillating_rips_edge_vector_range_constructor epsilonValues); } + /** + * @brief Builds the oscillating Rips filtration generated by the given parameters with maximal dimension 1 as a + * standard vector of @ref Zigzag_edge "". A vertex is represented by an edge whose two ends have the same value. + * See the @ref zigzagrips "introduction page" for more details about the arguments. + * + * @tparam PointRange Point range type. + * @tparam DistanceFunction Type of the distance function. + * @param nu Lower multiplier. + * @param mu Upper multiplier. + * @param orderedPoints Point cloud already ordered in filtration order. The format of a point has to correspond to + * the input format of the distance function. + * @param distance Distance function. Has to take two points as it from the range @p points as input parameters + * and return the distance between those points. + * @param epsilonValues Wanted epsilon values. Should be decreasing and end with 0. + */ template static std::vector > make_range(Filtration_value nu, Filtration_value mu, diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h index fed8908bba..1fc57dfdd6 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h @@ -2,7 +2,7 @@ * See file LICENSE or go to https://gudhi.inria.fr/licensing/ for full license details. * Author(s): Clément Maria and Hannah Schreiber * - * Copyright (C) 2023 Inria + * Copyright (C) 2023-25 Inria * * Modification(s): * - YYYY/MM Author: Description of the modification @@ -11,7 +11,7 @@ /** * @file oscillating_rips_simplex_ranges.h * @author Clément Maria, Hannah Schreiber - * @brief Contains the implementation of the @ref Gudhi::zigzag_persistence::Oscillating_rips_edge_order_policy enum, + * @brief Contains the implementation of the * @ref Gudhi::zigzag_persistence::Oscillating_rips_simplex_iterator_base class, * @ref Gudhi::zigzag_persistence::Oscillating_rips_simplex_iterator_range class and * @ref Gudhi::zigzag_persistence::Oscillating_rips_simplex_vector_range_constructor class. @@ -40,9 +40,8 @@ namespace zigzag_persistence { /** * @private - * @brief - * - * @tparam Filtration_value + * @ingroup zigzag_persistence + * @brief Manages the expansion or the collapse a flag complex if a set of edges is added or respectively removed. */ class Oscillating_rips_edge_expander { @@ -76,6 +75,22 @@ class Oscillating_rips_edge_expander StableFilteredComplex* st_; }; + /** + * @brief Adds necessary simplices in given complex to expand it as a flag complex to whom is added the given edges. + * The edges are obtained from the iterator which is incremented until the filtration value changes (or until end + * iterator is attained). + * + * @tparam StableFilteredComplex Complex type. + * @tparam EdgeRangeIterator Edge iterator type. Has to deference into @ref Zigzag_edge "". + * @param currentEdgeIt Iterator to the first edge in the range to be added. Then all edges which are following and + * and have the same filtration value are also added. + * @param edgeEndIterator End iterator of the edge range. Once reached, indicates that the loop should be stopped + * even if the filtration value did not change. + * @param maxDimension Maximal dimension of the expansion. If -1, it is expanded as much as possible. + * @param complex Complex to expand. + * @param currentSimplices Vector in which are added the simplex handles of all simplices which are added in + * the process. + */ template static void expand_edges_of_same_filtration_value( EdgeRangeIterator& currentEdgeIt, @@ -104,9 +119,19 @@ class Oscillating_rips_edge_expander } /** - * @brief Updates @p currentSimplices_ for removals. - * - * @param fil Current filtration value. + * @brief Selects all simplices in the given complex which should be removed if the given set of edges is removed. + * The edges are given as a range delimited by two iterators, but only the edges following the first and having the + * same filtration value are removed. + * + * @tparam StableFilteredComplex Complex type. + * @tparam EdgeRangeIterator Edge iterator type. Has to deference into @ref Zigzag_edge "". + * @param currentEdgeIt Iterator to the first edge in the range to be removed. Then all edges which are following and + * and have the same filtration value are also removed. + * @param edgeEndIterator End iterator of the edge range. Once reached, indicates that the loop should be stopped + * even if the filtration value did not change. + * @param complex Complex to collapse. + * @param currentSimplices Vector to which will be added all simplex handles of all simplices which should be + * removed. */ template static void collapse_edges_of_same_filtration_value( @@ -154,28 +179,16 @@ class Oscillating_rips_edge_expander }; /** - * @class Oscillating_rips_simplex_iterator oscillating_rips_simplex_ranges.h \ + * @class Oscillating_rips_simplex_iterator_base oscillating_rips_simplex_ranges.h \ * gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h - * @brief Custom iterator over the simplices of an oscillating rips filtration. Is a forward iterator only. - * - * It inherits from boost::iterator_facade. + * @ingroup zigzag_persistence * - * For each positive edge given by the edge range iterator, the given complex computes and adds all possible - * cofaces and they respective simplex handle are stored. For each negative edge, the given complex computes - * the simplex handles of all cofaces and they are stored. - * The simplex handles induced by an edge are stored only the time needed and are removed when reaching the next edge. - * That is, we start by storing the possible cofaces of an edge, then at each increment, - * the next element within the stored simplex handles is retrieved and once the end is reached, - * the simplex handles are erased and replaced by the possible cofaces of the next edge and so on... - * If the edge is negative, then a simplex is removed from the complex at the next increment. - * So, the maximum size of the complex corresponds to the maximum size of a complex in the zigzag filtration. + * @brief Heavy base for a custom iterator over the simplices of an oscillating rips filtration. * - * The simplices are returned by the iterator as tuples of three elements: - * the first is the simplex handle of the simplex in the given complex, - * the second is the filtration value of the corresponding arrow, - * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. - * - * @warning As the iterator stores possibly large ranges, avoid copying it. + * @tparam StableFilteredComplex Filtered complex structure that has stable simplex handles, + * that is, they do not invalidates after an insertion or a removal (except for the removed simplices). + * @tparam EdgeRangeIterator Type of the edge range iterator. Each element of the range is assumed to have + * type @ref Zigzag_edge or at least to be a class with the same public methods than @ref Zigzag_edge. */ template class Oscillating_rips_simplex_iterator_base @@ -187,13 +200,11 @@ class Oscillating_rips_simplex_iterator_base using Simplex_key = typename StableFilteredComplex::Simplex_key; /**< Key type. */ /** - * @brief Constructor. The edge iterators and the complex are not copied, so do not modify or invalidate - * them outside as long as this iterator is different from the end iterator. + * @brief Constructor. The complex is not copied, so do not modify or invalidate + * it outside as long as this iterator is different from the end iterator. * * @param edgeStartIterator Begin iterator of the oscillating Rips edge range. - * Is moved, so the original iterator will probably be invalidated. * @param edgeEndIterator End iterator of the oscillating Rips edge range. - * Is moved, so the original iterator will probably be invalidated. * @param complex Empty complex which will be used to store current simplices. * Only the address of the complex is stored, so the state of the complex can be * consulted between each increment. @@ -233,19 +244,14 @@ class Oscillating_rips_simplex_iterator_base } /** - * @brief Default constructor. Corresponds to the end iterator. + * @brief Default constructor. Equivalent to the end iterator. */ Oscillating_rips_simplex_iterator_base() : complex_(nullptr), currentSimplexIndex_(0), currentDirection_(true), maxDimension_(0), currentArrowNumber_(0) - { - } + {} /** - * @brief Mandatory for the boost::iterator_facade inheritance. Indicates if two iterators are equal. - * - * @param other Iterator to compare. - * @return True, if the two iterators point to the same position. - * @return False, otherwise. + * @brief Indicates if two iterators are equal. */ bool equal(Oscillating_rips_simplex_iterator_base const& other) const { @@ -256,14 +262,17 @@ class Oscillating_rips_simplex_iterator_base } /** - * @brief Mandatory for the boost::iterator_facade inheritance. Dereference the iterator. - * - * @return Value of the current return element. + * @brief Dereferences the iterator. Returns a tuple in this order: + * - simplex handle of the complex corresponding to the current simplex (use the complex passed by parameter at + * the constructor to retrieve information about the simplex) + * - Filtration value of the simplex + * - Boolean corresponding to the direction of the arrow, i.e., if the simplex was added or removed. The value + * is `true` for a forward arrow and `false` for a backward arrow. */ const std::tuple& dereference() const { return currentArrow_; } /** - * @brief Mandatory for the boost::iterator_facade inheritance. Increments the iterator. + * @brief Increments the iterator. */ void increment() { @@ -301,39 +310,13 @@ class Oscillating_rips_simplex_iterator_base } private: - /** - * @brief Reverse lexicographical order for the simplex handles. - */ - struct reverse_lexicographic_order { - explicit reverse_lexicographic_order(StableFilteredComplex* st) : st_(st) {} - - bool operator()(const Simplex_handle sh1, const Simplex_handle sh2) const - { - auto rg1 = st_->simplex_vertex_range(sh1); - auto rg2 = st_->simplex_vertex_range(sh2); - auto it1 = rg1.begin(); - auto it2 = rg2.begin(); - while (it1 != rg1.end() && it2 != rg2.end()) { - if (*it1 == *it2) { - ++it1; - ++it2; - } else { - return *it1 < *it2; - } - } - return ((it1 == rg1.end()) && (it2 != rg2.end())); - } - - StableFilteredComplex* st_; - }; - - std::vector currentSimplices_; /**< Stores current simplex handles. */ - StableFilteredComplex* complex_; /**< Pointer to the complex. */ - std::size_t currentSimplexIndex_; /**< Index to current position in currentSimplices_. */ - EdgeRangeIterator currentEdgeIt_; /**< Iterator pointing to the next edge. */ - EdgeRangeIterator endEdgeIt_; /**< End edge iterator. */ - bool currentDirection_; /**< Current direction. */ - const int maxDimension_; /**< Maximal dimension of expansion. */ + std::vector currentSimplices_; /**< Stores current simplex handles. */ + StableFilteredComplex* complex_; /**< Pointer to the complex. */ + std::size_t currentSimplexIndex_; /**< Index to current position in currentSimplices_. */ + EdgeRangeIterator currentEdgeIt_; /**< Iterator pointing to the next edge. */ + EdgeRangeIterator endEdgeIt_; /**< End edge iterator. */ + bool currentDirection_; /**< Current direction. */ + const int maxDimension_; /**< Maximal dimension of expansion. */ std::tuple currentArrow_; /**< Current return element. */ Simplex_key currentArrowNumber_; /**< Number of increments. */ @@ -346,23 +329,38 @@ class Oscillating_rips_simplex_iterator_base /** * @class Oscillating_rips_simplex_iterator_range oscillating_rips_simplex_ranges.h * gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h - * @brief Gives access to a range of the ordered simplices in an oscillating Rips filtration. - * * @ingroup zigzag_persistence * + * @brief Ordered range of tuples corresponding to the simplices of an oscillating Rips filtration generated by + * the given range of edges. + * * @details The simplices are returned as tuples of three elements: * the first is the simplex handle of the simplex in the given complex, * the second is the filtration value of the corresponding arrow, - * the third is the direction of the arrow, i.e., indicates if the simplex is inserted or removed. - * For more information, see @ref Oscillating_rips_iterator. + * the third is the direction of the arrow, i.e., indicates if the simplex is inserted (`true`) or removed (`false`). + * + * The range is forward traversal only, as the simplices are computed on the fly at each increment and unecessary + * information for the current simplex are not stored. This is useful when the filtration is very long. + * Note that each copy of the same iterator will increment simultaneously. That is, for example: + * ``` + * Oscillating_rips_simplex_iterator_range r(edgeBegin, edgeEnd, ...); + * auto it1 = r.begin(); + * auto it2 = r.begin(); + * auto it3 = it1; + * ++it1; + * ++it2; ++it2; + * // it1 and it2 are independent, so both have different values now: it1 points to the second simplex, where it2 points + * // to the third simplex. + * // but it3 is the copy of it1 and therefore, even if it3 was not explicitly incremented, it will still point to + * // the second simplex and not the first anymore. + * ``` + * If a more flexible range is needed, use @ref Oscillating_rips_simplex_vector_range_constructor::make_range instead. + * It will construct a std::vector of the tuples, but with the vertices of the simplex instead of the simplex handles. * * @tparam StableFilteredComplex Filtered complex structure that has stable simplex handles, * that is, they do not invalidates after an insertion or a removal (except for the removed simplices). * @tparam EdgeRangeIterator Type of the edge range iterator. Each element of the range is assumed to have * type @ref Zigzag_edge or at least to be a class with the same public methods than @ref Zigzag_edge. - * - * @warning As the custom iterator stores a possibly large range, avoid copying it. - * Use @ref get_iterator_range, @ref begin and @ref end wisely. */ template class Oscillating_rips_simplex_iterator_range @@ -371,14 +369,14 @@ class Oscillating_rips_simplex_iterator_range /** * @class Oscillating_rips_simplex_iterator oscillating_rips_simplex_ranges.h \ * gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h - * @brief Custom iterator over the edges of an oscillating rips filtration. + * @brief Custom iterator over the simplices of an oscillating rips filtration. * * It inherits from boost::iterator_facade. * - * The iterator stores the distance matrix and epsilon values to compute the current edge on the fly - * at each increment. - * - * @warning As the iterator stores possibly large ranges, avoid copying it. + * @warning Each **copy** of the same iterator is pointing to the same base and will therefore update + * **simultaneously**. This is to make the iterators copyable in the first place. If each copy would have its own + * base, a copy would be too heavy to build without caution. Note that the `begin()` method of + * @ref Oscillating_rips_simplex_iterator_range does **not** return copies of the same iterator. */ class Oscillating_rips_simplex_iterator : public boost::iterator_facade* base) : base_iterator_(base) - { - } + {} private: // mandatory for the boost::iterator_facade inheritance. @@ -437,7 +423,7 @@ class Oscillating_rips_simplex_iterator_range /** * @brief Mandatory for the boost::iterator_facade inheritance. Returns the value of the dereferenced iterator. * - * @return Current edge. + * @return Current simplex. */ const std::tuple& dereference() const { @@ -450,6 +436,18 @@ class Oscillating_rips_simplex_iterator_range void increment() { base_iterator_->increment(); } }; + /** + * @brief Constructor. The complex is not copied, so do not modify or invalidate + * it outside as long as this iterator is different from the end iterator. + * + * @param edgeStartIterator Begin iterator of the oscillating Rips edge range. + * @param edgeEndIterator End iterator of the oscillating Rips edge range. + * @param complex Empty complex which will be used to store current simplices. + * Only the address of the complex is stored, so the state of the complex can be + * consulted between each increment. + * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. + * Default value: -1. + */ Oscillating_rips_simplex_iterator_range(const EdgeRangeIterator& edgeStartIterator, const EdgeRangeIterator& edgeEndIterator, StableFilteredComplex& complex, @@ -458,29 +456,24 @@ class Oscillating_rips_simplex_iterator_range edgeEndIterator_(edgeEndIterator), complex_(&complex), maxDimension_(maxDimension) - { - } + {} /** - * @brief Returns the begin iterator of a the range of edges based on @ref Oscillating_rips_simplex_iterator. - * See the @ref zigzagrips "introduction page" for more details about the arguments. - * - * @tparam PointRange Point range type. - * @tparam DistanceFunction Type of the distance function. - * @param nu Lower multiplier. - * @param mu Upper multiplier. - * @param points Point cloud as a range.The format of a point has to correspond to the input format of the - * distance function. - * @param distance Distance function. Has to take two points as it from the range @p points as input parameters - * and return the distance between those points. - * @param orderPolicy Order policy for the points. Can be either - * @ref Oscillating_rips_edge_order_policy::FARTHEST_POINT_ORDERING, - * @ref Oscillating_rips_edge_order_policy::ALREADY_ORDERED or - * @ref Oscillating_rips_edge_order_policy::RANDOM_POINT_ORDERING. - * - * @return Instantiation of @ref Oscillating_rips_simplex_iterator. + * @brief Returns the begin iterator of a the range. * - * @warning Avoid copying the iterator as it is heavier than usual iterators. + * @warning Forward traversal only. And each copy of an iterator will increment simultaneously. That is, for example: + * ``` + * Oscillating_rips_simplex_iterator_range r(edgeBegin, edgeEnd, ...); + * auto it1 = r.begin(); + * auto it2 = r.begin(); + * auto it3 = it1; + * ++it1; + * ++it2; ++it2; + * // it1 and it2 are independent, so both have different values now: it1 points to the second simplex, where it2 + * // points to the third simplex. + * // but it3 is the copy of it1 and therefore, even if it3 was not explicitly incremented, it will still point to + * // the second simplex and not the first anymore. + * ``` */ Oscillating_rips_simplex_iterator begin() { @@ -493,35 +486,66 @@ class Oscillating_rips_simplex_iterator_range } /** - * @brief Returns the end iterator of a the range of edges based on @ref Oscillating_rips_simplex_iterator. - * - * @return Default instantiation of @ref Oscillating_rips_simplex_iterator. + * @brief Returns the end iterator of a the range. */ Oscillating_rips_simplex_iterator end() { return endIt_; } private: - EdgeRangeIterator edgeStartIterator_; - EdgeRangeIterator edgeEndIterator_; - StableFilteredComplex* complex_; - int maxDimension_; + EdgeRangeIterator edgeStartIterator_; /**< Begin edge iterator. */ + EdgeRangeIterator edgeEndIterator_; /**< End edge iterator. */ + StableFilteredComplex* complex_; /**< Pointer to the complex storing living simplices. */ + int maxDimension_; /**< Maximal expansion dimension. */ + /** + * @brief End iterator. Does not depend on any parameter and can therefore be shared. + */ inline static const Oscillating_rips_simplex_iterator endIt_ = Oscillating_rips_simplex_iterator( new Oscillating_rips_simplex_iterator_base()); }; +/** + * @class Oscillating_rips_simplex_vector_range_constructor oscillating_rips_simplex_ranges.h \ + * gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h + * @ingroup zigzag_persistence + * + * @brief Constructor class for standard vectors of @ref Simplex corresponding to the simplices of an oscillating Rips + * filtration generated by the given range of edges. + * + * @details The simplices are returned as @ref Simplex "", structure of three elements: + * the first is the vertex range of the simplex (vertices are labeled by their label used in the given edge range), + * the second is the filtration value of the corresponding arrow, + * the third is the direction of the arrow, i.e., indicates if the simplex is inserted (`true`) or removed (`false`). + * + * @tparam StableFilteredComplex Filtered complex structure that has stable simplex handles, + * that is, they do not invalidates after an insertion or a removal (except for the removed simplices). + */ template class Oscillating_rips_simplex_vector_range_constructor { public: - using Vertex_handle = typename StableFilteredComplex::Vertex_handle; - using Simplex_handle = typename StableFilteredComplex::Simplex_handle; - using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ + using Vertex_handle = typename StableFilteredComplex::Vertex_handle; /**< Vertex handle type. */ + using Simplex_handle = typename StableFilteredComplex::Simplex_handle; /**< Simplex handle type. */ + using Filtration_value = typename StableFilteredComplex::Filtration_value; /**< Filtration value type. */ + /** + * @brief Simplex structure. + */ struct Simplex { - std::vector vertices; - Filtration_value filtration_value; - bool direction; + std::vector vertices; /**< Vertices of the simplex. */ + Filtration_value filtration_value; /**< Filtration value of the simplex. */ + bool direction; /**< True if the simplex is inserted at the filtration value, or false + if the simplex is removed instead. */ }; + /** + * @brief Builds the oscillating Rips filtration from the given range of edges as a vector of @ref Simplex "". + * + * @tparam EdgeRangeIterator Type of the edge range iterator. Each element of the range is assumed to have + * type @ref Zigzag_edge or at least to be a class with the same public methods than @ref Zigzag_edge. + * @param edgeStartIterator Begin iterator of the oscillating Rips edge range. + * @param edgeEndIterator End iterator of the oscillating Rips edge range. + * @param maxDimension Maximal dimension to which to expand the complex. If set to -1, there is no limit. + * Default value: -1. + */ template static std::vector make_range(EdgeRangeIterator edgeStartIterator, EdgeRangeIterator edgeEndIterator, From 6d5d9013641dc173d306ecfd99983bc090cd3683 Mon Sep 17 00:00:00 2001 From: hschreiber <48448038+hschreiber@users.noreply.github.com> Date: Thu, 13 Feb 2025 13:45:35 +0100 Subject: [PATCH 18/21] Update src/Zigzag_persistence/doc/Intro_zigzag_persistence.h Co-authored-by: Marc Glisse --- src/Zigzag_persistence/doc/Intro_zigzag_persistence.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 4b6929b6d5..1e4f11421f 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -63,7 +63,7 @@ namespace zigzag_persistence { * This module implements the construction of such an oscillating Rips filtration as follows: * \image html "osc_rips.svg" width=70% * - * If \f$ P \f$ is the set of points generating the rips filtration, \f$ P_i \f$ corresponds to the subset containing + * If \f$ P \f$ is the set of points generating the Rips filtration, \f$ P_i \f$ corresponds to the subset containing * the \f$ i \f$ first points. So, at each forward inclusion, one vertex is added. * * The superscript of \f$ \mathcal{R} \f$ corresponds to the filtration values which will be associated to the cycles. From 3cf9a49e585f601b5027f5118fc2c70561ebf493 Mon Sep 17 00:00:00 2001 From: hschreiber <48448038+hschreiber@users.noreply.github.com> Date: Thu, 13 Feb 2025 13:45:46 +0100 Subject: [PATCH 19/21] Update src/Zigzag_persistence/doc/Intro_zigzag_persistence.h Co-authored-by: Marc Glisse --- src/Zigzag_persistence/doc/Intro_zigzag_persistence.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 1e4f11421f..dc7e456771 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -86,7 +86,7 @@ namespace zigzag_persistence { * can potentially pass any other type of edge range as long as the dereferenced format corresponds and the sequence * makes sense as a zigzag filtration. * - * If only the barcode of the filtration is of interest and not the filtration it-self, the helper method + * If only the barcode of the filtration is of interest and not the filtration itself, the helper method * @ref compute_oscillating_rips_persistence can be used. It will directly feed to the filtration constructed by the * two classes above into @ref Zigzag_persistence "". * From ba0d7db9bdbece5b42bf586b391490e2c1648363 Mon Sep 17 00:00:00 2001 From: hschreiber <48448038+hschreiber@users.noreply.github.com> Date: Thu, 13 Feb 2025 13:45:54 +0100 Subject: [PATCH 20/21] Update src/Zigzag_persistence/doc/Intro_zigzag_persistence.h Co-authored-by: Marc Glisse --- src/Zigzag_persistence/doc/Intro_zigzag_persistence.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index dc7e456771..7d59edb916 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -77,7 +77,7 @@ namespace zigzag_persistence { * * The construction is based on two types of classes: * - @ref Oscillating_rips_edge_iterator_range and @ref Oscillating_rips_edge_vector_range_constructor computes the - * range of inserted and removed vertices and edges in the filtration based on the elements descipted above. + * range of inserted and removed vertices and edges in the filtration based on the elements described above. * - @ref Oscillating_rips_simplex_iterator_range and @ref Oscillating_rips_simplex_vector_range_constructor infers * from @ref Oscillating_rips_edge_iterator_range or @ref Oscillating_rips_edge_vector_range_constructor all simplices * from higher dimensions (i.e. > 1) of the filtration if necessary. From b0ebdcb3475a12159de9b88d8217227ddf96f6cd Mon Sep 17 00:00:00 2001 From: hschreiber Date: Thu, 13 Feb 2025 15:13:40 +0100 Subject: [PATCH 21/21] doc corrections --- .../doc/Intro_zigzag_persistence.h | 17 ++++++++++++----- ...xample_usage_filtered_zigzag_persistence.cpp | 2 +- ...filtered_zigzag_persistence_with_storage.cpp | 2 +- .../example_usage_zigzag_persistence.cpp | 2 +- .../oscillating_rips_edge_ranges.h | 2 +- .../oscillating_rips_simplex_ranges.h | 2 +- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h index 7d59edb916..9360eca9cd 100644 --- a/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h +++ b/src/Zigzag_persistence/doc/Intro_zigzag_persistence.h @@ -66,15 +66,18 @@ namespace zigzag_persistence { * If \f$ P \f$ is the set of points generating the Rips filtration, \f$ P_i \f$ corresponds to the subset containing * the \f$ i \f$ first points. So, at each forward inclusion, one vertex is added. * - * The superscript of \f$ \mathcal{R} \f$ corresponds to the filtration values which will be associated to the cycles. - * The sequence of \f$ \varepsilon \f$ should be decreasing and end with \f$ 0 \f$. The sequence can either be specified - * by the user or automatically generated by choosing the distance of the added point from the other already added - * points from the set. In the second case, the user has to specify how the order of the points should be decided. + * The superscript of \f$ \mathcal{R} \f$ corresponds to a sequence of \f$ \varepsilon \f$ which should be decreasing + * and end with \f$ 0 \f$. The sequence can either be specified by the user or automatically generated by choosing + * the distance of the added point from the other already added points from the set. In the second case, the user has + * to specify how the order of the points should be decided. * * The subscript of \f$ \mathcal{R} \f$ corresponds like usual to the radius of the Rips complex. It is generated from - * the current epsilon / filtration value by multiplying it alternatively by two coefficients: \f$ \nu \leqslant \mu \f$. + * the current epsilon value by multiplying it alternatively by two coefficients: \f$ \nu \leqslant \mu \f$. * Both multipliers have to be specified by the user. * + * As filtration values for the simplices, the module chooses the epsilon value "active" at the moment of insertion or + * removal. + * * The construction is based on two types of classes: * - @ref Oscillating_rips_edge_iterator_range and @ref Oscillating_rips_edge_vector_range_constructor computes the * range of inserted and removed vertices and edges in the filtration based on the elements described above. @@ -86,6 +89,10 @@ namespace zigzag_persistence { * can potentially pass any other type of edge range as long as the dereferenced format corresponds and the sequence * makes sense as a zigzag filtration. * + * Both _*_iterator_range*_ and _*_vector_range*_ methods are computing the same, they differ just on their + * storing strategy: _*_iterator_range*_ methods are always computing the edges/simplices on the fly at each iteration + * while the _*_vector_range*_ methods compute everything at once and store the computed edges/simplices in a vector. + * * If only the barcode of the filtration is of interest and not the filtration itself, the helper method * @ref compute_oscillating_rips_persistence can be used. It will directly feed to the filtration constructed by the * two classes above into @ref Zigzag_persistence "". diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp index 1ceb362015..a97d381f53 100644 --- a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence.cpp @@ -25,7 +25,7 @@ int main() { }); // It is important that the operations of insertions and removals are made **in the same order** as in the zigzag - // filtration ones wants to compute the barcode from. + // filtration one wants to compute the barcode from. // A cell can be identified in the boundaries by any given numerical label, it is just important that the given // filtration values are monotonous (ie., either only increasing or only decreasing). diff --git a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp index cd14b60fe0..b82f4eb9f8 100644 --- a/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp +++ b/src/Zigzag_persistence/example/example_usage_filtered_zigzag_persistence_with_storage.cpp @@ -20,7 +20,7 @@ int main() { Zigzag_persistence zp; // It is important that the operations of insertions and removals are made **in the same order** as in the zigzag - // filtration ones wants to compute the barcode from. + // filtration one wants to compute the barcode from. // A cell can be identified in the boundaries by any given numerical label, it is just important that the given // filtration values are monotonous (ie., either only increasing or only decreasing). diff --git a/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp b/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp index e50f94cfc0..4883be4ace 100644 --- a/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp +++ b/src/Zigzag_persistence/example/example_usage_zigzag_persistence.cpp @@ -25,7 +25,7 @@ int main() { }); // It is important that the operations of insertions and removals are made **in the same order** as in the zigzag - // filtration ones wants to compute the barcode from. + // filtration one wants to compute the barcode from. // A cell has to be identified in the boundaries by the operation number the cell was inserted with in the sequence. // inserts vertex 0 -> birth at 0 of 0-cycle diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h index 9dc117a939..13d22f6a61 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h @@ -582,7 +582,7 @@ class Oscillating_rips_edge_iterator_range * gudhi/Zigzag_persistence/oscillating_rips_edge_ranges.h * @brief Custom iterator over the edges of an oscillating rips filtration. * - * It inherits from boost::iterator_facade. + * Category: LegacyInputIterator. And it inherits from boost::iterator_facade. * * @warning Each **copy** of the same iterator is pointing to the same base and will therefore update * **simultaneously**. This is to make the iterators copyable in the first place. If each copy would have its own diff --git a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h index 1fc57dfdd6..544d1a6101 100644 --- a/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h +++ b/src/Zigzag_persistence/include/gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h @@ -371,7 +371,7 @@ class Oscillating_rips_simplex_iterator_range * gudhi/Zigzag_persistence/oscillating_rips_simplex_ranges.h * @brief Custom iterator over the simplices of an oscillating rips filtration. * - * It inherits from boost::iterator_facade. + * Category: LegacyInputIterator. And it inherits from boost::iterator_facade. * * @warning Each **copy** of the same iterator is pointing to the same base and will therefore update * **simultaneously**. This is to make the iterators copyable in the first place. If each copy would have its own