TTK
Loading...
Searching...
No Matches
ScalarFieldCriticalPoints.h
Go to the documentation of this file.
1
46
47#pragma once
48
49#include <map>
50
51// base code includes
52#include <ProgressiveTopology.h>
53#include <Triangulation.h>
54#include <UnionFind.h>
55
56namespace ttk {
57
58 class ScalarFieldCriticalPoints : public virtual Debug {
59
60 public:
62
63 enum class BACKEND { GENERIC = 0, PROGRESSIVE_TOPOLOGY = 1 };
64
78 template <class triangulationType = AbstractTriangulation>
79 int execute(const SimplexId *const offsets,
80 const triangulationType *triangulation);
81
82 template <class triangulationType = AbstractTriangulation>
83 int executeLegacy(const SimplexId *const offsets,
84 const triangulationType *triangulation);
85
86 template <class triangulationType = AbstractTriangulation>
87 int executeProgressive(const SimplexId *const offsets,
88 const triangulationType *triangulation);
89
90 template <class triangulationType>
91 void checkProgressivityRequirement(const triangulationType *triangulation);
92
93 template <class triangulationType = AbstractTriangulation>
95 const SimplexId vertexId,
96 const SimplexId *const offsets,
97 const triangulationType *triangulation,
98 bool &isLowerOnBoundary,
99 bool &isUpperOnBoundary,
100 std::vector<std::vector<ttk::SimplexId>> *upperComponents,
101 std::vector<std::vector<ttk::SimplexId>> *lowerComponents) const;
102
103 template <class triangulationType = AbstractTriangulation>
104 char
105 getCriticalType(const SimplexId &vertexId,
106 const SimplexId *const offsets,
107 const triangulationType *triangulation,
108 std::vector<std::vector<ttk::SimplexId>> *upperComponents
109 = nullptr,
110 std::vector<std::vector<ttk::SimplexId>> *lowerComponents
111 = nullptr) const;
112
113 char getCriticalType(const SimplexId &vertexId,
114 const SimplexId *const offsets,
115 const std::vector<std::pair<SimplexId, SimplexId>>
116 &vertexLinkEdgeList) const;
117
118 inline void setDomainDimension(const int &dimension) {
119 dimension_ = dimension;
120 }
121
122 inline void
123 setOutput(std::vector<std::pair<SimplexId, char>> *criticalPoints) {
124 criticalPoints_ = criticalPoints;
125 }
126
127 inline void
129 // pre-condition functions
130 if(triangulation) {
131 triangulation->preconditionVertexNeighbors();
132 triangulation->preconditionVertexStars();
133 triangulation->preconditionBoundaryVertices();
134 }
135
136 setDomainDimension(triangulation->getDimensionality());
137 setVertexNumber(triangulation->getNumberOfVertices());
138 }
139
141 const std::vector<std::vector<std::pair<SimplexId, SimplexId>>>
142 *edgeList) {
143 vertexLinkEdgeLists_ = edgeList;
144 }
145
149 int setVertexNumber(const SimplexId &vertexNumber) {
150 vertexNumber_ = vertexNumber;
151 return 0;
152 }
153
154 void setNonManifold(const bool b) {
156 }
157
158 void displayStats();
159
160 protected:
163 const std::vector<std::vector<std::pair<SimplexId, SimplexId>>>
165 std::vector<std::pair<SimplexId, char>> *criticalPoints_{};
166
168
169 // progressive
174 bool IsResumable{false};
175 double TimeLimit{};
176 };
177} // namespace ttk
178
179// template functions
180template <class triangulationType>
182 const SimplexId *const offsets, const triangulationType *triangulation) {
183
184 checkProgressivityRequirement(triangulation);
185
186 switch(BackEnd) {
187
189 this->executeProgressive(offsets, triangulation);
190 break;
191
192 case BACKEND::GENERIC:
193 this->executeLegacy(offsets, triangulation);
194 break;
195
196 default:
197 printErr("No method was selected");
198 }
199
201 return 0;
202}
203
204template <class triangulationType>
206 const SimplexId *const offsets, const triangulationType *triangulation) {
207
208 progT_.setDebugLevel(debugLevel_);
209 progT_.setThreadNumber(threadNumber_);
210 progT_.setupTriangulation(const_cast<ttk::ImplicitTriangulation *>(
211 (const ImplicitTriangulation *)triangulation));
212 progT_.setStartingResolutionLevel(StartingResolutionLevel);
213 progT_.setStoppingResolutionLevel(StoppingResolutionLevel);
214 progT_.setTimeLimit(TimeLimit);
215 progT_.setIsResumable(IsResumable);
216 progT_.setPreallocateMemory(true);
217
218 progT_.computeProgressiveCP(criticalPoints_, offsets);
219
220 displayStats();
221 return 0;
222}
223
224template <class triangulationType>
226 const SimplexId *const offsets, const triangulationType *triangulation) {
227
228 // check the consistency of the variables -- to adapt
229#ifndef TTK_ENABLE_KAMIKAZE
230 if((!dimension_) && ((!triangulation) || (triangulation->isEmpty())))
231 return -1;
232 if((!vertexNumber_) && ((!triangulation) || (triangulation->isEmpty())))
233 return -2;
234 if((!vertexLinkEdgeLists_) && (!triangulation))
235 return -4;
236 if(!criticalPoints_)
237 return -5;
238#endif
239
240 if(triangulation) {
241 vertexNumber_ = triangulation->getNumberOfVertices();
242 dimension_ = triangulation->getCellVertexNumber(0) - 1;
243 }
244
245 printMsg("Extracting critical points...");
246
247 Timer t;
248
249 std::vector<char> vertexTypes(vertexNumber_, (char)(CriticalType::Regular));
250
251#ifdef TTK_ENABLE_OPENMP
252 int const chunkSize
253 = std::max(1000, (int)vertexNumber_ / (threadNumber_ * 100));
254#endif
255
256 if(triangulation) {
257
258#ifdef TTK_ENABLE_OPENMP
259#pragma omp parallel for schedule(dynamic, chunkSize) num_threads(threadNumber_)
260#endif
261 for(SimplexId i = 0; i < (SimplexId)vertexNumber_; i++) {
262#ifdef TTK_ENABLE_MPI
263 if(!isRunningWithMPI()
264 || (isRunningWithMPI()
265 && (triangulation->getVertexRank(i) == ttk::MPIrank_)))
266#endif // TTK_ENABLE_MPI
267 {
268 vertexTypes[i] = getCriticalType(i, offsets, triangulation);
269 }
270 }
271 } else if(vertexLinkEdgeLists_) {
272 // legacy implementation
273#ifdef TTK_ENABLE_OPENMP
274#pragma omp parallel for schedule(dynamic, chunkSize) num_threads(threadNumber_)
275#endif
276 for(SimplexId i = 0; i < (SimplexId)vertexNumber_; i++) {
277 // can't use MPI there since triangulation (~> rankArray) is nullptr
278 vertexTypes[i] = getCriticalType(i, offsets, (*vertexLinkEdgeLists_)[i]);
279 }
280 }
281
282 SimplexId minimumNumber = 0, maximumNumber = 0, saddleNumber = 0,
283 oneSaddleNumber = 0, twoSaddleNumber = 0, monkeySaddleNumber = 0;
284
285 // debug msg
287 if(dimension_ == 3) {
288 for(SimplexId i = 0; i < vertexNumber_; i++) {
289 switch(vertexTypes[i]) {
290
291 case(char)(CriticalType::Local_minimum):
292 minimumNumber++;
293 break;
294
295 case(char)(CriticalType::Saddle1):
296 oneSaddleNumber++;
297 break;
298
299 case(char)(CriticalType::Saddle2):
300 twoSaddleNumber++;
301 break;
302
303 case(char)(CriticalType::Local_maximum):
304 maximumNumber++;
305 break;
306
307 case(char)(CriticalType::Degenerate):
308 monkeySaddleNumber++;
309 break;
310 }
311 }
312 } else if(dimension_ == 2) {
313 for(SimplexId i = 0; i < vertexNumber_; i++) {
314 switch(vertexTypes[i]) {
315
316 case(char)(CriticalType::Local_minimum):
317 minimumNumber++;
318 break;
319
320 case(char)(CriticalType::Saddle1):
321 saddleNumber++;
322 break;
323
324 case(char)(CriticalType::Local_maximum):
325 maximumNumber++;
326 break;
327
328 case(char)(CriticalType::Degenerate):
329 monkeySaddleNumber++;
330 break;
331 }
332 }
333 }
334
335 {
336 std::vector<std::vector<std::string>> stats;
337 stats.push_back({" #Minima", std::to_string(minimumNumber)});
338 if(dimension_ == 3) {
339 stats.push_back({" #1-saddles", std::to_string(oneSaddleNumber)});
340 stats.push_back({" #2-saddles", std::to_string(twoSaddleNumber)});
341 }
342 if(dimension_ == 2) {
343 stats.push_back({" #Saddles", std::to_string(saddleNumber)});
344 }
345 stats.push_back({" #Multi-saddles", std::to_string(monkeySaddleNumber)});
346 stats.push_back({" #Maxima", std::to_string(maximumNumber)});
347
348 printMsg(stats);
349 }
350 }
351
352 // prepare the output
353 criticalPoints_->clear();
355 for(SimplexId i = 0; i < vertexNumber_; i++) {
356 if(vertexTypes[i] != (char)(CriticalType::Regular)) {
357 criticalPoints_->emplace_back(i, vertexTypes[i]);
358 }
359 }
360
361 printMsg("Processed " + std::to_string(vertexNumber_) + " vertices", 1,
363
364 return 0;
365}
366
367template <class triangulationType>
369 const SimplexId vertexId,
370 const SimplexId *const offsets,
371 const triangulationType *triangulation,
372 bool &isLowerOnBoundary,
373 bool &isUpperOnBoundary,
374 std::vector<std::vector<ttk::SimplexId>> *upperComponents,
375 std::vector<std::vector<ttk::SimplexId>> *lowerComponents) const {
376
377 SimplexId const neighborNumber
378 = triangulation->getVertexNeighborNumber(vertexId);
379 std::vector<SimplexId> lowerNeighbors, upperNeighbors;
380
381 for(SimplexId i = 0; i < neighborNumber; i++) {
382 SimplexId neighborId = 0;
383 triangulation->getVertexNeighbor(vertexId, i, neighborId);
384
385 if(offsets[neighborId] < offsets[vertexId]) {
386 lowerNeighbors.push_back(neighborId);
387 if(dimension_ == 3) {
388 if(triangulation->isVertexOnBoundary(neighborId))
389 isLowerOnBoundary = true;
390 }
391 }
392
393 // upper link
394 if(offsets[neighborId] > offsets[vertexId]) {
395 upperNeighbors.push_back(neighborId);
396 if(dimension_ == 3) {
397 if(triangulation->isVertexOnBoundary(neighborId))
398 isUpperOnBoundary = true;
399 }
400 }
401 }
402
403 // now do the actual work
404 std::vector<UnionFind> lowerSeeds(lowerNeighbors.size());
405 std::vector<UnionFind *> lowerList(lowerNeighbors.size());
406 std::vector<UnionFind> upperSeeds(upperNeighbors.size());
407 std::vector<UnionFind *> upperList(upperNeighbors.size());
408
409 for(SimplexId i = 0; i < (SimplexId)lowerSeeds.size(); i++) {
410 lowerList[i] = &(lowerSeeds[i]);
411 }
412 for(SimplexId i = 0; i < (SimplexId)upperSeeds.size(); i++) {
413 upperList[i] = &(upperSeeds[i]);
414 }
415
416 SimplexId const vertexStarSize = triangulation->getVertexStarNumber(vertexId);
417
418 for(SimplexId i = 0; i < vertexStarSize; i++) {
419 SimplexId cellId = 0;
420 triangulation->getVertexStar(vertexId, i, cellId);
421
422 SimplexId const cellSize = triangulation->getCellVertexNumber(cellId);
423 for(SimplexId j = 0; j < cellSize; j++) {
424 SimplexId neighborId0 = -1;
425 triangulation->getCellVertex(cellId, j, neighborId0);
426
427 if(neighborId0 != vertexId) {
428 // we are on the link
429
430 bool const lower0 = offsets[neighborId0] < offsets[vertexId];
431
432 // connect it to everybody except himself and vertexId
433 for(SimplexId k = j + 1; k < cellSize; k++) {
434
435 SimplexId neighborId1 = -1;
436 triangulation->getCellVertex(cellId, k, neighborId1);
437
438 if((neighborId1 != neighborId0) && (neighborId1 != vertexId)) {
439
440 bool const lower1 = offsets[neighborId1] < offsets[vertexId];
441
442 std::vector<SimplexId> *neighbors = &lowerNeighbors;
443 std::vector<UnionFind *> *seeds = &lowerList;
444
445 if(!lower0) {
446 neighbors = &upperNeighbors;
447 seeds = &upperList;
448 }
449
450 if(lower0 == lower1) {
451 // connect their union-find sets!
452 SimplexId lowerId0 = -1, lowerId1 = -1;
453 for(SimplexId l = 0; l < (SimplexId)neighbors->size(); l++) {
454 if((*neighbors)[l] == neighborId0) {
455 lowerId0 = l;
456 }
457 if((*neighbors)[l] == neighborId1) {
458 lowerId1 = l;
459 }
460 }
461 if((lowerId0 != -1) && (lowerId1 != -1)) {
462 (*seeds)[lowerId0] = UnionFind::makeUnion(
463 (*seeds)[lowerId0], (*seeds)[lowerId1]);
464 (*seeds)[lowerId1] = (*seeds)[lowerId0];
465 }
466 }
467 }
468 }
469 }
470 }
471 }
472
473 // let's remove duplicates now
474
475 // update the UF if necessary
476 for(SimplexId i = 0; i < (SimplexId)lowerList.size(); i++)
477 lowerList[i] = lowerList[i]->find();
478 for(SimplexId i = 0; i < (SimplexId)upperList.size(); i++)
479 upperList[i] = upperList[i]->find();
480
481 std::unordered_map<UnionFind *, std::vector<ttk::SimplexId>>::iterator it;
482 std::unordered_map<UnionFind *, std::vector<ttk::SimplexId>>
483 upperComponentId{};
484 std::unordered_map<UnionFind *, std::vector<ttk::SimplexId>>
485 lowerComponentId{};
486
487 // We retrieve the lower and upper components if we want them
488 for(ttk::SimplexId i = 0; i < (SimplexId)upperNeighbors.size(); i++) {
489 it = upperComponentId.find(upperList[i]);
490 if(it != upperComponentId.end()) {
491 upperComponentId[upperList[i]].push_back(upperNeighbors[i]);
492 } else {
493 upperComponentId[upperList[i]]
494 = std::vector<ttk::SimplexId>(1, upperNeighbors[i]);
495 }
496 }
497 for(auto elt : upperComponentId) {
498 upperComponents->push_back(std::vector<ttk::SimplexId>());
499 for(ttk::SimplexId i = 0; i < (ttk::SimplexId)elt.second.size(); i++) {
500 upperComponents->back().push_back(elt.second.at(i));
501 }
502 }
503
504 for(ttk::SimplexId i = 0; i < (SimplexId)lowerNeighbors.size(); i++) {
505 it = lowerComponentId.find(lowerList[i]);
506 if(it != lowerComponentId.end()) {
507 lowerComponentId[lowerList[i]].push_back(lowerNeighbors[i]);
508 } else {
509 lowerComponentId[lowerList[i]]
510 = std::vector<ttk::SimplexId>(1, lowerNeighbors[i]);
511 }
512 }
513 for(auto elt : lowerComponentId) {
514 lowerComponents->push_back(std::vector<ttk::SimplexId>());
515 for(ttk::SimplexId i = 0; i < (ttk::SimplexId)elt.second.size(); i++) {
516 lowerComponents->back().push_back(elt.second.at(i));
517 }
518 }
519
521 printMsg("Vertex #" + std::to_string(vertexId)
522 + ": lowerLink-#CC=" + std::to_string(lowerComponentId.size())
523 + " upperLink-#CC=" + std::to_string(upperComponentId.size()),
525 }
526
527 return 0;
528}
529
530template <class triangulationType>
532 const SimplexId &vertexId,
533 const SimplexId *const offsets,
534 const triangulationType *triangulation,
535 std::vector<std::vector<ttk::SimplexId>> *upperComponents,
536 std::vector<std::vector<ttk::SimplexId>> *lowerComponents) const {
537
538 bool isLowerOnBoundary = false, isUpperOnBoundary = false;
539 std::vector<std::vector<ttk::SimplexId>> localUpperComponents;
540 std::vector<std::vector<ttk::SimplexId>> localLowerComponents;
541 if(upperComponents == nullptr) {
542 upperComponents = &localUpperComponents;
543 }
544 if(lowerComponents == nullptr) {
545 lowerComponents = &localLowerComponents;
546 }
547 getLowerUpperComponents(vertexId, offsets, triangulation, isLowerOnBoundary,
548 isUpperOnBoundary, upperComponents, lowerComponents);
549 ttk::SimplexId const lowerComponentNumber = lowerComponents->size();
550 ttk::SimplexId const upperComponentNumber = upperComponents->size();
551
552 if(dimension_ == 1) {
553 if(lowerComponentNumber == 0 && upperComponentNumber != 0) {
554 return (char)(CriticalType::Local_minimum);
555 } else if(lowerComponentNumber != 0 && upperComponentNumber == 0) {
556 return (char)(CriticalType::Local_maximum);
557 } else if(lowerComponentNumber == 1 && upperComponentNumber == 1) {
558 return (char)(CriticalType::Regular);
559 }
560 return (char)(CriticalType::Saddle1);
561 }
562
563 if(lowerComponentNumber == 0 && upperComponentNumber == 1) {
564 return (char)(CriticalType::Local_minimum);
565 } else if(lowerComponentNumber == 1 && upperComponentNumber == 0) {
566 return (char)(CriticalType::Local_maximum);
567 } else if(lowerComponentNumber == 1 && upperComponentNumber == 1) {
568
569 if((dimension_ == 3) && (triangulation->isVertexOnBoundary(vertexId))) {
570 // special case of boundary saddles
571 if((isUpperOnBoundary) && (!isLowerOnBoundary))
572 return (char)(CriticalType::Saddle1);
573 if((!isUpperOnBoundary) && (isLowerOnBoundary))
574 return (char)(CriticalType::Saddle2);
575 }
576
577 // regular point
578 return (char)(CriticalType::Regular);
579 } else {
580 // saddles
581 if(dimension_ == 2 || dimension_ == 1) {
582 if((lowerComponentNumber == 2 && upperComponentNumber == 1)
583 || (lowerComponentNumber == 1 && upperComponentNumber == 2)
584 || (lowerComponentNumber == 2 && upperComponentNumber == 2)) {
585 // regular saddle
586 return (char)(CriticalType::Saddle1);
587 } else {
588 // monkey saddle, saddle + extremum
589 return (char)(CriticalType::Degenerate);
590 // NOTE: you may have multi-saddles on the boundary in that
591 // configuration
592 // to make this computation 100% correct, one would need to
593 // disambiguate boundary from interior vertices
594 }
595 } else if(dimension_ == 3) {
596 if(lowerComponentNumber == 2 && upperComponentNumber == 1) {
597 return (char)(CriticalType::Saddle1);
598 } else if(lowerComponentNumber == 1 && upperComponentNumber == 2) {
599 return (char)(CriticalType::Saddle2);
600 } else {
601 // monkey saddle, saddle + extremum
602 return (char)(CriticalType::Degenerate);
603 // NOTE: we may have a similar effect in 3D (TODO)
604 }
605 }
606 }
607
608 // -2: regular points
609 return (char)(CriticalType::Regular);
610}
611
612template <class triangulationType>
614 const triangulationType *ttkNotUsed(triangulation)) {
616 && !std::is_same<ttk::ImplicitWithPreconditions, triangulationType>::value
617 && !std::is_same<ttk::ImplicitNoPreconditions, triangulationType>::value) {
618
620 printWrn("Explicit, Compact or Periodic");
621 printWrn("triangulation detected.");
622 printWrn("Defaulting to the generic backend.");
624
626 }
627
628#ifdef TTK_ENABLE_MPI
629 if((ttk::hasInitializedMPI()) && (ttk::isRunningWithMPI())) {
631 printWrn("MPI run detected.");
632 printWrn("Defaulting to the generic backend.");
634
636 }
637#endif // TTK_ENABLE_MPI
638}
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
Definition BaseClass.h:47
AbstractTriangulation is an interface class that defines an interface for efficient traversal methods...
virtual SimplexId getNumberOfVertices() const
virtual int getDimensionality() const
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
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
ImplicitTriangulation is a class that provides time and memory efficient traversal methods on triangu...
TTK processing package for progressive Topological Data Analysis.
void checkProgressivityRequirement(const triangulationType *triangulation)
int executeProgressive(const SimplexId *const offsets, const triangulationType *triangulation)
void preconditionTriangulation(AbstractTriangulation *triangulation)
void setVertexLinkEdgeLists(const std::vector< std::vector< std::pair< SimplexId, SimplexId > > > *edgeList)
int setVertexNumber(const SimplexId &vertexNumber)
const std::vector< std::vector< std::pair< SimplexId, SimplexId > > > * vertexLinkEdgeLists_
void setOutput(std::vector< std::pair< SimplexId, char > > *criticalPoints)
int execute(const SimplexId *const offsets, const triangulationType *triangulation)
int executeLegacy(const SimplexId *const offsets, const triangulationType *triangulation)
char getCriticalType(const SimplexId &vertexId, const SimplexId *const offsets, const triangulationType *triangulation, std::vector< std::vector< ttk::SimplexId > > *upperComponents=nullptr, std::vector< std::vector< ttk::SimplexId > > *lowerComponents=nullptr) const
void setDomainDimension(const int &dimension)
int getLowerUpperComponents(const SimplexId vertexId, const SimplexId *const offsets, const triangulationType *triangulation, bool &isLowerOnBoundary, bool &isUpperOnBoundary, std::vector< std::vector< ttk::SimplexId > > *upperComponents, std::vector< std::vector< ttk::SimplexId > > *lowerComponents) const
std::vector< std::pair< SimplexId, char > > * criticalPoints_
double getElapsedTime()
Definition Timer.h:15
static UnionFind * makeUnion(UnionFind *uf0, UnionFind *uf1)
Definition UnionFind.h:40
TTK base package defining the standard types.
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
COMMON_EXPORTS int MPIrank_
Definition BaseClass.cpp:9
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)