diff options
-rw-r--r-- | ripser.cpp | 334 | ||||
-rw-r--r-- | ripser.xcodeproj/project.pbxproj | 17 |
2 files changed, 259 insertions, 92 deletions
@@ -98,30 +98,37 @@ std::vector<coefficient_t> multiplicative_inverse_vector(const coefficient_t m) return inverse; } +index_t get_next_vertex(index_t& v, const index_t idx, const index_t k, const binomial_coeff_table& binomial_coeff) { + + if (binomial_coeff(v, k) > idx) { + index_t count = v; + while (count > 0) { + index_t i = v; + index_t step = count >> 1; + i -= step; + if (binomial_coeff(i, k) > idx) { + v = --i; + count -= step + 1; + } else + count = step; + } + } + assert(binomial_coeff(v, k) <= idx); + assert(binomial_coeff(v + 1, k) > idx); + + return v; +} + template <typename OutputIterator> -OutputIterator get_simplex_vertices(index_t idx, const index_t dim, index_t n, +OutputIterator get_simplex_vertices(index_t idx, const index_t dim, index_t v, const binomial_coeff_table& binomial_coeff, OutputIterator out) { - --n; + --v; for (index_t k = dim + 1; k > 0; --k) { - if (binomial_coeff(n, k) > idx) { - index_t count = n; - while (count > 0) { - index_t i = n; - index_t step = count >> 1; - i -= step; - if (binomial_coeff(i, k) > idx) { - n = --i; - count -= step + 1; - } else - count = step; - } - } - assert(binomial_coeff(n, k) <= idx); - assert(binomial_coeff(n + 1, k) > idx); + get_next_vertex(v, idx, k, binomial_coeff); - *out++ = n; - idx -= binomial_coeff(n, k); + *out++ = v; + idx -= binomial_coeff(v, k); } return out; @@ -165,7 +172,7 @@ typedef index_t entry_t; const index_t get_index(entry_t i) { return i; } index_t get_coefficient(entry_t i) { return 1; } entry_t make_entry(index_t _index, coefficient_t _value) { return entry_t(_index); } -void set_coefficient(index_t& e, const coefficient_t c) { e = c; } +void set_coefficient(index_t& e, const coefficient_t c) { } #endif @@ -175,7 +182,11 @@ template <typename Entry> struct smaller_index { bool operator()(const Entry& a, const Entry& b) { return get_index(a) < get_index(b); } }; -typedef std::pair<value_t, index_t> diameter_index_t; +class diameter_index_t: public std::pair<value_t, index_t> { +public: + diameter_index_t() : std::pair<value_t, index_t>() {} + diameter_index_t(std::pair<value_t, index_t> p) : std::pair<value_t, index_t>(p) {} +}; value_t get_diameter(diameter_index_t i) { return i.first; } index_t get_index(diameter_index_t i) { return i.second; } @@ -184,6 +195,12 @@ public: diameter_entry_t(std::pair<value_t, entry_t> p) : std::pair<value_t, entry_t>(p) {} diameter_entry_t(entry_t e) : std::pair<value_t, entry_t>(0, e) {} diameter_entry_t() : diameter_entry_t(0) {} + diameter_entry_t(value_t _diameter, index_t _index, coefficient_t _coefficient) + : std::pair<value_t, entry_t>(_diameter, make_entry(_index, _coefficient)) {} + diameter_entry_t(diameter_index_t _diameter_index, coefficient_t _coefficient) + : std::pair<value_t, entry_t>(get_diameter(_diameter_index), + make_entry(get_index(_diameter_index), _coefficient)) {} + diameter_entry_t(diameter_index_t _diameter_index) : diameter_entry_t(_diameter_index, 1) {} }; const entry_t& get_entry(const diameter_entry_t& p) { return p.second; } @@ -192,12 +209,6 @@ const index_t get_index(const diameter_entry_t& p) { return get_index(get_entry( const coefficient_t get_coefficient(const diameter_entry_t& p) { return get_coefficient(get_entry(p)); } const value_t& get_diameter(const diameter_entry_t& p) { return p.first; } void set_coefficient(diameter_entry_t& p, const coefficient_t c) { set_coefficient(get_entry(p), c); } -diameter_entry_t make_diameter_entry(value_t _diameter, index_t _index, coefficient_t _coefficient) { - return std::make_pair(_diameter, make_entry(_index, _coefficient)); -} -diameter_entry_t make_diameter_entry(diameter_index_t _diameter_index, coefficient_t _coefficient) { - return std::make_pair(get_diameter(_diameter_index), make_entry(get_index(_diameter_index), _coefficient)); -} template <typename Entry> struct greater_diameter_or_smaller_index { bool operator()(const Entry& a, const Entry& b) { @@ -242,14 +253,26 @@ public: } }; -class simplex_coboundary_enumerator { +template <class DistanceMatrix> class simplex_coboundary_enumerator { private: index_t idx_below, idx_above, v, k; + + std::vector<index_t> vertices; + + const diameter_entry_t simplex; + + const coefficient_t modulus; + const DistanceMatrix& dist; const binomial_coeff_table& binomial_coeff; public: - simplex_coboundary_enumerator(index_t _idx, index_t _dim, index_t _n, const binomial_coeff_table& _binomial_coeff) - : idx_below(_idx), idx_above(0), v(_n - 1), k(_dim + 1), binomial_coeff(_binomial_coeff) {} + simplex_coboundary_enumerator(const diameter_entry_t _simplex, index_t _dim, index_t _n, + const coefficient_t _modulus, const DistanceMatrix& _dist, + const binomial_coeff_table& _binomial_coeff) + : simplex(_simplex), idx_below(get_index(_simplex)), idx_above(0), v(_n - 1), k(_dim + 1), modulus(_modulus), + binomial_coeff(_binomial_coeff), dist(_dist), vertices(_dim + 1) { + get_simplex_vertices(get_index(_simplex), _dim, _n, binomial_coeff, vertices.begin()); + } bool has_next() { while ((v != -1) && (binomial_coeff(v, k) <= idx_below)) { @@ -263,10 +286,16 @@ public: return v != -1; } - std::pair<entry_t, index_t> next() { - auto result = std::make_pair(make_entry(idx_above + binomial_coeff(v, k + 1) + idx_below, k & 1 ? -1 : 1), v); - --v; - return result; + index_t next_index() { return idx_above + binomial_coeff(v--, k + 1) + idx_below; } + + diameter_entry_t next() { + + value_t coface_diameter = get_diameter(simplex); + for (index_t w : vertices) { coface_diameter = std::max(coface_diameter, dist(v, w)); } + + coefficient_t coface_coefficient = (k & 1 ? -1 + modulus : 1) * get_coefficient(simplex) % modulus; + + return diameter_entry_t(coface_diameter, idx_above + binomial_coeff(v--, k + 1) + idx_below, coface_coefficient); } }; @@ -299,6 +328,98 @@ public: size_t size() const { return rows.size(); } }; +class sparse_distance_matrix { +public: + std::vector<std::vector<diameter_index_t>> neighbors; + + template <typename DistanceMatrix> + sparse_distance_matrix(const DistanceMatrix& mat, value_t threshold) : neighbors(mat.size()) { + + for (index_t i = 0; i < size(); ++i) + for (index_t j = 0; j < size(); ++j) + if (i != j && mat(i, j) <= threshold) neighbors[i].push_back(std::make_pair(mat(i, j), j)); + } + + size_t size() const { return neighbors.size(); } +}; + +template <class T> struct second_argument_greater { + bool operator()(const T& lhs, const T& rhs) const { return lhs.second > rhs.second; } +}; + +template <class T> struct second_argument_equal_to { + bool operator()(const T& lhs, const T& rhs) const { return lhs.second == rhs.second; } +}; + + +template<> +class simplex_coboundary_enumerator<const sparse_distance_matrix&> { +private: + index_t idx_below, idx_above, v, k, max_vertex_below; + const binomial_coeff_table& binomial_coeff; + const sparse_distance_matrix& sparse_dist; + std::vector<index_t> vertices; + + const diameter_entry_t simplex; + + std::vector<std::vector<diameter_index_t>::const_reverse_iterator> ii, ee; + + diameter_index_t x; + + const coefficient_t modulus; + +public: + simplex_coboundary_enumerator(const diameter_entry_t _simplex, index_t _dim, index_t _n, + const coefficient_t _modulus, const sparse_distance_matrix& _sparse_dist, + const binomial_coeff_table& _binomial_coeff) + : simplex(_simplex), idx_below(get_index(_simplex)), idx_above(0), v(_n - 1), k(_dim + 1), + max_vertex_below(_n - 1), modulus(_modulus), sparse_dist(_sparse_dist), binomial_coeff(_binomial_coeff) { + get_simplex_vertices(idx_below, _dim, _n, _binomial_coeff, std::back_inserter(vertices)); + + for (auto v : vertices) { + ii.push_back(sparse_dist.neighbors[v].rbegin()); + ee.push_back(sparse_dist.neighbors[v].rend()); + } + } + + bool has_next(bool all_cofaces = true) { + for (auto &it0 = ii[0], &end0 = ee[0]; it0 != end0; ++it0) { + x = *it0; + for (size_t idx = 1; idx < ii.size(); ++idx) { + auto &it = ii[idx], end = ee[idx]; + while (get_index(*it) > get_index(x)) + if (++it == end) return false; + auto y = *it; + if (get_index(y) != get_index(x)) + goto continue_outer; + else + x = std::min(x, y, greater_diameter_or_smaller_index<diameter_index_t>()); + } + return all_cofaces || !(k > 0 && get_next_vertex(max_vertex_below, idx_below, k, binomial_coeff) > get_index(x)); + continue_outer:; + } + return false; + } + + diameter_entry_t next() { + ++ii[0]; + + while (k > 0 && get_next_vertex(max_vertex_below, idx_below, k, binomial_coeff) > get_index(x)) { + idx_below -= binomial_coeff(max_vertex_below, k); + idx_above += binomial_coeff(max_vertex_below, k + 1); + --k; + } + + value_t coface_diameter = std::max(get_diameter(simplex), get_diameter(x)); + + coefficient_t coface_coefficient = (k & 1 ? -1 + modulus : 1) * get_coefficient(simplex) % modulus; + + return diameter_entry_t(coface_diameter, + idx_above + binomial_coeff(get_index(x), k + 1) + idx_below, + coface_coefficient); + } +}; + template <> void compressed_distance_matrix<LOWER_TRIANGULAR>::init_rows() { value_t* pointer = &distances[0]; for (index_t i = 1; i < size(); ++i) { @@ -484,6 +605,11 @@ void assemble_columns_to_reduce(std::vector<diameter_index_t>& columns_to_reduce if (pivot_column_index.find(index) == pivot_column_index.end()) { value_t diameter = comp.diameter(index); if (diameter <= threshold) columns_to_reduce.push_back(std::make_pair(diameter, index)); +#ifdef INDICATE_PROGRESS + if ((index + 1) % 1000 == 0) + std::cout << "\033[K" + << "assembled " << columns_to_reduce.size() << " out of " << (index + 1) << "/" << num_simplices << " columns" << std::flush << "\r"; +#endif } } @@ -499,11 +625,59 @@ void assemble_columns_to_reduce(std::vector<diameter_index_t>& columns_to_reduce #endif } -template <typename DistanceMatrix, typename ComparatorCofaces, typename Comparator> + + +template <typename Comparator> +void assemble_columns_to_reduce(std::vector<diameter_index_t>& simplices, + std::vector<diameter_index_t>& columns_to_reduce, + hash_map<index_t, index_t>& pivot_column_index, + const sparse_distance_matrix& sparse_dist, + const Comparator& comp, + index_t dim, index_t n, value_t threshold, const coefficient_t modulus, + const binomial_coeff_table& binomial_coeff) { + +#ifdef INDICATE_PROGRESS + std::cout << "\033[K" + << "assembling columns" << std::flush << "\r"; +#endif + + columns_to_reduce.clear(); + + std::vector<diameter_index_t> next_simplices; + + for (diameter_index_t simplex : simplices) { + simplex_coboundary_enumerator<const sparse_distance_matrix&> cofaces(simplex, dim, n, modulus, sparse_dist, binomial_coeff); + + while (cofaces.has_next(false)) { + auto coface = cofaces.next(); + + next_simplices.push_back(std::make_pair(get_diameter(coface), get_index(coface))); + + if (pivot_column_index.find(get_index(coface)) == pivot_column_index.end()) + columns_to_reduce.push_back(std::make_pair(get_diameter(coface), get_index(coface))); + } + } + + simplices.swap(next_simplices); + +#ifdef INDICATE_PROGRESS + std::cout << "\033[K" + << "sorting " << columns_to_reduce.size() << " columns" << std::flush << "\r"; +#endif + + std::sort(columns_to_reduce.begin(), columns_to_reduce.end(), + greater_diameter_or_smaller_index<diameter_index_t>()); +#ifdef INDICATE_PROGRESS + std::cout << "\033[K"; +#endif +} + +template <typename DistanceMatrix, +typename ComparatorCofaces, typename Comparator> void compute_pairs(std::vector<diameter_index_t>& columns_to_reduce, hash_map<index_t, index_t>& pivot_column_index, - const DistanceMatrix& dist, const ComparatorCofaces& comp, const Comparator& comp_prev, index_t dim, - index_t n, value_t threshold, coefficient_t modulus, - const std::vector<coefficient_t>& multiplicative_inverse, + index_t dim, index_t n, value_t threshold, coefficient_t modulus, + const std::vector<coefficient_t>& multiplicative_inverse, const DistanceMatrix& dist, + const ComparatorCofaces& comp, const Comparator& comp_prev, const binomial_coeff_table& binomial_coeff) { #ifdef PRINT_PERSISTENCE_PAIRS @@ -546,15 +720,15 @@ void compute_pairs(std::vector<diameter_index_t>& columns_to_reduce, hash_map<in // start with a dummy pivot entry with coefficient -1 in order to initialize // working_coboundary with the coboundary of the simplex with index column_to_reduce - diameter_entry_t pivot = make_diameter_entry(0, -1, -1 + modulus); + diameter_entry_t pivot(0, -1, -1 + modulus); #ifdef ASSEMBLE_REDUCTION_MATRIX // initialize reduction_matrix as identity matrix reduction_matrix.append_column(); - reduction_matrix.push_back(make_diameter_entry(column_to_reduce, 1)); + reduction_matrix.push_back(diameter_entry_t(column_to_reduce, 1)); #else #ifdef USE_COEFFICIENTS - reduction_coefficients.push_back(make_diameter_entry(column_to_reduce, 1)); + reduction_coefficients.push_back(diameter_entry_t(column_to_reduce, 1)); #endif #endif @@ -568,48 +742,35 @@ void compute_pairs(std::vector<diameter_index_t>& columns_to_reduce, hash_map<in #endif { #ifdef ASSEMBLE_REDUCTION_MATRIX - const auto& simplex = *it; + diameter_entry_t simplex = *it; #else #ifdef USE_COEFFICIENTS - const auto& simplex = reduction_coefficients[j]; + diameter_entry_t simplex = reduction_coefficients[j]; #else - const auto& simplex = columns_to_reduce[j]; + diameter_entry_t simplex = columns_to_reduce[j]; #endif #endif - - coefficient_t simplex_coefficient = get_coefficient(simplex) * factor % modulus; + set_coefficient(simplex, get_coefficient(simplex) * factor % modulus); #ifdef ASSEMBLE_REDUCTION_MATRIX - reduction_column.push( - make_diameter_entry(get_diameter(simplex), get_index(simplex), simplex_coefficient)); + reduction_column.push(simplex); #endif vertices.clear(); get_simplex_vertices(get_index(simplex), dim, n, binomial_coeff, std::back_inserter(vertices)); coface_entries.clear(); - simplex_coboundary_enumerator cofaces(get_index(simplex), dim, n, binomial_coeff); + simplex_coboundary_enumerator<decltype(dist)> cofaces(simplex, dim, n, modulus, dist, binomial_coeff); + while (cofaces.has_next()) { - auto coface_descriptor = cofaces.next(); - entry_t coface = coface_descriptor.first; - index_t covertex = coface_descriptor.second; - index_t coface_index = get_index(coface); - value_t coface_diameter = get_diameter(simplex); - for (index_t v : vertices) { coface_diameter = std::max(coface_diameter, dist(v, covertex)); } - assert(comp.diameter(coface_index) == coface_diameter); - - if (coface_diameter <= threshold) { - coefficient_t coface_coefficient = - (get_coefficient(coface) + modulus) * simplex_coefficient % modulus; - assert(coface_coefficient >= 0); - - diameter_entry_t coface_entry = - make_diameter_entry(coface_diameter, coface_index, coface_coefficient); - coface_entries.push_back(coface_entry); - - if (might_be_apparent_pair && (get_diameter(simplex) == coface_diameter)) { - if (pivot_column_index.find(coface_index) == pivot_column_index.end()) { - pivot = coface_entry; + diameter_entry_t coface = cofaces.next(); + + if (get_diameter(coface) <= threshold) { + coface_entries.push_back(coface); + + if (might_be_apparent_pair && (get_diameter(simplex) == get_diameter(coface))) { + if (pivot_column_index.find(get_index(coface)) == pivot_column_index.end()) { + pivot = coface; goto found_persistence_pair; } might_be_apparent_pair = false; @@ -661,20 +822,17 @@ void compute_pairs(std::vector<diameter_index_t>& columns_to_reduce, hash_map<in reduction_matrix.pop_back(); while (true) { diameter_entry_t e = pop_pivot(reduction_column, modulus); - index_t index = get_index(e); - if (index == -1) break; + if (get_index(e) == -1) break; #ifdef USE_COEFFICIENTS - const coefficient_t coefficient = inverse * get_coefficient(e) % modulus; - assert(coefficient > 0); -#else - const coefficient_t coefficient = 1; + set_coefficient(e, inverse * get_coefficient(e) % modulus); + assert(get_coefficient(e) > 0); #endif - reduction_matrix.push_back(make_diameter_entry(get_diameter(e), index, coefficient)); + reduction_matrix.push_back(e); } #else #ifdef USE_COEFFICIENTS reduction_coefficients.pop_back(); - reduction_coefficients.push_back(make_diameter_entry(column_to_reduce, inverse)); + reduction_coefficients.push_back(diameter_entry_t(column_to_reduce, inverse)); #endif #endif break; @@ -897,6 +1055,8 @@ int main(int argc, char** argv) { auto value_range = std::minmax_element(dist.distances.begin(), dist.distances.end()); std::cout << "value range: [" << *value_range.first << "," << *value_range.second << "]" << std::endl; + sparse_distance_matrix sparse_dist(dist, threshold); + dim_max = std::min(dim_max, n - 2); binomial_coeff_table binomial_coeff(n, dim_max + 2); @@ -904,14 +1064,16 @@ int main(int argc, char** argv) { std::vector<diameter_index_t> columns_to_reduce; + std::vector<diameter_index_t> simplices , &edges = simplices; + rips_filtration_comparator<decltype(dist)> comp(dist, 1, binomial_coeff); + for (index_t index = binomial_coeff(n, 2); index-- > 0;) { + value_t diameter = comp.diameter(index); + if (diameter <= threshold) edges.push_back(std::make_pair(diameter, index)); + } + { union_find dset(n); - std::vector<diameter_index_t> edges; - rips_filtration_comparator<decltype(dist)> comp(dist, 1, binomial_coeff); - for (index_t index = binomial_coeff(n, 2); index-- > 0;) { - value_t diameter = comp.diameter(index); - if (diameter <= threshold) edges.push_back(diameter_index_t(diameter, index)); - } + std::sort(edges.rbegin(), edges.rend(), greater_diameter_or_smaller_index<diameter_index_t>()); #ifdef PRINT_PERSISTENCE_PAIRS @@ -948,11 +1110,11 @@ int main(int argc, char** argv) { hash_map<index_t, index_t> pivot_column_index; pivot_column_index.reserve(columns_to_reduce.size()); - compute_pairs(columns_to_reduce, pivot_column_index, dist, comp, comp_prev, dim, n, threshold, modulus, - multiplicative_inverse, binomial_coeff); + compute_pairs(columns_to_reduce, pivot_column_index, dim, n, threshold, modulus, multiplicative_inverse, sparse_dist, + comp, comp_prev, binomial_coeff); if (dim < dim_max) { - assemble_columns_to_reduce(columns_to_reduce, pivot_column_index, comp, dim, n, threshold, binomial_coeff); + assemble_columns_to_reduce(simplices, columns_to_reduce, pivot_column_index, sparse_dist, comp, dim, n, threshold, modulus, binomial_coeff); } } } diff --git a/ripser.xcodeproj/project.pbxproj b/ripser.xcodeproj/project.pbxproj index 04bc3a6..24c7d8b 100644 --- a/ripser.xcodeproj/project.pbxproj +++ b/ripser.xcodeproj/project.pbxproj @@ -88,7 +88,7 @@ 551018401BD63CB300990BFF /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "ulrich-bauer.org"; TargetAttributes = { 551018471BD63CB300990BFF = { @@ -129,7 +129,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -138,15 +138,17 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_C_LANGUAGE_STANDARD = c11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -172,7 +174,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; @@ -181,15 +183,17 @@ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_C_LANGUAGE_STANDARD = c11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -212,7 +216,8 @@ _FILE_FORMAT_DISTANCE_MATRIX, _FILE_FORMAT_LOWER_DISTANCE_MATRIX, _FILE_FORMAT_POINT_CLOUD, - USE_COEFFICIENTS, + _USE_COEFFICIENTS, + INDICATE_PROGRESS, ); PRODUCT_NAME = "$(TARGET_NAME)"; }; |