summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--ripser.cpp204
2 files changed, 161 insertions, 47 deletions
diff --git a/Makefile b/Makefile
index ab410bd..921e438 100644
--- a/Makefile
+++ b/Makefile
@@ -5,10 +5,10 @@ all: ripser ripser-coeff ripser-debug
ripser: ripser.cpp
- c++ -std=c++11 -Wall ripser.cpp -o ripser -Ofast -D NDEBUG
+ c++ -std=c++11 -Wall ripser.cpp -o ripser -O3 -D NDEBUG
ripser-coeff: ripser.cpp
- c++ -std=c++11 -Wall ripser.cpp -o ripser-coeff -Ofast -D NDEBUG -D USE_COEFFICIENTS
+ c++ -std=c++11 -Wall ripser.cpp -o ripser-coeff -O3 -D NDEBUG -D USE_COEFFICIENTS
ripser-debug: ripser.cpp
c++ -std=c++11 -Wall ripser.cpp -o ripser-debug -g
diff --git a/ripser.cpp b/ripser.cpp
index 2deb58a..84da277 100644
--- a/ripser.cpp
+++ b/ripser.cpp
@@ -184,6 +184,9 @@ struct diameter_entry_t : std::pair<value_t, entry_t> {
diameter_entry_t(const diameter_index_t& _diameter_index, coefficient_t _coefficient)
: diameter_entry_t(get_diameter(_diameter_index),
make_entry(get_index(_diameter_index), _coefficient)) {}
+ diameter_entry_t(const diameter_index_t& _diameter_index)
+ : diameter_entry_t(get_diameter(_diameter_index),
+ make_entry(get_index(_diameter_index), 0)) {}
diameter_entry_t(const index_t& _index) : diameter_entry_t(0, _index, 0) {}
};
@@ -198,13 +201,17 @@ void set_coefficient(diameter_entry_t& p, const coefficient_t c) {
set_coefficient(get_entry(p), c);
}
-template <typename Entry> struct greater_diameter_or_smaller_index {
+template <typename Entry> struct greater_diameter_or_smaller_index_comp {
bool operator()(const Entry& a, const Entry& b) {
- return (get_diameter(a) > get_diameter(b)) ||
- ((get_diameter(a) == get_diameter(b)) && (get_index(a) < get_index(b)));
+ return greater_diameter_or_smaller_index(a, b);
}
};
+template <typename Entry> bool greater_diameter_or_smaller_index(const Entry& a, const Entry& b) {
+ return (get_diameter(a) > get_diameter(b)) ||
+ ((get_diameter(a) == get_diameter(b)) && (get_index(a) < get_index(b)));
+}
+
enum compressed_matrix_layout { LOWER_TRIANGULAR, UPPER_TRIANGULAR };
template <compressed_matrix_layout Layout> struct compressed_distance_matrix {
@@ -265,9 +272,6 @@ struct sparse_distance_matrix {
index_t num_edges;
- mutable std::vector<std::vector<index_diameter_t>::const_reverse_iterator> neighbor_it;
- mutable 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)
: neighbors(std::move(_neighbors)), num_edges(_num_edges) {}
@@ -284,6 +288,14 @@ struct sparse_distance_matrix {
}
}
+ value_t operator()(const index_t i, const index_t j) const {
+ auto neighbor =
+ std::lower_bound(neighbors[i].begin(), neighbors[i].end(), index_diameter_t{j, 0});
+ return (neighbor != neighbors[i].end() && get_index(*neighbor) == j)
+ ? get_diameter(*neighbor)
+ : std::numeric_limits<value_t>::infinity();
+ }
+
size_t size() const { return neighbors.size(); }
};
@@ -388,6 +400,7 @@ template <typename DistanceMatrix> class ripser {
const binomial_coeff_table binomial_coeff;
const std::vector<coefficient_t> multiplicative_inverse;
mutable std::vector<diameter_entry_t> cofacet_entries;
+ mutable std::vector<index_t> vertices;
struct entry_hash {
std::size_t operator()(const entry_t& e) const { return hash<index_t>()(::get_index(e)); }
@@ -429,15 +442,28 @@ public:
return out;
}
+ value_t compute_diameter(const index_t index, const index_t dim) const {
+ value_t diam = -std::numeric_limits<value_t>::infinity();
+
+ vertices.resize(dim + 1);
+ get_simplex_vertices(index, dim, dist.size(), vertices.rbegin());
+
+ for (index_t i = 0; i <= dim; ++i)
+ for (index_t j = 0; j < i; ++j) {
+ diam = std::max(diam, dist(vertices[i], vertices[j]));
+ }
+ return diam;
+ }
+
class simplex_coboundary_enumerator;
class simplex_boundary_enumerator {
private:
index_t idx_below, idx_above, j, k;
- const diameter_entry_t simplex;
+ diameter_entry_t simplex;
+ index_t dim;
const coefficient_t modulus;
const binomial_coeff_table& binomial_coeff;
- const index_t dim;
const ripser& parent;
public:
@@ -445,7 +471,19 @@ public:
const ripser& _parent)
: idx_below(get_index(_simplex)), idx_above(0), j(_parent.n - 1), k(_dim),
simplex(_simplex), modulus(_parent.modulus), binomial_coeff(_parent.binomial_coeff),
- dim(_dim), parent(_parent) {}
+ parent(_parent) {}
+
+ simplex_boundary_enumerator(const index_t _dim, const ripser& _parent)
+ : simplex_boundary_enumerator(-1, _dim, _parent) {}
+
+ void set_simplex(const diameter_entry_t _simplex, const index_t _dim) {
+ idx_below = get_index(_simplex);
+ idx_above = 0;
+ j = parent.n - 1;
+ k = _dim;
+ simplex = _simplex;
+ dim = _dim;
+ }
bool has_next() { return (k >= 0); }
@@ -468,6 +506,47 @@ public:
}
};
+ diameter_entry_t get_zero_pivot_facet(const diameter_entry_t simplex, const index_t dim) {
+ static simplex_boundary_enumerator facets(0, *this);
+ facets.set_simplex(simplex, dim);
+ while (facets.has_next()) {
+ diameter_entry_t facet = facets.next();
+ if (get_diameter(facet) == get_diameter(simplex)) return facet;
+ }
+ return diameter_entry_t(-1);
+ }
+
+ diameter_entry_t get_zero_pivot_cofacet(const diameter_entry_t simplex, const index_t dim) {
+ static simplex_coboundary_enumerator cofacets(*this);
+ cofacets.set_simplex(simplex, dim);
+ while (cofacets.has_next()) {
+ diameter_entry_t cofacet = cofacets.next();
+ if (get_diameter(cofacet) == get_diameter(simplex)) return cofacet;
+ }
+ return diameter_entry_t(-1);
+ }
+
+ diameter_entry_t get_zero_apparent_facet(const diameter_entry_t simplex, const index_t dim) {
+ diameter_entry_t facet = get_zero_pivot_facet(simplex, dim);
+ return ((get_index(facet) != -1) &&
+ (get_index(get_zero_pivot_cofacet(facet, dim - 1)) == get_index(simplex)))
+ ? facet
+ : diameter_entry_t(-1);
+ }
+
+ diameter_entry_t get_zero_apparent_cofacet(const diameter_entry_t simplex, const index_t dim) {
+ diameter_entry_t cofacet = get_zero_pivot_cofacet(simplex, dim);
+ return ((get_index(cofacet) != -1) &&
+ (get_index(get_zero_pivot_facet(cofacet, dim + 1)) == get_index(simplex)))
+ ? cofacet
+ : diameter_entry_t(-1);
+ }
+
+ bool is_in_zero_apparent_pair(const diameter_entry_t simplex, const index_t dim) {
+ return (get_index(get_zero_apparent_cofacet(simplex, dim)) != -1) ||
+ (get_index(get_zero_apparent_facet(simplex, dim)) != -1);
+ }
+
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) {
@@ -477,12 +556,13 @@ public:
std::chrono::steady_clock::time_point next = std::chrono::steady_clock::now() + time_step;
#endif
- --dim;
columns_to_reduce.clear();
std::vector<diameter_index_t> next_simplices;
+ simplex_coboundary_enumerator cofacets(*this);
+
for (diameter_index_t& simplex : simplices) {
- simplex_coboundary_enumerator cofacets(diameter_entry_t(simplex, 1), dim, *this);
+ cofacets.set_simplex(diameter_entry_t(simplex, 1), dim - 1);
while (cofacets.has_next(false)) {
#ifdef INDICATE_PROGRESS
@@ -495,10 +575,9 @@ public:
#endif
auto cofacet = cofacets.next();
if (get_diameter(cofacet) <= threshold) {
-
next_simplices.push_back({get_diameter(cofacet), get_index(cofacet)});
-
- if (pivot_column_index.find(get_entry(cofacet)) == pivot_column_index.end())
+ if (!is_in_zero_apparent_pair(cofacet, dim) &&
+ (pivot_column_index.find(get_entry(cofacet)) == pivot_column_index.end()))
columns_to_reduce.push_back({get_diameter(cofacet), get_index(cofacet)});
}
}
@@ -512,7 +591,7 @@ public:
#endif
std::sort(columns_to_reduce.begin(), columns_to_reduce.end(),
- greater_diameter_or_smaller_index<diameter_index_t>());
+ greater_diameter_or_smaller_index<diameter_index_t>);
#ifdef INDICATE_PROGRESS
std::cerr << clear_line << std::flush;
#endif
@@ -528,7 +607,7 @@ public:
edges = get_edges();
std::sort(edges.rbegin(), edges.rend(),
- greater_diameter_or_smaller_index<diameter_index_t>());
+ greater_diameter_or_smaller_index<diameter_index_t>);
std::vector<index_t> vertices_of_edge(2);
for (auto e : edges) {
get_simplex_vertices(get_index(e), 1, n, vertices_of_edge.rbegin());
@@ -540,7 +619,7 @@ public:
std::cout << " [0," << get_diameter(e) << ")" << std::endl;
#endif
dset.link(u, v);
- } else
+ } else if (get_index(get_zero_apparent_cofacet(e, 1)) == -1)
columns_to_reduce.push_back(e);
}
std::reverse(columns_to_reduce.begin(), columns_to_reduce.end());
@@ -586,15 +665,17 @@ public:
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) {
+ static simplex_coboundary_enumerator cofacets(*this);
bool check_for_emergent_pair = true;
cofacet_entries.clear();
- simplex_coboundary_enumerator cofacets(simplex, dim, *this);
+ cofacets.set_simplex(simplex, dim);
while (cofacets.has_next()) {
diameter_entry_t cofacet = cofacets.next();
if (get_diameter(cofacet) <= threshold) {
cofacet_entries.push_back(cofacet);
if (check_for_emergent_pair && (get_diameter(simplex) == get_diameter(cofacet))) {
- if (pivot_column_index.find(get_entry(cofacet)) == pivot_column_index.end())
+ if ((pivot_column_index.find(get_entry(cofacet)) == pivot_column_index.end()) &&
+ (get_index(get_zero_apparent_facet(cofacet, dim + 1)) == -1))
return cofacet;
check_for_emergent_pair = false;
}
@@ -607,8 +688,9 @@ public:
template <typename Column>
void add_simplex_coboundary(const diameter_entry_t simplex, const index_t& dim,
Column& working_reduction_column, Column& working_coboundary) {
+ static simplex_coboundary_enumerator cofacets(*this);
working_reduction_column.push(simplex);
- simplex_coboundary_enumerator cofacets(simplex, dim, *this);
+ cofacets.set_simplex(simplex, dim);
while (cofacets.has_next()) {
diameter_entry_t cofacet = cofacets.next();
if (get_diameter(cofacet) <= threshold) working_coboundary.push(cofacet);
@@ -638,7 +720,7 @@ public:
#endif
compressed_sparse_matrix<diameter_entry_t> reduction_matrix;
-
+
#ifdef INDICATE_PROGRESS
std::chrono::steady_clock::time_point next = std::chrono::steady_clock::now() + time_step;
#endif
@@ -651,11 +733,11 @@ public:
reduction_matrix.append_column();
std::priority_queue<diameter_entry_t, std::vector<diameter_entry_t>,
- greater_diameter_or_smaller_index<diameter_entry_t>>
+ greater_diameter_or_smaller_index_comp<diameter_entry_t>>
working_reduction_column, working_coboundary;
- diameter_entry_t pivot = init_coboundary_and_get_pivot(
- column_to_reduce, working_coboundary, dim, pivot_column_index);
+ diameter_entry_t e, pivot = init_coboundary_and_get_pivot(
+ column_to_reduce, working_coboundary, dim, pivot_column_index);
while (true) {
#ifdef INDICATE_PROGRESS
@@ -680,6 +762,12 @@ public:
factor, dim, working_reduction_column, working_coboundary);
pivot = get_pivot(working_coboundary);
+ } else if (get_index(e = get_zero_apparent_facet(pivot, dim + 1)) != -1) {
+ set_coefficient(e, modulus - get_coefficient(e));
+
+ add_simplex_coboundary(e, dim, working_reduction_column, working_coboundary);
+
+ pivot = get_pivot(working_coboundary);
} else {
#ifdef PRINT_PERSISTENCE_PAIRS
value_t death = get_diameter(pivot);
@@ -739,17 +827,31 @@ public:
template <> class ripser<compressed_lower_distance_matrix>::simplex_coboundary_enumerator {
index_t idx_below, idx_above, j, k;
std::vector<index_t> vertices;
- const diameter_entry_t simplex;
+ diameter_entry_t simplex;
const coefficient_t modulus;
const compressed_lower_distance_matrix& dist;
const binomial_coeff_table& binomial_coeff;
+ const ripser& parent;
public:
simplex_coboundary_enumerator(const diameter_entry_t _simplex, const index_t _dim,
- const ripser& parent)
- : idx_below(get_index(_simplex)), idx_above(0), j(parent.n - 1), k(_dim + 1),
- vertices(_dim + 1), simplex(_simplex), modulus(parent.modulus), dist(parent.dist),
- binomial_coeff(parent.binomial_coeff) {
+ const ripser& _parent)
+ : modulus(_parent.modulus), dist(_parent.dist),
+ binomial_coeff(_parent.binomial_coeff), parent(_parent) {
+ if (get_index(_simplex) != -1)
+ parent.get_simplex_vertices(get_index(_simplex), _dim, parent.n, vertices.rbegin());
+ }
+
+ simplex_coboundary_enumerator(const ripser& _parent) : modulus(_parent.modulus), dist(_parent.dist),
+ binomial_coeff(_parent.binomial_coeff), parent(_parent) {}
+
+ void set_simplex(const diameter_entry_t _simplex, const index_t _dim) {
+ idx_below = get_index(_simplex);
+ idx_above = 0;
+ j = parent.n - 1;
+ k = _dim + 1;
+ simplex = _simplex;
+ vertices.resize(_dim + 1);
parent.get_simplex_vertices(get_index(_simplex), _dim, parent.n, vertices.rbegin());
}
@@ -777,28 +879,41 @@ public:
template <> class ripser<sparse_distance_matrix>::simplex_coboundary_enumerator {
index_t idx_below, idx_above, k;
std::vector<index_t> vertices;
- const diameter_entry_t simplex;
+ diameter_entry_t simplex;
const coefficient_t modulus;
const sparse_distance_matrix& dist;
const binomial_coeff_table& binomial_coeff;
- std::vector<std::vector<index_diameter_t>::const_reverse_iterator>& neighbor_it;
- std::vector<std::vector<index_diameter_t>::const_reverse_iterator>& neighbor_end;
+ std::vector<std::vector<index_diameter_t>::const_reverse_iterator> neighbor_it;
+ std::vector<std::vector<index_diameter_t>::const_reverse_iterator> neighbor_end;
index_diameter_t neighbor;
+ const ripser& parent;
public:
simplex_coboundary_enumerator(const diameter_entry_t _simplex, const index_t _dim,
const ripser& _parent)
- : idx_below(get_index(_simplex)), idx_above(0), k(_dim + 1), vertices(_dim + 1),
- simplex(_simplex), modulus(_parent.modulus), dist(_parent.dist),
- binomial_coeff(_parent.binomial_coeff), neighbor_it(dist.neighbor_it),
- neighbor_end(dist.neighbor_end) {
- neighbor_it.clear();
- neighbor_end.clear();
- _parent.get_simplex_vertices(idx_below, _dim, _parent.n, vertices.rbegin());
-
- for (auto v : vertices) {
- neighbor_it.push_back(dist.neighbors[v].rbegin());
- neighbor_end.push_back(dist.neighbors[v].rend());
+ : modulus(_parent.modulus), dist(_parent.dist),
+ binomial_coeff(_parent.binomial_coeff), parent(_parent) {
+ if (get_index(_simplex) != -1) set_simplex(_simplex, _dim);
+ }
+
+ simplex_coboundary_enumerator(const ripser& _parent)
+ : modulus(_parent.modulus), dist(_parent.dist),
+ binomial_coeff(_parent.binomial_coeff), parent(_parent) {}
+
+ void set_simplex(const diameter_entry_t _simplex, const index_t _dim) {
+ idx_below = get_index(_simplex);
+ idx_above = 0;
+ k = _dim + 1;
+ simplex = _simplex;
+ vertices.resize(_dim + 1);
+ parent.get_simplex_vertices(idx_below, _dim, parent.n, vertices.rbegin());
+
+ neighbor_it.resize(_dim + 1);
+ neighbor_end.resize(_dim + 1);
+ for (index_t i = 0; i <= _dim; ++i) {
+ auto v = vertices[i];
+ neighbor_it[i] = dist.neighbors[v].rbegin();
+ neighbor_end[i] = dist.neighbors[v].rend();
}
}
@@ -1145,8 +1260,7 @@ int main(int argc, char** argv) {
for (auto d : dist.distances) {
min = std::min(min, d);
max = std::max(max, d);
- max_finite =
- d != std::numeric_limits<value_t>::infinity() ? std::max(max, d) : max_finite;
+ if (d != std::numeric_limits<value_t>::infinity()) max_finite = std::max(max_finite, d);
if (d <= threshold) ++num_edges;
}
std::cout << "value range: [" << min << "," << max_finite << "]" << std::endl;