7#include <CGAL/Delaunay_triangulation.h>
8#include <CGAL/Epick_d.h>
9#include <CGAL/Triangulation_full_cell.h>
10#include <CGAL/Triangulation_vertex.h>
20 constexpr static unsigned DYN_DIM = 0;
23 using DSimplex = std::
24 conditional_t<D == DYN_DIM, std::vector<id_t>, std::array<id_t, D + 1>>;
27 using ValueArray = std::conditional_t<D == DYN_DIM,
28 std::vector<rpd::value_t>,
29 std::array<rpd::value_t, D>>;
32 using ConnectivityHashMap
35#ifdef TTK_CONCURRENT_HASHTABLE_AVAILABLE
37 using ConcurrentConnectivityHashMap
38 = ConcurrentHashMap<DSimplex<D>, std::vector<std::pair<int, int>>>;
42 struct FiltratedDSimplex {
49 bool operator<(FiltratedDSimplex<D>
const &s1,
50 FiltratedDSimplex<D>
const &s2) {
51 return std::make_pair(s1.d, s1.a) < std::make_pair(s2.d, s2.a);
55 FiltratedDSimplex<D>
max(FiltratedDSimplex<D>
const &s1,
56 FiltratedDSimplex<D>
const &s2) {
57 return s1 < s2 ? s2 : s1;
61 using DSimplicialComplex = std::vector<FiltratedDSimplex<D>>;
63 template <
unsigned DIM>
64 class DRPersistenceD {
65 using DimTag = std::conditional_t<DIM == DYN_DIM,
66 CGAL::Dynamic_dimension_tag,
67 CGAL::Dimension_tag<DIM>>;
68 using K = CGAL::Epick_d<DimTag>;
69 using Vb = CGAL::Triangulation_vertex<K, id_t>;
70 using FCb = CGAL::Triangulation_full_cell<K, id_t>;
71 using Tds = CGAL::Triangulation_data_structure<DimTag, Vb, FCb>;
72 using Delaunay = CGAL::Delaunay_triangulation<K, Tds>;
73 using Point =
typename Delaunay::Point;
74 using Facet =
typename Delaunay::Facet;
75 using VertexHandle =
typename Delaunay::Vertex_handle;
76 using CellHandle =
typename Delaunay::Full_cell_handle;
78 static constexpr unsigned minus1(
const unsigned D) {
79 return D == DYN_DIM ? DYN_DIM : D - 1;
82 static constexpr unsigned plus1(
const unsigned D) {
83 return D == DYN_DIM ? DYN_DIM : D + 1;
86 struct FiltratedQuadFacet {
87 DSimplex<minus1(DIM)> s;
95 explicit DRPersistenceD(PointCloud<DIM> &points,
unsigned nThreads = 1);
96 void run(MultidimensionalDiagram &ph);
102 PointCloud<DIM> &points_;
103 const int nThreads_{1};
105 [[nodiscard]]
double squaredDistance(
const unsigned i1,
106 const unsigned i2)
const {
107 return std::inner_product(
108 points_[i1].
begin(), points_[i1].
end(), points_[i2].
begin(), 0.,
110 [](
const double u,
const double v) {
return (u - v) * (u - v); });
113 template <
unsigned D>
114 std::pair<double, double>
115 squaredPerturbedDiameter(DSimplex<D>
const &s)
const {
118 for(
unsigned p1 = 0; p1 < s.size(); ++p1) {
119 for(
unsigned p2 = p1 + 1; p2 < s.size(); ++p2) {
120 const double dist = squaredDistance(s[p1], s[p2]);
121 diam = std::max(diam, dist);
128 template <
unsigned D>
129 void getLengths(DSimplex<D>
const &s,
130 ValueArray<D *(D + 1) / 2> &lengths)
const {
131 if constexpr(D == DYN_DIM)
132 lengths.resize((s.size() - 1) * s.size() / 2);
136 for(
unsigned p1 = 0; p1 < s.size(); ++p1) {
137 for(
unsigned p2 = p1 + 1; p2 < s.size(); ++p2)
138 lengths[i++] = squaredDistance(s[p1], s[p2]);
142 template <
unsigned D>
143 bool checkLinkUrquhart(DSimplex<D>
const &s,
144 ValueArray<D *(D + 1) / 2>
const &lengths,
145 std::pair<double, double> diam,
147 ValueArray<plus1(D)> linkLengths;
148 if constexpr(D == DYN_DIM)
149 linkLengths.resize(s.size());
150 for(
unsigned i = 0; i < s.size(); ++i)
151 linkLengths[i] = squaredDistance(s[i], linkId);
153 for(
unsigned i = 0; i < s.size(); ++i) {
157 for(
unsigned p1 = 0; p1 < s.size(); ++p1) {
158 for(
unsigned p2 = p1 + 1; p2 < s.size(); ++p2) {
161 dist = linkLengths[p2];
163 dist = linkLengths[p1];
167 d = std::max(d, dist);
172 if(std::make_pair(d, a) > diam)
179 void computeDelaunay();
181 void computeDPH(Diagram &ph, DSimplicialComplex<minus1(DIM)> &MSA);
183 void computeDPH_p(Diagram &ph, DSimplicialComplex<minus1(DIM)> &MSA);
185 template <
unsigned D>
186 void computeNextPH(Diagram &ph,
187 DSimplicialComplex<D> &MSA,
188 DSimplicialComplex<minus1(D)> &nextMSA)
const;
190 template <
unsigned D>
191 void computeNextPH_p(Diagram &ph,
192 DSimplicialComplex<D> &MSA,
193 DSimplicialComplex<minus1(D)> &nextMSA)
const;
195 template <
unsigned D>
196 void recurse(MultidimensionalDiagram &ph, DSimplicialComplex<D> &MSA)
const;
199 template <
unsigned DIM>
201 const unsigned nThreads)
202 : N_p(points.size()), del_(DIM), points_(points), nThreads_(nThreads) {
206 inline DRPersistenceD<DYN_DIM>::DRPersistenceD(PointCloud<DYN_DIM> &points,
207 const unsigned nThreads)
208 : N_p(points.size()), del_(points[0].size()), points_(points),
209 nThreads_(nThreads) {
212 template <
unsigned DIM>
213 void DRPersistenceD<DIM>::run(MultidimensionalDiagram &ph) {
218 DSimplicialComplex<DIM - 1> MSA;
220 computeDPH(ph[DIM - 1], MSA);
222 computeDPH_p(ph[DIM - 1], MSA);
228 inline void DRPersistenceD<DYN_DIM>::run(MultidimensionalDiagram &ph) {
229 const unsigned DIM = points_[0].size();
234 DSimplicialComplex<DYN_DIM> MSA;
236 computeDPH(ph[DIM - 1], MSA);
238 computeDPH_p(ph[DIM - 1], MSA);
240 unsigned D = DIM - 1;
242 DSimplicialComplex<DYN_DIM> nextMSA;
244 computeNextPH<DYN_DIM>(ph[D - 1], MSA, nextMSA);
246 computeNextPH_p<DYN_DIM>(ph[D - 1], MSA, nextMSA);
247 MSA = std::move(nextMSA);
250 for(FiltratedDSimplex<DYN_DIM>
const &e : MSA)
255 template <
unsigned DIM>
256 template <
unsigned D>
257 void DRPersistenceD<DIM>::recurse(MultidimensionalDiagram &ph,
258 DSimplicialComplex<D> &MSA)
const {
259 if constexpr(D >= 2) {
260 DSimplicialComplex<D - 1> nextMSA;
262 computeNextPH(ph[D - 1], MSA, nextMSA);
264 computeNextPH_p(ph[D - 1], MSA, nextMSA);
265 recurse(ph, nextMSA);
266 }
else if constexpr(D == 1) {
267 for(FiltratedDSimplex<1>
const &e : MSA)
268 ph[0].emplace_back(FiltratedSimplex{{-1}, 0.},
273 template <
unsigned DIM>
274 void DRPersistenceD<DIM>::computeDelaunay() {
275 del_.insert(points_.begin(), points_.end());
279 for(
auto p_it = del_.finite_vertices_begin();
280 p_it != del_.finite_vertices_end(); ++p_it) {
281 points_[p] = p_it->point();
287 for(
auto c_it = del_.full_cells_begin(); c_it != del_.full_cells_end();
293 template <
unsigned DIM>
294 void DRPersistenceD<DIM>::computeDPH(Diagram &ph,
295 DSimplicialComplex<minus1(DIM)> &MSA) {
296 DSimplicialComplex<minus1(DIM)> maxDelaunay(N_c);
298 std::vector<FiltratedQuadFacet> hyperUrquhart;
301 for(
auto f_it = del_.facets_begin(); f_it != del_.facets_end(); ++f_it) {
303 const Facet f = *f_it;
304 const CellHandle c = f.full_cell();
305 const CellHandle c_mirror = c->neighbor(f.index_of_covertex());
307 if(del_.is_infinite(f))
308 UF.merge(c->data(), c_mirror->data());
311 const auto linkPoint1 = c->vertex(f.index_of_covertex());
312 const auto linkPoint2
313 = c_mirror->vertex(c->mirror_index(f.index_of_covertex()));
315 DSimplex<minus1(DIM)> facet;
316 if constexpr(DIM == DYN_DIM)
317 facet.resize(del_.current_dimension());
318 for(
unsigned i = 0; i < facet.size(); ++i)
320 = c->vertex((f.index_of_covertex() + i + 1) % (facet.size() + 1))
322 std::sort(facet.begin(), facet.end());
323 const auto diam = squaredPerturbedDiameter<minus1(DIM)>(facet);
325 bool is_urquhart =
true;
326 for(
auto linkPoint : {linkPoint1, linkPoint2}) {
327 if(!del_.is_infinite(linkPoint)) {
328 const id_t k = linkPoint->data();
330 for(
unsigned i = 0; i < facet.size(); ++i) {
331 DSimplex<minus1(DIM)> neighbor = facet;
333 if(squaredPerturbedDiameter<minus1(DIM)>(neighbor) > diam) {
346 hyperUrquhart.push_back(
347 {facet, diam.first, diam.second, c->data(), c_mirror->data()});
349 const int poly1 =
UF.
find(c->data());
350 const int poly2 =
UF.
find(c_mirror->data());
351 maxDelaunay[
UF.mergeRet(poly1, poly2)] =
max(
352 FiltratedDSimplex<minus1(DIM)>{facet, diam.first, diam.second},
353 max(maxDelaunay[poly1], maxDelaunay[poly2]));
357 if(del_.is_infinite(c))
359 else if(del_.is_infinite(c_mirror))
360 maxDelaunay[
UF.
find(c_mirror->data())].d =
inf;
364 std::sort(hyperUrquhart.begin(), hyperUrquhart.end(),
365 [](
const FiltratedQuadFacet &f1,
const FiltratedQuadFacet &f2) {
372 std::vector<int> latest(maxDelaunay.size());
373 std::iota(latest.begin(), latest.end(), 0);
375 for(FiltratedQuadFacet
const &f :
377 const int v1 =
UF.
find(f.c1);
378 const int v2 =
UF.
find(f.c2);
383 const int latest1 = latest[v1];
384 const int latest2 = latest[v2];
385 const FiltratedDSimplex<minus1(DIM)> &death1 = maxDelaunay[latest1];
386 const FiltratedDSimplex<minus1(DIM)> &death2 = maxDelaunay[latest2];
388 if(death1.d < death2.d) {
391 FiltratedSimplex{
Simplex(f.s.begin(), f.s.end()), sqrt(f.d)},
393 Simplex(death1.s.begin(), death1.s.end()), sqrt(death1.d)});
394 latest[
UF.
find(v1)] = latest2;
395 }
else if(death2.d < death1.d) {
398 FiltratedSimplex{
Simplex(f.s.begin(), f.s.end()), sqrt(f.d)},
400 Simplex(death2.s.begin(), death2.s.end()), sqrt(death2.d)});
401 latest[
UF.
find(v1)] = latest1;
404 MSA.push_back({f.s, f.d, f.a});
408 template <
unsigned DIM>
409 void DRPersistenceD<DIM>::computeDPH_p(Diagram &ph,
410 DSimplicialComplex<minus1(DIM)> &MSA) {
411#ifndef TTK_GPH_PARALLEL
414 omp_set_num_threads(nThreads_);
417 tbb::concurrent_vector<FiltratedQuadFacet> hyperUrquhart;
419 std::vector<FiltratedDSimplex<DIM>> cells(N_c);
420 for(
auto c_it = del_.finite_full_cells_begin();
421 c_it != del_.finite_full_cells_end(); ++c_it) {
422 FiltratedDSimplex<DIM> cell;
423 if constexpr(DIM == DYN_DIM)
424 cell.s.resize(del_.current_dimension() + 1);
426 for(
unsigned i = 0; i < cell.s.size(); ++i)
427 cell.s[i] = c_it->vertex(i)->data();
428 cells[c_it->data()] = cell;
431#pragma omp parallel for
432 for(
unsigned i = 0; i < cells.size(); ++i) {
433 if(cells[i].d == -1.) {
434 auto [d, a] = squaredPerturbedDiameter<DIM>(cells[i].s);
440 std::vector<std::tuple<DSimplex<minus1(DIM)>, int, int, int,
int>> facets;
441 for(
auto f_it = del_.facets_begin(); f_it != del_.facets_end(); ++f_it) {
442 const Facet f = *f_it;
443 const CellHandle c = f.full_cell();
444 const CellHandle c_mirror = c->neighbor(f.index_of_covertex());
445 if(del_.is_infinite(f))
446 UF.unite(c->data(), c_mirror->data());
448 DSimplex<minus1(DIM)> facet;
449 if constexpr(DIM == DYN_DIM)
450 facet.resize(del_.current_dimension());
451 for(
unsigned i = 0; i < facet.size(); ++i)
453 = c->vertex((f.index_of_covertex() + i + 1) % (facet.size() + 1))
456 const auto linkPoint1 = c->vertex(f.index_of_covertex());
457 const auto linkPoint2
458 = c_mirror->vertex(c->mirror_index(f.index_of_covertex()));
459 const int n1 = del_.is_infinite(linkPoint1) ? -1 : linkPoint1->data();
460 const int n2 = del_.is_infinite(linkPoint2) ? -1 : linkPoint2->data();
461 const int c1 = c->data();
462 const int c2 = c_mirror->data();
463 facets.emplace_back(facet, c1, n1, c2, n2);
467#pragma omp parallel for
468 for(
auto &[facet, c1, n1, c2, n2] : facets) {
470 std::sort(facet.begin(), facet.end());
471 const auto diam = squaredPerturbedDiameter<minus1(DIM)>(facet);
473 bool is_urquhart =
true;
474 for(
const id_t k : {n1, n2}) {
477 for(
unsigned i = 0; i < facet.size(); ++i) {
478 DSimplex<minus1(DIM)> neighbor = facet;
480 if(squaredPerturbedDiameter<minus1(DIM)>(neighbor) > diam) {
493 hyperUrquhart.push_back({facet, diam.first, diam.second, c1, c2});
503 std::vector<std::mutex> maxDelaunayLocks(N_c);
504#pragma omp parallel for
505 for(
unsigned x = 0; x < N_c; ++x) {
506 const int poly =
UF.
find(x);
507 std::lock_guard lock(maxDelaunayLocks[poly]);
508 cells[poly] =
max(cells[poly], cells[x]);
511 TTK_PSORT(nThreads_, hyperUrquhart.begin(), hyperUrquhart.end(),
512 [](
const FiltratedQuadFacet &f1,
const FiltratedQuadFacet &f2) {
519 std::vector<int> latest(N_c);
520 std::iota(latest.begin(), latest.end(), 0);
522 for(FiltratedQuadFacet
const &f :
524 const int v1 =
UF.
find(f.c1);
525 const int v2 =
UF.
find(f.c2);
530 const int latest1 = latest[v1];
531 const int latest2 = latest[v2];
532 const FiltratedDSimplex<DIM> &death1 = cells[latest1];
533 const FiltratedDSimplex<DIM> &death2 = cells[latest2];
535 if(death1.d < death2.d) {
538 FiltratedSimplex{
Simplex(f.s.begin(), f.s.end()), sqrt(f.d)},
540 Simplex(death1.s.begin(), death1.s.end()), sqrt(death1.d)});
541 latest[
UF.
find(v1)] = latest2;
542 }
else if(death2.d < death1.d) {
545 FiltratedSimplex{
Simplex(f.s.begin(), f.s.end()), sqrt(f.d)},
547 Simplex(death2.s.begin(), death2.s.end()), sqrt(death2.d)});
548 latest[
UF.
find(v1)] = latest1;
551 MSA.push_back({f.s, f.d, f.a});
556 template <
unsigned DIM>
557 template <
unsigned D>
558 void DRPersistenceD<DIM>::computeNextPH(
560 DSimplicialComplex<D> &MSA,
561 DSimplicialComplex<minus1(D)> &nextMSA)
const {
562 const unsigned N_msa = MSA.size();
566 ConnectivityHashMap<minus1(D)> msa_connectivity;
567 msa_connectivity.reserve(N_msa);
568 for(
unsigned i = 0; i < N_msa; ++i) {
569 DSimplex<minus1(D)> face;
570 if constexpr(D == DYN_DIM)
571 face.resize(MSA[0].s.size() - 1);
572 for(
unsigned k = 0; k < MSA[0].s.size(); ++k) {
573 for(
unsigned j = 0; j < MSA[0].s.size() - 1; ++j)
574 face[j] = MSA[i].s[j + (j >= k)];
575 msa_connectivity[face].reserve(4);
576 msa_connectivity[face].emplace_back(i, MSA[i].s[k]);
582 std::vector<FiltratedDSimplex<minus1(D)>> critical;
583 UnionFind UF_msa(N_msa);
585 for(
auto const &[s, neighbors] : msa_connectivity) {
588 bool is_urquhart =
true;
589 ValueArray<D * minus1(D) / 2> lengths;
590 getLengths<minus1(D)>(s, lengths);
591 std::pair diam{0.0, 0.0};
592 for(
double const &l : lengths)
593 diam = {std::max(diam.first, l), diam.second + l};
595 for(
auto [coface_id, linkPoint_id] : neighbors) {
596 if(checkLinkUrquhart<minus1(D)>(s, lengths, diam, linkPoint_id)) {
604 critical.push_back({s, diam.first, diam.second});
606 if(neighbors.size() == 1)
607 MSA[neighbors[0].first].d =
inf;
608 else if(neighbors.size() == 2)
609 UF_msa.merge(neighbors[0].first, neighbors[1].first);
611 critical.push_back({s, diam.first, diam.second});
615 for(
unsigned x = 0; x < N_msa; ++x) {
616 const int poly = UF_msa.find(x);
617 MSA[poly] =
max(MSA[poly], MSA[x]);
622 std::vector<id_t> polytopes;
623 for(
unsigned x = 0; x < N_msa; ++x) {
624 if(UF_msa.isRoot(x) && MSA[x].d < inf)
625 polytopes.emplace_back(x);
628 std::sort(polytopes.begin(), polytopes.end(),
629 [&](
const int x1,
const int x2) { return MSA[x1] < MSA[x2]; });
631 std::vector<int> criticalIndices(critical.size());
632 std::iota(criticalIndices.begin(), criticalIndices.end(), 0);
633 std::sort(criticalIndices.begin(), criticalIndices.end(),
634 [&](
const int x1,
const int x2) {
635 return critical[x1].d < critical[x2].d;
637 std::vector<int> criticalOrder(critical.size());
638 for(
unsigned i = 0; i < criticalIndices.size(); ++i)
639 criticalOrder[criticalIndices[i]] = i;
641 std::vector<std::vector<int>> poly_to_crit(N_msa);
642 for(
const int poly : polytopes)
643 poly_to_crit[poly].reserve(D + 1);
644 for(
unsigned i = 0; i < critical.size(); ++i) {
645 for(
const auto &[poly, _] : msa_connectivity[critical[i].s]) {
646 if(MSA[UF_msa.find(poly)].d < inf) {
647 auto &neighbors = poly_to_crit[UF_msa.find(poly)];
649 = std::find(neighbors.begin(), neighbors.end(), criticalOrder[i]);
650 if(it == neighbors.end())
651 neighbors.push_back(criticalOrder[i]);
660 std::vector<int> partner(critical.size(), -1);
661 for(
const int poly : polytopes) {
662 HashSet<int> boundary(poly_to_crit[poly].
begin(),
663 poly_to_crit[poly].
end(),
664 poly_to_crit[poly].size());
666 const int youngest_id
667 = *std::max_element(boundary.begin(), boundary.end());
668 if(partner[youngest_id] == -1) {
669 partner[youngest_id] = poly;
670 const FiltratedDSimplex<minus1(D)> &c
671 = critical[criticalIndices[youngest_id]];
672 const FiltratedDSimplex<D> &death = MSA[poly];
675 FiltratedSimplex{
Simplex(c.s.begin(), c.s.end()), sqrt(c.d)},
677 Simplex(death.s.begin(), death.s.end()), sqrt(death.d)});
680 for(
const int crit : poly_to_crit[partner[youngest_id]]) {
681 const auto it = boundary.find(crit);
682 if(it == boundary.end())
683 boundary.insert(crit);
689 poly_to_crit[poly].assign(boundary.begin(), boundary.end());
693 for(
unsigned i = 0; i < critical.size(); ++i) {
695 nextMSA.emplace_back(critical[criticalIndices[i]]);
699 template <
unsigned DIM>
700 template <
unsigned D>
701 void DRPersistenceD<DIM>::computeNextPH_p(
703 DSimplicialComplex<D> &MSA,
704 DSimplicialComplex<minus1(D)> &nextMSA)
const {
705#ifndef TTK_GPH_PARALLEL
706 computeNextPH(ph, MSA, nextMSA);
708 tbb::global_control gc(
709 tbb::global_control::max_allowed_parallelism, nThreads_);
710 omp_set_num_threads(nThreads_);
711 const unsigned N_msa = MSA.size();
715 ConcurrentConnectivityHashMap<minus1(D)> concurrent_msa_connectivity;
716 concurrent_msa_connectivity.reserve(N_msa);
718#pragma omp parallel for
719 for(
unsigned i = 0; i < N_msa; ++i) {
720 DSimplex<minus1(D)> face;
721 if constexpr(D == DYN_DIM)
722 face.resize(MSA[0].s.size() - 1);
723 for(
unsigned k = 0; k < MSA[0].s.size(); ++k) {
724 for(
unsigned j = 0; j < MSA[0].s.size() - 1; ++j)
725 face[j] = MSA[i].s[j + (j >= k)];
726 concurrent_msa_connectivity.emplace_or_visit(
728 std::vector<std::pair<int, int>>{std::make_pair(i, MSA[i].s[k])},
729 [&](
auto &x) { x.second.emplace_back(i, MSA[i].s[k]); });
735 tbb::concurrent_vector<FiltratedDSimplex<minus1(D)>> critical;
738 concurrent_msa_connectivity.cvisit_all(
739#ifdef __cpp_lib_execution
743 const auto &[s, neighbors] = x;
746 bool is_urquhart =
true;
747 ValueArray<D * minus1(D) / 2> lengths;
748 getLengths<minus1(D)>(s, lengths);
749 std::pair diam{0.0, 0.0};
750 for(
double const &l : lengths)
751 diam = {std::max(diam.first, l), diam.second + l};
753 for(
auto [coface_id, linkPoint_id] : neighbors) {
754 if(checkLinkUrquhart<minus1(D)>(s, lengths, diam, linkPoint_id)) {
762 critical.push_back({s, diam.first, diam.second});
764 if(neighbors.size() == 1)
765 MSA[neighbors[0].first].d =
inf;
766 else if(neighbors.size() == 2)
767 UF_msa.unite(neighbors[0].first, neighbors[1].first);
769 critical.push_back({s, diam.first, diam.second});
773 std::vector<std::mutex> maxDelaunayLocks(N_msa);
774#pragma omp parallel for
775 for(
unsigned x = 0; x < N_msa; ++x) {
776 const int poly = UF_msa.find(x);
777 std::lock_guard lock(maxDelaunayLocks[poly]);
778 MSA[poly] =
max(MSA[poly], MSA[x]);
781 const ConnectivityHashMap<minus1(D)> msa_connectivity
782 = std::move(concurrent_msa_connectivity);
786 std::vector<id_t> polytopes;
787 for(
unsigned x = 0; x < N_msa; ++x) {
788 if(UF_msa.isRoot(x) && MSA[x].d < inf)
789 polytopes.emplace_back(x);
792 TTK_PSORT(nThreads_, polytopes.begin(), polytopes.end(),
793 [&](
const int x1,
const int x2) { return MSA[x1] < MSA[x2]; });
795 std::vector<int> criticalIndices(critical.size());
796 std::iota(criticalIndices.begin(), criticalIndices.end(), 0);
797 TTK_PSORT(nThreads_, criticalIndices.begin(), criticalIndices.end(),
798 [&](
const int x1,
const int x2) {
799 return critical[x1].d < critical[x2].d;
801 std::vector<int> criticalOrder(critical.size());
802 for(
unsigned i = 0; i < criticalIndices.size(); ++i)
803 criticalOrder[criticalIndices[i]] = i;
805 std::vector<std::vector<int>> poly_to_crit(N_msa);
806 std::vector<std::mutex> poly_mutex(N_msa);
807 std::vector<std::mutex> crit_mutex(critical.size());
809#pragma omp parallel for
810 for(
unsigned i = 0; i < critical.size(); ++i) {
811 for(
const auto &[poly, _] :
812 msa_connectivity.find(critical[i].s)->second) {
813 const int uf_poly = UF_msa.find(poly);
814 if(MSA[uf_poly].d < inf) {
815 auto &neighbors = poly_to_crit[uf_poly];
816 std::lock_guard lock(poly_mutex[uf_poly]);
818 = std::find(neighbors.begin(), neighbors.end(), criticalOrder[i]);
819 if(it == neighbors.end())
820 neighbors.push_back(criticalOrder[i]);
829 std::vector<int> partner(critical.size(), -1);
831 auto eliminateBoundary = [&](
const int poly) ->
int {
832 std::lock_guard lock(poly_mutex[poly]);
833 HashSet<int> boundary(poly_to_crit[poly].
begin(),
834 poly_to_crit[poly].
end(),
835 poly_to_crit[poly].size());
838 const int youngest_id
839 = *std::max_element(boundary.begin(), boundary.end());
840 std::lock_guard lock_crit(crit_mutex[youngest_id]);
841 if(partner[youngest_id] == -1) {
842 partner[youngest_id] = poly;
843 poly_to_crit[poly].assign(boundary.begin(), boundary.end());
846 std::lock_guard lock_partner(poly_mutex[partner[youngest_id]]);
847 if(MSA[partner[youngest_id]].d > MSA[poly].d) {
848 const int tmp = partner[youngest_id];
849 partner[youngest_id] = poly;
850 poly_to_crit[poly].assign(boundary.begin(), boundary.end());
853 for(
const int crit : poly_to_crit[partner[youngest_id]]) {
854 const auto it = boundary.find(crit);
855 if(it == boundary.end())
856 boundary.insert(crit);
863#pragma omp parallel for
864 for(
const int poly : polytopes) {
867 p = eliminateBoundary(p);
871 for(
unsigned i = 0; i < critical.size(); ++i) {
872 const int poly = partner[i];
874 nextMSA.emplace_back(critical[criticalIndices[i]]);
876 const FiltratedDSimplex<minus1(D)> &c = critical[criticalIndices[i]];
877 const FiltratedDSimplex<D> &death = MSA[poly];
880 FiltratedSimplex{
Simplex(c.s.begin(), c.s.end()), sqrt(c.d)},
882 Simplex(death.s.begin(), death.s.end()), sqrt(death.d)});
888 template <
unsigned DIM>
889 void runDelaunayRipsPersistenceDiagram(rpd::PointCloud
const &points,
890 MultidimensionalDiagram &diagram,
892 PointCloud<DIM> p(points.size());
893 for(
unsigned i = 0; i < points.size(); ++i) {
894 for(
unsigned d = 0; d < DIM; ++d)
895 p[i][d] = points[i][d];
897 DRPersistenceD<DIM> drpd(p, threads);
903 runDelaunayRipsPersistenceDiagram<DYN_DIM>(rpd::PointCloud
const &points,
904 MultidimensionalDiagram &diagram,
906 PointCloud<DYN_DIM> p = points;
907 DRPersistenceD<DYN_DIM> drpd(p, threads);
911 template <
unsigned DIM>
912 void tryDimension(rpd::PointCloud
const &points,
913 MultidimensionalDiagram &diagram,
915 if constexpr(DIM <= TTK_DELAUNAY_MAX_COMPILED_DIMENSION) {
916 if(points[0].size() == DIM)
917 runDelaunayRipsPersistenceDiagram<DIM>(points, diagram, threads);
919 tryDimension<DIM + 1>(points, diagram, threads);
921 runDelaunayRipsPersistenceDiagram<DYN_DIM>(points, diagram, threads);
924 inline void tryDimensions(rpd::PointCloud
const &points,
925 MultidimensionalDiagram &diagram,
927 tryDimension<4>(points, diagram, threads);
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
boost::tuple< double, double > Point
std::vector< PointD< DIM > > PointCloud
boost::unordered_map< X, Y > HashMap
FiltratedEdge max(const FiltratedEdge &a, const FiltratedEdge &b)
std::array< id_t, 3 > Facet
bool operator<(const FiltratedEdge &e1, const FiltratedEdge &e2)
std::vector< Diagram > MultidimensionalDiagram
std::pair< Simplex, value_t > FiltratedSimplex
std::vector< PersistencePair > Diagram
std::vector< id_t > Simplex
T end(std::pair< T, T > &p)
T begin(std::pair< T, T > &p)