TTK
Loading...
Searching...
No Matches
LocalizedTopologicalSimplification.h
Go to the documentation of this file.
1
19
20#pragma once
21
22// for numerical perturbation
23#include <boost/math/special_functions/next.hpp>
24
25#include <Debug.h>
26#include <Triangulation.h>
27
28#include <Propagation.h>
29
30namespace ttk {
31 namespace lts {
32
33 inline std::string toFixed(const float &number, const int precision = 2) {
34 std::stringstream vFraction;
35 vFraction << std::fixed << std::setprecision(precision) << number;
36 return vFraction.str();
37 }
38
39 template <typename IT>
40 std::string
41 toFixed(const IT &number0, const IT &number1, const int precision = 2) {
42 return toFixed(((float)number0) / ((float)number1), precision);
43 }
44
46
47 public:
53
58
60 ttk::AbstractTriangulation *triangulation) const {
61 return triangulation->preconditionVertexNeighbors();
62 }
63
66 template <typename IT>
67 int allocateMemory(std::vector<IT> &segmentation,
68 std::vector<IT> &queueMask,
69 std::vector<IT> &localOrder,
70 std::vector<Propagation<IT> *> &propagationMask,
71 std::vector<std::tuple<IT, IT, IT>> &sortedIndices,
72
73 const IT &nVertices) const {
74 ttk::Timer timer;
75
76 constexpr char msg[] = "Allocating Memory";
77 this->printMsg(
78 msg, 0, 0, this->threadNumber_, debug::LineMode::REPLACE);
79
80 segmentation.resize(nVertices);
81 queueMask.resize(nVertices);
82 localOrder.resize(nVertices);
83 propagationMask.resize(nVertices);
84 sortedIndices.resize(nVertices);
85
86 this->printMsg(msg, 1, timer.getElapsedTime(), this->threadNumber_);
87
88 return 0;
89 }
90
93 template <typename IT>
94 int initializeMemory(IT *segmentation,
95 IT *queueMask,
96 IT *localOrder,
97 Propagation<IT> **propagationMask,
98
99 const IT &nVertices) const {
100 ttk::Timer timer;
101
102 constexpr char msg[] = "Initializing Memory";
103 this->printMsg(
104 msg, 0, 0, this->threadNumber_, debug::LineMode::REPLACE);
105
106#ifdef TTK_ENABLE_OPENMP
107#pragma omp parallel for num_threads(this->threadNumber_)
108#endif // TTK_ENABLE_OPENMP
109 for(IT i = 0; i < nVertices; i++) {
110 segmentation[i] = -1;
111 queueMask[i] = -1;
112 localOrder[i] = -1;
113 propagationMask[i] = nullptr;
114 }
115
116 this->printMsg(msg, 1, timer.getElapsedTime(), this->threadNumber_);
117
118 return 0;
119 }
120
128 template <typename IT, class TT>
130 std::vector<Propagation<IT>> &propagations,
131 IT *authorizationMask, // assumes preservation mask is initialized as -1
132 IT *maximaBuffer,
133
134 const IT *authorizedExtremaIndices,
135 const IT &nAuthorizedExtremaIndices,
136 const IT *order,
137 const TT *triangulation) const {
138
139 ttk::Timer timer;
140 this->printMsg("Initializing Propagations", 0, 0, this->threadNumber_,
142
143 const IT nVertices = triangulation->getNumberOfVertices();
144#ifdef TTK_ENABLE_OPENMP
145#pragma omp parallel for num_threads(this->threadNumber_)
146#endif // TTK_ENABLE_OPENMP
147 for(IT i = 0; i < nAuthorizedExtremaIndices; i++)
148 authorizationMask[authorizedExtremaIndices[i]] = -2;
149
150 IT writeIndex = 0;
151// find discarded maxima
152#ifdef TTK_ENABLE_OPENMP
153#pragma omp parallel for num_threads(this->threadNumber_)
154#endif // TTK_ENABLE_OPENMP
155 for(IT v = 0; v < nVertices; v++) {
156
157 // if v needs to be preserved then skip
158 if(authorizationMask[v] == -2)
159 continue;
160
161 // check if v has larger neighbors
162 bool hasLargerNeighbor = false;
163 const IT &vOrder = order[v];
164 const IT nNeighbors = triangulation->getVertexNeighborNumber(v);
165 for(IT n = 0; n < nNeighbors; n++) {
166 IT u{-1};
167 triangulation->getVertexNeighbor(v, n, u);
168 if(vOrder < order[u]) {
169 hasLargerNeighbor = true;
170 break;
171 }
172 }
173
174 // if v has larger neighbors then v can not be maximum
175 if(hasLargerNeighbor)
176 continue;
177
178 // get local write index for this thread
179 IT localWriteIndex = 0;
180#ifdef TTK_ENABLE_OPENMP
181#pragma omp atomic capture
182#endif // TTK_ENABLE_OPENMP
183 localWriteIndex = writeIndex++;
184
185// atomic write to prevent cache line conflicts
186#ifdef TTK_ENABLE_OPENMP
187#pragma omp atomic write
188#endif // TTK_ENABLE_OPENMP
189 maximaBuffer[localWriteIndex] = v;
190 }
191
192 // sort maxima in ascending order
193 std::sort(maximaBuffer, maximaBuffer + writeIndex,
194 [=](const IT &a, const IT &b) -> bool {
195 return order[a] < order[b];
196 });
197
198 // if there are no authorized maxima then always skip most persistent
199 // prop
200 if(nAuthorizedExtremaIndices < 1)
201 writeIndex--;
202
203 // init propagations
204 propagations.resize(writeIndex);
205#ifdef TTK_ENABLE_OPENMP
206#pragma omp parallel for num_threads(this->threadNumber_)
207#endif // TTK_ENABLE_OPENMP
208 for(IT p = 0; p < writeIndex; p++) {
209 propagations[p].criticalPoints.push_back(maximaBuffer[p]);
210 }
211
212 this->printMsg("Initializing Propagations ("
213 + std::to_string(writeIndex) + "|"
214 + std::to_string(nVertices) + ")",
215 1, timer.getElapsedTime(), this->threadNumber_);
216
217 return 0;
218 }
219
225 template <typename IT, typename TT>
227 Propagation<IT> &propagation,
228 Propagation<IT> **propagationMask,
229 IT *segmentation, // used here to also store registered larger vertices
230 IT *queueMask, // used to mark vertices that have already been added to
231 // the queue by this thread
232
233 const TT *triangulation,
234 const IT *order) const {
235
236 // pointer used to compare against representative
237 auto *currentPropagation = &propagation;
238
239 // add extremumIndex (stored in segmentId) to queue
240 IT segmentId = currentPropagation->criticalPoints[0];
241 auto queue = &currentPropagation->queue;
242 queue->emplace(order[segmentId], segmentId);
243
244 queueMask[segmentId] = segmentId;
245
246 // grow segment until prop reaches a saddle and then decide if prop
247 // should continue
248 IT v = -1;
249 while(!queue->empty()) {
250 v = std::get<1>(queue->top());
251 queue->pop();
252
253 // continue if this thread has already seen this vertex
254 if(propagationMask[v] != nullptr)
255 continue;
256
257 const IT &orderV = order[v];
258
259 // add neighbors to queue AND check if v is a saddle
260 bool isSaddle = false;
261 const IT nNeighbors = triangulation->getVertexNeighborNumber(v);
262
263 IT numberOfLargerNeighbors = 0;
264 IT numberOfLargerNeighborsThisThreadVisited = 0;
265 for(IT n = 0; n < nNeighbors; n++) {
266 IT u{-1};
267 triangulation->getVertexNeighbor(v, n, u);
268
269 const IT &orderU = order[u];
270
271 // if larger neighbor
272 if(orderU > orderV) {
273 numberOfLargerNeighbors++;
274
275 auto uPropagation = propagationMask[u];
276 if(uPropagation == nullptr
277 || currentPropagation != uPropagation->find())
278 isSaddle = true;
279 else
280 numberOfLargerNeighborsThisThreadVisited++;
281 } else if(queueMask[u] != segmentId) {
282 queue->emplace(orderU, u);
283 queueMask[u] = segmentId;
284 }
285 }
286
287 // if v is a saddle we have to check if the current thread is the last
288 // visitor
289 if(isSaddle) {
290 currentPropagation->criticalPoints.push_back(v);
291
292 IT numberOfRegisteredLargerVertices = 0;
293#ifdef TTK_ENABLE_OPENMP
294#pragma omp atomic capture
295#endif // TTK_ENABLE_OPENMP
296 {
297 segmentation[v] -= numberOfLargerNeighborsThisThreadVisited;
298 numberOfRegisteredLargerVertices = segmentation[v];
299 }
300
301 // if this thread did not register the last remaining larger
302 // vertices then terminate propagation
303 if(numberOfRegisteredLargerVertices != -numberOfLargerNeighbors - 1)
304 return 0;
305
306 // get most dominant propagation
307 std::vector<Propagation<IT> *> neighborPropagations(
308 nNeighbors, nullptr);
309 for(IT n = 0; n < nNeighbors; n++) {
310 IT u{-1};
311 triangulation->getVertexNeighbor(v, n, u);
312 if(propagationMask[u] != nullptr) {
313 auto &neighborPropagation = neighborPropagations[n];
314 neighborPropagation = propagationMask[u]->find();
315 if(order[neighborPropagation->criticalPoints[0]]
316 > order[currentPropagation->criticalPoints[0]])
317 currentPropagation = neighborPropagation;
318 }
319 }
320
321 // merge propagations
322 for(IT n = 0; n < nNeighbors; n++) {
324 currentPropagation, neighborPropagations[n]);
325 }
326
327 queue = &currentPropagation->queue;
328 segmentId = currentPropagation->criticalPoints[0];
329 }
330
331 // mark vertex as visited and continue
332 propagationMask[v] = currentPropagation;
333 segmentation[v] = segmentId;
334 currentPropagation->segmentSize++;
335 }
336
337 this->printErr(
338 "Simple propagations should never reach global minimum/maximum.");
339
340 return 1;
341 }
342
347 template <typename IT, typename DT, typename TT>
349 Propagation<IT> &propagation,
350 Propagation<IT> **propagationMask,
351 IT *segmentation, // used here to also store registered larger vertices
352 IT *queueMask, // used to mark vertices that have already been added to
353 // the queue by this thread
354
355 const TT *triangulation,
356 const IT *order,
357 const DT *scalars,
358 const DT persistenceThreshold) const {
359
360 // pointer used to compare against representative
361 auto *currentPropagation = &propagation;
362
363 // add extremumIndex (stored in segmentId) to queue
364 IT segmentId = currentPropagation->criticalPoints[0];
365 auto queue = &currentPropagation->queue;
366 queue->emplace(order[segmentId], segmentId);
367
368 DT s0 = scalars[segmentId];
369
370 queueMask[segmentId] = segmentId;
371
372 // grow segment until prop reaches a saddle and then decide if prop
373 // should continue
374 IT v = -1;
375 while(!queue->empty()) {
376 v = std::get<1>(queue->top());
377 queue->pop();
378
379 // continue if this thread has already seen this vertex
380 if(propagationMask[v] != nullptr)
381 continue;
382
383 // if propagation exceeds persistence threshold stop
384 const DT &s1 = scalars[v];
385 const DT &sd = s0 < s1 ? s1 - s0 : s0 - s1;
386 if(sd > persistenceThreshold) {
387 currentPropagation->aborted = true;
388 return 0;
389 }
390
391 const IT &orderV = order[v];
392
393 // add neighbors to queue AND check if v is a saddle
394 bool isSaddle = false;
395 const IT nNeighbors = triangulation->getVertexNeighborNumber(v);
396
397 IT numberOfLargerNeighbors = 0;
398 IT numberOfLargerNeighborsThisThreadVisited = 0;
399 for(IT n = 0; n < nNeighbors; n++) {
400 IT u{-1};
401 triangulation->getVertexNeighbor(v, n, u);
402
403 const IT &orderU = order[u];
404
405 // if larger neighbor
406 if(orderU > orderV) {
407 numberOfLargerNeighbors++;
408
409 auto uPropagation = propagationMask[u];
410 if(uPropagation == nullptr
411 || currentPropagation != uPropagation->find())
412 isSaddle = true;
413 else
414 numberOfLargerNeighborsThisThreadVisited++;
415 } else if(queueMask[u] != segmentId) {
416 queue->emplace(orderU, u);
417 queueMask[u] = segmentId;
418 }
419 }
420
421 // if v is a saddle we have to check if the current thread is the last
422 // visitor
423 if(isSaddle) {
424 currentPropagation->criticalPoints.push_back(v);
425
426 IT numberOfRegisteredLargerVertices = 0;
427#ifdef TTK_ENABLE_OPENMP
428#pragma omp atomic capture
429#endif // TTK_ENABLE_OPENMP
430 {
431 segmentation[v] -= numberOfLargerNeighborsThisThreadVisited;
432 numberOfRegisteredLargerVertices = segmentation[v];
433 }
434
435 // if this thread did not register the last remaining larger
436 // vertices then terminate propagation
437 if(numberOfRegisteredLargerVertices != -numberOfLargerNeighbors - 1)
438 return 0;
439
440 // get most dominant propagation
441 std::vector<Propagation<IT> *> neighborPropagations(
442 nNeighbors, nullptr);
443 for(IT n = 0; n < nNeighbors; n++) {
444 IT u{-1};
445 triangulation->getVertexNeighbor(v, n, u);
446 if(propagationMask[u] != nullptr) {
447 auto &neighborPropagation = neighborPropagations[n];
448 neighborPropagation = propagationMask[u]->find();
449 if(order[neighborPropagation->criticalPoints[0]]
450 > order[currentPropagation->criticalPoints[0]])
451 currentPropagation = neighborPropagation;
452 }
453 }
454
455 // merge propagations
456 for(IT n = 0; n < nNeighbors; n++) {
458 currentPropagation, neighborPropagations[n]);
459 }
460
461 queue = &currentPropagation->queue;
462 segmentId = currentPropagation->criticalPoints[0];
463 s0 = scalars[segmentId];
464 }
465
466 // mark vertex as visited and continue
467 propagationMask[v] = currentPropagation;
468 segmentation[v] = segmentId;
469 currentPropagation->segmentSize++;
470 }
471
472 return 1;
473 }
474
477 template <typename IT, class TT>
478 int computeSimplePropagations(std::vector<Propagation<IT>> &propagations,
479 Propagation<IT> **propagationMask,
480 IT *segmentation,
481 IT *queueMask,
482
483 const TT *triangulation,
484 const IT *inputOrder) const {
485 ttk::Timer timer;
486 const IT nPropagations = propagations.size();
487 const std::string msg
488 = "Computing Propagations (" + std::to_string(nPropagations) + ")";
489 this->printMsg(
490 msg, 0, 0, this->threadNumber_, debug::LineMode::REPLACE);
491
492 int status = 0;
493// compute propagations
494#ifdef TTK_ENABLE_OPENMP
495#pragma omp parallel for schedule(dynamic, 1) num_threads(this->threadNumber_)
496#endif // TTK_ENABLE_OPENMP
497 for(IT p = 0; p < nPropagations; p++) {
498 int const localStatus = this->computeSimplePropagation<IT, TT>(
499 propagations[p], propagationMask, segmentation, queueMask,
500
501 triangulation, inputOrder);
502
503 if(localStatus)
504 status = 1;
505 }
506
507 if(status)
508 return 1;
509
510 this->printMsg(msg, 1, timer.getElapsedTime(), this->threadNumber_);
511
512 return 0;
513 }
514
517 template <typename IT, typename DT, class TT>
519 std::vector<Propagation<IT>> &propagations,
520 Propagation<IT> **propagationMask,
521 IT *segmentation,
522 IT *queueMask,
523
524 const TT *triangulation,
525 const IT *order,
526 const DT *scalars,
527 const DT persistenceThreshold) const {
528 ttk::Timer timer;
529 const IT nPropagations = propagations.size();
530 const std::string msg = "Computing Persistence-Sensitive Propagations ("
531 + std::to_string(nPropagations) + ")";
532 this->printMsg(
533 msg, 0, 0, this->threadNumber_, debug::LineMode::REPLACE);
534
535 int status = 0;
536// compute propagations
537#ifdef TTK_ENABLE_OPENMP
538#pragma omp parallel for schedule(dynamic, 1) num_threads(this->threadNumber_)
539#endif // TTK_ENABLE_OPENMP
540 for(IT p = 0; p < nPropagations; p++) {
541 int const localStatus
543 propagations[p], propagationMask, segmentation, queueMask,
544
545 triangulation, order, scalars, persistenceThreshold);
546
547 if(localStatus)
548 status = 1;
549 }
550
551 if(status)
552 return 1;
553
554 this->printMsg(msg, 1, timer.getElapsedTime(), this->threadNumber_);
555
556 return 0;
557 }
558
564 template <typename IT>
565 int
566 finalizePropagations(std::vector<Propagation<IT> *> &parentPropagations,
567 std::vector<Propagation<IT>> &propagations,
568
569 const IT nVertices) const {
570 ttk::Timer timer;
571
572 const IT nPropagations = propagations.size();
573
574 this->printMsg(
575 "Finalizing Propagations (" + std::to_string(nPropagations) + ")", 0,
576 timer.getElapsedTime(), this->threadNumber_,
578
579 IT nSegmentVertices = 0;
580 IT nParentPropagations = 0;
581 parentPropagations.resize(nPropagations);
582 for(IT p = 0; p < nPropagations; p++) {
583 auto *propagation = &propagations[p];
584 if(!propagation->aborted
585 && (propagation->parent == propagation
586 || propagation->parent->aborted)) {
587 nSegmentVertices = nSegmentVertices + propagation->segmentSize;
588 parentPropagations[nParentPropagations++] = propagation;
589 }
590 }
591 parentPropagations.resize(nParentPropagations);
592
593 this->printMsg("Finalizing Propagations ("
594 + std::to_string(nParentPropagations) + "|"
595 + toFixed(nParentPropagations, nPropagations) + "|"
596 + toFixed(nSegmentVertices, nVertices) + ")",
597 1, timer.getElapsedTime(), this->threadNumber_);
598
599 return 0;
600 }
601
605 template <typename IT, class TT>
606 int computeSegment(IT *segmentation,
607 Propagation<IT> *propagation,
608
609 const IT *order,
610 const TT *triangulation) const {
611
612 const IT &extremumIndex = propagation->criticalPoints[0];
613 const IT &saddleIndex = propagation->criticalPoints.back();
614 const IT &saddleOrder = order[saddleIndex];
615
616 // collect segment
617 auto &segment = propagation->segment;
618
619 segment.resize(propagation->segmentSize);
620 IT segmentIndex = 0;
621 if(propagation->segmentSize > 0) {
622 std::vector<IT> queue(propagation->segmentSize);
623 IT queueIndex = 0;
624
625 // init queue
626 {
627 queue[queueIndex++] = extremumIndex;
628 segmentation[extremumIndex] = -1000; // mark as visited
629 }
630
631 // flood fill by starting from extremum
632 while(queueIndex > 0) {
633 const IT v = queue[--queueIndex];
634
635 segment[segmentIndex++] = v;
636
637 IT nNeighbors = triangulation->getVertexNeighborNumber(v);
638 for(IT n = 0; n < nNeighbors; n++) {
639 IT u{-1};
640 triangulation->getVertexNeighbor(v, n, u);
641 auto &s = segmentation[u];
642 if(s >= 0 && order[u] > saddleOrder) {
643 s = -1000; // mark as visited
644 queue[queueIndex++] = u; // add to queue
645 }
646 }
647 }
648 }
649
650 if(segmentIndex != propagation->segmentSize) {
651 this->printErr("Segment size incorrect: "
652 + std::to_string(segmentIndex) + " "
653 + std::to_string(propagation->segmentSize));
654 return 1;
655 }
656
657 for(auto idx : propagation->segment)
658 segmentation[idx] = extremumIndex;
659
660 return 0;
661 }
662
664 template <typename IT, class TT>
665 int computeSegments(IT *segmentation,
666 std::vector<Propagation<IT> *> &propagations,
667
668 const IT *order,
669 const TT *triangulation) const {
670
671 const IT nPropagations = propagations.size();
672 const IT nVertices = triangulation->getNumberOfVertices();
673
674 ttk::Timer timer;
675 const std::string msg
676 = "Computing Segments (" + std::to_string(nPropagations) + ")";
677 this->printMsg(
678 msg, 0, 0, this->threadNumber_, debug::LineMode::REPLACE);
679
680 int status = 0;
681
682// compute segments in parallel
683#ifdef TTK_ENABLE_OPENMP
684#pragma omp parallel for schedule(dynamic) num_threads(this->threadNumber_)
685#endif // TTK_ENABLE_OPENMP
686 for(IT p = 0; p < nPropagations; p++) {
687 int const localStatus
688 = this->computeSegment<IT, TT>(segmentation, propagations[p],
689
690 order, triangulation);
691 if(localStatus)
692 status = 1;
693 }
694 if(status)
695 return 1;
696
697 // print status
698 if(this->debugLevel_ < 4 || nPropagations == 0) {
699 this->printMsg(msg, 1, timer.getElapsedTime(), this->threadNumber_);
700 } else {
701
702 IT min = propagations[0]->segmentSize;
703 IT max = min;
704 IT avg = 0;
705
706 for(IT p = 0; p < nPropagations; p++) {
707 const auto propagation = propagations[p];
708 if(min > propagation->segmentSize)
709 min = propagation->segmentSize;
710 if(max < propagation->segmentSize)
711 max = propagation->segmentSize;
712 avg += propagation->segmentSize;
713 }
714
715 avg /= nPropagations;
716
717 this->printMsg("Computing Segments (" + std::to_string(nPropagations)
718 + "|" + toFixed(min, nVertices) + "|"
719 + toFixed(avg, nVertices) + "|"
720 + toFixed(max, nVertices) + ")",
721 1, timer.getElapsedTime(), this->threadNumber_);
722 }
723
724 return 0;
725 }
726
727 template <typename IT, class TT>
729 IT *localOrder,
730 IT *localVertexSequence,
731
732 const bool &performSuperlevelSetPropagation,
733 const TT *triangulation,
734 const IT *segmentation,
735 const IT &segmentId,
736 const std::vector<IT> &boundary,
737 const std::vector<IT> &segment,
738 const IT &saddleIdx) const {
739 // init priority queue
740 std::priority_queue<std::pair<IT, IT>, std::vector<std::pair<IT, IT>>>
741 queue;
742
743 IT nSegmentVertices = segment.size();
744
745 if(performSuperlevelSetPropagation) {
746 // add saddle to queue
747 queue.emplace(std::numeric_limits<IT>::max(), saddleIdx);
748 } else {
749 // invert order
750 IT offset = -nSegmentVertices - 1; // ensures that inverted order < 0
751 for(IT i = 0; i < nSegmentVertices; i++)
752 localOrder[segment[i]] = offset - localOrder[segment[i]];
753
754 // add all boundary vertices to queue
755 for(const auto &v : boundary) {
756 queue.emplace(localOrder[v], v);
757 localOrder[v] = 0;
758 }
759
760 // also add saddle to queue
761 queue.emplace(std::numeric_limits<IT>::min(), saddleIdx);
762 }
763
764 IT q = 0;
765 while(!queue.empty()) {
766 IT v = std::get<1>(queue.top());
767 queue.pop();
768
769 localVertexSequence[q++] = v;
770
771 IT nNeighbors = triangulation->getVertexNeighborNumber(v);
772 for(IT n = 0; n < nNeighbors; n++) {
773 IT u{-1};
774 triangulation->getVertexNeighbor(v, n, u);
775
776 // if u is in segment and has not already been added to the queue
777 if(segmentation[u] == segmentId && localOrder[u] < 0) {
778 queue.emplace(localOrder[u], u);
779 localOrder[u] = 0;
780 }
781 }
782 }
783
784 if(performSuperlevelSetPropagation) {
785 // skip first idx as it corresponds to saddle
786 for(IT i = 1; i <= nSegmentVertices; i++)
787 localOrder[localVertexSequence[i]] = -i;
788 } else {
789 IT order = -nSegmentVertices;
790 // skip last idx as it corresponds to saddle
791 for(IT i = 0; i < nSegmentVertices; i++)
792 localOrder[localVertexSequence[i]] = order++;
793 }
794
795 return 0;
796 }
797
798 template <typename IT, class TT>
799 int computeLocalOrderOfSegment(IT *localOrder,
800
801 const Propagation<IT> *propagation,
802 const TT *triangulation,
803 const IT *segmentation,
804 const IT *inputOrder) const {
805 // quick escape for small segments
806 if(propagation->segmentSize == 1) {
807 localOrder[propagation->segment[0]] = -2;
808 return 0;
809 }
810
811 // init local order by input order
812 // Note: force orders to be smaller than 0 for in situ updates in
813 // propagation procedures
814 IT nVertices = triangulation->getNumberOfVertices();
815 for(const auto &v : propagation->segment)
816 localOrder[v] = inputOrder[v] - nVertices;
817
818 const IT &extremumIndex = propagation->criticalPoints[0];
819 const IT &saddleIndex = propagation->criticalPoints.back();
820
821 // vector to record boundary
822 std::vector<IT> boundary;
823
824 // make enough room for segment + saddle
825 std::vector<IT> localVertexSequence(propagation->segmentSize + 1);
826
827 int status = 0;
828 bool containsResidualExtrema = true;
829 bool performSuperlevelSetPropagation = true;
830 while(containsResidualExtrema) {
831 propagation->nIterations++;
832
833 if(this->debugLevel_ > 3 && propagation->nIterations == 20)
834 this->printWrn("Unable to converge with less than 20 iterations!");
835
836 // execute superlevel set propagation
838 localOrder, localVertexSequence.data(),
839
840 performSuperlevelSetPropagation, triangulation, segmentation,
841 extremumIndex, boundary, propagation->segment, saddleIndex);
842 if(status)
843 return 1;
844
845 performSuperlevelSetPropagation = !performSuperlevelSetPropagation;
846
847 IT boundaryWriteIdx = 0;
848 IT nResidualMaxima = 0;
849 IT nResidualMinima = 0;
850
851 for(const auto &v : propagation->segment) {
852 bool isOnSegmentBoundary = false;
853 bool hasSmallerNeighbor = false;
854 bool hasLargerNeighbor = false;
855
856 const auto &vOrder = localOrder[v];
857
858 IT nNeighbors = triangulation->getVertexNeighborNumber(v);
859 for(IT n = 0; n < nNeighbors; n++) {
860 IT u{-1};
861 triangulation->getVertexNeighbor(v, n, u);
862
863 if(u == saddleIndex) {
864 hasLargerNeighbor = true;
865 continue;
866 }
867
868 // if u is not inside segment -> v is on segment boundary
869 if(segmentation[u] != extremumIndex) {
870 isOnSegmentBoundary = true;
871 } else {
872 if(localOrder[u] > vOrder)
873 hasLargerNeighbor = true;
874 else
875 hasSmallerNeighbor = true;
876 }
877 }
878
879 if(!hasLargerNeighbor)
880 nResidualMaxima++;
881
882 if(isOnSegmentBoundary) {
883 localVertexSequence[boundaryWriteIdx++] = v;
884 } else if(!hasSmallerNeighbor) {
885 nResidualMinima++;
886 }
887 }
888
889 // perform another iteration iff
890 // (i) there are residual maxima, OR
891 // (ii) there are residual minima and there exists a boundary on which
892 // they can be placed
893 containsResidualExtrema
894 = nResidualMaxima > 0
895 || (nResidualMinima > 0 && boundaryWriteIdx > 0);
896
897 if(containsResidualExtrema && boundary.size() == 0) {
898 boundary.resize(boundaryWriteIdx);
899 for(IT i = 0; i < boundaryWriteIdx; i++) {
900 boundary[i] = localVertexSequence[i];
901 }
902 }
903 }
904
905 return 0;
906 }
907
908 template <typename IT, class TT>
910 IT *localOrder,
911
912 const TT *triangulation,
913 const IT *segmentation,
914 const IT *inputOrder,
915 const std::vector<Propagation<IT> *> &propagations) const {
916 ttk::Timer timer;
917
918 const IT nPropagations = propagations.size();
919 this->printMsg("Computing Local Order of Segments ("
920 + std::to_string(nPropagations) + ")",
922
923 int status = 0;
924#ifdef TTK_ENABLE_OPENMP
925#pragma omp parallel for schedule(dynamic) num_threads(this->threadNumber_)
926#endif // TTK_ENABLE_OPENMP
927 for(IT p = 0; p < nPropagations; p++) {
928 int const localStatus = this->computeLocalOrderOfSegment<IT>(
929 localOrder,
930
931 propagations[p], triangulation, segmentation, inputOrder);
932 if(localStatus)
933 status = 1;
934 }
935 if(status)
936 return 1;
937
938// enforce that saddles have the highest local order
939#ifdef TTK_ENABLE_OPENMP
940#pragma omp parallel for num_threads(this->threadNumber_)
941#endif // TTK_ENABLE_OPENMP
942 for(IT p = 0; p < nPropagations; p++) {
943 localOrder[propagations[p]->criticalPoints.back()]
944 = std::numeric_limits<IT>::max();
945 }
946
947 if(this->debugLevel_ < 4 || nPropagations == 0) {
948 this->printMsg("Computing Local Order of Segments ("
949 + std::to_string(nPropagations) + ")",
950 1, timer.getElapsedTime(), this->threadNumber_);
951 } else {
952 IT min = propagations[0]->nIterations;
953 IT max = min;
954 IT avg = 0;
955
956 for(IT p = 0; p < nPropagations; p++) {
957 const auto propagation = propagations[p];
958 if(min > propagation->nIterations)
959 min = propagation->nIterations;
960 if(max < propagation->nIterations)
961 max = propagation->nIterations;
962 avg += propagation->nIterations;
963 }
964
965 avg /= nPropagations;
966
967 this->printMsg("Computing Local Order of Segments ("
968 + std::to_string(nPropagations) + "|"
969 + std::to_string(min) + "|" + std::to_string(avg)
970 + "|" + std::to_string(max) + ")",
971 1, timer.getElapsedTime(), this->threadNumber_);
972 }
973
974 return 0;
975 }
976
977 template <typename IT>
979 IT *outputOrder,
980 const std::vector<Propagation<IT> *> &parentPropagations) const {
981
982 ttk::Timer timer;
983 this->printMsg("Flattening Order Array", 0, 0, this->threadNumber_,
985
986 const IT nParentPropagations = parentPropagations.size();
987
988// flatten segemnt to order of last encountered saddles
989#ifdef TTK_ENABLE_OPENMP
990#pragma omp parallel for num_threads(this->threadNumber_)
991#endif // TTK_ENABLE_OPENMP
992 for(IT p = 0; p < nParentPropagations; p++) {
993 const auto *propagation = parentPropagations[p];
994 const auto &saddleOrder
995 = outputOrder[propagation->criticalPoints.back()];
996 for(const auto &v : propagation->segment)
997 outputOrder[v] = saddleOrder;
998 }
999
1000 this->printMsg("Flattening Order Array", 1, timer.getElapsedTime(),
1001 this->threadNumber_);
1002
1003 return 0;
1004 }
1005
1006 template <typename DT, typename IT>
1007 int flattenScalars(DT *scalars,
1008 const std::vector<Propagation<IT>> &propagationsA,
1009 const std::vector<Propagation<IT>> &propagationsB
1010 = {}) const {
1011
1012 ttk::Timer timer;
1013 this->printMsg("Flattening Scalar Array", 0, 0, this->threadNumber_,
1015
1016 std::vector<const std::vector<Propagation<IT>> *> const propagationsPair
1017 = {&propagationsA, &propagationsB};
1018
1019 for(const auto propagations : propagationsPair) {
1020 const IT nPropagations = propagations->size();
1021
1022// flatten scalars to saddle value
1023#ifdef TTK_ENABLE_OPENMP
1024#pragma omp parallel for schedule(dynamic) num_threads(this->threadNumber_)
1025#endif // TTK_ENABLE_OPENMP
1026 for(IT p = 0; p < nPropagations; p++) {
1027 const auto propagation = &((*propagations)[p]);
1028
1029 if(!propagation->aborted
1030 && (propagation->parent == propagation
1031 || propagation->parent->aborted)) {
1032 const auto &saddleScalar
1033 = scalars[propagation->criticalPoints.back()];
1034 for(const auto &v : propagation->segment)
1035 scalars[v] = saddleScalar;
1036 }
1037 }
1038 }
1039
1040 this->printMsg("Flattening Scalar Array", 1, timer.getElapsedTime(),
1041 this->threadNumber_);
1042
1043 return 0;
1044 }
1045
1046 template <typename IT>
1048 IT *order,
1049 const IT *localOrder,
1050 std::vector<std::tuple<IT, IT, IT>> &sortedIndices) const {
1051 ttk::Timer timer;
1052
1053 const IT nVertices = sortedIndices.size();
1054
1055// init tuples
1056#ifdef TTK_ENABLE_OPENMP
1057#pragma omp parallel for num_threads(this->threadNumber_)
1058#endif // TTK_ENABLE_OPENMP
1059 for(IT i = 0; i < nVertices; i++) {
1060 auto &t = sortedIndices[i];
1061 std::get<0>(t) = order[i];
1062 std::get<1>(t) = localOrder[i];
1063 std::get<2>(t) = i;
1064 }
1065
1066 this->printMsg("Computing Global Order", 0.2, timer.getElapsedTime(),
1067 this->threadNumber_, debug::LineMode::REPLACE);
1068
1069 TTK_PSORT(
1070 this->threadNumber_, sortedIndices.begin(), sortedIndices.end());
1071
1072 this->printMsg("Computing Global Order", 0.8, timer.getElapsedTime(),
1073 this->threadNumber_, debug::LineMode::REPLACE);
1074
1075#ifdef TTK_ENABLE_OPENMP
1076#pragma omp parallel for num_threads(this->threadNumber_)
1077#endif // TTK_ENABLE_OPENMP
1078 for(IT i = 0; i < nVertices; i++)
1079 order[std::get<2>(sortedIndices[i])] = i;
1080
1081 this->printMsg("Computing Global Order", 1, timer.getElapsedTime(),
1082 this->threadNumber_);
1083
1084 return 0;
1085 }
1086
1087 template <typename DT, typename IT>
1089 DT *scalars,
1090 const std::vector<std::tuple<IT, IT, IT>> &sortedIndices,
1091 const bool descending = false) const {
1092 ttk::Timer timer;
1093 this->printMsg("Applying numerical perturbation", 0, 0,
1095
1096 const IT nVertices = sortedIndices.size();
1097
1098 if(descending)
1099 for(IT i = 1; i < nVertices; i++) {
1100 const IT &v0 = std::get<2>(sortedIndices[i - 1]);
1101 const IT &v1 = std::get<2>(sortedIndices[i]);
1102 if(scalars[v0] >= scalars[v1])
1103 scalars[v1] = boost::math::float_next(scalars[v0]);
1104 }
1105 else
1106 for(IT i = nVertices - 1; i > 0; i--) {
1107 const IT &v0 = std::get<2>(sortedIndices[i]);
1108 const IT &v1 = std::get<2>(sortedIndices[i - 1]);
1109 if(scalars[v0] >= scalars[v1])
1110 scalars[v1] = boost::math::float_next(scalars[v0]);
1111 }
1112
1113 this->printMsg("Applying numerical perturbation", 1,
1114 timer.getElapsedTime(), this->threadNumber_);
1115
1116 return 0;
1117 }
1118
1119 template <typename IT, class TT>
1121 IT *order,
1122 IT *segmentation,
1123 IT *queueMask,
1124 IT *localOrder,
1125 Propagation<IT> **propagationMask,
1126 std::vector<Propagation<IT>> &propagations,
1127 std::vector<std::tuple<IT, IT, IT>> &sortedIndices,
1128
1129 const TT *triangulation,
1130 const IT *authorizedExtremaIndices,
1131 const IT &nAuthorizedExtremaIndices) const {
1132 const IT nVertices = triangulation->getNumberOfVertices();
1133
1134 int status = 0;
1135
1136 // init propagations
1137 status = this->initializeMemory<IT>(segmentation, queueMask, localOrder,
1138 propagationMask,
1139
1140 nVertices);
1141 if(status)
1142 return 1;
1143
1144 // init propagations
1145 status = this->initializePropagations<IT, TT>(
1146 propagations,
1147 queueMask, // use as authorization mask (will be overridden by
1148 // subsequent procedures)
1149 localOrder, // use as maxima buffer (will be overridden by subsequent
1150 // procedures)
1151
1152 authorizedExtremaIndices, nAuthorizedExtremaIndices, order,
1153 triangulation);
1154 if(status)
1155 return 1;
1156
1157 // compute propagations
1158 status = this->computeSimplePropagations<IT, TT>(
1159 propagations, propagationMask, segmentation, queueMask,
1160
1161 triangulation, order);
1162 if(status)
1163 return 1;
1164
1165 // finalize master propagations
1166 std::vector<Propagation<IT> *> parentPropagations;
1167 status
1168 = this->finalizePropagations<IT>(parentPropagations, propagations,
1169
1170 nVertices);
1171 if(status)
1172 return 1;
1173
1174 // compute segments
1175 status = this->computeSegments<IT, TT>(segmentation, parentPropagations,
1176
1177 order, triangulation);
1178 if(status)
1179 return 1;
1180
1181 // compute local order of segments
1183 localOrder,
1184
1185 triangulation, segmentation, order, parentPropagations);
1186 if(status)
1187 return 1;
1188
1189 // flatten order
1190 status = this->flattenOrder<IT>(order, parentPropagations);
1191 if(status)
1192 return 1;
1193
1194 // compute global offsets
1195 status = this->computeGlobalOrder<IT>(order, localOrder, sortedIndices);
1196 if(status)
1197 return 1;
1198
1199 return 0;
1200 }
1201
1202 template <typename IT, typename DT, class TT>
1204 DT *scalars,
1205 IT *order,
1206 IT *segmentation,
1207 IT *queueMask,
1208 IT *localOrder,
1209 Propagation<IT> **propagationMask,
1210 std::vector<Propagation<IT>> &propagations,
1211 std::vector<std::tuple<IT, IT, IT>> &sortedIndices,
1212
1213 const TT *triangulation,
1214 const DT persistenceThreshold) const {
1215
1216 const IT nVertices = triangulation->getNumberOfVertices();
1217
1218 int status = 0;
1219
1220 // init propagations
1221 status = this->initializeMemory<IT>(segmentation, queueMask, localOrder,
1222 propagationMask,
1223
1224 nVertices);
1225 if(status)
1226 return 1;
1227
1228 // init propagations
1229 status = this->initializePropagations<IT, TT>(
1230 propagations,
1231 queueMask, // will be ignored since there are no authorized maxima
1232 localOrder, // use as maxima buffer (will be overridden by subsequent
1233 // procedures)
1234
1235 nullptr, 0, order, triangulation);
1236 if(status)
1237 return 1;
1238
1239 // compute propagations
1241 propagations, propagationMask, segmentation, queueMask,
1242
1243 triangulation, order, scalars, persistenceThreshold);
1244 if(status)
1245 return 1;
1246
1247 // finalize master propagations
1248 std::vector<Propagation<IT> *> parentPropagations;
1249 status
1250 = this->finalizePropagations<IT>(parentPropagations, propagations,
1251
1252 nVertices);
1253 if(status)
1254 return 1;
1255
1256 // compute segments
1257 status = this->computeSegments<IT, TT>(segmentation, parentPropagations,
1258
1259 order, triangulation);
1260 if(status)
1261 return 1;
1262
1263 // compute local order of segments
1265 localOrder,
1266
1267 triangulation, segmentation, order, parentPropagations);
1268 if(status)
1269 return 1;
1270
1271 // flatten order
1272 status = this->flattenOrder<IT>(order, parentPropagations);
1273 if(status)
1274 return 1;
1275
1276 // compute global offsets
1277 status = this->computeGlobalOrder<IT>(order, localOrder, sortedIndices);
1278 if(status)
1279 return 1;
1280
1281 status = this->flattenScalars<DT, IT>(scalars, propagations);
1282 if(status)
1283 return 1;
1284
1285 return 0;
1286 }
1287
1288 template <typename IT>
1289 int invertOrder(IT *outputOrder, const IT &nVertices) const {
1290 ttk::Timer timer;
1291 this->printMsg("Inverting Order", 0, 0, this->threadNumber_,
1293
1294 const auto nVerticesM1 = nVertices - 1;
1295
1296#ifdef TTK_ENABLE_OPENMP
1297#pragma omp parallel for num_threads(this->threadNumber_)
1298#endif // TTK_ENABLE_OPENMP
1299 for(IT v = 0; v < nVertices; v++)
1300 outputOrder[v] = nVerticesM1 - outputOrder[v];
1301
1302 this->printMsg(
1303 "Inverting Order", 1, timer.getElapsedTime(), this->threadNumber_);
1304
1305 return 0;
1306 }
1307
1308 template <typename DT, typename IT, class TT>
1310 IT *order,
1311
1312 const TT *triangulation,
1313 const IT *authorizedExtremaIndices,
1314 const IT &nAuthorizedExtremaIndices,
1315 const bool &computePerturbation) const {
1316 ttk::Timer globalTimer;
1317
1318 IT nVertices = triangulation->getNumberOfVertices();
1319
1320 // Allocating Memory
1321 int status = 0;
1322 std::vector<IT> segmentation;
1323 std::vector<IT> queueMask;
1324 std::vector<IT> localOrder;
1325 std::vector<Propagation<IT> *> propagationMask;
1326 std::vector<Propagation<IT>> propagationsMax;
1327 std::vector<Propagation<IT>> propagationsMin;
1328 std::vector<std::tuple<IT, IT, IT>> sortedIndices;
1329
1330 this->allocateMemory<IT>(segmentation, queueMask, localOrder,
1331 propagationMask, sortedIndices,
1332
1333 nVertices);
1334
1335 bool hasMinima{false};
1336 bool hasMaxima{false};
1337 for(IT i = 0; i < nAuthorizedExtremaIndices; i++) {
1338 const auto &extremum{authorizedExtremaIndices[i]};
1339 const auto nNeigh{triangulation->getVertexNeighborNumber(extremum)};
1340 if(nNeigh > 0) {
1341 // look at the first neighbor to determine if minimum or maximum
1342 SimplexId neigh{};
1343 triangulation->getVertexNeighbor(extremum, 0, neigh);
1344 if(order[extremum] > order[neigh]) {
1345 hasMaxima = true;
1346 } else {
1347 hasMinima = true;
1348 }
1349 }
1350 // don't look further if we have both minima and maxima
1351 if(hasMaxima && hasMinima) {
1352 break;
1353 }
1354 }
1355
1356 // Maxima
1357 if(hasMaxima) {
1358 this->printMsg("----------- [Removing Unauthorized Maxima]",
1360
1362 order, segmentation.data(), queueMask.data(), localOrder.data(),
1363 propagationMask.data(), propagationsMax, sortedIndices,
1364
1365 triangulation, authorizedExtremaIndices, nAuthorizedExtremaIndices);
1366 if(status)
1367 return 1;
1368 }
1369
1370 // Minima
1371 if(hasMinima) {
1372 this->printMsg("----------- [Removing Unauthorized Minima]",
1374
1375 // invert order
1376 if(this->invertOrder(order, nVertices))
1377 return 1;
1378
1380 order, segmentation.data(), queueMask.data(), localOrder.data(),
1381 propagationMask.data(), propagationsMin, sortedIndices,
1382
1383 triangulation, authorizedExtremaIndices, nAuthorizedExtremaIndices);
1384 if(status)
1385 return 1;
1386
1387 // revert order
1388 if(this->invertOrder(order, nVertices))
1389 return 1;
1390 }
1391
1392 // flatten scalars
1393 status = this->flattenScalars<DT, IT>(
1394 scalars, propagationsMax, propagationsMin);
1395 if(status)
1396 return 1;
1397
1398 // optionally compute perturbation
1399 if(computePerturbation) {
1402 scalars, sortedIndices);
1403 if(status)
1404 return 1;
1405 }
1406
1408 this->printMsg(
1409 "Complete", 1, globalTimer.getElapsedTime(), this->threadNumber_);
1410
1412
1413 return 0;
1414 }
1415
1416 template <typename DT, typename IT, class TT>
1418 IT *order,
1419
1420 const TT *triangulation,
1421 const DT persistenceThreshold,
1422 const bool &computePerturbation,
1423 const PAIR_TYPE &pairType
1425 ttk::Timer globalTimer;
1426
1427 IT nVertices = triangulation->getNumberOfVertices();
1428
1429 // Allocating Memory
1430 int status = 0;
1431 std::vector<IT> segmentation;
1432 std::vector<IT> queueMask;
1433 std::vector<IT> localOrder;
1434 std::vector<Propagation<IT> *> propagationMask;
1435 std::vector<Propagation<IT>> propagationsMax;
1436 std::vector<Propagation<IT>> propagationsMin;
1437 std::vector<std::tuple<IT, IT, IT>> sortedIndices;
1438
1439 this->allocateMemory<IT>(segmentation, queueMask, localOrder,
1440 propagationMask, sortedIndices,
1441
1442 nVertices);
1443
1444 // Maxima
1445 if(pairType == PAIR_TYPE::EXTREMUM_SADDLE
1446 || pairType == PAIR_TYPE::MAXIMUM_SADDLE) {
1447 this->printMsg("----------- [Removing Non-Persistent Maxima]",
1449
1451 scalars, order, segmentation.data(), queueMask.data(),
1452 localOrder.data(), propagationMask.data(), propagationsMax,
1453 sortedIndices,
1454
1455 triangulation, persistenceThreshold);
1456 if(status)
1457 return 1;
1458 }
1459
1460 // Minima
1461 if(pairType == PAIR_TYPE::EXTREMUM_SADDLE
1462 || pairType == PAIR_TYPE::MINIMUM_SADDLE) {
1463 this->printMsg("----------- [Removing Non-Persistent Minima]",
1465
1466 if(this->invertOrder(order, nVertices))
1467 return 1;
1468
1470 scalars, order, segmentation.data(), queueMask.data(),
1471 localOrder.data(), propagationMask.data(), propagationsMin,
1472 sortedIndices,
1473
1474 triangulation, persistenceThreshold);
1475 if(status)
1476 return 1;
1477
1478 if(this->invertOrder(order, nVertices))
1479 return 1;
1480 }
1481
1482 // optionally compute perturbation
1483 if(computePerturbation) {
1486 scalars, sortedIndices, pairType == PAIR_TYPE::MAXIMUM_SADDLE);
1487 if(status)
1488 return 1;
1489 }
1490
1492 this->printMsg(
1493 "Complete", 1, globalTimer.getElapsedTime(), this->threadNumber_);
1494
1496
1497 return 0;
1498 }
1499
1500 }; // class
1501 } // namespace lts
1502} // namespace ttk
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
Definition OpenMP.h:46
AbstractTriangulation is an interface class that defines an interface for efficient traversal methods...
int debugLevel_
Definition Debug.h:379
int printWrn(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:159
void setDebugMsgPrefix(const std::string &prefix)
Definition Debug.h:364
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
double getElapsedTime()
Definition Timer.h:15
int flattenScalars(DT *scalars, const std::vector< Propagation< IT > > &propagationsA, const std::vector< Propagation< IT > > &propagationsB={}) const
int computeLocalOrderOfSegment(IT *localOrder, const Propagation< IT > *propagation, const TT *triangulation, const IT *segmentation, const IT *inputOrder) const
int computeSimplePropagations(std::vector< Propagation< IT > > &propagations, Propagation< IT > **propagationMask, IT *segmentation, IT *queueMask, const TT *triangulation, const IT *inputOrder) const
int computeSimplePropagation(Propagation< IT > &propagation, Propagation< IT > **propagationMask, IT *segmentation, IT *queueMask, const TT *triangulation, const IT *order) const
int computeSegments(IT *segmentation, std::vector< Propagation< IT > * > &propagations, const IT *order, const TT *triangulation) const
This method computes the segments of a given list of propagations.
int detectAndRemoveNonPersistentMaxima(DT *scalars, IT *order, IT *segmentation, IT *queueMask, IT *localOrder, Propagation< IT > **propagationMask, std::vector< Propagation< IT > > &propagations, std::vector< std::tuple< IT, IT, IT > > &sortedIndices, const TT *triangulation, const DT persistenceThreshold) const
int computeNumericalPerturbation(DT *scalars, const std::vector< std::tuple< IT, IT, IT > > &sortedIndices, const bool descending=false) const
int initializePropagations(std::vector< Propagation< IT > > &propagations, IT *authorizationMask, IT *maximaBuffer, const IT *authorizedExtremaIndices, const IT &nAuthorizedExtremaIndices, const IT *order, const TT *triangulation) const
int computeLocalOrderOfSegments(IT *localOrder, const TT *triangulation, const IT *segmentation, const IT *inputOrder, const std::vector< Propagation< IT > * > &propagations) const
int initializeMemory(IT *segmentation, IT *queueMask, IT *localOrder, Propagation< IT > **propagationMask, const IT &nVertices) const
int computePersistenceSensitivePropagations(std::vector< Propagation< IT > > &propagations, Propagation< IT > **propagationMask, IT *segmentation, IT *queueMask, const TT *triangulation, const IT *order, const DT *scalars, const DT persistenceThreshold) const
int finalizePropagations(std::vector< Propagation< IT > * > &parentPropagations, std::vector< Propagation< IT > > &propagations, const IT nVertices) const
int computeSegment(IT *segmentation, Propagation< IT > *propagation, const IT *order, const TT *triangulation) const
int allocateMemory(std::vector< IT > &segmentation, std::vector< IT > &queueMask, std::vector< IT > &localOrder, std::vector< Propagation< IT > * > &propagationMask, std::vector< std::tuple< IT, IT, IT > > &sortedIndices, const IT &nVertices) const
~LocalizedTopologicalSimplification() override=default
int computeGlobalOrder(IT *order, const IT *localOrder, std::vector< std::tuple< IT, IT, IT > > &sortedIndices) const
int flattenOrder(IT *outputOrder, const std::vector< Propagation< IT > * > &parentPropagations) const
int computePersistenceSensitivePropagation(Propagation< IT > &propagation, Propagation< IT > **propagationMask, IT *segmentation, IT *queueMask, const TT *triangulation, const IT *order, const DT *scalars, const DT persistenceThreshold) const
int preconditionTriangulation(ttk::AbstractTriangulation *triangulation) const
int detectAndRemoveUnauthorizedMaxima(IT *order, IT *segmentation, IT *queueMask, IT *localOrder, Propagation< IT > **propagationMask, std::vector< Propagation< IT > > &propagations, std::vector< std::tuple< IT, IT, IT > > &sortedIndices, const TT *triangulation, const IT *authorizedExtremaIndices, const IT &nAuthorizedExtremaIndices) const
int removeUnauthorizedExtrema(DT *scalars, IT *order, const TT *triangulation, const IT *authorizedExtremaIndices, const IT &nAuthorizedExtremaIndices, const bool &computePerturbation) const
int invertOrder(IT *outputOrder, const IT &nVertices) const
int computeLocalOrderOfSegmentIteration(IT *localOrder, IT *localVertexSequence, const bool &performSuperlevelSetPropagation, const TT *triangulation, const IT *segmentation, const IT &segmentId, const std::vector< IT > &boundary, const std::vector< IT > &segment, const IT &saddleIdx) const
int removeNonPersistentExtrema(DT *scalars, IT *order, const TT *triangulation, const DT persistenceThreshold, const bool &computePerturbation, const PAIR_TYPE &pairType=PAIR_TYPE::EXTREMUM_SADDLE) const
std::string toFixed(const float &number, const int precision=2)
TTK base package defining the standard types.
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
Superlevel Set Component Propagation.
Definition Propagation.h:11
std::vector< IT > criticalPoints
Definition Propagation.h:20
std::vector< IT > segment
Definition Propagation.h:23
Propagation * find()
Definition Propagation.h:29
static int unify(Propagation< IT > *p0, Propagation< IT > *p1)
Definition Propagation.h:42
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)