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#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;
98
99void check_overflow(index_t i) {
100 if
101#ifdef USE_COEFFICIENTS
102 (i > max_simplex_index)
103#else
104 (i < 0)
105#endif
106 throw std::overflow_error("simplex index " + std::to_string((uint64_t)i)
107 + " in filtration is larger than maximum index");
108}
109#else
110// 1L on windows is ALWAYS 32 bits, when on unix systems is pointer size
111static const index_t max_simplex_index
112 = (uintptr_t(1) << (8 * sizeof(index_t) - 1 - num_coefficient_bits)) - 1;
113
115 if
116#ifdef USE_COEFFICIENTS
117 (i > max_simplex_index)
118#else
119 (i < 0)
120#endif
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));
124}
125#endif
126
128 /* Using flatten matrix */
129 std::vector<index_t> B;
130 size_t offset;
131
132public:
133 binomial_coeff_table(index_t n, index_t k) : B((n + 1) * (k + 1)) {
134 offset = k + 1;
135 for(index_t i = 0; i <= n; ++i) {
136 B[i * offset] = 1;
137 for(index_t j = 1; j < std::min(i, k + 1); ++j)
138 B[i * offset + j]
139 = B[(i - 1) * offset + j - 1] + B[(i - 1) * offset + j];
140 if(i <= k)
141 B[i * offset + i] = 1;
142 check_overflow(B[i * offset + std::min(i >> 1, k)]);
143 }
144 }
145
147 assert(n < index_t(B.size() / offset) && k < index_t(offset) && n >= k - 1);
148 return B[n * offset + k];
149 }
150};
151
152/* Modulo operator is expensive, using a mask when modulus is equal 2
153 * is much less expensive and speed-ups where observed
154 */
156 return (modulus == 2) ? val & 1 : val % modulus;
157}
158
160 return n > modulus / 2 ? n - modulus : n;
161}
162
163std::vector<coefficient_t>
165 std::vector<coefficient_t> inverse(m);
166 inverse[1] = 1;
167 // m = a * (m / a) + m % a
168 // Multiplying with inverse(a) * inverse(m % a):
169 // 0 = inverse(m % a) * (m / a) + inverse(a) (mod m)
170 for(coefficient_t a = 2; a < m; ++a)
171 inverse[a] = m - (inverse[m % a] * (m / a)) % m;
172 return inverse;
173}
174
175#ifdef USE_COEFFICIENTS
176
177// https://stackoverflow.com/a/3312896/13339777
178#ifdef _MSC_VER
179#define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
180#else
181#define PACK(...) __attribute__((__packed__)) __VA_ARGS__
182#endif
183
184PACK(struct entry_t {
185 index_t index : 8 * sizeof(index_t) - num_coefficient_bits;
186 index_t coefficient : num_coefficient_bits;
187 entry_t(index_t _index, coefficient_t _coefficient)
188 : index(_index), coefficient(_coefficient) {
189 }
190 entry_t(index_t _index) : index(_index), coefficient(0) {
191 }
192 entry_t() : index(0), coefficient(0) {
193 }
194});
195
196static_assert(sizeof(entry_t) == sizeof(index_t),
197 "size of entry_t is not the same as index_t");
198
200index_t get_index(const entry_t &e);
202void set_coefficient(entry_t &e, const coefficient_t c);
203std::ostream &operator<<(std::ostream &stream, const entry_t &e);
204
206 return entry_t(i, c);
207}
208index_t get_index(const entry_t &e) {
209 return e.index;
210}
212 return e.coefficient;
213}
214void set_coefficient(entry_t &e, const coefficient_t c) {
215 e.coefficient = c;
216}
217
218std::ostream &operator<<(std::ostream &stream, const entry_t &e) {
219 stream << get_index(e) << ":" << get_coefficient(e);
220 return stream;
221}
222
223#else
224
226index_t get_index(const entry_t &i);
227index_t get_coefficient(const entry_t & /*i*/);
228entry_t make_entry(index_t _index, coefficient_t /*_value*/);
229void set_coefficient(entry_t & /*e*/, const coefficient_t /*c*/);
230
232 return i;
233}
235 return 1;
236}
238 return entry_t(_index);
239}
240void set_coefficient(entry_t & /*e*/, const coefficient_t /*c*/) {
241}
242
243#endif
244
245const entry_t &get_entry(const entry_t &e);
246const entry_t &get_entry(const entry_t &e) {
247 return e;
248}
249
250using diameter_index_t = std::pair<value_t, index_t>;
254 return i.first;
255}
257 return i.second;
258}
259
260using index_diameter_t = std::pair<index_t, value_t>;
264 return i.first;
265}
267 return i.second;
268}
269
270struct diameter_entry_t : std::pair<value_t, entry_t> {
271 using std::pair<value_t, entry_t>::pair;
272 diameter_entry_t() = default;
274 index_t _index,
275 coefficient_t _coefficient)
276 : diameter_entry_t(_diameter, make_entry(_index, _coefficient)) {
277 }
278 diameter_entry_t(const diameter_index_t &_diameter_index,
279 coefficient_t _coefficient)
280 : diameter_entry_t(get_diameter(_diameter_index),
281 make_entry(get_index(_diameter_index), _coefficient)) {
282 }
283 diameter_entry_t(const index_t &_index) : diameter_entry_t(0, _index, 0) {
284 }
285};
286
287const entry_t &get_entry(const diameter_entry_t &p);
291const value_t &get_diameter(const diameter_entry_t &p);
293
295 return p.second;
296}
298 return p.second;
299}
301 return get_index(get_entry(p));
302}
307 return p.first;
308}
312
313template <typename Entry>
315 bool operator()(const Entry &a, const Entry &b) {
316 return (get_diameter(a) > get_diameter(b))
317 || ((get_diameter(a) == get_diameter(b))
318 && (get_index(a) < get_index(b)));
319 }
320};
321
326
327template <compressed_matrix_layout Layout>
329public:
330 std::vector<value_t> distances;
331 std::vector<value_t *> rows;
332
333 compressed_distance_matrix(std::vector<value_t> &&_distances)
334 : distances(std::move(_distances)),
335 rows((1 + std::sqrt(1 + 8 * distances.size())) / 2) {
336 assert(distances.size() == size() * (size() - 1) / 2);
337 init_rows();
338 }
339
340 template <typename DistanceMatrix>
341 compressed_distance_matrix(const DistanceMatrix &mat)
342 : distances(mat.size() * (mat.size() - 1) / 2), rows(mat.size()) {
343 init_rows();
344
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);
348 }
349
350 value_t operator()(const index_t i, const index_t j) const;
351
352 size_t size() const {
353 return rows.size();
354 }
355 void init_rows();
356};
357
362
363template <>
365 value_t *pointer = &distances[0];
366 for(size_t i = 1; i < size(); ++i) {
367 rows[i] = pointer;
368 pointer += i;
369 }
370}
371
372template <>
374 value_t *pointer = &distances[0] - 1;
375 for(size_t i = 0; i < size() - 1; ++i) {
376 rows[i] = pointer;
377 pointer += size() - i - 2;
378 }
379}
380
381template <>
383 const index_t j) const {
384 return i == j ? 0 : i < j ? rows[j][i] : rows[i][j];
385}
386
387template <>
389 const index_t j) const {
390 return i == j ? 0 : i > j ? rows[j][i] : rows[i][j];
391}
392
394 std::vector<std::vector<index_diameter_t>> neighbors;
396
397 mutable std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
399 mutable std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
401
403 std::vector<std::vector<index_diameter_t>> &&_neighbors, index_t _num_edges)
404 : neighbors(std::move(_neighbors)), num_edges(_num_edges) {
405 }
406
407 template <typename DistanceMatrix>
408 sparse_distance_matrix(const DistanceMatrix &mat, const value_t threshold)
409 : neighbors(mat.size()), num_edges(0) {
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) {
413 ++num_edges;
414 neighbors[i].emplace_back(j, mat(i, j));
415 }
416 }
417
418 size_t size() const {
419 return neighbors.size();
420 }
421};
422
424 std::vector<std::vector<value_t>> points;
425
426 euclidean_distance_matrix(std::vector<std::vector<value_t>> &&_points)
427 : points(std::move(_points)) {
428 }
429
430 value_t operator()(const index_t i, const index_t j) const {
431 assert(i < index_t(points.size()));
432 assert(j < index_t(points.size()));
433 return std::sqrt(std::inner_product(
434 points[i].begin(), points[i].end(), points[j].begin(), value_t(),
435 std::plus<value_t>(),
436 [](value_t u, value_t v) { return (u - v) * (u - v); }));
437 }
438
439 size_t size() const {
440 return points.size();
441 }
442};
443
445 std::vector<index_t> parent;
446 std::vector<uint8_t> rank;
447
448public:
449 union_find(const index_t n) : parent(n), rank(n, 0) {
450 for(index_t i = 0; i < n; ++i)
451 parent[i] = i;
452 }
453
455 index_t y = x, z;
456 while((z = parent[y]) != y)
457 y = z;
458 while((z = parent[x]) != y) {
459 parent[x] = y;
460 x = z;
461 }
462 return z;
463 }
464
465 void link(index_t x, index_t y) {
466 if((x = find(x)) == (y = find(y)))
467 return;
468 if(rank[x] > rank[y])
469 parent[y] = x;
470 else {
471 parent[x] = y;
472 if(rank[x] == rank[y])
473 ++rank[y];
474 }
475 }
476};
477
478template <typename T>
479T begin(std::pair<T, T> &p) {
480 return p.first;
481}
482template <typename T>
483T end(std::pair<T, T> &p) {
484 return p.second;
485}
486
487template <typename ValueType>
489 std::vector<size_t> bounds;
490 std::vector<ValueType> entries;
491
492 using iterator = typename std::vector<ValueType>::iterator;
493 using iterator_pair = std::pair<iterator, iterator>;
494
495public:
496 size_t size() const {
497 return bounds.size();
498 }
499
500 iterator_pair subrange(const index_t index) {
501 return {entries.begin() + (index == 0 ? 0 : bounds[index - 1]),
502 entries.begin() + bounds[index]};
503 }
504
506 bounds.push_back(entries.size());
507 }
508
509 void push_back(const ValueType e) {
510 assert(0 < size());
511 entries.push_back(e);
512 ++bounds.back();
513 }
514
515 void pop_back() {
516 assert(0 < size());
517 entries.pop_back();
518 --bounds.back();
519 }
520};
521
522template <typename DistanceMatrix>
523class Ripser {
524 const DistanceMatrix dist;
525 index_t n, dim_max;
526 const value_t threshold;
527 const float ratio;
528 const coefficient_t modulus;
529 const binomial_coeff_table binomial_coeff;
530 const std::vector<coefficient_t> multiplicative_inverse;
531 mutable std::vector<diameter_entry_t> cofacet_entries;
532
533 struct entry_hash {
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));
537#else
538 return std::hash<index_t>()(::get_index(e));
539#endif
540 }
541 };
542
543 struct equal_index {
544 bool operator()(const entry_t &e, const entry_t &f) const {
545 return ::get_index(e) == ::get_index(f);
546 }
547 };
548
550
551public:
552 Ripser(DistanceMatrix &&_dist,
553 index_t _dim_max,
554 value_t _threshold,
555 float _ratio,
556 coefficient_t _modulus)
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),
560 multiplicative_inverse(multiplicative_inverse_vector(_modulus)) {
561 }
562
563 index_t
564 get_max_vertex(const index_t idx, const index_t k, const index_t n_) const {
565 auto top = n_;
566 auto bottom = k - 1;
567 if(binomial_coeff(top, k) > idx) {
568 index_t count = top - bottom;
569 index_t step;
570 index_t mid;
571 while(count > 0) {
572 step = count >> 1;
573 mid = top - step;
574 if(binomial_coeff(mid, k) > idx) {
575 top = mid - 1;
576 count -= step + 1;
577 } else
578 count = step;
579 }
580 }
581 return top;
582 }
583
584 index_t get_edge_index(const index_t i, const index_t j) const {
585 return binomial_coeff(i, 2) + j;
586 }
587
588 template <typename OutputIterator>
589 OutputIterator get_simplex_vertices(index_t idx,
590 const index_t dim,
591 index_t n_,
592 OutputIterator out) const {
593 --n_;
594 for(index_t k = dim + 1; k > 0; --k) {
595 n_ = get_max_vertex(idx, k, n_);
596 *out++ = n_;
597 idx -= binomial_coeff(n_, k);
598 }
599 return out;
600 }
601
602 class simplex_coboundary_enumerator;
603
604 void
605 assemble_columns_to_reduce(std::vector<diameter_index_t> &simplices,
606 std::vector<diameter_index_t> &columns_to_reduce,
607 entry_hash_map &pivot_column_index,
608 index_t dim) {
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;
613#endif
614 --dim;
615 columns_to_reduce.clear();
616 std::vector<diameter_index_t> next_simplices;
617
618 for(diameter_index_t &simplex : simplices) {
620 diameter_entry_t(simplex, 1), dim, *this);
621
622 while(cofacets.has_next(false)) {
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;
630 }
631#endif
632 auto cofacet = cofacets.next();
633 if(get_diameter(cofacet) <= threshold) {
634 if(dim != dim_max)
635 next_simplices.emplace_back(
636 get_diameter(cofacet), get_index(cofacet));
637
638 if(pivot_column_index.find(get_entry(cofacet))
639 == pivot_column_index.end())
640 columns_to_reduce.emplace_back(
641 get_diameter(cofacet), get_index(cofacet));
642 }
643 }
644 }
645
646 simplices.swap(next_simplices);
647
648#ifdef INDICATE_PROGRESS
649 std::cerr << clear_line << "sorting " << columns_to_reduce.size()
650 << " columns" << std::flush;
651#endif
652
653 std::sort(columns_to_reduce.begin(), columns_to_reduce.end(),
655
656#ifdef INDICATE_PROGRESS
657 std::cerr << clear_line << std::flush;
658#endif
659 }
660
661 void compute_dim_0_pairs(std::vector<diameter_index_t> &edges,
662 std::vector<diameter_index_t> &columns_to_reduce,
663 std::vector<std::vector<pers_pair_t>> &ph) {
664 union_find dset(n);
665
666 edges = get_edges();
667 std::sort(edges.rbegin(), edges.rend(),
669 std::vector<index_t> vertices_of_edge(2);
670 for(auto e : edges) {
671 get_simplex_vertices(get_index(e), 1, n, vertices_of_edge.rbegin());
672 index_t u = dset.find(vertices_of_edge[0]),
673 v = dset.find(vertices_of_edge[1]);
674
675 if(u != v) {
676 dset.link(u, v);
677 if(get_diameter(e) != 0)
678 ph[0].emplace_back(
680 {u == dset.find(vertices_of_edge[0]) ? vertices_of_edge[1]
681 : vertices_of_edge[0]},
682 0.},
684 {vertices_of_edge[0], vertices_of_edge[1]}, get_diameter(e)});
685 } else
686 columns_to_reduce.push_back(e);
687 }
688 std::reverse(columns_to_reduce.begin(), columns_to_reduce.end());
689
690 for(index_t i = 0; i < n; ++i) {
691 if(dset.find(i) == i)
692 ph[0].emplace_back(
693 simplex_diam_t{{i}, 0.},
694 simplex_diam_t{{-1}, std::numeric_limits<value_t>::infinity()});
695 }
696 }
697
698 template <typename Column>
699 diameter_entry_t pop_pivot(Column &column) {
700 diameter_entry_t pivot(-1);
701#ifdef USE_COEFFICIENTS
702 while(!column.empty()) {
703 if(get_coefficient(pivot) == 0)
704 pivot = column.top();
705 else if(get_index(column.top()) != get_index(pivot))
706 return pivot;
707 else
709 + get_coefficient(column.top())),
710 modulus));
711 column.pop();
712 }
713 return (get_coefficient(pivot) == 0) ? -1 : pivot;
714#else
715 while(!column.empty()) {
716 pivot = column.top();
717 column.pop();
718 if(column.empty() || get_index(column.top()) != get_index(pivot))
719 return pivot;
720 column.pop();
721 }
722 return -1;
723#endif
724 }
725
726 template <typename Column>
727 diameter_entry_t get_pivot(Column &column) {
728 diameter_entry_t result = pop_pivot(column);
729 if(get_index(result) != -1)
730 column.push(result);
731 return result;
732 }
733
734 template <typename Column>
737 Column &working_coboundary,
738 const index_t &dim,
739 entry_hash_map &pivot_column_index) {
740 bool check_for_emergent_pair = true;
741 cofacet_entries.clear();
742 simplex_coboundary_enumerator cofacets(simplex, dim, *this);
743 while(cofacets.has_next()) {
744 diameter_entry_t cofacet = cofacets.next();
745 if(get_diameter(cofacet) <= threshold) {
746 cofacet_entries.push_back(cofacet);
747 if(check_for_emergent_pair
748 && (get_diameter(simplex) == get_diameter(cofacet))) {
749 if(pivot_column_index.find(get_entry(cofacet))
750 == pivot_column_index.end())
751 return cofacet;
752 check_for_emergent_pair = false;
753 }
754 }
755 }
756 for(auto cofacet : cofacet_entries)
757 working_coboundary.push(cofacet);
758 return get_pivot(working_coboundary);
759 }
760
761 template <typename Column>
763 const index_t &dim,
764 Column &working_reduction_column,
765 Column &working_coboundary) {
766 working_reduction_column.push(simplex);
767 simplex_coboundary_enumerator cofacets(simplex, dim, *this);
768 while(cofacets.has_next()) {
769 diameter_entry_t cofacet = cofacets.next();
770 if(get_diameter(cofacet) <= threshold)
771 working_coboundary.push(cofacet);
772 }
773 }
774
775 template <typename Column>
776 void
778 const std::vector<diameter_index_t> &columns_to_reduce,
779 const size_t index_column_to_add,
780 const coefficient_t factor,
781 const size_t &dim,
782 Column &working_reduction_column,
783 Column &working_coboundary) {
784 diameter_entry_t column_to_add(
785 columns_to_reduce[index_column_to_add], factor);
787 column_to_add, dim, working_reduction_column, working_coboundary);
788
789 for(diameter_entry_t simplex :
790 reduction_matrix.subrange(index_column_to_add)) {
791 set_coefficient(simplex, get_coefficient(simplex) * factor % modulus);
793 simplex, dim, working_reduction_column, working_coboundary);
794 }
795 }
796
798 = std::priority_queue<diameter_entry_t,
799 std::vector<diameter_entry_t>,
801
802 void compute_pairs(std::vector<diameter_index_t> &columns_to_reduce,
803 entry_hash_map &pivot_column_index,
804 index_t dim,
805 std::vector<std::vector<pers_pair_t>> &ph) {
807 size_t index_column_to_add;
808
809#ifdef INDICATE_PROGRESS
810 std::chrono::steady_clock::time_point next
811 = std::chrono::steady_clock::now() + time_step;
812#endif
813
814 for(size_t index_column_to_reduce = 0;
815 index_column_to_reduce < columns_to_reduce.size();
816 ++index_column_to_reduce) {
817 diameter_entry_t column_to_reduce(
818 columns_to_reduce[index_column_to_reduce], 1);
819 value_t diameter = get_diameter(column_to_reduce);
820
821 reduction_matrix.append_column();
822
823 working_t working_reduction_column;
824 working_t working_coboundary;
825
826 working_reduction_column.push(column_to_reduce);
827
829 column_to_reduce, working_coboundary, dim, pivot_column_index);
830
831 while(true) {
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;
839 }
840#endif
841 if(get_index(pivot) != -1) {
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;
846 coefficient_t factor
847 = modulus
848 - get_modulo(
849 get_coefficient(pivot)
850 * multiplicative_inverse[get_coefficient(other_pivot)],
851 modulus);
852
853 add_coboundary(reduction_matrix, columns_to_reduce,
854 index_column_to_add, factor, dim,
855 working_reduction_column, working_coboundary);
856
857 pivot = get_pivot(working_coboundary);
858 } else {
859 value_t death = get_diameter(pivot);
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());
867 ph[dim].emplace_back(simplex_diam_t{vertices_birth, diameter},
868 simplex_diam_t{vertices_death, death});
869 }
870
871 pivot_column_index.insert(
872 {get_entry(pivot), index_column_to_reduce});
873
874 pop_pivot(working_reduction_column);
875 while(true) {
876 diameter_entry_t e = pop_pivot(working_reduction_column);
877
878 if(get_index(e) == -1)
879 break;
880 assert(get_coefficient(e) > 0);
881 reduction_matrix.push_back(e);
882 }
883 break;
884 }
885 } else {
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(
890 simplex_diam_t{vertices_birth, diameter},
891 simplex_diam_t{{-1}, std::numeric_limits<value_t>::infinity()});
892 break;
893 }
894 }
895 }
896#ifdef INDICATE_PROGRESS
897 std::cerr << clear_line << std::flush;
898#endif
899 }
900
901 std::vector<diameter_index_t> get_edges();
902 void compute_barcodes(std::vector<std::vector<pers_pair_t>> &ph) {
903 std::vector<diameter_index_t> simplices, columns_to_reduce;
904
905 /* prevent cases where dim_max < 0 */
906 if(dim_max < 0)
907 dim_max = 0;
908
909 compute_dim_0_pairs(simplices, columns_to_reduce, ph);
910
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());
914
915 compute_pairs(columns_to_reduce, pivot_column_index, dim, ph);
916
917 if(dim < dim_max)
919 simplices, columns_to_reduce, pivot_column_index, dim + 1);
920 }
921 }
922};
923
924template <>
926private:
927 index_t idx_below, idx_above, v, k;
928 std::vector<index_t> vertices;
929 const diameter_entry_t simplex;
930 const coefficient_t modulus;
932 const binomial_coeff_table &binomial_coeff;
933
934public:
936 const diameter_entry_t _simplex,
937 index_t _dim,
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());
945 }
946
947 bool has_next(bool all_cofacets = true) {
948 return (v >= k && (all_cofacets || binomial_coeff(v, k) > idx_below));
949 }
950
952 while((binomial_coeff(v, k) <= idx_below)) {
953 idx_below -= binomial_coeff(v, k);
954 idx_above += binomial_coeff(v, k + 1);
955 --v;
956 --k;
957 assert(k != -1);
958 }
959 value_t cofacet_diameter = get_diameter(simplex);
960 for(index_t w : vertices)
961 cofacet_diameter = std::max(cofacet_diameter, dist(v, w));
962 index_t cofacet_index = idx_above + binomial_coeff(v--, k + 1) + idx_below;
963 coefficient_t cofacet_coefficient
964 = (k & 1 ? modulus - 1 : 1) * get_coefficient(simplex) % modulus;
965 return diameter_entry_t(
966 cofacet_diameter, cofacet_index, cofacet_coefficient);
967 }
968};
969
970template <>
971class Ripser<sparse_distance_matrix>::simplex_coboundary_enumerator {
972 index_t idx_below, idx_above, k;
973 std::vector<index_t> vertices;
974 const diameter_entry_t simplex;
975 const coefficient_t modulus;
976 const sparse_distance_matrix &dist;
977 const binomial_coeff_table &binomial_coeff;
978 std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
979 &neighbor_it;
980 std::vector<std::vector<index_diameter_t>::const_reverse_iterator>
981 &neighbor_end;
982 index_diameter_t neighbor;
983
984public:
986 const index_t _dim,
987 const Ripser<sparse_distance_matrix> &parent)
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) {
992 neighbor_it.clear();
993 neighbor_end.clear();
994
995 parent.get_simplex_vertices(idx_below, _dim, parent.n, vertices.rbegin());
996
997 for(auto v : vertices) {
998 neighbor_it.push_back(dist.neighbors[v].rbegin());
999 neighbor_end.push_back(dist.neighbors[v].rend());
1000 }
1001 }
1002
1003 bool has_next(bool all_cofacets = true) {
1004 for(auto &it0 = neighbor_it[0], &end0 = neighbor_end[0]; it0 != end0;
1005 ++it0) {
1006 neighbor = *it0;
1007 for(size_t idx = 1; idx < neighbor_it.size(); ++idx) {
1008 auto &it = neighbor_it[idx], end = neighbor_end[idx];
1009 while(get_index(*it) > get_index(neighbor))
1010 if(++it == end)
1011 return false;
1012 if(get_index(*it) != get_index(neighbor))
1013 goto continue_outer;
1014 else
1015 neighbor = std::max(neighbor, *it);
1016 }
1017 while(k > 0 && vertices[k - 1] > get_index(neighbor)) {
1018 if(!all_cofacets)
1019 return false;
1020 idx_below -= binomial_coeff(vertices[k - 1], k);
1021 idx_above += binomial_coeff(vertices[k - 1], k + 1);
1022 --k;
1023 }
1024 return true;
1025 continue_outer:;
1026 }
1027 return false;
1028 }
1029
1031 ++neighbor_it[0];
1032 value_t cofacet_diameter
1033 = std::max(get_diameter(simplex), get_diameter(neighbor));
1034 index_t cofacet_index
1035 = idx_above + binomial_coeff(get_index(neighbor), k + 1) + idx_below;
1036 coefficient_t cofacet_coefficient
1037 = (k & 1 ? modulus - 1 : 1) * get_coefficient(simplex) % modulus;
1038 return diameter_entry_t(
1039 cofacet_diameter, cofacet_index, cofacet_coefficient);
1040 }
1041};
1042
1043template <>
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);
1053 }
1054 return edges;
1055}
1056
1057template <>
1058std::vector<diameter_index_t> Ripser<sparse_distance_matrix>::get_edges() {
1059 std::vector<diameter_index_t> edges;
1060 for(index_t i = 0; i < n; ++i)
1061 for(auto n_ : dist.neighbors[i]) {
1062 index_t j = get_index(n_);
1063 if(i > j)
1064 edges.emplace_back(get_diameter(n_), get_edge_index(i, j));
1065 }
1066 return edges;
1067}
1068
1069void ripser::ripser(std::vector<std::vector<value_t>> points,
1070 value_t threshold,
1071 index_t dim_max,
1072 bool distanceMatrix,
1073 std::vector<std::vector<pers_pair_t>> &ph) {
1074 double ratio = 1;
1075 coefficient_t modulus = 2;
1076
1077 ph = std::vector<std::vector<pers_pair_t>>(
1078 dim_max + 1, std::vector<pers_pair_t>(0));
1079
1080 if(!distanceMatrix) {
1081 euclidean_distance_matrix eucl_dist(std::move(points));
1082 sparse_distance_matrix dist(eucl_dist, threshold);
1084 std::move(dist), dim_max, threshold, ratio, modulus);
1085 ripser.compute_barcodes(ph);
1086 } else {
1087 compressed_lower_distance_matrix lower_dist(std::move(points[0]));
1088 sparse_distance_matrix dist(lower_dist, threshold);
1090 std::move(dist), dim_max, threshold, ratio, modulus);
1091 ripser.compute_barcodes(ph);
1092 }
1093}
simplex_coboundary_enumerator(const diameter_entry_t _simplex, index_t _dim, const Ripser< compressed_lower_distance_matrix > &parent)
Definition ripserpy.cpp:935
simplex_coboundary_enumerator(const diameter_entry_t _simplex, const index_t _dim, const Ripser< sparse_distance_matrix > &parent)
Definition ripserpy.cpp:985
bool has_next(bool all_cofacets=true)
Definition ripserpy.cpp:947
index_t get_edge_index(const index_t i, const index_t j) const
Definition ripserpy.cpp:584
Ripser(DistanceMatrix &&_dist, index_t _dim_max, value_t _threshold, float _ratio, coefficient_t _modulus)
Definition ripserpy.cpp:552
OutputIterator get_simplex_vertices(index_t idx, const index_t dim, index_t n_, OutputIterator out) const
Definition ripserpy.cpp:589
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:605
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:736
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:661
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:777
void compute_barcodes(std::vector< std::vector< pers_pair_t > > &ph)
Definition ripserpy.cpp:902
diameter_entry_t get_pivot(Column &column)
Definition ripserpy.cpp:727
void add_simplex_coboundary(const diameter_entry_t simplex, const index_t &dim, Column &working_reduction_column, Column &working_coboundary)
Definition ripserpy.cpp:762
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:564
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:802
diameter_entry_t pop_pivot(Column &column)
Definition ripserpy.cpp:699
std::priority_queue< diameter_entry_t, std::vector< diameter_entry_t >, greater_diameter_or_smaller_index< diameter_entry_t > > working_t
Definition ripserpy.cpp:800
binomial_coeff_table(index_t n, index_t k)
Definition ripserpy.cpp:133
index_t operator()(index_t n, index_t k) const
Definition ripserpy.cpp:146
std::vector< value_t > distances
Definition ripserpy.cpp:330
value_t operator()(const index_t i, const index_t j) const
Definition ripserpy.cpp:382
compressed_distance_matrix(std::vector< value_t > &&_distances)
Definition ripserpy.cpp:333
std::vector< value_t * > rows
Definition ripserpy.cpp:331
compressed_distance_matrix(const DistanceMatrix &mat)
Definition ripserpy.cpp:341
void push_back(const ValueType e)
Definition ripserpy.cpp:509
iterator_pair subrange(const index_t index)
Definition ripserpy.cpp:500
index_t find(index_t x)
Definition ripserpy.cpp:454
union_find(const index_t n)
Definition ripserpy.cpp:449
void link(index_t x, index_t y)
Definition ripserpy.cpp:465
uint16_t coefficient_t
Definition ripser.h:27
std::pair< simplex_t, value_t > simplex_diam_t
Definition ripser.h:30
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:25
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:250
index_t get_coefficient(const entry_t &)
Definition ripserpy.cpp:234
index_t entry_t
Definition ripserpy.cpp:225
index_t get_index(const entry_t &i)
Definition ripserpy.cpp:231
coefficient_t get_modulo(const coefficient_t val, const coefficient_t modulus)
Definition ripserpy.cpp:155
T end(std::pair< T, T > &p)
Definition ripserpy.cpp:483
compressed_matrix_layout
Definition ripserpy.cpp:322
@ UPPER_TRIANGULAR
Definition ripserpy.cpp:324
@ LOWER_TRIANGULAR
Definition ripserpy.cpp:323
value_t get_diameter(const diameter_index_t &i)
Definition ripserpy.cpp:253
coefficient_t normalize(const coefficient_t n, const coefficient_t modulus)
Definition ripserpy.cpp:159
std::pair< index_t, value_t > index_diameter_t
Definition ripserpy.cpp:260
std::vector< coefficient_t > multiplicative_inverse_vector(const coefficient_t m)
Definition ripserpy.cpp:164
entry_t make_entry(index_t _index, coefficient_t)
Definition ripserpy.cpp:237
const entry_t & get_entry(const entry_t &e)
Definition ripserpy.cpp:246
void set_coefficient(entry_t &, const coefficient_t)
Definition ripserpy.cpp:240
void check_overflow(index_t i)
Definition ripserpy.cpp:114
std::unordered_map< Key, T, H, E > hash_map
Definition ripserpy.cpp:80
T begin(std::pair< T, T > &p)
Definition ripserpy.cpp:479
Definition ripserpy.cpp:270
diameter_entry_t(value_t _diameter, index_t _index, coefficient_t _coefficient)
Definition ripserpy.cpp:273
diameter_entry_t()=default
diameter_entry_t(const index_t &_index)
Definition ripserpy.cpp:283
diameter_entry_t(const diameter_index_t &_diameter_index, coefficient_t _coefficient)
Definition ripserpy.cpp:278
value_t operator()(const index_t i, const index_t j) const
Definition ripserpy.cpp:430
euclidean_distance_matrix(std::vector< std::vector< value_t > > &&_points)
Definition ripserpy.cpp:426
std::vector< std::vector< value_t > > points
Definition ripserpy.cpp:424
bool operator()(const Entry &a, const Entry &b)
Definition ripserpy.cpp:315
size_t size() const
Definition ripserpy.cpp:418
std::vector< std::vector< index_diameter_t > > neighbors
Definition ripserpy.cpp:394
std::vector< std::vector< index_diameter_t >::const_reverse_iterator > neighbor_end
Definition ripserpy.cpp:400
sparse_distance_matrix(std::vector< std::vector< index_diameter_t > > &&_neighbors, index_t _num_edges)
Definition ripserpy.cpp:402
std::vector< std::vector< index_diameter_t >::const_reverse_iterator > neighbor_it
Definition ripserpy.cpp:398
sparse_distance_matrix(const DistanceMatrix &mat, const value_t threshold)
Definition ripserpy.cpp:408