7#include <CGAL/Delaunay_triangulation_3.h>
8#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
9#include <CGAL/Triangulation_cell_base_with_info_3.h>
10#include <CGAL/Triangulation_vertex_base_with_info_3.h>
15 using rpd::FiltratedEdge;
23 using Facet = std::array<id_t, 3>;
25 struct FiltratedFacet {
30 inline FiltratedFacet
max(FiltratedFacet
const &a, FiltratedFacet
const &b) {
36 struct FiltratedQuadFacet {
44 class DRPersistence3 {
45 using K = CGAL::Exact_predicates_inexact_constructions_kernel;
46 using Vb = CGAL::Triangulation_vertex_base_with_info_3<int, K>;
47 using Fb = CGAL::Triangulation_cell_base_with_info_3<int, K>;
48 using Tds = CGAL::Triangulation_data_structure_3<Vb, Fb>;
49 using Delaunay = CGAL::Delaunay_triangulation_3<K, Tds>;
50 using Point = Delaunay::Point_3;
52 using AdjacencyList = std::vector<int>;
53 using ConnectivityHashMap = HashMap<Edge, std::pair<AdjacencyList, bool>>;
56 explicit DRPersistence3(
const PointCloud<3> &points)
57 : N_p(points.size()), p(points) {
64 void run(MultidimensionalDiagram &ph) {
70 computeFacetUrquhart(UF);
82 void run(MultidimensionalDiagram &ph,
83 std::vector<Generator1> &generators1,
84 std::vector<Generator2> &generators2) {
86 generators1.resize(0);
87 generators2.resize(0);
92 computeFacetUrquhart(UF);
93 compute2PH(UF, ph, generators2);
97 compute1PH(ph, generators1);
103 const PointCloud<3> &p;
107 std::vector<FiltratedEdge> urquhart;
108 std::vector<FiltratedEdge> critical1;
110 ConnectivityHashMap msa_edges{};
115 std::vector<FiltratedQuadFacet> hyperUrquhart;
116 std::vector<FiltratedFacet> msa;
117 std::vector<FiltratedFacet> maxDelaunay;
119 [[nodiscard]]
double squaredDistance(
const unsigned i1,
120 const unsigned i2)
const {
121 PointD<3>
const p1 = p[i1], p2 = p[i2];
122 return (p1[0] - p2[0]) * (p1[0] - p2[0])
123 + (p1[1] - p2[1]) * (p1[1] - p2[1])
124 + (p1[2] - p2[2]) * (p1[2] - p2[2]);
126 [[nodiscard]]
static std::pair<double, double>
127 perturbedDiameter(
const double d1,
const double d2,
const double d3) {
128 return {std::max(d1, std::max(d2, d3)), d1 + d2 + d3};
135 void computeDelaunay() {
136 std::vector<std::pair<Point, unsigned>> points(N_p);
137 for(
unsigned i = 0; i < N_p; ++i)
138 points[i] = std::make_pair(
Point(p[i][0], p[i][1], p[i][2]), i);
139 del = Delaunay(points.begin(), points.end());
141 for(
auto const &c : del.all_cell_handles())
146 void computeFacetUrquhart(UnionFind &UF) {
147 maxDelaunay.resize(N_c, FiltratedFacet{{-1, -1, -1}, 0.});
148 for(Delaunay::Facet
const &f : del.all_facets()) {
149 if(del.is_infinite(f))
150 UF.merge(f.first->info(), del.mirror_facet(f).first->info());
152 Facet facet{f.first->vertex((f.second + 1) % 4)->info(),
153 f.first->vertex((f.second + 2) % 4)->info(),
154 f.first->vertex((f.second + 3) % 4)->info()};
155 const double d01 = squaredDistance(facet[0], facet[1]);
156 const double d02 = squaredDistance(facet[0], facet[2]);
157 const double d12 = squaredDistance(facet[1], facet[2]);
158 const auto diam = perturbedDiameter(d01, d02, d12);
159 const Delaunay::Facet f_m = del.mirror_facet(f);
162 bool is_urquhart =
true;
163 if(!del.is_infinite(f.first->vertex(f.second))) {
164 const unsigned k = f.first->vertex(f.second)->info();
165 const double d0k = squaredDistance(facet[0], k);
166 const double d1k = squaredDistance(facet[1], k);
167 const double d2k = squaredDistance(facet[2], k);
168 if(perturbedDiameter(d01, d0k, d1k) < diam
169 && perturbedDiameter(d02, d0k, d2k) < diam
170 && perturbedDiameter(d12, d1k, d2k) < diam)
173 if(is_urquhart && !del.is_infinite(f_m.first->vertex(f_m.second))) {
174 const unsigned l = f_m.first->vertex(f_m.second)->info();
175 const double d0l = squaredDistance(facet[0], l);
176 const double d1l = squaredDistance(facet[1], l);
177 const double d2l = squaredDistance(facet[2], l);
178 if(perturbedDiameter(d01, d0l, d1l) < diam
179 && perturbedDiameter(d02, d0l, d2l) < diam
180 && perturbedDiameter(d12, d1l, d2l) < diam)
185 std::sort(facet.begin(), facet.end());
186 hyperUrquhart.push_back({facet, f.first->info(), f_m.first->info(),
187 sqrt(diam.first), diam.second});
189 const int poly1 =
UF.
find(f.first->info());
190 const int poly2 =
UF.
find(f_m.first->info());
191 maxDelaunay[
UF.mergeRet(poly1, poly2)]
192 =
max({facet, sqrt(diam.first)},
193 max(maxDelaunay[poly1], maxDelaunay[poly2]));
197 if(del.is_infinite(f.first))
198 maxDelaunay[
UF.
find(f.first->info())].d =
inf;
199 else if(del.is_infinite(f_m.first))
200 maxDelaunay[
UF.
find(f_m.first->info())].d =
inf;
204 std::sort(hyperUrquhart.begin(), hyperUrquhart.end(),
205 [](
const FiltratedQuadFacet &f1,
const FiltratedQuadFacet &f2) {
212 void compute2PH(UnionFind &UF, MultidimensionalDiagram &ph) {
213 std::vector<int> latest(maxDelaunay.size());
214 std::iota(latest.begin(), latest.end(), 0);
216 for(FiltratedQuadFacet
const &f :
218 const int v1 =
UF.
find(f.c1);
219 const int v2 =
UF.
find(f.c2);
224 const int latest1 = latest[v1];
225 const int latest2 = latest[v2];
226 const FiltratedFacet &death1 = maxDelaunay[latest1];
227 const FiltratedFacet &death2 = maxDelaunay[latest2];
229 if(death1.d < death2.d) {
232 FiltratedSimplex{{f.f[0], f.f[1], f.f[2]}, f.d},
234 {death1.f[0], death1.f[1], death1.f[2]}, death1.d});
235 latest[
UF.
find(v1)] = latest2;
236 }
else if(death2.d < death1.d) {
239 FiltratedSimplex{{f.f[0], f.f[1], f.f[2]}, f.d},
241 {death2.f[0], death2.f[1], death2.f[2]}, death2.d});
242 latest[
UF.
find(v1)] = latest1;
245 msa.push_back({f.f, f.d});
249 void compute2PH(UnionFind &UF,
250 MultidimensionalDiagram &ph,
251 std::vector<Generator2> &generators2) {
252 std::vector<int> latest(maxDelaunay.size());
253 std::iota(latest.begin(), latest.end(), 0);
255 std::vector<std::vector<Facet>> elementary_generators(N_c);
256 for(
const FiltratedQuadFacet &f : hyperUrquhart) {
258 if(maxDelaunay[
UF.
find(f.c1)].d != inf)
259 elementary_generators[
UF.
find(f.c1)].push_back(f.f);
260 if(maxDelaunay[
UF.
find(f.c2)].d != inf)
261 elementary_generators[
UF.
find(f.c2)].push_back(f.f);
264 std::vector<std::vector<int>> cascade(N_c, {0});
265 for(
unsigned i = 0; i < N_c; i++)
268 for(FiltratedQuadFacet
const &f :
270 const int v1 =
UF.
find(f.c1);
271 const int v2 =
UF.
find(f.c2);
276 const int latest1 = latest[v1];
277 const int latest2 = latest[v2];
278 const FiltratedFacet &death1 = maxDelaunay[latest1];
279 const FiltratedFacet &death2 = maxDelaunay[latest2];
281 if(death1.d < death2.d) {
284 FiltratedSimplex{{f.f[0], f.f[1], f.f[2]}, f.d},
286 {death1.f[0], death1.f[1], death1.f[2]}, death1.d});
287 HashMap<Facet, unsigned> generator;
288 for(
auto const &c : cascade[latest1]) {
289 for(Facet
const &f_ : elementary_generators[c])
292 std::vector<Facet> generator_facets;
293 for(
auto const &[f_, v] : generator) {
295 generator_facets.push_back(f_);
297 generators2.push_back({generator_facets, {f.d, death1.d}});
299 latest[
UF.
find(v1)] = latest2;
300 cascade[latest2].insert(cascade[latest2].
end(),
301 cascade[latest1].
begin(),
302 cascade[latest1].
end());
303 }
else if(death2.d < death1.d) {
306 FiltratedSimplex{{f.f[0], f.f[1], f.f[2]}, f.d},
308 {death2.f[0], death2.f[1], death2.f[2]}, death2.d});
309 HashMap<Facet, unsigned> generator;
310 for(
auto const &c : cascade[latest2]) {
311 for(Facet
const &f_ : elementary_generators[c])
314 std::vector<Facet> generator_facets;
315 for(
auto const &[f_, v] : generator) {
317 generator_facets.push_back(f_);
319 generators2.push_back({generator_facets, {f.d, death2.d}});
321 latest[
UF.
find(v1)] = latest1;
322 cascade[latest1].insert(cascade[latest1].
end(),
323 cascade[latest2].
begin(),
324 cascade[latest2].
end());
327 msa.push_back({f.f, f.d});
331 void connectivity() {
335 for(
unsigned i = 0; i < msa.size(); ++i) {
336 const FiltratedFacet f = msa[i];
337 const double d1 = squaredDistance(f.f[0], f.f[1]);
338 const double d2 = squaredDistance(f.f[0], f.f[2]);
339 const double d3 = squaredDistance(f.f[1], f.f[2]);
340 auto &edge1 = msa_edges[std::make_pair(f.f[0], f.f[1])];
341 edge1.first.reserve(4);
342 edge1.first.push_back(i);
343 edge1.second |= d1 > d2 && d1 > d3;
344 auto &edge2 = msa_edges[std::make_pair(f.f[0], f.f[2])];
345 edge2.first.reserve(4);
346 edge2.first.push_back(i);
347 edge2.second |= d2 > d1 && d2 > d3;
348 auto &edge3 = msa_edges[std::make_pair(f.f[1], f.f[2])];
349 edge3.first.reserve(4);
350 edge3.first.push_back(i);
351 edge3.second |= d3 > d1 && d3 > d2;
355 void computePolygons(MultidimensionalDiagram &ph) {
356 const unsigned N_msa = msa.size();
357 UF_msa = UnionFind(N_msa);
359 for(
const auto &[e, val] : msa_edges) {
360 auto &[adj_f, isNotUrquhart] = val;
362 if(adj_f.size() == 1)
363 msa[UF_msa.find(adj_f[0])].d =
inf;
364 else if(adj_f.size() == 2)
365 UF_msa.merge(adj_f[0], adj_f[1]);
367 critical1.push_back({e, sqrt(squaredDistance(e.first, e.second))});
369 urquhart.push_back({e, sqrt(squaredDistance(e.first, e.second))});
372 for(
unsigned x = 0; x < N_msa; ++x) {
373 const int poly = UF_msa.find(x);
374 msa[poly] =
max(msa[poly], msa[x]);
377 std::sort(urquhart.begin(), urquhart.end(),
378 [](
const FiltratedEdge &a,
const FiltratedEdge &b) {
382 ph[0].reserve(N_p - 1);
383 for(FiltratedEdge
const &e : urquhart) {
384 if(UF_p.find(e.e.first)
385 != UF_p.find(e.e.second)) {
386 UF_p.merge(e.e.first, e.e.second);
387 ph[0].emplace_back(FiltratedSimplex{{-1}, 0.},
390 critical1.emplace_back(e);
394 void compute1PH(MultidimensionalDiagram &ph) {
395 const unsigned N_msa = msa.size();
396 std::vector<int> polys;
397 for(
unsigned x = 0; x < N_msa; ++x) {
398 if(UF_msa.isRoot(x) && msa[x].d < inf)
402 std::sort(polys.begin(), polys.end(), [&](
const int x1,
const int x2) {
403 return msa[x1].d < msa[x2].d;
406 std::vector<int> criticalIndices(critical1.size());
407 std::iota(criticalIndices.begin(), criticalIndices.end(), 0);
408 std::sort(criticalIndices.begin(), criticalIndices.end(),
409 [&](
const int e1_id,
const int e2_id) {
410 return critical1[e1_id].d < critical1[e2_id].d;
412 std::vector<int> criticalOrder(critical1.size());
413 for(
unsigned i = 0; i < criticalIndices.size(); ++i)
414 criticalOrder[criticalIndices[i]] = i;
416 std::vector<std::vector<int>> poly_to_crit(N_msa);
417 for(
const int poly : polys)
418 poly_to_crit[poly].reserve(3);
419 for(
unsigned i = 0; i < critical1.size(); ++i) {
420 const FiltratedEdge &e = critical1[i];
421 for(
const int poly : msa_edges[e.e].first) {
422 if(msa[UF_msa.find(poly)].d < inf) {
423 auto &neighbors = poly_to_crit[UF_msa.find(poly)];
425 = std::find(neighbors.begin(), neighbors.end(), criticalOrder[i]);
426 if(it == neighbors.end())
427 neighbors.push_back(criticalOrder[i]);
434 std::vector<int> partner(critical1.size(), -1);
435 for(
const int poly : polys) {
436 HashSet<int> boundary(poly_to_crit[poly].
begin(),
437 poly_to_crit[poly].
end(),
438 poly_to_crit[poly].size());
440 const int youngest_id
441 = *std::max_element(boundary.begin(), boundary.end());
442 if(partner[youngest_id] == -1) {
443 partner[youngest_id] = poly;
444 const FiltratedEdge &e = critical1[criticalIndices[youngest_id]];
445 const FiltratedFacet &death = msa[poly];
448 FiltratedSimplex{{e.e.first, e.e.second}, e.d},
450 {death.f[0], death.f[1], death.f[2]}, death.d});
453 for(
const int crit : poly_to_crit[partner[youngest_id]]) {
454 const auto it = boundary.find(crit);
455 if(it == boundary.end())
456 boundary.insert(crit);
462 poly_to_crit[poly].assign(boundary.begin(), boundary.end());
466 void compute1PH(MultidimensionalDiagram &ph,
467 std::vector<Generator1> &generators1) {
468 const unsigned N_msa = msa.size();
469 std::vector<int> polys;
470 for(
unsigned x = 0; x < N_msa; ++x) {
471 if(UF_msa.isRoot(x) && msa[x].d < inf)
475 std::sort(polys.begin(), polys.end(), [&](
const int x1,
const int x2) {
476 return msa[x1].d < msa[x2].d;
479 std::vector<int> criticalIndices(critical1.size());
480 std::iota(criticalIndices.begin(), criticalIndices.end(), 0);
481 std::sort(criticalIndices.begin(), criticalIndices.end(),
482 [&](
const int e1_id,
const int e2_id) {
483 return critical1[e1_id].d < critical1[e2_id].d;
485 std::vector<int> criticalOrder(critical1.size());
486 for(
unsigned i = 0; i < criticalIndices.size(); ++i)
487 criticalOrder[criticalIndices[i]] = i;
489 std::vector<std::vector<int>> poly_to_crit(N_msa);
490 for(
const int poly : polys)
491 poly_to_crit[poly].reserve(3);
492 for(
unsigned i = 0; i < critical1.size(); ++i) {
493 const FiltratedEdge &e = critical1[i];
494 for(
const int poly : msa_edges[e.e].first) {
495 if(msa[UF_msa.find(poly)].d < inf) {
496 auto &neighbors = poly_to_crit[UF_msa.find(poly)];
498 = std::find(neighbors.begin(), neighbors.end(), criticalOrder[i]);
499 if(it == neighbors.end())
500 neighbors.push_back(criticalOrder[i]);
507 std::vector<std::vector<Edge>> elementary_generators(N_msa);
508 auto action = [&](
const FiltratedEdge &e) {
509 auto adj_f = msa_edges[e.e].first;
512 std::sort(adj_f.begin(), adj_f.end());
513 const auto last = std::unique(adj_f.begin(), adj_f.end());
514 for(
auto it = adj_f.begin(); it != last; ++it) {
515 if(msa[UF_msa.find(*it)].d != inf)
516 elementary_generators[UF_msa.find(*it)].push_back(e.e);
519 for(
auto const &e : urquhart)
521 for(
auto const &e : critical1) {
527 std::vector<int> partner(critical1.size(), -1);
528 for(
const int poly : polys) {
529 HashSet<int> boundary(poly_to_crit[poly].
begin(),
530 poly_to_crit[poly].
end(),
531 poly_to_crit[poly].size());
532 std::vector<Edge> &generator = elementary_generators[poly];
534 const int youngest_id
535 = *std::max_element(boundary.begin(), boundary.end());
536 if(partner[youngest_id] == -1) {
537 partner[youngest_id] = poly;
538 const FiltratedEdge &e = critical1[criticalIndices[youngest_id]];
539 const FiltratedFacet &death = msa[poly];
542 FiltratedSimplex{{e.e.first, e.e.second}, e.d},
544 {death.f[0], death.f[1], death.f[2]}, death.d});
545 generators1.emplace_back(generator, std::make_pair(e.d, death.d));
549 for(
const int crit : poly_to_crit[partner[youngest_id]]) {
550 const auto it = boundary.find(crit);
551 if(it == boundary.end())
552 boundary.insert(crit);
556 for(Edge
const &e : elementary_generators[partner[youngest_id]]) {
557 auto it = std::find(generator.begin(), generator.end(), e);
558 if(it == generator.end())
559 generator.push_back(e);
565 poly_to_crit[poly].assign(boundary.begin(), boundary.end());
570#ifdef TTK_GPH_PARALLEL
571 class DRPersistence3_p {
572 using K = CGAL::Exact_predicates_inexact_constructions_kernel;
573 using Vb = CGAL::Triangulation_vertex_base_with_info_3<int, K>;
574 using Fb = CGAL::Triangulation_cell_base_with_info_3<int, K>;
576 Triangulation_data_structure_3<Vb, Fb, CGAL::Parallel_if_available_tag>;
577 using Delaunay = CGAL::Delaunay_triangulation_3<K, Tds>;
578 using Point = Delaunay::Point_3;
580 using AdjacencyList = std::vector<int>;
581 using ConnectivityHashMap = HashMap<Edge, std::pair<AdjacencyList, bool>>;
582 using ConcurrentConnectivityHashMap
583 = ConcurrentHashMap<Edge, std::pair<AdjacencyList, bool>>;
586 explicit DRPersistence3_p(
const PointCloud<3> &points,
587 const unsigned nThreads = 1)
588 : N_p(points.size()), p(points), nThreads_(nThreads) {
595 void run(MultidimensionalDiagram &ph) {
601 computeFacetUrquhart(UF);
613 void run(MultidimensionalDiagram &ph,
614 std::vector<Generator1> &generators1,
615 std::vector<Generator2> &generators2) {
617 generators1.resize(0);
618 generators2.resize(0);
623 computeFacetUrquhart(UF);
624 compute2PH(UF, ph, generators2);
628 compute1PH(ph, generators1);
634 const PointCloud<3> &p;
636 const int nThreads_{1};
639 tbb::concurrent_vector<FiltratedEdge> urquhart;
640 tbb::concurrent_vector<FiltratedEdge> critical1;
642 ConcurrentConnectivityHashMap msa_edges{};
644 DisjointSets UF_msa{0};
647 std::vector<FiltratedQuadFacet> hyperUrquhart;
648 std::vector<FiltratedFacet> msa;
649 std::vector<FiltratedFacet> maxDelaunay;
651 [[nodiscard]]
double squaredDistance(
const unsigned i1,
652 const unsigned i2)
const {
653 PointD<3>
const p1 = p[i1], p2 = p[i2];
654 return (p1[0] - p2[0]) * (p1[0] - p2[0])
655 + (p1[1] - p2[1]) * (p1[1] - p2[1])
656 + (p1[2] - p2[2]) * (p1[2] - p2[2]);
658 [[nodiscard]]
static std::pair<double, double>
659 perturbedDiameter(
const double d1,
const double d2,
const double d3) {
660 return {std::max(d1, std::max(d2, d3)), d1 + d2 + d3};
667 void computeDelaunay() {
668 tbb::global_control gc(
669 tbb::global_control::max_allowed_parallelism, nThreads_);
670 std::vector<std::pair<Point, unsigned>> points(N_p);
671 CGAL::Bbox_3 bbox(p[0][0], p[0][1], p[0][2], p[0][0], p[0][1], p[0][2]);
672 for(
unsigned i = 0; i < N_p; ++i) {
673 points[i] = std::make_pair(
Point(p[i][0], p[i][1], p[i][2]), i);
675 += CGAL::Bbox_3(p[i][0], p[i][1], p[i][2], p[i][0], p[i][1], p[i][2]);
677 Delaunay::Lock_data_structure lock_ds(bbox, 100);
678 del = Delaunay(points.begin(), points.end(), &lock_ds);
680 for(
auto const &c : del.all_cell_handles())
685 void computeFacetUrquhart(UnionFind &UF) {
686 maxDelaunay.resize(N_c, FiltratedFacet{{-1, -1, -1}, 0.});
687 for(Delaunay::Facet
const &f : del.all_facets()) {
688 if(del.is_infinite(f))
689 UF.merge(f.first->info(), del.mirror_facet(f).first->info());
691 Facet facet{f.first->vertex((f.second + 1) % 4)->info(),
692 f.first->vertex((f.second + 2) % 4)->info(),
693 f.first->vertex((f.second + 3) % 4)->info()};
694 const double d01 = squaredDistance(facet[0], facet[1]);
695 const double d02 = squaredDistance(facet[0], facet[2]);
696 const double d12 = squaredDistance(facet[1], facet[2]);
697 const auto diam = perturbedDiameter(d01, d02, d12);
698 const Delaunay::Facet f_m = del.mirror_facet(f);
701 bool is_urquhart =
true;
702 if(!del.is_infinite(f.first->vertex(f.second))) {
703 const unsigned k = f.first->vertex(f.second)->info();
704 const double d0k = squaredDistance(facet[0], k);
705 const double d1k = squaredDistance(facet[1], k);
706 const double d2k = squaredDistance(facet[2], k);
707 if(perturbedDiameter(d01, d0k, d1k) < diam
708 && perturbedDiameter(d02, d0k, d2k) < diam
709 && perturbedDiameter(d12, d1k, d2k) < diam)
712 if(is_urquhart && !del.is_infinite(f_m.first->vertex(f_m.second))) {
713 const unsigned l = f_m.first->vertex(f_m.second)->info();
714 const double d0l = squaredDistance(facet[0], l);
715 const double d1l = squaredDistance(facet[1], l);
716 const double d2l = squaredDistance(facet[2], l);
717 if(perturbedDiameter(d01, d0l, d1l) < diam
718 && perturbedDiameter(d02, d0l, d2l) < diam
719 && perturbedDiameter(d12, d1l, d2l) < diam)
724 std::sort(facet.begin(), facet.end());
725 hyperUrquhart.push_back({facet, f.first->info(), f_m.first->info(),
726 sqrt(diam.first), diam.second});
728 const int poly1 =
UF.
find(f.first->info());
729 const int poly2 =
UF.
find(f_m.first->info());
730 maxDelaunay[
UF.mergeRet(poly1, poly2)]
731 =
max({facet, sqrt(diam.first)},
732 max(maxDelaunay[poly1], maxDelaunay[poly2]));
736 if(del.is_infinite(f.first))
737 maxDelaunay[
UF.
find(f.first->info())].d =
inf;
738 else if(del.is_infinite(f_m.first))
739 maxDelaunay[
UF.
find(f_m.first->info())].d =
inf;
743 TTK_PSORT(nThreads_, hyperUrquhart.begin(), hyperUrquhart.end(),
744 [](
const FiltratedQuadFacet &f1,
const FiltratedQuadFacet &f2) {
751 void compute2PH(UnionFind &UF, MultidimensionalDiagram &ph) {
752 std::vector<int> latest(maxDelaunay.size());
753 std::iota(latest.begin(), latest.end(), 0);
755 for(FiltratedQuadFacet
const &f :
757 const int v1 =
UF.
find(f.c1);
758 const int v2 =
UF.
find(f.c2);
763 const int latest1 = latest[v1];
764 const int latest2 = latest[v2];
765 const FiltratedFacet &death1 = maxDelaunay[latest1];
766 const FiltratedFacet &death2 = maxDelaunay[latest2];
768 if(death1.d < death2.d) {
771 FiltratedSimplex{{f.f[0], f.f[1], f.f[2]}, f.d},
773 {death1.f[0], death1.f[1], death1.f[2]}, death1.d});
774 latest[
UF.
find(v1)] = latest2;
775 }
else if(death2.d < death1.d) {
778 FiltratedSimplex{{f.f[0], f.f[1], f.f[2]}, f.d},
780 {death2.f[0], death2.f[1], death2.f[2]}, death2.d});
781 latest[
UF.
find(v1)] = latest1;
784 msa.push_back({f.f, f.d});
788 void compute2PH(UnionFind &UF,
789 MultidimensionalDiagram &ph,
790 std::vector<Generator2> &generators2) {
791 std::vector<int> latest(maxDelaunay.size());
792 std::iota(latest.begin(), latest.end(), 0);
794 std::vector<std::vector<Facet>> elementary_generators(N_c);
795 for(
const FiltratedQuadFacet &f : hyperUrquhart) {
797 if(maxDelaunay[
UF.
find(f.c1)].d != inf)
798 elementary_generators[
UF.
find(f.c1)].push_back(f.f);
799 if(maxDelaunay[
UF.
find(f.c2)].d != inf)
800 elementary_generators[
UF.
find(f.c2)].push_back(f.f);
803 std::vector<std::vector<int>> cascade(N_c, {0});
804 for(
unsigned i = 0; i < N_c; i++)
807 for(FiltratedQuadFacet
const &f :
809 const int v1 =
UF.
find(f.c1);
810 const int v2 =
UF.
find(f.c2);
815 const int latest1 = latest[v1];
816 const int latest2 = latest[v2];
817 const FiltratedFacet &death1 = maxDelaunay[latest1];
818 const FiltratedFacet &death2 = maxDelaunay[latest2];
820 if(death1.d < death2.d) {
823 FiltratedSimplex{{f.f[0], f.f[1], f.f[2]}, f.d},
825 {death1.f[0], death1.f[1], death1.f[2]}, death1.d});
826 HashMap<Facet, unsigned> generator;
827 for(
auto const &c : cascade[latest1]) {
828 for(Facet
const &f_ : elementary_generators[c])
831 std::vector<Facet> generator_facets;
832 for(
auto const &[f_, v] : generator) {
834 generator_facets.push_back(f_);
836 generators2.push_back({generator_facets, {f.d, death1.d}});
838 latest[
UF.
find(v1)] = latest2;
839 cascade[latest2].insert(cascade[latest2].
end(),
840 cascade[latest1].
begin(),
841 cascade[latest1].
end());
842 }
else if(death2.d < death1.d) {
845 FiltratedSimplex{{f.f[0], f.f[1], f.f[2]}, f.d},
847 {death2.f[0], death2.f[1], death2.f[2]}, death2.d});
848 HashMap<Facet, unsigned> generator;
849 for(
auto const &c : cascade[latest2]) {
850 for(Facet
const &f_ : elementary_generators[c])
853 std::vector<Facet> generator_facets;
854 for(
auto const &[f_, v] : generator) {
856 generator_facets.push_back(f_);
858 generators2.push_back({generator_facets, {f.d, death2.d}});
860 latest[
UF.
find(v1)] = latest1;
861 cascade[latest1].insert(cascade[latest1].
end(),
862 cascade[latest2].
begin(),
863 cascade[latest2].
end());
866 msa.push_back({f.f, f.d});
870 void connectivity() {
871 omp_set_num_threads(nThreads_);
875#pragma omp parallel for
876 for(
unsigned i = 0; i < msa.size(); ++i) {
877 const FiltratedFacet f = msa[i];
878 const double d1 = squaredDistance(f.f[0], f.f[1]);
879 const double d2 = squaredDistance(f.f[0], f.f[2]);
880 const double d3 = squaredDistance(f.f[1], f.f[2]);
881 msa_edges.emplace_or_visit(
882 std::make_pair(f.f[0], f.f[1]),
883 std::make_pair(AdjacencyList{(int)i}, d1 > d2 && d1 > d3),
885 x.second.first.push_back(i);
886 x.second.second |= d1 > d2 && d1 > d3;
888 msa_edges.emplace_or_visit(
889 std::make_pair(f.f[0], f.f[2]),
890 std::make_pair(AdjacencyList{(int)i}, d2 > d1 && d2 > d3),
892 x.second.first.push_back(i);
893 x.second.second |= d2 > d1 && d2 > d3;
895 msa_edges.emplace_or_visit(
896 std::make_pair(f.f[1], f.f[2]),
897 std::make_pair(AdjacencyList{(int)i}, d3 > d1 && d3 > d2),
899 x.second.first.push_back(i);
900 x.second.second |= d3 > d1 && d3 > d2;
905 void computePolygons(MultidimensionalDiagram &ph) {
906 omp_set_num_threads(nThreads_);
907 const unsigned N_msa = msa.size();
908 UF_msa = DisjointSets(N_msa);
910 msa_edges.cvisit_all(
911#ifdef __cpp_lib_execution
916 auto &[adj_f, isNotUrquhart] = val;
918 if(adj_f.size() == 1)
919 msa[adj_f[0]].d =
inf;
920 if(adj_f.size() == 2)
921 UF_msa.unite(adj_f[0], adj_f[1]);
924 {e, sqrt(squaredDistance(e.first, e.second))});
926 urquhart.push_back({e, sqrt(squaredDistance(e.first, e.second))});
929 std::vector<std::mutex> maxDelaunayLocks(N_msa);
930#pragma omp parallel for
931 for(
unsigned x = 0; x < N_msa; ++x) {
932 const int poly = UF_msa.find(x);
933 std::lock_guard lock(maxDelaunayLocks[poly]);
934 msa[poly] =
max(msa[poly], msa[x]);
937 TTK_PSORT(nThreads_, urquhart.begin(), urquhart.end(),
938 [](
const FiltratedEdge &a,
const FiltratedEdge &b) {
942 ph[0].reserve(N_p - 1);
943 for(FiltratedEdge
const &e : urquhart) {
944 if(UF_p.find(e.e.first)
945 != UF_p.find(e.e.second)) {
946 UF_p.merge(e.e.first, e.e.second);
947 ph[0].emplace_back(FiltratedSimplex{{-1}, 0.},
950 critical1.emplace_back(e);
954 void compute1PH(MultidimensionalDiagram &ph) {
955 omp_set_num_threads(nThreads_);
956 const ConnectivityHashMap sequential_msa_edges = std::move(msa_edges);
958 const unsigned N_msa = msa.size();
959 std::vector<int> polys;
960 for(
unsigned x = 0; x < N_msa; ++x) {
961 if(UF_msa.isRoot(x) && msa[x].d < inf)
966 nThreads_, polys.begin(), polys.end(),
967 [&](
const int x1,
const int x2) { return msa[x1].d < msa[x2].d; });
969 std::vector<int> criticalIndices(critical1.size());
970 std::iota(criticalIndices.begin(), criticalIndices.end(), 0);
971 TTK_PSORT(nThreads_, criticalIndices.begin(), criticalIndices.end(),
972 [&](
const int e1_id,
const int e2_id) {
973 return critical1[e1_id].d < critical1[e2_id].d;
975 std::vector<int> criticalOrder(critical1.size());
976 for(
unsigned i = 0; i < criticalIndices.size(); ++i)
977 criticalOrder[criticalIndices[i]] = i;
979 std::vector<std::vector<int>> poly_to_crit(N_msa);
980 for(
const int poly : polys)
981 poly_to_crit[poly].reserve(3);
982 std::vector<std::mutex> poly_mutex(N_msa);
983 std::vector<std::mutex> crit_mutex(critical1.size());
985#pragma omp parallel for
986 for(
unsigned i = 0; i < critical1.size(); ++i) {
987 const FiltratedEdge &e = critical1[i];
988 for(
const int poly : sequential_msa_edges.find(e.e)->second.first) {
989 const int uf_poly = UF_msa.find(poly);
990 if(msa[uf_poly].d < inf) {
991 auto &neighbors = poly_to_crit[uf_poly];
992 std::lock_guard lock(poly_mutex[uf_poly]);
994 = std::find(neighbors.begin(), neighbors.end(), criticalOrder[i]);
995 if(it == neighbors.end())
996 neighbors.push_back(criticalOrder[i]);
1003 std::vector<int> partner(critical1.size(), -1);
1005 auto eliminateBoundary = [&](
const int poly) ->
int {
1006 std::lock_guard lock(poly_mutex[poly]);
1007 HashSet<int> boundary(poly_to_crit[poly].
begin(),
1008 poly_to_crit[poly].
end(),
1009 poly_to_crit[poly].size());
1011 const int youngest_id
1012 = *std::max_element(boundary.begin(), boundary.end());
1013 std::lock_guard lock_crit(crit_mutex[youngest_id]);
1014 if(partner[youngest_id] == -1) {
1015 partner[youngest_id] = poly;
1016 poly_to_crit[poly].assign(boundary.begin(), boundary.end());
1019 std::lock_guard lock_partner(poly_mutex[partner[youngest_id]]);
1020 if(msa[partner[youngest_id]].d > msa[poly].d) {
1021 const int tmp = partner[youngest_id];
1022 partner[youngest_id] = poly;
1023 poly_to_crit[poly].assign(boundary.begin(), boundary.end());
1026 for(
const int crit : poly_to_crit[partner[youngest_id]]) {
1027 const auto it = boundary.find(crit);
1028 if(it == boundary.end())
1029 boundary.insert(crit);
1036#pragma omp parallel for
1037 for(
const int poly : polys) {
1040 tmp_p = eliminateBoundary(tmp_p);
1043 for(
unsigned i = 0; i < critical1.size(); ++i) {
1044 const int poly = partner[i];
1045 const FiltratedEdge &e = critical1[criticalIndices[i]];
1046 const FiltratedFacet &death = msa[poly];
1049 FiltratedSimplex{{e.e.first, e.e.second}, e.d},
1054 void compute1PH(MultidimensionalDiagram &ph,
1055 std::vector<Generator1> &generators1) {
1056 omp_set_num_threads(nThreads_);
1057 ConnectivityHashMap sequential_msa_edges = std::move(msa_edges);
1059 const unsigned N_msa = msa.size();
1060 std::vector<int> polys;
1061 for(
unsigned x = 0; x < N_msa; ++x) {
1062 if(UF_msa.isRoot(x) && msa[x].d < inf)
1067 nThreads_, polys.begin(), polys.end(),
1068 [&](
const int x1,
const int x2) { return msa[x1].d < msa[x2].d; });
1070 std::vector<int> criticalIndices(critical1.size());
1071 std::iota(criticalIndices.begin(), criticalIndices.end(), 0);
1072 TTK_PSORT(nThreads_, criticalIndices.begin(), criticalIndices.end(),
1073 [&](
const int e1_id,
const int e2_id) {
1074 return critical1[e1_id].d < critical1[e2_id].d;
1076 std::vector<int> criticalOrder(critical1.size());
1077 for(
unsigned i = 0; i < criticalIndices.size(); ++i)
1078 criticalOrder[criticalIndices[i]] = i;
1080 std::vector<std::vector<int>> poly_to_crit(N_msa);
1081 for(
const int poly : polys)
1082 poly_to_crit[poly].reserve(3);
1083 std::vector<std::mutex> poly_mutex(N_msa);
1084 std::vector<std::mutex> crit_mutex(critical1.size());
1086#pragma omp parallel for
1087 for(
unsigned i = 0; i < critical1.size(); ++i) {
1088 const FiltratedEdge &e = critical1[i];
1089 for(
const int poly : sequential_msa_edges.find(e.e)->second.first) {
1090 const int uf_poly = UF_msa.find(poly);
1091 if(msa[uf_poly].d < inf) {
1092 auto &neighbors = poly_to_crit[uf_poly];
1093 std::lock_guard lock(poly_mutex[uf_poly]);
1095 = std::find(neighbors.begin(), neighbors.end(), criticalOrder[i]);
1096 if(it == neighbors.end())
1097 neighbors.push_back(criticalOrder[i]);
1099 neighbors.erase(it);
1104 std::vector<std::vector<Edge>> elementary_generators(N_msa);
1105 auto action = [&](
const FiltratedEdge &e) {
1106 auto adj_f = sequential_msa_edges[e.e].first;
1109 std::sort(adj_f.begin(), adj_f.end());
1110 const auto last = std::unique(adj_f.begin(), adj_f.end());
1111 for(
auto it = adj_f.begin(); it != last; ++it) {
1112 if(msa[UF_msa.find(*it)].d != inf)
1113 elementary_generators[UF_msa.find(*it)].push_back(e.e);
1116 for(
auto const &e : urquhart)
1118 for(
auto const &e : critical1) {
1119 if(sequential_msa_edges[e.e]
1124 std::vector<int> partner(critical1.size(), -1);
1126 auto eliminateBoundary = [&](
const int poly) ->
int {
1127 std::lock_guard lock(poly_mutex[poly]);
1128 HashSet<int> boundary(poly_to_crit[poly].
begin(),
1129 poly_to_crit[poly].
end(),
1130 poly_to_crit[poly].size());
1131 std::vector<Edge> &generator = elementary_generators[poly];
1133 const int youngest_id
1134 = *std::max_element(boundary.begin(), boundary.end());
1135 std::lock_guard lock_crit(crit_mutex[youngest_id]);
1136 if(partner[youngest_id] == -1) {
1137 partner[youngest_id] = poly;
1138 poly_to_crit[poly].assign(boundary.begin(), boundary.end());
1141 std::lock_guard lock_partner(poly_mutex[partner[youngest_id]]);
1142 if(msa[partner[youngest_id]].d > msa[poly].d) {
1143 const int tmp = partner[youngest_id];
1144 partner[youngest_id] = poly;
1145 poly_to_crit[poly].assign(boundary.begin(), boundary.end());
1148 for(
const int crit : poly_to_crit[partner[youngest_id]]) {
1149 const auto it = boundary.find(crit);
1150 if(it == boundary.end())
1151 boundary.insert(crit);
1155 for(Edge
const &e : elementary_generators[partner[youngest_id]]) {
1156 auto it = std::find(generator.begin(), generator.end(), e);
1157 if(it == generator.end())
1158 generator.push_back(e);
1160 generator.erase(it);
1165#pragma omp parallel for
1166 for(
const int poly : polys) {
1169 tmp_p = eliminateBoundary(tmp_p);
1172 for(
unsigned i = 0; i < critical1.size(); ++i) {
1173 const int poly = partner[i];
1174 const FiltratedEdge &e = critical1[criticalIndices[i]];
1175 const FiltratedFacet &death = msa[poly];
1176 const std::vector<Edge> &generator = elementary_generators[poly];
1179 FiltratedSimplex{{e.e.first, e.e.second}, e.d},
1181 generators1.emplace_back(generator, std::make_pair(e.d, death.d));
1190 MultidimensionalDiagram &diagram,
1193 for(
unsigned i = 0; i < points.size(); ++i)
1194 p[i] = {points[i][0], points[i][1], points[i][2]};
1196#ifdef TTK_GPH_PARALLEL
1197 DRPersistence3_p drpd(p, threads);
1199 DRPersistence3 drpd(p);
1203 DRPersistence3 drpd(p);
1210 MultidimensionalDiagram &diagram,
1211 std::vector<Generator1> &generators1,
1212 std::vector<Generator2> &generators2,
1215 for(
unsigned i = 0; i < points.size(); ++i)
1216 p[i] = {points[i][0], points[i][1], points[i][2]};
1218#ifdef TTK_GPH_PARALLEL
1219 DRPersistence3_p drpd(p, threads);
1221 DRPersistence3 drpd(p);
1223 drpd.run(diagram, generators1, generators2);
1225 DRPersistence3 drpd(p);
1226 drpd.run(diagram, generators1, generators2);
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
boost::tuple< double, double > Point
std::vector< PointD< DIM > > PointCloud
FiltratedEdge max(const FiltratedEdge &a, const FiltratedEdge &b)
std::pair< id_t, id_t > Edge
std::array< id_t, 3 > Facet
std::vector< std::vector< value_t > > PointCloud
std::vector< Diagram > MultidimensionalDiagram
std::pair< std::vector< Edge >, std::pair< value_t, value_t > > Generator1
std::pair< Simplex, value_t > FiltratedSimplex
std::pair< std::vector< Facet >, std::pair< value_t, value_t > > Generator2
T end(std::pair< T, T > &p)
T begin(std::pair< T, T > &p)