TTK
Loading...
Searching...
No Matches
DiscreteMorseSandwich.h
Go to the documentation of this file.
1
21
22#pragma once
23
24#include <DiscreteGradient.h>
25
26#include <algorithm>
27#include <numeric>
28
29namespace ttk {
30 class DiscreteMorseSandwich : virtual public Debug {
31 public:
33
43 int type;
44
46 : birth{b}, death{d}, type{t} {
47 }
48 };
49
52 }
53
54 inline void setInputOffsets(const SimplexId *const offsets) {
55 this->dg_.setInputOffsets(offsets);
56 }
57
58 inline void setComputeMinSad(const bool data) {
59 this->ComputeMinSad = data;
60 }
61 inline void setComputeSadSad(const bool data) {
62 this->ComputeSadSad = data;
63 }
64 inline void setComputeSadMax(const bool data) {
65 this->ComputeSadMax = data;
66 }
67
68 template <typename triangulationType>
69 inline int buildGradient(const void *const scalars,
70 const size_t scalarsMTime,
71 const SimplexId *const offsets,
72 const triangulationType &triangulation,
73 const std::vector<bool> *updateMask = nullptr) {
74 this->dg_.setDebugLevel(this->debugLevel_);
76 this->dg_.setInputOffsets(offsets);
77 this->dg_.setInputScalarField(scalars, scalarsMTime);
78 return this->dg_.buildGradient(triangulation, false, updateMask);
79 }
80
93 this->dg_ = std::move(dg);
94 // reset gradient pointer to local storage
95 this->dg_.setLocalGradient();
96 }
98 return std::move(this->dg_);
99 }
100
101 template <typename triangulationType>
102 inline SimplexId
104 const triangulationType &triangulation) {
105 return this->dg_.getCellGreaterVertex(c, triangulation);
106 }
107
108 inline const std::vector<std::vector<SimplexId>> &
110 return this->s2Children_;
111 }
112
127 template <typename triangulationType>
128 int computePersistencePairs(std::vector<PersistencePair> &pairs,
129 const SimplexId *const offsets,
130 const triangulationType &triangulation,
131 const bool ignoreBoundary,
132 const bool compute2SaddlesChildren = false);
133
142 std::vector<SimplexId> boundary;
147 std::array<SimplexId, 2> critVertsIds;
148 };
149
150 protected:
160 template <typename triangulationType>
161 std::vector<std::vector<SimplexId>>
162 getSaddle1ToMinima(const std::vector<SimplexId> &criticalEdges,
163 const triangulationType &triangulation) const;
164
180 template <typename triangulationType,
181 typename GFS,
182 typename GFSN,
183 typename OB>
184 std::vector<std::vector<SimplexId>>
185 getSaddle2ToMaxima(const std::vector<SimplexId> &criticalCells,
186 const GFS &getFaceStar,
187 const GFSN &getFaceStarNumber,
188 const OB &isOnBoundary,
189 const triangulationType &triangulation) const;
190
202 template <typename triangulationType>
203 void getMinSaddlePairs(std::vector<PersistencePair> &pairs,
204 std::vector<bool> &pairedMinima,
205 std::vector<bool> &paired1Saddles,
206 const std::vector<SimplexId> &criticalEdges,
207 const std::vector<SimplexId> &critEdgesOrder,
208 const SimplexId *const offsets,
209 const triangulationType &triangulation) const;
210
222 template <typename triangulationType>
223 void getMaxSaddlePairs(std::vector<PersistencePair> &pairs,
224 std::vector<bool> &pairedMaxima,
225 std::vector<bool> &pairedSaddles,
226 const std::vector<SimplexId> &criticalSaddles,
227 const std::vector<SimplexId> &critSaddlesOrder,
228 const std::vector<SimplexId> &critMaxsOrder,
229 const triangulationType &triangulation) const;
230
244 template <typename triangulationType>
245 void getSaddleSaddlePairs(std::vector<PersistencePair> &pairs,
246 std::vector<bool> &paired1Saddles,
247 std::vector<bool> &paired2Saddles,
248 const bool exportBoundaries,
249 std::vector<GeneratorType> &boundaries,
250 const std::vector<SimplexId> &critical1Saddles,
251 const std::vector<SimplexId> &critical2Saddles,
252 const std::vector<SimplexId> &crit1SaddlesOrder,
253 const triangulationType &triangulation) const;
254
264 template <typename triangulationType>
266 std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
267 std::array<std::vector<SimplexId>, 4> &critCellsOrder,
268 const SimplexId *const offsets,
269 const triangulationType &triangulation,
270 const bool sortEdges) const;
271
283 void displayStats(
284 const std::vector<PersistencePair> &pairs,
285 const std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
286 const std::vector<bool> &pairedMinima,
287 const std::vector<bool> &paired1Saddles,
288 const std::vector<bool> &paired2Saddles,
289 const std::vector<bool> &pairedMaxima) const;
290
298 using tripletType = std::array<SimplexId, 3>;
299
312 void tripletsToPersistencePairs(std::vector<PersistencePair> &pairs,
313 std::vector<bool> &pairedExtrema,
314 std::vector<bool> &pairedSaddles,
315 std::vector<SimplexId> &reps,
316 std::vector<tripletType> &triplets,
317 const SimplexId *const saddlesOrder,
318 const SimplexId *const extremaOrder,
319 const SimplexId pairDim) const;
320
339 template <typename triangulationType, typename Container>
342 std::vector<bool> &onBoundary,
343 std::vector<Container> &s2Boundaries,
344 const std::vector<SimplexId> &s2Mapping,
345 const std::vector<SimplexId> &s1Mapping,
346 std::vector<SimplexId> &partners,
347 std::vector<Lock> &s1Locks,
348 std::vector<Lock> &s2Locks,
349 const triangulationType &triangulation) const;
350
356 template <size_t n>
357 struct Simplex {
362 std::array<SimplexId, n> vertsOrder_{};
365 friend bool operator<(const Simplex<n> &lhs, const Simplex<n> &rhs) {
366 return lhs.vertsOrder_ < rhs.vertsOrder_;
367 }
368 };
369
373 struct EdgeSimplex : Simplex<2> {
374 template <typename triangulationType>
375 void fillEdge(const SimplexId id,
376 const SimplexId *const offsets,
377 const triangulationType &triangulation) {
378 this->id_ = id;
379 triangulation.getEdgeVertex(id, 0, this->vertsOrder_[0]);
380 triangulation.getEdgeVertex(id, 1, this->vertsOrder_[1]);
381 this->vertsOrder_[0] = offsets[this->vertsOrder_[0]];
382 this->vertsOrder_[1] = offsets[this->vertsOrder_[1]];
383 // sort vertices in decreasing order
384 std::sort(this->vertsOrder_.rbegin(), this->vertsOrder_.rend());
385 }
386 };
387
392 template <typename triangulationType>
393 void fillTriangle(const SimplexId id,
394 const SimplexId *const offsets,
395 const triangulationType &triangulation) {
396 this->id_ = id;
397 triangulation.getTriangleVertex(id, 0, this->vertsOrder_[0]);
398 triangulation.getTriangleVertex(id, 1, this->vertsOrder_[1]);
399 triangulation.getTriangleVertex(id, 2, this->vertsOrder_[2]);
400 this->vertsOrder_[0] = offsets[this->vertsOrder_[0]];
401 this->vertsOrder_[1] = offsets[this->vertsOrder_[1]];
402 this->vertsOrder_[2] = offsets[this->vertsOrder_[2]];
403 // sort vertices in decreasing order
404 std::sort(this->vertsOrder_.rbegin(), this->vertsOrder_.rend());
405 }
406 };
407
411 struct TetraSimplex : Simplex<4> {
412 template <typename triangulationType>
413 void fillTetra(const SimplexId id,
414 const SimplexId *const offsets,
415 const triangulationType &triangulation) {
416 this->id_ = id;
417 triangulation.getCellVertex(id, 0, this->vertsOrder_[0]);
418 triangulation.getCellVertex(id, 1, this->vertsOrder_[1]);
419 triangulation.getCellVertex(id, 2, this->vertsOrder_[2]);
420 triangulation.getCellVertex(id, 3, this->vertsOrder_[3]);
421 this->vertsOrder_[0] = offsets[this->vertsOrder_[0]];
422 this->vertsOrder_[1] = offsets[this->vertsOrder_[1]];
423 this->vertsOrder_[2] = offsets[this->vertsOrder_[2]];
424 this->vertsOrder_[3] = offsets[this->vertsOrder_[3]];
425 // sort vertices in decreasing order
426 std::sort(this->vertsOrder_.rbegin(), this->vertsOrder_.rend());
427 }
428 };
429
430 template <typename triangulationType>
431 void alloc(const triangulationType &triangulation) {
432 Timer tm{};
433 const auto dim{this->dg_.getDimensionality()};
434 if(dim > 3 || dim < 1) {
435 return;
436 }
437#ifdef TTK_ENABLE_OPENMP
438#pragma omp parallel master num_threads(threadNumber_)
439#endif
440 {
441#ifdef TTK_ENABLE_OPENMP
442#pragma omp task
443#endif // TTK_ENABLE_OPENMP
444 this->firstRepMin_.resize(triangulation.getNumberOfVertices());
445 if(dim > 1) {
446#ifdef TTK_ENABLE_OPENMP
447#pragma omp task
448#endif
449 this->firstRepMax_.resize(triangulation.getNumberOfCells());
450 }
451 if(dim > 2) {
452#ifdef TTK_ENABLE_OPENMP
453#pragma omp task
454#endif
455 this->critEdges_.resize(triangulation.getNumberOfEdges());
456#ifdef TTK_ENABLE_OPENMP
457#pragma omp task
458#endif
459 this->edgeTrianglePartner_.resize(
460 triangulation.getNumberOfEdges(), -1);
461#ifdef TTK_ENABLE_OPENMP
462#pragma omp task
463#endif
464 this->onBoundary_.resize(triangulation.getNumberOfEdges(), false);
465#ifdef TTK_ENABLE_OPENMP
466#pragma omp task
467#endif
468 this->s2Mapping_.resize(triangulation.getNumberOfTriangles(), -1);
469#ifdef TTK_ENABLE_OPENMP
470#pragma omp task
471#endif
472 this->s1Mapping_.resize(triangulation.getNumberOfEdges(), -1);
473 }
474 for(int i = 0; i < dim + 1; ++i) {
475#ifdef TTK_ENABLE_OPENMP
476#pragma omp task
477#endif
478 this->pairedCritCells_[i].resize(
479 this->dg_.getNumberOfCells(i, triangulation), false);
480 }
481 // NOTE:
482 // a for loop used to stand here, but gcc 13 looks buggy with it...
483 if(dim >= 1) {
484#ifdef TTK_ENABLE_OPENMP
485#pragma omp task
486#endif
487 this->critCellsOrder_[1].resize(
488 this->dg_.getNumberOfCells(1, triangulation), -1);
489 }
490 if(dim >= 2) {
491#ifdef TTK_ENABLE_OPENMP
492#pragma omp task
493#endif
494 this->critCellsOrder_[2].resize(
495 this->dg_.getNumberOfCells(2, triangulation), -1);
496 }
497 if(dim >= 3) {
498#ifdef TTK_ENABLE_OPENMP
499#pragma omp task
500#endif
501 this->critCellsOrder_[3].resize(
502 this->dg_.getNumberOfCells(3, triangulation), -1);
503 }
504 }
505 this->printMsg("Memory allocations", 1.0, tm.getElapsedTime(), 1,
507 }
508
509 void clear() {
510 Timer tm{};
511 this->firstRepMin_ = {};
512 this->firstRepMax_ = {};
513 this->edgeTrianglePartner_ = {};
514 this->s2Mapping_ = {};
515 this->s1Mapping_ = {};
516 this->critEdges_ = {};
517 this->pairedCritCells_ = {};
518 this->onBoundary_ = {};
519 this->critCellsOrder_ = {};
520 this->printMsg("Memory cleanup", 1.0, tm.getElapsedTime(), 1,
522 }
523
525
526 // factor memory allocations outside computation loops
527 mutable std::vector<SimplexId> firstRepMin_{}, firstRepMax_{},
529 mutable std::vector<EdgeSimplex> critEdges_{};
530 mutable std::array<std::vector<bool>, 4> pairedCritCells_{};
531 mutable std::vector<bool> onBoundary_{};
532 mutable std::array<std::vector<SimplexId>, 4> critCellsOrder_{};
533 mutable std::vector<std::vector<SimplexId>> s2Children_{};
534
535 bool ComputeMinSad{true};
536 bool ComputeSadSad{true};
537 bool ComputeSadMax{true};
539 };
540} // namespace ttk
541
542template <typename triangulationType>
543std::vector<std::vector<SimplexId>>
545 const std::vector<SimplexId> &criticalEdges,
546 const triangulationType &triangulation) const {
547
548 Timer tm{};
549
550 std::vector<std::vector<SimplexId>> res(criticalEdges.size());
551
552 // follow vpaths from 1-saddles to minima
553#ifdef TTK_ENABLE_OPENMP
554#pragma omp parallel for num_threads(threadNumber_)
555#endif
556 for(size_t i = 0; i < criticalEdges.size(); ++i) {
557 auto &mins = res[i];
558
559 const auto followVPath = [this, &mins, &triangulation](const SimplexId v) {
560 std::vector<Cell> vpath{};
561 this->dg_.getDescendingPath(Cell{0, v}, vpath, triangulation);
562 const Cell &lastCell = vpath.back();
563 if(lastCell.dim_ == 0 && this->dg_.isCellCritical(lastCell)) {
564 mins.emplace_back(lastCell.id_);
565 }
566 };
567
568 // critical edge vertices
569 SimplexId v0{}, v1{};
570 triangulation.getEdgeVertex(criticalEdges[i], 0, v0);
571 triangulation.getEdgeVertex(criticalEdges[i], 1, v1);
572
573 // follow vpath from each vertex of the critical edge
574 followVPath(v0);
575 followVPath(v1);
576 }
577
578 this->printMsg("Computed the descending 1-separatrices", 1.0,
579 tm.getElapsedTime(), this->threadNumber_, debug::LineMode::NEW,
581
582 return res;
583}
584
585template <typename triangulationType, typename GFS, typename GFSN, typename OB>
586std::vector<std::vector<SimplexId>>
588 const std::vector<SimplexId> &criticalCells,
589 const GFS &getFaceStar,
590 const GFSN &getFaceStarNumber,
591 const OB &isOnBoundary,
592 const triangulationType &triangulation) const {
593
594 Timer tm{};
595
596 const auto dim = this->dg_.getDimensionality();
597 std::vector<std::vector<SimplexId>> res(criticalCells.size());
598
599 // follow vpaths from 2-saddles to maxima
600#ifdef TTK_ENABLE_OPENMP
601#pragma omp parallel for num_threads(threadNumber_)
602#endif
603 for(size_t i = 0; i < criticalCells.size(); ++i) {
604 const auto sid = criticalCells[i];
605 auto &maxs = res[i];
606
607 const auto followVPath
608 = [this, dim, &maxs, &triangulation](const SimplexId v) {
609 std::vector<Cell> vpath{};
610 this->dg_.getAscendingPath(Cell{dim, v}, vpath, triangulation);
611 const Cell &lastCell = vpath.back();
612 if(lastCell.dim_ == dim && this->dg_.isCellCritical(lastCell)) {
613 maxs.emplace_back(lastCell.id_);
614 } else if(lastCell.dim_ == dim - 1) {
615 maxs.emplace_back(-1);
616 }
617 };
618
619 const auto starNumber = getFaceStarNumber(sid);
620
621 for(SimplexId j = 0; j < starNumber; ++j) {
622 SimplexId cellId{};
623 getFaceStar(sid, j, cellId);
624 followVPath(cellId);
625 }
626
627 if(isOnBoundary(sid)) {
628 // critical saddle is on boundary
629 maxs.emplace_back(-1);
630 }
631 }
632
633 this->printMsg("Computed the ascending 1-separatrices", 1.0,
634 tm.getElapsedTime(), this->threadNumber_, debug::LineMode::NEW,
636
637 return res;
638}
639
640template <typename triangulationType>
642 std::vector<PersistencePair> &pairs,
643 std::vector<bool> &pairedMinima,
644 std::vector<bool> &paired1Saddles,
645 const std::vector<SimplexId> &criticalEdges,
646 const std::vector<SimplexId> &critEdgesOrder,
647 const SimplexId *const offsets,
648 const triangulationType &triangulation) const {
649
650 Timer tm{};
651
652 auto saddle1ToMinima = getSaddle1ToMinima(criticalEdges, triangulation);
653
654 Timer tmseq{};
655
656 auto &firstRep{this->firstRepMin_};
657 std::iota(firstRep.begin(), firstRep.end(), 0);
658 std::vector<tripletType> sadMinTriplets{};
659
660 for(size_t i = 0; i < saddle1ToMinima.size(); ++i) {
661 auto &mins = saddle1ToMinima[i];
662 const auto s1 = criticalEdges[i];
663 // remove duplicates
664 TTK_PSORT(this->threadNumber_, mins.begin(), mins.end());
665 const auto last = std::unique(mins.begin(), mins.end());
666 mins.erase(last, mins.end());
667 if(mins.size() != 2) {
668 continue;
669 }
670 sadMinTriplets.emplace_back(tripletType{s1, mins[0], mins[1]});
671 }
672
673 tripletsToPersistencePairs(pairs, pairedMinima, paired1Saddles, firstRep,
674 sadMinTriplets, critEdgesOrder.data(), offsets, 0);
675
676 const auto nMinSadPairs = pairs.size();
677
678 this->printMsg(
679 "Computed " + std::to_string(nMinSadPairs) + " min-saddle pairs", 1.0,
680 tm.getElapsedTime(), this->threadNumber_);
681
682 this->printMsg("min-saddle pairs sequential part", 1.0,
683 tmseq.getElapsedTime(), 1, debug::LineMode::NEW,
685}
686
687template <typename triangulationType>
689 std::vector<PersistencePair> &pairs,
690 std::vector<bool> &pairedMaxima,
691 std::vector<bool> &pairedSaddles,
692 const std::vector<SimplexId> &criticalSaddles,
693 const std::vector<SimplexId> &critSaddlesOrder,
694 const std::vector<SimplexId> &critMaxsOrder,
695 const triangulationType &triangulation) const {
696
697 Timer tm{};
698
699 const auto dim = this->dg_.getDimensionality();
700
701 auto saddle2ToMaxima
702 = dim == 3
703 ? getSaddle2ToMaxima(
704 criticalSaddles,
705 [&triangulation](const SimplexId a, const SimplexId i, SimplexId &r) {
706 return triangulation.getTriangleStar(a, i, r);
707 },
708 [&triangulation](const SimplexId a) {
709 return triangulation.getTriangleStarNumber(a);
710 },
711 [&triangulation](const SimplexId a) {
712 return triangulation.isTriangleOnBoundary(a);
713 },
714 triangulation)
715 : getSaddle2ToMaxima(
716 criticalSaddles,
717 [&triangulation](const SimplexId a, const SimplexId i, SimplexId &r) {
718 return triangulation.getEdgeStar(a, i, r);
719 },
720 [&triangulation](const SimplexId a) {
721 return triangulation.getEdgeStarNumber(a);
722 },
723 [&triangulation](const SimplexId a) {
724 return triangulation.isEdgeOnBoundary(a);
725 },
726 triangulation);
727
728 Timer tmseq{};
729
730 auto &firstRep{this->firstRepMax_};
731 std::iota(firstRep.begin(), firstRep.end(), 0);
732 std::vector<tripletType> sadMaxTriplets{};
733
734 for(size_t i = 0; i < saddle2ToMaxima.size(); ++i) {
735 auto &maxs = saddle2ToMaxima[i];
736 // remove duplicates
737 TTK_PSORT(this->threadNumber_, maxs.begin(), maxs.end(),
738 [](const SimplexId a, const SimplexId b) {
739 // positive values (actual maxima) before negative ones
740 // (boundary component id)
741 if(a * b >= 0) {
742 return a < b;
743 } else {
744 return a > b;
745 }
746 });
747 const auto last = std::unique(maxs.begin(), maxs.end());
748 maxs.erase(last, maxs.end());
749
750 // remove "doughnut" configurations: two ascending separatrices
751 // leading to the same maximum/boundary component
752 if(maxs.size() != 2) {
753 continue;
754 }
755
756 const auto s2 = criticalSaddles[i];
757 if(!pairedSaddles[s2]) {
758 sadMaxTriplets.emplace_back(tripletType{s2, maxs[0], maxs[1]});
759 }
760 }
761
762 const auto nMinSadPairs = pairs.size();
763
764 tripletsToPersistencePairs(pairs, pairedMaxima, pairedSaddles, firstRep,
765 sadMaxTriplets, critSaddlesOrder.data(),
766 critMaxsOrder.data(), dim - 1);
767
768 const auto nSadMaxPairs = pairs.size() - nMinSadPairs;
769
770 this->printMsg(
771 "Computed " + std::to_string(nSadMaxPairs) + " saddle-max pairs", 1.0,
772 tm.getElapsedTime(), this->threadNumber_);
773
774 this->printMsg("saddle-max pairs sequential part", 1.0,
775 tmseq.getElapsedTime(), 1, debug::LineMode::NEW,
777}
778
779template <typename triangulationType, typename Container>
781 const SimplexId s2,
782 std::vector<bool> &onBoundary,
783 std::vector<Container> &s2Boundaries,
784 const std::vector<SimplexId> &s2Mapping,
785 const std::vector<SimplexId> &s1Mapping,
786 std::vector<SimplexId> &partners,
787 std::vector<Lock> &s1Locks,
788 std::vector<Lock> &s2Locks,
789 const triangulationType &triangulation) const {
790
791 auto &boundaryIds{s2Boundaries[s2Mapping[s2]]};
792
793 const auto addBoundary = [&boundaryIds, &onBoundary](const SimplexId e) {
794 // add edge e to boundaryIds/onBoundary modulo 2
795 if(!onBoundary[e]) {
796 boundaryIds.emplace(e);
797 onBoundary[e] = true;
798 } else {
799 const auto it = boundaryIds.find(e);
800 boundaryIds.erase(it);
801 onBoundary[e] = false;
802 }
803 };
804
805 const auto clearOnBoundary = [&boundaryIds, &onBoundary]() {
806 // clear the onBoundary vector (set everything to false)
807 for(const auto e : boundaryIds) {
808 onBoundary[e] = false;
809 }
810 };
811
812 if(!boundaryIds.empty()) {
813 // restore previously computed s2 boundary
814 for(const auto e : boundaryIds) {
815 onBoundary[e] = true;
816 }
817 } else {
818 // init cascade with s2 triangle boundary (3 edges)
819 for(SimplexId i = 0; i < 3; ++i) {
820 SimplexId e{};
821 triangulation.getTriangleEdge(s2, i, e);
822 addBoundary(e);
823 }
824 }
825
826 // lock the 2-saddle to ensure that only one thread can perform the
827 // boundary expansion
828 s2Locks[s2Mapping[s2]].lock();
829
830 while(!boundaryIds.empty()) {
831 // tau: youngest edge on boundary
832 const auto tau{*boundaryIds.begin()};
833 // use the Discrete Gradient to find a triangle paired to tau
834 auto pTau{this->dg_.getPairedCell(Cell{1, tau}, triangulation)};
835 bool critical{false};
836 if(pTau == -1) {
837 // maybe tau is critical and paired to a critical triangle
838 do {
839#ifdef TTK_ENABLE_OPENMP
840#pragma omp atomic read
841#endif // TTK_ENABLE_OPENMP
842 pTau = partners[tau];
843 if(pTau == -1 || s2Boundaries[s2Mapping[pTau]].empty()) {
844 break;
845 }
846 } while(*s2Boundaries[s2Mapping[pTau]].begin() != tau);
847
848 critical = true;
849 }
850 if(pTau == -1) {
851 // tau is critical and not paired
852
853 // compare-and-swap from "Towards Lockfree Persistent Homology"
854 // using locks over 1-saddles instead of atomics (OpenMP compatibility)
855 s1Locks[s1Mapping[tau]].lock();
856 const auto cap = partners[tau];
857 if(partners[tau] == -1) {
858 partners[tau] = s2;
859 }
860 s1Locks[s1Mapping[tau]].unlock();
861
862 // cleanup before exiting
863 clearOnBoundary();
864 s2Locks[s2Mapping[s2]].unlock();
865 if(cap == -1) {
866 return tau;
867 } else {
868 return this->eliminateBoundariesSandwich(
869 s2, onBoundary, s2Boundaries, s2Mapping, s1Mapping, partners, s1Locks,
870 s2Locks, triangulation);
871 }
872
873 } else {
874 // expand boundary
875 if(critical && s2Mapping[pTau] != -1) {
876 if(s2Mapping[pTau] < s2Mapping[s2]) {
877 // pTau is an already-paired 2-saddle
878 // merge pTau boundary into s2 boundary
879
880 // make sure that pTau boundary is not modified by another
881 // thread while we merge the two boundaries...
882 s2Locks[s2Mapping[pTau]].lock();
883 for(const auto e : s2Boundaries[s2Mapping[pTau]]) {
884 addBoundary(e);
885 }
886 s2Locks[s2Mapping[pTau]].unlock();
887 if(this->Compute2SaddlesChildren) {
888 this->s2Children_[s2Mapping[s2]].emplace_back(s2Mapping[pTau]);
889 }
890
891 } else if(s2Mapping[pTau] > s2Mapping[s2]) {
892
893 // compare-and-swap from "Towards Lockfree Persistent
894 // Homology" using locks over 1-saddles
895 s1Locks[s1Mapping[tau]].lock();
896 const auto cap = partners[tau];
897 if(partners[tau] == pTau) {
898 partners[tau] = s2;
899 }
900 s1Locks[s1Mapping[tau]].unlock();
901
902 if(cap == pTau) {
903 // cleanup before exiting
904 clearOnBoundary();
905 s2Locks[s2Mapping[s2]].unlock();
906 return this->eliminateBoundariesSandwich(
907 pTau, onBoundary, s2Boundaries, s2Mapping, s1Mapping, partners,
908 s1Locks, s2Locks, triangulation);
909 }
910 }
911 } else { // pTau is a regular triangle
912 // add pTau triangle boundary (3 edges)
913 for(SimplexId i = 0; i < 3; ++i) {
914 SimplexId e{};
915 triangulation.getTriangleEdge(pTau, i, e);
916 addBoundary(e);
917 }
918 }
919 }
920 }
921
922 // cleanup before exiting
923 clearOnBoundary();
924 s2Locks[s2Mapping[s2]].unlock();
925 return -1;
926}
927
928template <typename triangulationType>
930 std::vector<PersistencePair> &pairs,
931 std::vector<bool> &paired1Saddles,
932 std::vector<bool> &paired2Saddles,
933 const bool exportBoundaries,
934 std::vector<GeneratorType> &boundaries,
935 const std::vector<SimplexId> &critical1Saddles,
936 const std::vector<SimplexId> &critical2Saddles,
937 const std::vector<SimplexId> &crit1SaddlesOrder,
938 const triangulationType &triangulation) const {
939
940 Timer tm2{};
941 const auto nSadExtrPairs = pairs.size();
942
943 // 1- and 2-saddles yet to be paired
944 std::vector<SimplexId> saddles1{}, saddles2{};
945 // filter out already paired 1-saddles (edge id)
946 for(const auto s1 : critical1Saddles) {
947 if(!paired1Saddles[s1]) {
948 saddles1.emplace_back(s1);
949 }
950 }
951 // filter out already paired 2-saddles (triangle id)
952 for(const auto s2 : critical2Saddles) {
953 if(!paired2Saddles[s2]) {
954 saddles2.emplace_back(s2);
955 }
956 }
957
958 if(this->Compute2SaddlesChildren) {
959 this->s2Children_.resize(saddles2.size());
960 }
961
962 // sort every triangulation edges by filtration order
963 const auto &edgesFiltrOrder{crit1SaddlesOrder};
964
965 auto &onBoundary{this->onBoundary_};
966 auto &edgeTrianglePartner{this->edgeTrianglePartner_};
967
968 const auto cmpEdges
969 = [&edgesFiltrOrder](const SimplexId a, const SimplexId b) {
970 return edgesFiltrOrder[a] > edgesFiltrOrder[b];
971 };
972 using Container = std::set<SimplexId, decltype(cmpEdges)>;
973 std::vector<Container> s2Boundaries(saddles2.size(), Container(cmpEdges));
974
975 // unpaired critical triangle id -> index in saddle2 vector
976 auto &s2Mapping{this->s2Mapping_};
977#ifdef TTK_ENABLE_OPENMP
978#pragma omp parallel for num_threads(threadNumber_)
979#endif // TTK_ENABLE_OPENMP
980 for(size_t i = 0; i < saddles2.size(); ++i) {
981 s2Mapping[saddles2[i]] = i;
982 }
983
984 // unpaired critical edge id -> index in saddle1 vector
985 auto &s1Mapping{this->s1Mapping_};
986#ifdef TTK_ENABLE_OPENMP
987#pragma omp parallel for num_threads(threadNumber_)
988#endif // TTK_ENABLE_OPENMP
989 for(size_t i = 0; i < saddles1.size(); ++i) {
990 s1Mapping[saddles1[i]] = i;
991 }
992
993 // one lock per 1-saddle
994 std::vector<Lock> s1Locks(saddles1.size());
995 // one lock per 2-saddle
996 std::vector<Lock> s2Locks(saddles2.size());
997
998 // compute 2-saddles boundaries in parallel
999
1000#ifdef TTK_ENABLE_OPENMP4
1001#pragma omp parallel for num_threads(threadNumber_) schedule(dynamic) \
1002 firstprivate(onBoundary)
1003#endif // TTK_ENABLE_OPENMP4
1004 for(size_t i = 0; i < saddles2.size(); ++i) {
1005 // 2-saddles sorted in increasing order
1006 const auto s2 = saddles2[i];
1007 this->eliminateBoundariesSandwich(s2, onBoundary, s2Boundaries, s2Mapping,
1008 s1Mapping, edgeTrianglePartner, s1Locks,
1009 s2Locks, triangulation);
1010 }
1011
1012 Timer tmseq{};
1013
1014 // extract saddle-saddle pairs from computed boundaries
1015
1016 for(size_t i = 0; i < saddles2.size(); ++i) {
1017 if(!s2Boundaries[i].empty()) {
1018 const auto s2 = saddles2[i];
1019 const auto s1 = *s2Boundaries[i].begin();
1020 // we found a pair
1021 pairs.emplace_back(s1, s2, 1);
1022 paired1Saddles[s1] = true;
1023 paired2Saddles[s2] = true;
1024 }
1025 }
1026
1027 if(exportBoundaries) {
1028 boundaries.resize(s2Boundaries.size());
1029 for(size_t i = 0; i < boundaries.size(); ++i) {
1030 const auto &boundSet{s2Boundaries[i]};
1031 if(boundSet.empty()) {
1032 continue;
1033 }
1034 boundaries[i] = {
1035 {boundSet.begin(), boundSet.end()},
1036 saddles2[i],
1037 std::array<SimplexId, 2>{
1038 this->dg_.getCellGreaterVertex(Cell{2, saddles2[i]}, triangulation),
1039 this->dg_.getCellGreaterVertex(
1040 Cell{1, *boundSet.begin()}, triangulation),
1041 }};
1042 }
1043 }
1044
1045 const auto nSadSadPairs = pairs.size() - nSadExtrPairs;
1046
1047 this->printMsg(
1048 "Computed " + std::to_string(nSadSadPairs) + " saddle-saddle pairs", 1.0,
1049 tm2.getElapsedTime(), this->threadNumber_);
1050
1051 this->printMsg("saddle-saddle pairs sequential part", 1.0,
1052 tmseq.getElapsedTime(), 1, debug::LineMode::NEW,
1054}
1055
1056template <typename triangulationType>
1058 std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
1059 std::array<std::vector<SimplexId>, 4> &critCellsOrder,
1060 const SimplexId *const offsets,
1061 const triangulationType &triangulation,
1062 const bool sortEdges) const {
1063
1064 Timer tm{};
1065
1066 this->dg_.getCriticalPoints(criticalCellsByDim, triangulation);
1067
1068 this->printMsg("Extracted critical cells", 1.0, tm.getElapsedTime(),
1069 this->threadNumber_, debug::LineMode::NEW,
1071
1072 // memory allocations
1073 auto &critEdges{this->critEdges_};
1074 if(!sortEdges) {
1075 critEdges.resize(criticalCellsByDim[1].size());
1076 }
1077 std::vector<TriangleSimplex> critTriangles(criticalCellsByDim[2].size());
1078 std::vector<TetraSimplex> critTetras(criticalCellsByDim[3].size());
1079
1080#ifdef TTK_ENABLE_OPENMP
1081#pragma omp parallel num_threads(threadNumber_)
1082#endif // TTK_ENABLE_OPENMP
1083 {
1084 if(sortEdges) {
1085#ifdef TTK_ENABLE_OPENMP
1086#pragma omp for nowait
1087#endif // TTK_ENABLE_OPENMP
1088 for(size_t i = 0; i < critEdges.size(); ++i) {
1089 critEdges[i].fillEdge(i, offsets, triangulation);
1090 }
1091 } else {
1092#ifdef TTK_ENABLE_OPENMP
1093#pragma omp for nowait
1094#endif // TTK_ENABLE_OPENMP
1095 for(size_t i = 0; i < critEdges.size(); ++i) {
1096 critEdges[i].fillEdge(criticalCellsByDim[1][i], offsets, triangulation);
1097 }
1098 }
1099
1100#ifdef TTK_ENABLE_OPENMP
1101#pragma omp for nowait
1102#endif // TTK_ENABLE_OPENMP
1103 for(size_t i = 0; i < critTriangles.size(); ++i) {
1104 critTriangles[i].fillTriangle(
1105 criticalCellsByDim[2][i], offsets, triangulation);
1106 }
1107
1108#ifdef TTK_ENABLE_OPENMP
1109#pragma omp for
1110#endif // TTK_ENABLE_OPENMP
1111 for(size_t i = 0; i < critTetras.size(); ++i) {
1112 critTetras[i].fillTetra(criticalCellsByDim[3][i], offsets, triangulation);
1113 }
1114 }
1115
1116 TTK_PSORT(this->threadNumber_, critEdges.begin(), critEdges.end());
1117 TTK_PSORT(this->threadNumber_, critTriangles.begin(), critTriangles.end());
1118 TTK_PSORT(this->threadNumber_, critTetras.begin(), critTetras.end());
1119
1120#ifdef TTK_ENABLE_OPENMP
1121#pragma omp parallel num_threads(threadNumber_)
1122#endif // TTK_ENABLE_OPENMP
1123 {
1124#ifdef TTK_ENABLE_OPENMP
1125#pragma omp for nowait
1126#endif // TTK_ENABLE_OPENMP
1127 for(size_t i = 0; i < critEdges.size(); ++i) {
1128 critCellsOrder[1][critEdges[i].id_] = i;
1129 }
1130
1131#ifdef TTK_ENABLE_OPENMP
1132#pragma omp for nowait
1133#endif // TTK_ENABLE_OPENMP
1134 for(size_t i = 0; i < critTriangles.size(); ++i) {
1135 criticalCellsByDim[2][i] = critTriangles[i].id_;
1136 critCellsOrder[2][critTriangles[i].id_] = i;
1137 }
1138
1139#ifdef TTK_ENABLE_OPENMP
1140#pragma omp for
1141#endif // TTK_ENABLE_OPENMP
1142 for(size_t i = 0; i < critTetras.size(); ++i) {
1143 criticalCellsByDim[3][i] = critTetras[i].id_;
1144 critCellsOrder[3][critTetras[i].id_] = i;
1145 }
1146 }
1147
1148 if(sortEdges) {
1149 TTK_PSORT(this->threadNumber_, criticalCellsByDim[1].begin(),
1150 criticalCellsByDim[1].end(),
1151 [&critCellsOrder](const SimplexId a, const SimplexId b) {
1152 return critCellsOrder[1][a] < critCellsOrder[1][b];
1153 });
1154 } else {
1155#ifdef TTK_ENABLE_OPENMP
1156#pragma omp parallel for num_threads(threadNumber_)
1157#endif // TTK_ENABLE_OPENMP
1158 for(size_t i = 0; i < critEdges.size(); ++i) {
1159 criticalCellsByDim[1][i] = critEdges[i].id_;
1160 }
1161 }
1162
1163 this->printMsg("Extracted & sorted critical cells", 1.0, tm.getElapsedTime(),
1164 this->threadNumber_, debug::LineMode::NEW,
1166}
1167
1168template <typename triangulationType>
1170 std::vector<PersistencePair> &pairs,
1171 const SimplexId *const offsets,
1172 const triangulationType &triangulation,
1173 const bool ignoreBoundary,
1174 const bool compute2SaddlesChildren) {
1175
1176 // allocate memory
1177 this->alloc(triangulation);
1178
1179 Timer tm{};
1180 pairs.clear();
1181 const auto dim = this->dg_.getDimensionality();
1182 this->Compute2SaddlesChildren = compute2SaddlesChildren;
1183
1184 // get every critical cell sorted them by dimension
1185 std::array<std::vector<SimplexId>, 4> criticalCellsByDim{};
1186 // holds the critical cells order
1187 auto &critCellsOrder{this->critCellsOrder_};
1188
1189 this->extractCriticalCells(
1190 criticalCellsByDim, critCellsOrder, offsets, triangulation, dim == 3);
1191
1192 // if minima are paired
1193 auto &pairedMinima{this->pairedCritCells_[0]};
1194 // if 1-saddles are paired
1195 auto &paired1Saddles{this->pairedCritCells_[1]};
1196 // if 2-saddles are paired
1197 auto &paired2Saddles{this->pairedCritCells_[dim - 1]};
1198 // if maxima are paired
1199 auto &pairedMaxima{this->pairedCritCells_[dim]};
1200
1201 // connected components (global min/max pair)
1202 size_t nConnComp{};
1203
1204 if(this->ComputeMinSad) {
1205 // minima - saddle pairs
1206 this->getMinSaddlePairs(pairs, pairedMinima, paired1Saddles,
1207 criticalCellsByDim[1], critCellsOrder[1], offsets,
1208 triangulation);
1209
1210 // non-paired minima
1211 for(const auto min : criticalCellsByDim[0]) {
1212 if(!pairedMinima[min]) {
1213 pairs.emplace_back(min, -1, 0);
1214 pairedMinima[min] = true;
1215 nConnComp++;
1216 }
1217 }
1218 } else {
1219 // still extract the global pair
1220 const auto globMin{*std::min_element(
1221 criticalCellsByDim[0].begin(), criticalCellsByDim[0].end(),
1222 [offsets](const SimplexId a, const SimplexId b) {
1223 return offsets[a] < offsets[b];
1224 })};
1225 pairs.emplace_back(globMin, -1, 0);
1226 pairedMinima[globMin] = true;
1227 nConnComp++;
1228 }
1229
1230 if(dim > 1 && this->ComputeSadMax) {
1231 // saddle - maxima pairs
1232 this->getMaxSaddlePairs(
1233 pairs, pairedMaxima, paired2Saddles, criticalCellsByDim[dim - 1],
1234 critCellsOrder[dim - 1], critCellsOrder[dim], triangulation);
1235 }
1236
1237 if(ignoreBoundary) {
1238 // post-process saddle-max pairs: remove the one with the global
1239 // maximum (if it exists) to be (more) compatible with FTM
1240 const auto it
1241 = std::find_if(pairs.begin(), pairs.end(), [&](const PersistencePair &p) {
1242 if(p.type < dim - 1) {
1243 return false;
1244 }
1245 const Cell cmax{dim, p.death};
1246 const auto vmax{this->getCellGreaterVertex(cmax, triangulation)};
1247 return offsets[vmax] == triangulation.getNumberOfVertices() - 1;
1248 });
1249
1250 if(it != pairs.end()) {
1251 // remove saddle-max pair with global maximum
1252 paired2Saddles[it->birth] = false;
1253 pairedMaxima[it->death] = false;
1254 pairs.erase(it);
1255 }
1256 }
1257
1258 // saddle - saddle pairs
1259 if(dim == 3 && !criticalCellsByDim[1].empty()
1260 && !criticalCellsByDim[2].empty() && this->ComputeSadSad) {
1261 std::vector<GeneratorType> tmp{};
1262 this->getSaddleSaddlePairs(
1263 pairs, paired1Saddles, paired2Saddles, false, tmp, criticalCellsByDim[1],
1264 criticalCellsByDim[2], critCellsOrder[1], triangulation);
1265 }
1266
1267 if(std::is_same<triangulationType, ttk::ExplicitTriangulation>::value) {
1268 // create infinite pairs from non-paired 1-saddles, 2-saddles and maxima
1269 size_t nHandles{}, nCavities{}, nNonPairedMax{};
1270 if((dim == 2 && !ignoreBoundary && this->ComputeMinSad
1271 && this->ComputeSadMax)
1272 || (dim == 3 && this->ComputeMinSad && this->ComputeSadSad)) {
1273 // non-paired 1-saddles
1274 for(const auto s1 : criticalCellsByDim[1]) {
1275 if(!paired1Saddles[s1]) {
1276 paired1Saddles[s1] = true;
1277 // topological handles
1278 pairs.emplace_back(s1, -1, 1);
1279 nHandles++;
1280 }
1281 }
1282 }
1283 if(dim == 3 && !ignoreBoundary && this->ComputeSadMax
1284 && this->ComputeSadSad) {
1285 // non-paired 2-saddles
1286 for(const auto s2 : criticalCellsByDim[2]) {
1287 if(!paired2Saddles[s2]) {
1288 paired2Saddles[s2] = true;
1289 // cavities
1290 pairs.emplace_back(s2, -1, 2);
1291 nCavities++;
1292 }
1293 }
1294 }
1295 if(dim == 2 && !ignoreBoundary && this->ComputeSadMax) {
1296 // non-paired maxima
1297 for(const auto max : criticalCellsByDim[dim]) {
1298 if(!pairedMaxima[max]) {
1299 pairs.emplace_back(max, -1, 2);
1300 nNonPairedMax++;
1301 }
1302 }
1303 }
1304
1305 int nBoundComp
1306 = (dim == 3 ? nCavities : nHandles) + nConnComp - nNonPairedMax;
1307 nBoundComp = std::max(nBoundComp, 0);
1308
1309 // print Betti numbers
1310 const std::vector<std::vector<std::string>> rows{
1311 {" #Connected components", std::to_string(nConnComp)},
1312 {" #Topological handles", std::to_string(nHandles)},
1313 {" #Cavities", std::to_string(nCavities)},
1314 {" #Boundary components", std::to_string(nBoundComp)},
1315 };
1316
1317 this->printMsg(rows, debug::Priority::DETAIL);
1318 }
1319
1320 this->printMsg(
1321 "Computed " + std::to_string(pairs.size()) + " persistence pairs", 1.0,
1322 tm.getElapsedTime(), this->threadNumber_);
1323
1324 this->displayStats(pairs, criticalCellsByDim, pairedMinima, paired1Saddles,
1325 paired2Saddles, pairedMaxima);
1326
1327 // free memory
1328 this->clear();
1329
1330 return 0;
1331}
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
Definition OpenMP.h:46
AbstractTriangulation is an interface class that defines an interface for efficient traversal methods...
virtual int setThreadNumber(const int threadNumber)
Definition BaseClass.h:80
Minimalist debugging class.
Definition Debug.h:88
int debugLevel_
Definition Debug.h:379
virtual int setDebugLevel(const int &debugLevel)
Definition Debug.cpp:147
TTK DiscreteMorseSandwich processing package.
void setComputeSadMax(const bool data)
std::array< SimplexId, 3 > tripletType
Triplet type for persistence pairs.
std::vector< SimplexId > s1Mapping_
ttk::dcg::DiscreteGradient && getGradient()
std::vector< SimplexId > firstRepMin_
std::vector< std::vector< SimplexId > > s2Children_
const std::vector< std::vector< SimplexId > > & get2SaddlesChildren() const
std::array< std::vector< SimplexId >, 4 > critCellsOrder_
SimplexId getCellGreaterVertex(const dcg::Cell &c, const triangulationType &triangulation)
void alloc(const triangulationType &triangulation)
std::vector< EdgeSimplex > critEdges_
std::vector< SimplexId > edgeTrianglePartner_
void setGradient(ttk::dcg::DiscreteGradient &&dg)
Ugly hack to avoid a call to buildGradient()
void setComputeMinSad(const bool data)
void setInputOffsets(const SimplexId *const offsets)
void preconditionTriangulation(AbstractTriangulation *const data)
std::vector< SimplexId > firstRepMax_
int buildGradient(const void *const scalars, const size_t scalarsMTime, const SimplexId *const offsets, const triangulationType &triangulation, const std::vector< bool > *updateMask=nullptr)
void setComputeSadSad(const bool data)
void getMinSaddlePairs(std::vector< PersistencePair > &pairs, std::vector< bool > &pairedMinima, std::vector< bool > &paired1Saddles, const std::vector< SimplexId > &criticalEdges, const std::vector< SimplexId > &critEdgesOrder, const SimplexId *const offsets, const triangulationType &triangulation) const
Compute the pairs of dimension 0.
void extractCriticalCells(std::array< std::vector< SimplexId >, 4 > &criticalCellsByDim, std::array< std::vector< SimplexId >, 4 > &critCellsOrder, const SimplexId *const offsets, const triangulationType &triangulation, const bool sortEdges) const
Extract & sort critical cell from the DiscreteGradient.
void displayStats(const std::vector< PersistencePair > &pairs, const std::array< std::vector< SimplexId >, 4 > &criticalCellsByDim, const std::vector< bool > &pairedMinima, const std::vector< bool > &paired1Saddles, const std::vector< bool > &paired2Saddles, const std::vector< bool > &pairedMaxima) const
Print number of pairs, critical cells per dimension & unpaired cells.
std::vector< SimplexId > s2Mapping_
void getSaddleSaddlePairs(std::vector< PersistencePair > &pairs, std::vector< bool > &paired1Saddles, std::vector< bool > &paired2Saddles, const bool exportBoundaries, std::vector< GeneratorType > &boundaries, const std::vector< SimplexId > &critical1Saddles, const std::vector< SimplexId > &critical2Saddles, const std::vector< SimplexId > &crit1SaddlesOrder, const triangulationType &triangulation) const
Compute the saddle-saddle pairs (in 3D)
int computePersistencePairs(std::vector< PersistencePair > &pairs, const SimplexId *const offsets, const triangulationType &triangulation, const bool ignoreBoundary, const bool compute2SaddlesChildren=false)
Compute the persistence pairs from the discrete gradient.
std::vector< std::vector< SimplexId > > getSaddle2ToMaxima(const std::vector< SimplexId > &criticalCells, const GFS &getFaceStar, const GFSN &getFaceStarNumber, const OB &isOnBoundary, const triangulationType &triangulation) const
Follow the ascending 1-separatrices to compute the saddles -> maxima association.
std::array< std::vector< bool >, 4 > pairedCritCells_
SimplexId eliminateBoundariesSandwich(const SimplexId s2, std::vector< bool > &onBoundary, std::vector< Container > &s2Boundaries, const std::vector< SimplexId > &s2Mapping, const std::vector< SimplexId > &s1Mapping, std::vector< SimplexId > &partners, std::vector< Lock > &s1Locks, std::vector< Lock > &s2Locks, const triangulationType &triangulation) const
Detect 1-saddles paired to a given 2-saddle.
void getMaxSaddlePairs(std::vector< PersistencePair > &pairs, std::vector< bool > &pairedMaxima, std::vector< bool > &pairedSaddles, const std::vector< SimplexId > &criticalSaddles, const std::vector< SimplexId > &critSaddlesOrder, const std::vector< SimplexId > &critMaxsOrder, const triangulationType &triangulation) const
Compute the pairs of dimension dim - 1.
void tripletsToPersistencePairs(std::vector< PersistencePair > &pairs, std::vector< bool > &pairedExtrema, std::vector< bool > &pairedSaddles, std::vector< SimplexId > &reps, std::vector< tripletType > &triplets, const SimplexId *const saddlesOrder, const SimplexId *const extremaOrder, const SimplexId pairDim) const
Compute persistence pairs from triplets.
std::vector< std::vector< SimplexId > > getSaddle1ToMinima(const std::vector< SimplexId > &criticalEdges, const triangulationType &triangulation) const
Follow the descending 1-separatrices to compute the saddles -> minima association.
TTK discreteGradient processing package.
void setInputOffsets(const SimplexId *const data)
void setInputScalarField(const void *const data, const size_t mTime)
SimplexId getNumberOfCells(const int dimension, const triangulationType &triangulation) const
void setLocalGradient()
Use local storage instead of cache.
SimplexId getCellGreaterVertex(const Cell c, const triangulationType &triangulation) const
int buildGradient(const triangulationType &triangulation, bool bypassCache=false, const std::vector< bool > *updateMask=nullptr)
void preconditionTriangulation(AbstractTriangulation *const data)
int getDescendingPath(const Cell &cell, std::vector< Cell > &vpath, const triangulationType &triangulation) const
The Topology ToolKit.
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
T end(std::pair< T, T > &p)
Definition ripserpy.cpp:483
T begin(std::pair< T, T > &p)
Definition ripserpy.cpp:479
void fillEdge(const SimplexId id, const SimplexId *const offsets, const triangulationType &triangulation)
Type for exporting persistent generators.
Persistence pair struct as exported by DiscreteGradient.
PersistencePair(SimplexId b, SimplexId d, int t)
Ad-hoc struct for sorting simplices.
friend bool operator<(const Simplex< n > &lhs, const Simplex< n > &rhs)
void fillTetra(const SimplexId id, const SimplexId *const offsets, const triangulationType &triangulation)
void fillTriangle(const SimplexId id, const SimplexId *const offsets, const triangulationType &triangulation)
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)