68#ifdef USE_ROBINHOOD_HASHMAP
70#include "robin_hood.h"
72template <
class Key,
class T,
class H,
class E>
73using hash_map = robin_hood::unordered_map<Key, T, H, E>;
75using hash = robin_hood::hash<Key>;
79template <
class Key,
class T,
class H,
class E>
80using hash_map = std::unordered_map<Key, T, H, E>;
82using hash = std::hash<Key>;
86#ifdef INDICATE_PROGRESS
87static const std::chrono::milliseconds time_step(40);
90static const std::string clear_line(
"\r\033[K");
92static const size_t num_coefficient_bits = 8;
94#if defined(TTK_ENABLE_RIPSER_128BITS_IDS) \
95 && (defined(__GNUC__) || defined(__clang__))
96static const index_t max_simplex_index
97 = (__int128(1) << (8 *
sizeof(
index_t) - 1 - num_coefficient_bits)) - 1;
101#ifdef USE_COEFFICIENTS
102 (i > max_simplex_index)
106 throw std::overflow_error(
"simplex index " + std::to_string((uint64_t)i)
107 +
" in filtration is larger than maximum index");
111static const index_t max_simplex_index
112 = (uintptr_t(1) << (8 *
sizeof(
index_t) - 1 - num_coefficient_bits)) - 1;
116#ifdef USE_COEFFICIENTS
117 (i > max_simplex_index)
121 throw std::overflow_error(
"simplex index " + std::to_string((uint64_t)i)
122 +
" in filtration is larger than maximum index "
123 + std::to_string(max_simplex_index));
129 std::vector<index_t> B;
135 for(
index_t i = 0; i <= n; ++i) {
137 for(
index_t j = 1; j < std::min(i, k + 1); ++j)
139 = B[(i - 1) * offset + j - 1] + B[(i - 1) * offset + j];
141 B[i * offset + i] = 1;
147 assert(n <
index_t(B.size() / offset) && k <
index_t(offset) && n >= k - 1);
148 return B[n * offset + k];
156 return (modulus == 2) ? val & 1 : val % modulus;
160 return n > modulus / 2 ? n - modulus : n;
163std::vector<coefficient_t>
165 std::vector<coefficient_t> inverse(m);
171 inverse[a] = m - (inverse[m % a] * (m / a)) % m;
175#ifdef USE_COEFFICIENTS
179#define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
181#define PACK(...) __attribute__((__packed__)) __VA_ARGS__
186 index_t coefficient : num_coefficient_bits;
188 : index(_index), coefficient(_coefficient) {
192 entry_t() : index(0), coefficient(0) {
197 "size of entry_t is not the same as index_t");
212 return e.coefficient;
313template <
typename Entry>
327template <compressed_matrix_layout Layout>
340 template <
typename DistanceMatrix>
345 for(
size_t i = 1; i <
size(); ++i)
346 for(
size_t j = 0; j < i; ++j)
347 rows[i][j] = mat(i, j);
366 for(
size_t i = 1; i <
size(); ++i) {
375 for(
size_t i = 0; i <
size() - 1; ++i) {
377 pointer +=
size() - i - 2;
384 return i == j ? 0 : i < j ?
rows[j][i] :
rows[i][j];
390 return i == j ? 0 : i > j ?
rows[j][i] :
rows[i][j];
397 mutable std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
399 mutable std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
403 std::vector<std::vector<index_diameter_t>> &&_neighbors,
index_t _num_edges)
407 template <
typename DistanceMatrix>
410 for(
size_t i = 0; i <
size(); ++i)
411 for(
size_t j = 0; j <
size(); ++j)
412 if(i != j && mat(i, j) <= threshold) {
424 std::vector<std::vector<value_t>>
points;
427 :
points(std::move(_points)) {
433 return std::sqrt(std::inner_product(
435 std::plus<value_t>(),
445 std::vector<index_t> parent;
446 std::vector<uint8_t> rank;
456 while((z = parent[y]) != y)
458 while((z = parent[x]) != y) {
468 if(rank[x] > rank[y])
472 if(rank[x] == rank[y])
483T
end(std::pair<T, T> &p) {
487template <
typename ValueType>
489 std::vector<size_t> bounds;
490 std::vector<ValueType> entries;
492 using iterator =
typename std::vector<ValueType>::iterator;
493 using iterator_pair = std::pair<iterator, iterator>;
497 return bounds.size();
501 return {entries.begin() + (index == 0 ? 0 : bounds[index - 1]),
502 entries.begin() + bounds[index]};
506 bounds.push_back(entries.size());
511 entries.push_back(e);
522template <
typename DistanceMatrix>
524 const DistanceMatrix dist;
530 const std::vector<coefficient_t> multiplicative_inverse;
531 mutable std::vector<diameter_entry_t> cofacet_entries;
534 std::size_t operator()(
const entry_t &e)
const {
535#if defined(USE_ROBINHOOD_HASHMAP)
536 return robin_hood::hash<index_t>()(
::get_index(e));
557 : dist(std::move(_dist)), n(dist.size()), dim_max(_dim_max),
558 threshold(_threshold), ratio(_ratio), modulus(_modulus),
559 binomial_coeff(n, dim_max + 2),
567 if(binomial_coeff(top, k) > idx) {
574 if(binomial_coeff(mid, k) > idx) {
585 return binomial_coeff(i, 2) + j;
588 template <
typename OutputIterator>
592 OutputIterator out)
const {
594 for(
index_t k = dim + 1; k > 0; --k) {
597 idx -= binomial_coeff(n_, k);
602 class simplex_coboundary_enumerator;
606 std::vector<diameter_index_t> &columns_to_reduce,
607 entry_hash_map &pivot_column_index,
609#ifdef INDICATE_PROGRESS
610 std::cerr << clear_line <<
"assembling columns" << std::flush;
611 std::chrono::steady_clock::time_point next
612 = std::chrono::steady_clock::now() + time_step;
615 columns_to_reduce.clear();
616 std::vector<diameter_index_t> next_simplices;
623#ifdef INDICATE_PROGRESS
624 if(std::chrono::steady_clock::now() > next) {
625 std::cerr << clear_line <<
"assembling " << next_simplices.size()
626 <<
" columns (processing "
627 << std::distance(&simplices[0], &simplex) <<
"/"
628 << simplices.size() <<
" simplices)" << std::flush;
629 next = std::chrono::steady_clock::now() + time_step;
632 auto cofacet = cofacets.
next();
635 next_simplices.emplace_back(
638 if(pivot_column_index.find(
get_entry(cofacet))
639 == pivot_column_index.end())
640 columns_to_reduce.emplace_back(
646 simplices.swap(next_simplices);
648#ifdef INDICATE_PROGRESS
649 std::cerr << clear_line <<
"sorting " << columns_to_reduce.size()
650 <<
" columns" << std::flush;
653 std::sort(columns_to_reduce.begin(), columns_to_reduce.end(),
656#ifdef INDICATE_PROGRESS
657 std::cerr << clear_line << std::flush;
662 std::vector<diameter_index_t> &columns_to_reduce,
663 std::vector<std::vector<pers_pair_t>> &ph) {
667 std::sort(edges.rbegin(), edges.rend(),
669 std::vector<index_t> vertices_of_edge(2);
670 for(
auto e : edges) {
673 v = dset.
find(vertices_of_edge[1]);
680 {u == dset.
find(vertices_of_edge[0]) ? vertices_of_edge[1]
681 : vertices_of_edge[0]},
684 {vertices_of_edge[0], vertices_of_edge[1]},
get_diameter(e)});
686 columns_to_reduce.push_back(e);
688 std::reverse(columns_to_reduce.begin(), columns_to_reduce.end());
690 for(
index_t i = 0; i < n; ++i) {
691 if(dset.
find(i) == i)
698 template <
typename Column>
701#ifdef USE_COEFFICIENTS
702 while(!column.empty()) {
704 pivot = column.top();
715 while(!column.empty()) {
716 pivot = column.top();
726 template <
typename Column>
734 template <
typename Column>
737 Column &working_coboundary,
739 entry_hash_map &pivot_column_index) {
740 bool check_for_emergent_pair =
true;
741 cofacet_entries.clear();
746 cofacet_entries.push_back(cofacet);
747 if(check_for_emergent_pair
749 if(pivot_column_index.find(
get_entry(cofacet))
750 == pivot_column_index.end())
752 check_for_emergent_pair =
false;
756 for(
auto cofacet : cofacet_entries)
757 working_coboundary.push(cofacet);
761 template <
typename Column>
764 Column &working_reduction_column,
765 Column &working_coboundary) {
766 working_reduction_column.push(simplex);
771 working_coboundary.push(cofacet);
775 template <
typename Column>
778 const std::vector<diameter_index_t> &columns_to_reduce,
779 const size_t index_column_to_add,
782 Column &working_reduction_column,
783 Column &working_coboundary) {
785 columns_to_reduce[index_column_to_add], factor);
787 column_to_add, dim, working_reduction_column, working_coboundary);
790 reduction_matrix.
subrange(index_column_to_add)) {
793 simplex, dim, working_reduction_column, working_coboundary);
799 std::vector<diameter_entry_t>,
803 entry_hash_map &pivot_column_index,
805 std::vector<std::vector<pers_pair_t>> &ph) {
807 size_t index_column_to_add;
809#ifdef INDICATE_PROGRESS
810 std::chrono::steady_clock::time_point next
811 = std::chrono::steady_clock::now() + time_step;
814 for(
size_t index_column_to_reduce = 0;
815 index_column_to_reduce < columns_to_reduce.size();
816 ++index_column_to_reduce) {
818 columns_to_reduce[index_column_to_reduce], 1);
826 working_reduction_column.push(column_to_reduce);
829 column_to_reduce, working_coboundary, dim, pivot_column_index);
832#ifdef INDICATE_PROGRESS
833 if(std::chrono::steady_clock::now() > next) {
834 std::cerr << clear_line <<
"reducing column "
835 << index_column_to_reduce + 1 <<
"/"
836 << columns_to_reduce.size() <<
" (diameter " << diameter
837 <<
")" << std::flush;
838 next = std::chrono::steady_clock::now() + time_step;
842 auto pair = pivot_column_index.find(
get_entry(pivot));
843 if(pair != pivot_column_index.end()) {
844 entry_t other_pivot = pair->first;
845 index_column_to_add = pair->second;
854 index_column_to_add, factor, dim,
855 working_reduction_column, working_coboundary);
860 if(death > diameter * ratio) {
861 std::vector<index_t> vertices_birth(dim + 1),
862 vertices_death(dim + 2);
864 get_index(column_to_reduce), dim, n, vertices_birth.rbegin());
866 get_index(pivot), dim + 1, n, vertices_death.rbegin());
871 pivot_column_index.insert(
872 {
get_entry(pivot), index_column_to_reduce});
886 std::vector<index_t> vertices_birth(dim + 1);
888 get_index(column_to_reduce), dim, n, vertices_birth.rbegin());
889 ph[dim].emplace_back(
896#ifdef INDICATE_PROGRESS
897 std::cerr << clear_line << std::flush;
903 std::vector<diameter_index_t> simplices, columns_to_reduce;
911 for(
index_t dim = 1; dim <= dim_max; ++dim) {
912 entry_hash_map pivot_column_index;
913 pivot_column_index.reserve(columns_to_reduce.size());
915 compute_pairs(columns_to_reduce, pivot_column_index, dim, ph);
919 simplices, columns_to_reduce, pivot_column_index, dim + 1);
927 index_t idx_below, idx_above, v, k;
928 std::vector<index_t> vertices;
939 : idx_below(
get_index(_simplex)), idx_above(0), v(parent.n - 1),
940 k(_dim + 1), vertices(_dim + 1), simplex(_simplex),
941 modulus(parent.modulus), dist(parent.dist),
942 binomial_coeff(parent.binomial_coeff) {
944 get_index(_simplex), _dim, parent.n, vertices.begin());
948 return (v >= k && (all_cofacets || binomial_coeff(v, k) > idx_below));
952 while((binomial_coeff(v, k) <= idx_below)) {
953 idx_below -= binomial_coeff(v, k);
954 idx_above += binomial_coeff(v, k + 1);
961 cofacet_diameter = std::max(cofacet_diameter, dist(v, w));
962 index_t cofacet_index = idx_above + binomial_coeff(v--, k + 1) + idx_below;
966 cofacet_diameter, cofacet_index, cofacet_coefficient);
972 index_t idx_below, idx_above, k;
973 std::vector<index_t> vertices;
978 std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
980 std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
988 : idx_below(
get_index(_simplex)), idx_above(0), k(_dim + 1),
989 vertices(_dim + 1), simplex(_simplex), modulus(parent.modulus),
990 dist(parent.dist), binomial_coeff(parent.binomial_coeff),
991 neighbor_it(dist.neighbor_it), neighbor_end(dist.neighbor_end) {
993 neighbor_end.clear();
997 for(
auto v : vertices) {
998 neighbor_it.push_back(dist.
neighbors[v].rbegin());
999 neighbor_end.push_back(dist.
neighbors[v].rend());
1004 for(
auto &it0 = neighbor_it[0], &end0 = neighbor_end[0]; it0 != end0;
1007 for(
size_t idx = 1; idx < neighbor_it.size(); ++idx) {
1008 auto &it = neighbor_it[idx],
end = neighbor_end[idx];
1013 goto continue_outer;
1015 neighbor = std::max(neighbor, *it);
1017 while(k > 0 && vertices[k - 1] >
get_index(neighbor)) {
1020 idx_below -= binomial_coeff(vertices[k - 1], k);
1021 idx_above += binomial_coeff(vertices[k - 1], k + 1);
1035 = idx_above + binomial_coeff(
get_index(neighbor), k + 1) + idx_below;
1039 cofacet_diameter, cofacet_index, cofacet_coefficient);
1044std::vector<diameter_index_t>
1046 std::vector<diameter_index_t> edges;
1047 std::vector<index_t> vertices(2);
1048 for(
index_t index = binomial_coeff(n, 2); index-- > 0;) {
1049 get_simplex_vertices(index, 1, dist.size(), vertices.rbegin());
1050 value_t length = dist(vertices[0], vertices[1]);
1051 if(length <= threshold)
1052 edges.emplace_back(length, index);
1059 std::vector<diameter_index_t> edges;
1060 for(
index_t i = 0; i < n; ++i)
1061 for(
auto n_ : dist.neighbors[i]) {
1064 edges.emplace_back(
get_diameter(n_), get_edge_index(i, j));
1072 bool distanceMatrix,
1073 std::vector<std::vector<pers_pair_t>> &ph) {
1077 ph = std::vector<std::vector<pers_pair_t>>(
1078 dim_max + 1, std::vector<pers_pair_t>(0));
1080 if(!distanceMatrix) {
1084 std::move(dist), dim_max, threshold, ratio, modulus);
1085 ripser.compute_barcodes(ph);
1090 std::move(dist), dim_max, threshold, ratio, modulus);
1091 ripser.compute_barcodes(ph);
simplex_coboundary_enumerator(const diameter_entry_t _simplex, index_t _dim, const Ripser< compressed_lower_distance_matrix > &parent)
simplex_coboundary_enumerator(const diameter_entry_t _simplex, const index_t _dim, const Ripser< sparse_distance_matrix > &parent)
bool has_next(bool all_cofacets=true)
index_t get_edge_index(const index_t i, const index_t j) const
Ripser(DistanceMatrix &&_dist, index_t _dim_max, value_t _threshold, float _ratio, coefficient_t _modulus)
OutputIterator get_simplex_vertices(index_t idx, const index_t dim, index_t n_, OutputIterator out) const
void assemble_columns_to_reduce(std::vector< diameter_index_t > &simplices, std::vector< diameter_index_t > &columns_to_reduce, entry_hash_map &pivot_column_index, index_t dim)
diameter_entry_t init_coboundary_and_get_pivot(const diameter_entry_t simplex, Column &working_coboundary, const index_t &dim, entry_hash_map &pivot_column_index)
void compute_dim_0_pairs(std::vector< diameter_index_t > &edges, std::vector< diameter_index_t > &columns_to_reduce, std::vector< std::vector< pers_pair_t > > &ph)
void add_coboundary(compressed_sparse_matrix< diameter_entry_t > &reduction_matrix, const std::vector< diameter_index_t > &columns_to_reduce, const size_t index_column_to_add, const coefficient_t factor, const size_t &dim, Column &working_reduction_column, Column &working_coboundary)
void compute_barcodes(std::vector< std::vector< pers_pair_t > > &ph)
diameter_entry_t get_pivot(Column &column)
void add_simplex_coboundary(const diameter_entry_t simplex, const index_t &dim, Column &working_reduction_column, Column &working_coboundary)
std::vector< diameter_index_t > get_edges()
index_t get_max_vertex(const index_t idx, const index_t k, const index_t n_) const
void compute_pairs(std::vector< diameter_index_t > &columns_to_reduce, entry_hash_map &pivot_column_index, index_t dim, std::vector< std::vector< pers_pair_t > > &ph)
diameter_entry_t pop_pivot(Column &column)
std::priority_queue< diameter_entry_t, std::vector< diameter_entry_t >, greater_diameter_or_smaller_index< diameter_entry_t > > working_t
binomial_coeff_table(index_t n, index_t k)
index_t operator()(index_t n, index_t k) const
std::vector< value_t > distances
value_t operator()(const index_t i, const index_t j) const
compressed_distance_matrix(std::vector< value_t > &&_distances)
std::vector< value_t * > rows
compressed_distance_matrix(const DistanceMatrix &mat)
void push_back(const ValueType e)
iterator_pair subrange(const index_t index)
union_find(const index_t n)
void link(index_t x, index_t y)
std::pair< simplex_t, value_t > simplex_diam_t
void ripser(std::vector< std::vector< value_t > > points, value_t threshold, index_t dim_max, bool distanceMatrix, std::vector< std::vector< pers_pair_t > > &ph)
std::ostream & operator<<(std::ostream &o, Node const &n)
std::pair< value_t, index_t > diameter_index_t
index_t get_coefficient(const entry_t &)
index_t get_index(const entry_t &i)
coefficient_t get_modulo(const coefficient_t val, const coefficient_t modulus)
T end(std::pair< T, T > &p)
value_t get_diameter(const diameter_index_t &i)
coefficient_t normalize(const coefficient_t n, const coefficient_t modulus)
std::pair< index_t, value_t > index_diameter_t
std::vector< coefficient_t > multiplicative_inverse_vector(const coefficient_t m)
entry_t make_entry(index_t _index, coefficient_t)
const entry_t & get_entry(const entry_t &e)
void set_coefficient(entry_t &, const coefficient_t)
void check_overflow(index_t i)
std::unordered_map< Key, T, H, E > hash_map
T begin(std::pair< T, T > &p)
diameter_entry_t(value_t _diameter, index_t _index, coefficient_t _coefficient)
diameter_entry_t()=default
diameter_entry_t(const index_t &_index)
diameter_entry_t(const diameter_index_t &_diameter_index, coefficient_t _coefficient)
value_t operator()(const index_t i, const index_t j) const
euclidean_distance_matrix(std::vector< std::vector< value_t > > &&_points)
std::vector< std::vector< value_t > > points
bool operator()(const Entry &a, const Entry &b)
std::vector< std::vector< index_diameter_t > > neighbors
std::vector< std::vector< index_diameter_t >::const_reverse_iterator > neighbor_end
sparse_distance_matrix(std::vector< std::vector< index_diameter_t > > &&_neighbors, index_t _num_edges)
std::vector< std::vector< index_diameter_t >::const_reverse_iterator > neighbor_it
sparse_distance_matrix(const DistanceMatrix &mat, const value_t threshold)