TTK
Loading...
Searching...
No Matches
ripserpy.cpp
Go to the documentation of this file.
1
10
18
19/*
20
21Ripser: a lean C++ code for computation of Vietoris-Rips persistence barcodes
22
23MIT License
24
25Original Copyright 2015-2018 Ulrich Bauer.
26Modifications Copyright 2018 Christopher Tralie
27
28
29Permission is hereby granted, free of charge, to any person obtaining a copy
30of this software and associated documentation files (the "Software"), to deal
31in the Software without restriction, including without limitation the rights
32to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33copies of the Software, and to permit persons to whom the Software is
34furnished to do so, subject to the following conditions:
35
36The above copyright notice and this permission notice shall be included in all
37copies or substantial portions of the Software.
38
39THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
42AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
45SOFTWARE.
46You are under no obligation whatsoever to provide any bug fixes, patches, or
47upgrades to the features, functionality or performance of the source code
48("Enhancements") to anyone; however, if you choose to make your Enhancements
49available either publicly, or directly to the author of this software, without
50imposing a separate written license agreement for such Enhancements, then you
51hereby grant the following license: a non-exclusive, royalty-free perpetual
52license to install, use, modify, prepare derivative works, incorporate into
53other computer software, distribute, and sublicense such enhancements or
54derivative works thereof, in binary and source code form.
55
56
57*/
58
59#include "ripser.h"
60
61using namespace ripser;
62
64coefficient_t get_modulo(const coefficient_t val, const coefficient_t modulus);
66std::vector<coefficient_t> multiplicative_inverse_vector(const coefficient_t m);
67
68#ifdef USE_ROBINHOOD_HASHMAP
69
70#include "robin_hood.h"
71
72template <class Key, class T, class H, class E>
73using hash_map = robin_hood::unordered_map<Key, T, H, E>;
74template <class Key>
75using hash = robin_hood::hash<Key>;
76
77#else
78
79template <class Key, class T, class H, class E>
80using hash_map = std::unordered_map<Key, T, H, E>;
81template <class Key>
82using hash = std::hash<Key>;
83
84#endif
85
86#ifdef INDICATE_PROGRESS
87static const std::chrono::milliseconds time_step(40);
88#endif
89
90static const std::string clear_line("\r\033[K");
91
92static const size_t num_coefficient_bits = 8;
93
94// 1L on windows is ALWAYS 32 bits, when on unix systems is pointer size
95static const index_t max_simplex_index
96 = (__int128(1) << (8 * sizeof(index_t) - 1 - num_coefficient_bits)) - 1;
97
98namespace std {
99 inline std::string to_string(__int128) {
100 return "";
101 }
102} // namespace std
103
105 if
106#ifdef USE_COEFFICIENTS
107 (i > max_simplex_index)
108#else
109 (i < 0)
110#endif
111 throw std::overflow_error("simplex index " + std::to_string((uint64_t)i)
112 + " in filtration is larger than maximum index "
113 + std::to_string(max_simplex_index));
114}
115
117 /* Using flatten matrix */
118 std::vector<index_t> B;
119 size_t offset;
120
121public:
122 binomial_coeff_table(index_t n, index_t k) : B((n + 1) * (k + 1)) {
123 offset = k + 1;
124 for(index_t i = 0; i <= n; ++i) {
125 B[i * offset] = 1;
126 for(index_t j = 1; j < std::min(i, k + 1); ++j)
127 B[i * offset + j]
128 = B[(i - 1) * offset + j - 1] + B[(i - 1) * offset + j];
129 if(i <= k)
130 B[i * offset + i] = 1;
131 check_overflow(B[i * offset + std::min(i >> 1, k)]);
132 }
133 }
134
136 assert(n < index_t(B.size() / offset) && k < index_t(offset) && n >= k - 1);
137 return B[n * offset + k];
138 }
139};
140
141/* Modulo operator is expensive, using a mask when modulus is equal 2
142 * is much less expensive and speed-ups where observed
143 */
145 return (modulus == 2) ? val & 1 : val % modulus;
146}
147
149 return n > modulus / 2 ? n - modulus : n;
150}
151
152std::vector<coefficient_t>
154 std::vector<coefficient_t> inverse(m);
155 inverse[1] = 1;
156 // m = a * (m / a) + m % a
157 // Multiplying with inverse(a) * inverse(m % a):
158 // 0 = inverse(m % a) * (m / a) + inverse(a) (mod m)
159 for(coefficient_t a = 2; a < m; ++a)
160 inverse[a] = m - (inverse[m % a] * (m / a)) % m;
161 return inverse;
162}
163
164#ifdef USE_COEFFICIENTS
165
166// https://stackoverflow.com/a/3312896/13339777
167#ifdef _MSC_VER
168#define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
169#else
170#define PACK(...) __attribute__((__packed__)) __VA_ARGS__
171#endif
172
173PACK(struct entry_t {
174 index_t index : 8 * sizeof(index_t) - num_coefficient_bits;
175 index_t coefficient : num_coefficient_bits;
176 entry_t(index_t _index, coefficient_t _coefficient)
177 : index(_index), coefficient(_coefficient) {
178 }
179 entry_t(index_t _index) : index(_index), coefficient(0) {
180 }
181 entry_t() : index(0), coefficient(0) {
182 }
183});
184
185static_assert(sizeof(entry_t) == sizeof(index_t),
186 "size of entry_t is not the same as index_t");
187
189index_t get_index(const entry_t &e);
191void set_coefficient(entry_t &e, const coefficient_t c);
192std::ostream &operator<<(std::ostream &stream, const entry_t &e);
193
195 return entry_t(i, c);
196}
197index_t get_index(const entry_t &e) {
198 return e.index;
199}
201 return e.coefficient;
202}
203void set_coefficient(entry_t &e, const coefficient_t c) {
204 e.coefficient = c;
205}
206
207std::ostream &operator<<(std::ostream &stream, const entry_t &e) {
208 stream << get_index(e) << ":" << get_coefficient(e);
209 return stream;
210}
211
212#else
213
215index_t get_index(const entry_t &i);
216index_t get_coefficient(const entry_t & /*i*/);
217entry_t make_entry(index_t _index, coefficient_t /*_value*/);
218void set_coefficient(entry_t & /*e*/, const coefficient_t /*c*/);
219
221 return i;
222}
224 return 1;
225}
227 return entry_t(_index);
228}
229void set_coefficient(entry_t & /*e*/, const coefficient_t /*c*/) {
230}
231
232#endif
233
234const entry_t &get_entry(const entry_t &e);
235const entry_t &get_entry(const entry_t &e) {
236 return e;
237}
238
239using diameter_index_t = std::pair<value_t, index_t>;
243 return i.first;
244}
246 return i.second;
247}
248
249using index_diameter_t = std::pair<index_t, value_t>;
253 return i.first;
254}
256 return i.second;
257}
258
259struct diameter_entry_t : std::pair<value_t, entry_t> {
260 using std::pair<value_t, entry_t>::pair;
261 diameter_entry_t() = default;
263 index_t _index,
264 coefficient_t _coefficient)
265 : diameter_entry_t(_diameter, make_entry(_index, _coefficient)) {
266 }
267 diameter_entry_t(const diameter_index_t &_diameter_index,
268 coefficient_t _coefficient)
269 : diameter_entry_t(get_diameter(_diameter_index),
270 make_entry(get_index(_diameter_index), _coefficient)) {
271 }
272 diameter_entry_t(const index_t &_index) : diameter_entry_t(0, _index, 0) {
273 }
274};
275
276const entry_t &get_entry(const diameter_entry_t &p);
280const value_t &get_diameter(const diameter_entry_t &p);
282
284 return p.second;
285}
287 return p.second;
288}
290 return get_index(get_entry(p));
291}
296 return p.first;
297}
301
302template <typename Entry>
304 bool operator()(const Entry &a, const Entry &b) {
305 return (get_diameter(a) > get_diameter(b))
306 || ((get_diameter(a) == get_diameter(b))
307 && (get_index(a) < get_index(b)));
308 }
309};
310
315
316template <compressed_matrix_layout Layout>
318public:
319 std::vector<value_t> distances;
320 std::vector<value_t *> rows;
321
322 compressed_distance_matrix(std::vector<value_t> &&_distances)
323 : distances(std::move(_distances)),
324 rows((1 + std::sqrt(1 + 8 * distances.size())) / 2) {
325 assert(distances.size() == size() * (size() - 1) / 2);
326 init_rows();
327 }
328
329 template <typename DistanceMatrix>
330 compressed_distance_matrix(const DistanceMatrix &mat)
331 : distances(mat.size() * (mat.size() - 1) / 2), rows(mat.size()) {
332 init_rows();
333
334 for(size_t i = 1; i < size(); ++i)
335 for(size_t j = 0; j < i; ++j)
336 rows[i][j] = mat(i, j);
337 }
338
339 value_t operator()(const index_t i, const index_t j) const;
340
341 size_t size() const {
342 return rows.size();
343 }
344 void init_rows();
345};
346
351
352template <>
354 value_t *pointer = &distances[0];
355 for(size_t i = 1; i < size(); ++i) {
356 rows[i] = pointer;
357 pointer += i;
358 }
359}
360
361template <>
363 value_t *pointer = &distances[0] - 1;
364 for(size_t i = 0; i < size() - 1; ++i) {
365 rows[i] = pointer;
366 pointer += size() - i - 2;
367 }
368}
369
370template <>
372 const index_t j) const {
373 return i == j ? 0 : i < j ? rows[j][i] : rows[i][j];
374}
375
376template <>
378 const index_t j) const {
379 return i == j ? 0 : i > j ? rows[j][i] : rows[i][j];
380}
381
383 std::vector<std::vector<index_diameter_t>> neighbors;
385
386 mutable std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
388 mutable std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
390
392 std::vector<std::vector<index_diameter_t>> &&_neighbors, index_t _num_edges)
393 : neighbors(std::move(_neighbors)), num_edges(_num_edges) {
394 }
395
396 template <typename DistanceMatrix>
397 sparse_distance_matrix(const DistanceMatrix &mat, const value_t threshold)
398 : neighbors(mat.size()), num_edges(0) {
399 for(size_t i = 0; i < size(); ++i)
400 for(size_t j = 0; j < size(); ++j)
401 if(i != j && mat(i, j) <= threshold) {
402 ++num_edges;
403 neighbors[i].emplace_back(j, mat(i, j));
404 }
405 }
406
407 size_t size() const {
408 return neighbors.size();
409 }
410};
411
413 std::vector<std::vector<value_t>> points;
414
415 euclidean_distance_matrix(std::vector<std::vector<value_t>> &&_points)
416 : points(std::move(_points)) {
417 }
418
419 value_t operator()(const index_t i, const index_t j) const {
420 assert(i < index_t(points.size()));
421 assert(j < index_t(points.size()));
422 return std::sqrt(std::inner_product(
423 points[i].begin(), points[i].end(), points[j].begin(), value_t(),
424 std::plus<value_t>(),
425 [](value_t u, value_t v) { return (u - v) * (u - v); }));
426 }
427
428 size_t size() const {
429 return points.size();
430 }
431};
432
434 std::vector<index_t> parent;
435 std::vector<uint8_t> rank;
436
437public:
438 union_find(const index_t n) : parent(n), rank(n, 0) {
439 for(index_t i = 0; i < n; ++i)
440 parent[i] = i;
441 }
442
444 index_t y = x, z;
445 while((z = parent[y]) != y)
446 y = z;
447 while((z = parent[x]) != y) {
448 parent[x] = y;
449 x = z;
450 }
451 return z;
452 }
453
454 void link(index_t x, index_t y) {
455 if((x = find(x)) == (y = find(y)))
456 return;
457 if(rank[x] > rank[y])
458 parent[y] = x;
459 else {
460 parent[x] = y;
461 if(rank[x] == rank[y])
462 ++rank[y];
463 }
464 }
465};
466
467template <typename T>
468T begin(std::pair<T, T> &p) {
469 return p.first;
470}
471template <typename T>
472T end(std::pair<T, T> &p) {
473 return p.second;
474}
475
476template <typename ValueType>
478 std::vector<size_t> bounds;
479 std::vector<ValueType> entries;
480
481 using iterator = typename std::vector<ValueType>::iterator;
482 using iterator_pair = std::pair<iterator, iterator>;
483
484public:
485 size_t size() const {
486 return bounds.size();
487 }
488
489 iterator_pair subrange(const index_t index) {
490 return {entries.begin() + (index == 0 ? 0 : bounds[index - 1]),
491 entries.begin() + bounds[index]};
492 }
493
495 bounds.push_back(entries.size());
496 }
497
498 void push_back(const ValueType e) {
499 assert(0 < size());
500 entries.push_back(e);
501 ++bounds.back();
502 }
503
504 void pop_back() {
505 assert(0 < size());
506 entries.pop_back();
507 --bounds.back();
508 }
509};
510
511template <typename DistanceMatrix>
512class Ripser {
513 const DistanceMatrix dist;
514 index_t n, dim_max;
515 const value_t threshold;
516 const float ratio;
517 const coefficient_t modulus;
518 const binomial_coeff_table binomial_coeff;
519 const std::vector<coefficient_t> multiplicative_inverse;
520 mutable std::vector<diameter_entry_t> cofacet_entries;
521
522 struct entry_hash {
523 std::size_t operator()(const entry_t &e) const {
524#if defined(USE_ROBINHOOD_HASHMAP)
525 return robin_hood::hash<index_t>()(::get_index(e));
526#else
527 return std::hash<index_t>()(::get_index(e));
528#endif
529 }
530 };
531
532 struct equal_index {
533 bool operator()(const entry_t &e, const entry_t &f) const {
534 return ::get_index(e) == ::get_index(f);
535 }
536 };
537
539
540public:
541 Ripser(DistanceMatrix &&_dist,
542 index_t _dim_max,
543 value_t _threshold,
544 float _ratio,
545 coefficient_t _modulus)
546 : dist(std::move(_dist)), n(dist.size()), dim_max(_dim_max),
547 threshold(_threshold), ratio(_ratio), modulus(_modulus),
548 binomial_coeff(n, dim_max + 2),
549 multiplicative_inverse(multiplicative_inverse_vector(_modulus)) {
550 }
551
552 index_t
553 get_max_vertex(const index_t idx, const index_t k, const index_t n_) const {
554 auto top = n_;
555 auto bottom = k - 1;
556 if(binomial_coeff(top, k) > idx) {
557 index_t count = top - bottom;
558 index_t step;
559 index_t mid;
560 while(count > 0) {
561 step = count >> 1;
562 mid = top - step;
563 if(binomial_coeff(mid, k) > idx) {
564 top = mid - 1;
565 count -= step + 1;
566 } else
567 count = step;
568 }
569 }
570 return top;
571 }
572
573 index_t get_edge_index(const index_t i, const index_t j) const {
574 return binomial_coeff(i, 2) + j;
575 }
576
577 template <typename OutputIterator>
578 OutputIterator get_simplex_vertices(index_t idx,
579 const index_t dim,
580 index_t n_,
581 OutputIterator out) const {
582 --n_;
583 for(index_t k = dim + 1; k > 0; --k) {
584 n_ = get_max_vertex(idx, k, n_);
585 *out++ = n_;
586 idx -= binomial_coeff(n_, k);
587 }
588 return out;
589 }
590
591 class simplex_coboundary_enumerator;
592
593 void
594 assemble_columns_to_reduce(std::vector<diameter_index_t> &simplices,
595 std::vector<diameter_index_t> &columns_to_reduce,
596 entry_hash_map &pivot_column_index,
597 index_t dim) {
598#ifdef INDICATE_PROGRESS
599 std::cerr << clear_line << "assembling columns" << std::flush;
600 std::chrono::steady_clock::time_point next
601 = std::chrono::steady_clock::now() + time_step;
602#endif
603 --dim;
604 columns_to_reduce.clear();
605 std::vector<diameter_index_t> next_simplices;
606
607 for(diameter_index_t &simplex : simplices) {
609 diameter_entry_t(simplex, 1), dim, *this);
610
611 while(cofacets.has_next(false)) {
612#ifdef INDICATE_PROGRESS
613 if(std::chrono::steady_clock::now() > next) {
614 std::cerr << clear_line << "assembling " << next_simplices.size()
615 << " columns (processing "
616 << std::distance(&simplices[0], &simplex) << "/"
617 << simplices.size() << " simplices)" << std::flush;
618 next = std::chrono::steady_clock::now() + time_step;
619 }
620#endif
621 auto cofacet = cofacets.next();
622 if(get_diameter(cofacet) <= threshold) {
623 if(dim != dim_max)
624 next_simplices.emplace_back(
625 get_diameter(cofacet), get_index(cofacet));
626
627 if(pivot_column_index.find(get_entry(cofacet))
628 == pivot_column_index.end())
629 columns_to_reduce.emplace_back(
630 get_diameter(cofacet), get_index(cofacet));
631 }
632 }
633 }
634
635 simplices.swap(next_simplices);
636
637#ifdef INDICATE_PROGRESS
638 std::cerr << clear_line << "sorting " << columns_to_reduce.size()
639 << " columns" << std::flush;
640#endif
641
642 std::sort(columns_to_reduce.begin(), columns_to_reduce.end(),
644
645#ifdef INDICATE_PROGRESS
646 std::cerr << clear_line << std::flush;
647#endif
648 }
649
650 void compute_dim_0_pairs(std::vector<diameter_index_t> &edges,
651 std::vector<diameter_index_t> &columns_to_reduce,
652 std::vector<std::vector<pers_pair_t>> &ph) {
653 union_find dset(n);
654
655 edges = get_edges();
656 std::sort(edges.rbegin(), edges.rend(),
658 std::vector<index_t> vertices_of_edge(2);
659 for(auto e : edges) {
660 get_simplex_vertices(get_index(e), 1, n, vertices_of_edge.rbegin());
661 index_t u = dset.find(vertices_of_edge[0]),
662 v = dset.find(vertices_of_edge[1]);
663
664 if(u != v) {
665 dset.link(u, v);
666 if(get_diameter(e) != 0)
667 ph[0].emplace_back(
669 {u == dset.find(vertices_of_edge[0]) ? vertices_of_edge[1]
670 : vertices_of_edge[0]},
671 0.},
673 {vertices_of_edge[0], vertices_of_edge[1]}, get_diameter(e)});
674 } else
675 columns_to_reduce.push_back(e);
676 }
677 std::reverse(columns_to_reduce.begin(), columns_to_reduce.end());
678
679 for(index_t i = 0; i < n; ++i) {
680 if(dset.find(i) == i)
681 ph[0].emplace_back(
682 simplex_diam_t{{i}, 0.},
683 simplex_diam_t{{-1}, std::numeric_limits<value_t>::infinity()});
684 }
685 }
686
687 template <typename Column>
688 diameter_entry_t pop_pivot(Column &column) {
689 diameter_entry_t pivot(-1);
690#ifdef USE_COEFFICIENTS
691 while(!column.empty()) {
692 if(get_coefficient(pivot) == 0)
693 pivot = column.top();
694 else if(get_index(column.top()) != get_index(pivot))
695 return pivot;
696 else
698 + get_coefficient(column.top())),
699 modulus));
700 column.pop();
701 }
702 return (get_coefficient(pivot) == 0) ? -1 : pivot;
703#else
704 while(!column.empty()) {
705 pivot = column.top();
706 column.pop();
707 if(column.empty() || get_index(column.top()) != get_index(pivot))
708 return pivot;
709 column.pop();
710 }
711 return -1;
712#endif
713 }
714
715 template <typename Column>
716 diameter_entry_t get_pivot(Column &column) {
717 diameter_entry_t result = pop_pivot(column);
718 if(get_index(result) != -1)
719 column.push(result);
720 return result;
721 }
722
723 template <typename Column>
726 Column &working_coboundary,
727 const index_t &dim,
728 entry_hash_map &pivot_column_index) {
729 bool check_for_emergent_pair = true;
730 cofacet_entries.clear();
731 simplex_coboundary_enumerator cofacets(simplex, dim, *this);
732 while(cofacets.has_next()) {
733 diameter_entry_t cofacet = cofacets.next();
734 if(get_diameter(cofacet) <= threshold) {
735 cofacet_entries.push_back(cofacet);
736 if(check_for_emergent_pair
737 && (get_diameter(simplex) == get_diameter(cofacet))) {
738 if(pivot_column_index.find(get_entry(cofacet))
739 == pivot_column_index.end())
740 return cofacet;
741 check_for_emergent_pair = false;
742 }
743 }
744 }
745 for(auto cofacet : cofacet_entries)
746 working_coboundary.push(cofacet);
747 return get_pivot(working_coboundary);
748 }
749
750 template <typename Column>
752 const index_t &dim,
753 Column &working_reduction_column,
754 Column &working_coboundary) {
755 working_reduction_column.push(simplex);
756 simplex_coboundary_enumerator cofacets(simplex, dim, *this);
757 while(cofacets.has_next()) {
758 diameter_entry_t cofacet = cofacets.next();
759 if(get_diameter(cofacet) <= threshold)
760 working_coboundary.push(cofacet);
761 }
762 }
763
764 template <typename Column>
765 void
767 const std::vector<diameter_index_t> &columns_to_reduce,
768 const size_t index_column_to_add,
769 const coefficient_t factor,
770 const size_t &dim,
771 Column &working_reduction_column,
772 Column &working_coboundary) {
773 diameter_entry_t column_to_add(
774 columns_to_reduce[index_column_to_add], factor);
776 column_to_add, dim, working_reduction_column, working_coboundary);
777
778 for(diameter_entry_t simplex :
779 reduction_matrix.subrange(index_column_to_add)) {
780 set_coefficient(simplex, get_coefficient(simplex) * factor % modulus);
782 simplex, dim, working_reduction_column, working_coboundary);
783 }
784 }
785
787 = std::priority_queue<diameter_entry_t,
788 std::vector<diameter_entry_t>,
790
791 void compute_pairs(std::vector<diameter_index_t> &columns_to_reduce,
792 entry_hash_map &pivot_column_index,
793 index_t dim,
794 std::vector<std::vector<pers_pair_t>> &ph) {
796 size_t index_column_to_add;
797
798#ifdef INDICATE_PROGRESS
799 std::chrono::steady_clock::time_point next
800 = std::chrono::steady_clock::now() + time_step;
801#endif
802
803 for(size_t index_column_to_reduce = 0;
804 index_column_to_reduce < columns_to_reduce.size();
805 ++index_column_to_reduce) {
806 diameter_entry_t column_to_reduce(
807 columns_to_reduce[index_column_to_reduce], 1);
808 value_t diameter = get_diameter(column_to_reduce);
809
810 reduction_matrix.append_column();
811
812 working_t working_reduction_column;
813 working_t working_coboundary;
814
815 working_reduction_column.push(column_to_reduce);
816
818 column_to_reduce, working_coboundary, dim, pivot_column_index);
819
820 while(true) {
821#ifdef INDICATE_PROGRESS
822 if(std::chrono::steady_clock::now() > next) {
823 std::cerr << clear_line << "reducing column "
824 << index_column_to_reduce + 1 << "/"
825 << columns_to_reduce.size() << " (diameter " << diameter
826 << ")" << std::flush;
827 next = std::chrono::steady_clock::now() + time_step;
828 }
829#endif
830 if(get_index(pivot) != -1) {
831 auto pair = pivot_column_index.find(get_entry(pivot));
832 if(pair != pivot_column_index.end()) {
833 entry_t other_pivot = pair->first;
834 index_column_to_add = pair->second;
835 coefficient_t factor
836 = modulus
837 - get_modulo(
838 get_coefficient(pivot)
839 * multiplicative_inverse[get_coefficient(other_pivot)],
840 modulus);
841
842 add_coboundary(reduction_matrix, columns_to_reduce,
843 index_column_to_add, factor, dim,
844 working_reduction_column, working_coboundary);
845
846 pivot = get_pivot(working_coboundary);
847 } else {
848 value_t death = get_diameter(pivot);
849 if(death > diameter * ratio) {
850 std::vector<index_t> vertices_birth(dim + 1),
851 vertices_death(dim + 2);
853 get_index(column_to_reduce), dim, n, vertices_birth.rbegin());
855 get_index(pivot), dim + 1, n, vertices_death.rbegin());
856 ph[dim].emplace_back(simplex_diam_t{vertices_birth, diameter},
857 simplex_diam_t{vertices_death, death});
858 }
859
860 pivot_column_index.insert(
861 {get_entry(pivot), index_column_to_reduce});
862
863 pop_pivot(working_reduction_column);
864 while(true) {
865 diameter_entry_t e = pop_pivot(working_reduction_column);
866
867 if(get_index(e) == -1)
868 break;
869 assert(get_coefficient(e) > 0);
870 reduction_matrix.push_back(e);
871 }
872 break;
873 }
874 } else {
875 std::vector<index_t> vertices_birth(dim + 1);
877 get_index(column_to_reduce), dim, n, vertices_birth.rbegin());
878 ph[dim].emplace_back(
879 simplex_diam_t{vertices_birth, diameter},
880 simplex_diam_t{{-1}, std::numeric_limits<value_t>::infinity()});
881 break;
882 }
883 }
884 }
885#ifdef INDICATE_PROGRESS
886 std::cerr << clear_line << std::flush;
887#endif
888 }
889
890 std::vector<diameter_index_t> get_edges();
891 void compute_barcodes(std::vector<std::vector<pers_pair_t>> &ph) {
892 std::vector<diameter_index_t> simplices, columns_to_reduce;
893
894 /* prevent cases where dim_max < 0 */
895 if(dim_max < 0)
896 dim_max = 0;
897
898 compute_dim_0_pairs(simplices, columns_to_reduce, ph);
899
900 for(index_t dim = 1; dim <= dim_max; ++dim) {
901 entry_hash_map pivot_column_index;
902 pivot_column_index.reserve(columns_to_reduce.size());
903
904 compute_pairs(columns_to_reduce, pivot_column_index, dim, ph);
905
906 if(dim < dim_max)
908 simplices, columns_to_reduce, pivot_column_index, dim + 1);
909 }
910 }
911};
912
913template <>
915private:
916 index_t idx_below, idx_above, v, k;
917 std::vector<index_t> vertices;
918 const diameter_entry_t simplex;
919 const coefficient_t modulus;
921 const binomial_coeff_table &binomial_coeff;
922
923public:
925 const diameter_entry_t _simplex,
926 index_t _dim,
928 : idx_below(get_index(_simplex)), idx_above(0), v(parent.n - 1),
929 k(_dim + 1), vertices(_dim + 1), simplex(_simplex),
930 modulus(parent.modulus), dist(parent.dist),
931 binomial_coeff(parent.binomial_coeff) {
933 get_index(_simplex), _dim, parent.n, vertices.begin());
934 }
935
936 bool has_next(bool all_cofacets = true) {
937 return (v >= k && (all_cofacets || binomial_coeff(v, k) > idx_below));
938 }
939
941 while((binomial_coeff(v, k) <= idx_below)) {
942 idx_below -= binomial_coeff(v, k);
943 idx_above += binomial_coeff(v, k + 1);
944 --v;
945 --k;
946 assert(k != -1);
947 }
948 value_t cofacet_diameter = get_diameter(simplex);
949 for(index_t w : vertices)
950 cofacet_diameter = std::max(cofacet_diameter, dist(v, w));
951 index_t cofacet_index = idx_above + binomial_coeff(v--, k + 1) + idx_below;
952 coefficient_t cofacet_coefficient
953 = (k & 1 ? modulus - 1 : 1) * get_coefficient(simplex) % modulus;
954 return diameter_entry_t(
955 cofacet_diameter, cofacet_index, cofacet_coefficient);
956 }
957};
958
959template <>
960class Ripser<sparse_distance_matrix>::simplex_coboundary_enumerator {
961 index_t idx_below, idx_above, k;
962 std::vector<index_t> vertices;
963 const diameter_entry_t simplex;
964 const coefficient_t modulus;
965 const sparse_distance_matrix &dist;
966 const binomial_coeff_table &binomial_coeff;
967 std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
968 &neighbor_it;
969 std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
970 &neighbor_end;
971 index_diameter_t neighbor;
972
973public:
975 const index_t _dim,
976 const Ripser<sparse_distance_matrix> &parent)
977 : idx_below(get_index(_simplex)), idx_above(0), k(_dim + 1),
978 vertices(_dim + 1), simplex(_simplex), modulus(parent.modulus),
979 dist(parent.dist), binomial_coeff(parent.binomial_coeff),
980 neighbor_it(dist.neighbor_it), neighbor_end(dist.neighbor_end) {
981 neighbor_it.clear();
982 neighbor_end.clear();
983
984 parent.get_simplex_vertices(idx_below, _dim, parent.n, vertices.rbegin());
985
986 for(auto v : vertices) {
987 neighbor_it.push_back(dist.neighbors[v].rbegin());
988 neighbor_end.push_back(dist.neighbors[v].rend());
989 }
990 }
991
992 bool has_next(bool all_cofacets = true) {
993 for(auto &it0 = neighbor_it[0], &end0 = neighbor_end[0]; it0 != end0;
994 ++it0) {
995 neighbor = *it0;
996 for(size_t idx = 1; idx < neighbor_it.size(); ++idx) {
997 auto &it = neighbor_it[idx], end = neighbor_end[idx];
998 while(get_index(*it) > get_index(neighbor))
999 if(++it == end)
1000 return false;
1001 if(get_index(*it) != get_index(neighbor))
1002 goto continue_outer;
1003 else
1004 neighbor = std::max(neighbor, *it);
1005 }
1006 while(k > 0 && vertices[k - 1] > get_index(neighbor)) {
1007 if(!all_cofacets)
1008 return false;
1009 idx_below -= binomial_coeff(vertices[k - 1], k);
1010 idx_above += binomial_coeff(vertices[k - 1], k + 1);
1011 --k;
1012 }
1013 return true;
1014 continue_outer:;
1015 }
1016 return false;
1017 }
1018
1020 ++neighbor_it[0];
1021 value_t cofacet_diameter
1022 = std::max(get_diameter(simplex), get_diameter(neighbor));
1023 index_t cofacet_index
1024 = idx_above + binomial_coeff(get_index(neighbor), k + 1) + idx_below;
1025 coefficient_t cofacet_coefficient
1026 = (k & 1 ? modulus - 1 : 1) * get_coefficient(simplex) % modulus;
1027 return diameter_entry_t(
1028 cofacet_diameter, cofacet_index, cofacet_coefficient);
1029 }
1030};
1031
1032template <>
1033std::vector<diameter_index_t>
1035 std::vector<diameter_index_t> edges;
1036 std::vector<index_t> vertices(2);
1037 for(index_t index = binomial_coeff(n, 2); index-- > 0;) {
1038 get_simplex_vertices(index, 1, dist.size(), vertices.rbegin());
1039 value_t length = dist(vertices[0], vertices[1]);
1040 if(length <= threshold)
1041 edges.emplace_back(length, index);
1042 }
1043 return edges;
1044}
1045
1046template <>
1047std::vector<diameter_index_t> Ripser<sparse_distance_matrix>::get_edges() {
1048 std::vector<diameter_index_t> edges;
1049 for(index_t i = 0; i < n; ++i)
1050 for(auto n_ : dist.neighbors[i]) {
1051 index_t j = get_index(n_);
1052 if(i > j)
1053 edges.emplace_back(get_diameter(n_), get_edge_index(i, j));
1054 }
1055 return edges;
1056}
1057
1058void ripser::ripser(std::vector<std::vector<value_t>> points,
1059 value_t threshold,
1060 index_t dim_max,
1061 bool distanceMatrix,
1062 std::vector<std::vector<pers_pair_t>> &ph) {
1063 double ratio = 1;
1064 coefficient_t modulus = 2;
1065
1066 ph = std::vector<std::vector<pers_pair_t>>(
1067 dim_max + 1, std::vector<pers_pair_t>(0));
1068
1069 if(!distanceMatrix) {
1070 euclidean_distance_matrix eucl_dist(std::move(points));
1071 sparse_distance_matrix dist(eucl_dist, threshold);
1073 std::move(dist), dim_max, threshold, ratio, modulus);
1074 ripser.compute_barcodes(ph);
1075 } else {
1076 compressed_lower_distance_matrix lower_dist(std::move(points[0]));
1077 sparse_distance_matrix dist(lower_dist, threshold);
1079 std::move(dist), dim_max, threshold, ratio, modulus);
1080 ripser.compute_barcodes(ph);
1081 }
1082}
simplex_coboundary_enumerator(const diameter_entry_t _simplex, index_t _dim, const Ripser< compressed_lower_distance_matrix > &parent)
Definition ripserpy.cpp:924
simplex_coboundary_enumerator(const diameter_entry_t _simplex, const index_t _dim, const Ripser< sparse_distance_matrix > &parent)
Definition ripserpy.cpp:974
bool has_next(bool all_cofacets=true)
Definition ripserpy.cpp:936
index_t get_edge_index(const index_t i, const index_t j) const
Definition ripserpy.cpp:573
Ripser(DistanceMatrix &&_dist, index_t _dim_max, value_t _threshold, float _ratio, coefficient_t _modulus)
Definition ripserpy.cpp:541
OutputIterator get_simplex_vertices(index_t idx, const index_t dim, index_t n_, OutputIterator out) const
Definition ripserpy.cpp:578
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)
Definition ripserpy.cpp:594
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)
Definition ripserpy.cpp:725
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)
Definition ripserpy.cpp:650
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)
Definition ripserpy.cpp:766
void compute_barcodes(std::vector< std::vector< pers_pair_t > > &ph)
Definition ripserpy.cpp:891
diameter_entry_t get_pivot(Column &column)
Definition ripserpy.cpp:716
void add_simplex_coboundary(const diameter_entry_t simplex, const index_t &dim, Column &working_reduction_column, Column &working_coboundary)
Definition ripserpy.cpp:751
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
Definition ripserpy.cpp:553
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)
Definition ripserpy.cpp:791
diameter_entry_t pop_pivot(Column &column)
Definition ripserpy.cpp:688
std::priority_queue< diameter_entry_t, std::vector< diameter_entry_t >, greater_diameter_or_smaller_index< diameter_entry_t > > working_t
Definition ripserpy.cpp:789
binomial_coeff_table(index_t n, index_t k)
Definition ripserpy.cpp:122
index_t operator()(index_t n, index_t k) const
Definition ripserpy.cpp:135
std::vector< value_t > distances
Definition ripserpy.cpp:319
value_t operator()(const index_t i, const index_t j) const
Definition ripserpy.cpp:371
compressed_distance_matrix(std::vector< value_t > &&_distances)
Definition ripserpy.cpp:322
std::vector< value_t * > rows
Definition ripserpy.cpp:320
compressed_distance_matrix(const DistanceMatrix &mat)
Definition ripserpy.cpp:330
void push_back(const ValueType e)
Definition ripserpy.cpp:498
iterator_pair subrange(const index_t index)
Definition ripserpy.cpp:489
index_t find(index_t x)
Definition ripserpy.cpp:443
union_find(const index_t n)
Definition ripserpy.cpp:438
void link(index_t x, index_t y)
Definition ripserpy.cpp:454
uint16_t coefficient_t
Definition ripser.h:22
std::pair< simplex_t, value_t > simplex_diam_t
Definition ripser.h:25
double value_t
Definition ripser.h:20
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)
int64_t index_t
Definition ripser.h:21
std::string to_string(__int128)
Definition ripserpy.cpp:99
std::ostream & operator<<(std::ostream &o, Node const &n)
std::hash< Key > hash
Definition ripserpy.cpp:82
std::pair< value_t, index_t > diameter_index_t
Definition ripserpy.cpp:239
index_t get_coefficient(const entry_t &)
Definition ripserpy.cpp:223
index_t entry_t
Definition ripserpy.cpp:214
index_t get_index(const entry_t &i)
Definition ripserpy.cpp:220
coefficient_t get_modulo(const coefficient_t val, const coefficient_t modulus)
Definition ripserpy.cpp:144
T end(std::pair< T, T > &p)
Definition ripserpy.cpp:472
compressed_matrix_layout
Definition ripserpy.cpp:311
@ UPPER_TRIANGULAR
Definition ripserpy.cpp:313
@ LOWER_TRIANGULAR
Definition ripserpy.cpp:312
value_t get_diameter(const diameter_index_t &i)
Definition ripserpy.cpp:242
coefficient_t normalize(const coefficient_t n, const coefficient_t modulus)
Definition ripserpy.cpp:148
std::pair< index_t, value_t > index_diameter_t
Definition ripserpy.cpp:249
std::vector< coefficient_t > multiplicative_inverse_vector(const coefficient_t m)
Definition ripserpy.cpp:153
entry_t make_entry(index_t _index, coefficient_t)
Definition ripserpy.cpp:226
const entry_t & get_entry(const entry_t &e)
Definition ripserpy.cpp:235
void set_coefficient(entry_t &, const coefficient_t)
Definition ripserpy.cpp:229
void check_overflow(index_t i)
Definition ripserpy.cpp:104
std::unordered_map< Key, T, H, E > hash_map
Definition ripserpy.cpp:80
T begin(std::pair< T, T > &p)
Definition ripserpy.cpp:468
Definition ripserpy.cpp:259
diameter_entry_t(value_t _diameter, index_t _index, coefficient_t _coefficient)
Definition ripserpy.cpp:262
diameter_entry_t()=default
diameter_entry_t(const index_t &_index)
Definition ripserpy.cpp:272
diameter_entry_t(const diameter_index_t &_diameter_index, coefficient_t _coefficient)
Definition ripserpy.cpp:267
value_t operator()(const index_t i, const index_t j) const
Definition ripserpy.cpp:419
euclidean_distance_matrix(std::vector< std::vector< value_t > > &&_points)
Definition ripserpy.cpp:415
std::vector< std::vector< value_t > > points
Definition ripserpy.cpp:413
bool operator()(const Entry &a, const Entry &b)
Definition ripserpy.cpp:304
size_t size() const
Definition ripserpy.cpp:407
std::vector< std::vector< index_diameter_t > > neighbors
Definition ripserpy.cpp:383
std::vector< std::vector< index_diameter_t >::const_reverse_iterator > neighbor_end
Definition ripserpy.cpp:389
sparse_distance_matrix(std::vector< std::vector< index_diameter_t > > &&_neighbors, index_t _num_edges)
Definition ripserpy.cpp:391
std::vector< std::vector< index_diameter_t >::const_reverse_iterator > neighbor_it
Definition ripserpy.cpp:387
sparse_distance_matrix(const DistanceMatrix &mat, const value_t threshold)
Definition ripserpy.cpp:397