TTK
Loading...
Searching...
No Matches
VectorSimplification.h
Go to the documentation of this file.
1
33
34#pragma once
35
36#include <DiscreteVectorField.h>
37
38using ttk::dcg::Cell;
39
40#include <algorithm>
41#include <numeric>
42
43namespace ttk {
44 class VectorSimplification : virtual public Debug {
45 public:
47
48 /***
49 * @brief The discrete vector field this class will be building and
50 * simplifing Can be used to access information about the simplified field.
51 */
53
72 bool generateOrbit{false};
73
75 int type;
79 float weight;
80
81 CandidatePair(SimplexId b, Cell next, SimplexId d, int t, float w)
82 : birth{b}, nextCell{next}, death{d}, type{t}, weight{w} {
83 }
84 };
85
86 struct PlotPoint {
87 /* Number of Critical points remaining*/
89 /* Number of Sink(dim=0) Critical points remaining*/
91 /* Number of Source(dim=dim)Critical points remaining*/
93 /* Number of Orbits generated so far */
95 /* Number of Orbits removed so far */
97 /* Weight Threshold */
98 double weight;
99
100 PlotPoint(SimplexId num, SimplexId sinks, SimplexId sources, double w)
101 : numCP{num}, numSinks{sinks}, numSources{sources}, weight{w} {
102 }
103 };
104
106 this->dcvf_.preconditionTriangulation(data);
107 }
108
109 template <typename dataType, typename triangulationType>
110 inline int buildField(const void *const vectors,
111 const size_t vectorsMTime,
112 const triangulationType &triangulation) {
113 this->dcvf_.setDebugLevel(this->debugLevel_);
114 this->dcvf_.setThreadNumber(this->threadNumber_);
115 this->dcvf_.setInputVectorField(vectors, vectorsMTime);
116 return this->dcvf_.buildField<dataType, triangulationType>(triangulation);
117 }
118
119 template <typename dataType, typename triangulationType>
120 inline int performSimplification(const int criticalThreshold,
121 const bool storePlotPoints,
122 std::vector<PlotPoint> &listOfPlotPoints,
123 const triangulationType &triangulation) {
124 Timer tm{}; // Time the entire simplification
125 // Extract Critical Simplices
126 std::array<std::vector<SimplexId>, 4> criticalCellsByDim;
127 this->dcvf_.getCriticalPoints(criticalCellsByDim, triangulation);
128 int dim = this->dcvf_.getDimensionality();
129 int numCriticalPoints = criticalCellsByDim[0].size()
130 + criticalCellsByDim[1].size()
131 + criticalCellsByDim[2].size();
132 int numSinks = criticalCellsByDim[0].size();
133 int numSources = criticalCellsByDim[2].size();
134 int orbitsAdded{0}, orbitsRemoved{0};
135 double weightThres{-1000000};
136 if(numCriticalPoints < criticalThreshold)
137 return 0;
138 // Trace the saddles and track the candidate pairs
139 std::vector<CandidatePair> pairs;
140 if(dim == 2) {
141 // saddle downward pairs
143 pairs, criticalCellsByDim[1], triangulation);
144 // saddle upward pairs
146 pairs, criticalCellsByDim[dim - 1], triangulation);
147 } else if(dim == 3) {
148 this->printErr("This filter can not simplify for 3D yet.");
149 }
150 // Sort the candidate pairs to remove the smallest ones first and update
151 // the paths as needed
152 if(dim == 2) {
153 const auto orderPairs
154 = [&](const CandidatePair &a, const CandidatePair &b) -> bool {
155 return a.weight > b.weight;
156 };
157
158 // Type alias for the priority queue
159 using pqType
160 = std::priority_queue<CandidatePair, std::vector<CandidatePair>,
161 decltype(orderPairs)>;
162 pqType options{orderPairs};
163 for(auto pair : pairs) {
164 options.push(pair);
165 }
166 pairs.clear();
167 // Simplify based on smallest options
168 while(!options.empty()
169 and numCriticalPoints - 2
170 >= static_cast<int>(criticalThreshold)) {
171 CandidatePair bestOption = options.top();
172 // Ensure the critical pair is still valid (need the end(is it a
173 // cycle that still exists?))
174 if(bestOption.type == 0) {
175 if(!this->dcvf_.isCellCritical(1, bestOption.death)) {
176 options.pop();
177 continue;
178 }
179 if(!this->dcvf_.isCellCritical(0, bestOption.birth)) {
180 options.pop();
181 // Trace the saddle (based on ascending(upward) or
182 // descending(downward))
183 std::vector<SimplexId> saddle{bestOption.death};
184 if(bestOption.nextCell.dim_ == 0) {
186 pairs, saddle, triangulation);
187 } else {
189 pairs, saddle, triangulation);
190 }
191 for(auto pair : pairs) {
192 options.push(pair);
193 }
194 pairs.clear();
195 continue;
196 }
197
198 } else if(bestOption.type == 2) {
199 if(!this->dcvf_.isCellCritical(1, bestOption.birth)) {
200 options.pop();
201 continue;
202 }
203 if(!this->dcvf_.isCellCritical(2, bestOption.death)) {
204 options.pop();
205 // Trace the saddle (based on ascending(upward) or
206 // descending(downward))
207 std::vector<SimplexId> saddle{bestOption.birth};
208 if(bestOption.nextCell.dim_ == 2) {
210 pairs, saddle, triangulation);
211 } else {
213 pairs, saddle, triangulation);
214 }
215 for(auto pair : pairs) {
216 options.push(pair);
217 }
218 pairs.clear();
219 continue;
220 }
221 }
222 // Trace the path to confirm it still ends at the same CP
223 std::vector<Cell> vpath;
224 // Add the saddle
225 if(bestOption.type == 0) {
226 vpath.emplace_back(Cell(1, bestOption.death));
227 } else if(bestOption.type == 2) {
228 vpath.emplace_back(Cell(1, bestOption.birth));
229 }
230 // Then trace desc/asc path
231 if(bestOption.nextCell.dim_ == 0) {
232 this->dcvf_.getDescendingPath<dataType, triangulationType>(
233 bestOption.nextCell, vpath, triangulation, false);
234 } else {
235 this->dcvf_.getAscendingPath<dataType, triangulationType>(
236 bestOption.nextCell, vpath, triangulation, false);
237 }
238 if(bestOption.type == 0) {
239 if(bestOption.birth
240 != vpath.back().id_) { // Ends elsewhere (needs retraced)
241 options.pop();
242 // Trace the new saddle destinations
243 std::vector<SimplexId> saddle{bestOption.death};
244 if(bestOption.nextCell.dim_ == 0) {
246 pairs, saddle, triangulation);
247 } else {
249 pairs, saddle, triangulation);
250 }
251 for(auto pair : pairs) {
252 options.push(pair);
253 }
254 pairs.clear();
255 continue;
256 }
257 } else if(bestOption.type == 2) {
258 if(bestOption.death
259 != vpath.back().id_) { // Ends elsewhere (needs retraced)
260 options.pop();
261 // Trace the new saddle destinations
262 std::vector<SimplexId> saddle{bestOption.birth};
263 if(bestOption.nextCell.dim_ == 2) {
265 pairs, saddle, triangulation);
266 } else {
268 pairs, saddle, triangulation);
269 }
270 for(auto pair : pairs) {
271 options.push(pair);
272 }
273 pairs.clear();
274 continue;
275 }
276 }
277
278 // Path has been Validated, so we can Trace and simplify
279
280 // Update weight threshold to be max of newest weight and previous
281 // weight threshold Since lower weights are possible
282 weightThres
283 = std::max(weightThres, static_cast<double>(bestOption.weight));
284
285 // First add plot point if desired (before)
286 if(storePlotPoints) {
287 listOfPlotPoints.emplace_back(
288 numCriticalPoints, numSinks, numSources, weightThres);
289 listOfPlotPoints.back().orbitsAdded = orbitsAdded;
290 listOfPlotPoints.back().orbitsRemoved = orbitsRemoved;
291 }
292
293 // Then update tracking values
294 if(bestOption.type == 0) {
295 numSinks--;
296 } else if(bestOption.type == 2) {
297 numSources--;
298 }
299 if(bestOption.generateOrbit) {
300 orbitsAdded++;
301 }
302 if(bestOption.alternations > 0) {
303 orbitsRemoved = orbitsRemoved + bestOption.alternations;
304 }
305 // Then add plot point if desired (after)
306 if(storePlotPoints) {
307 listOfPlotPoints.emplace_back(
308 numCriticalPoints - 2, numSinks, numSources, weightThres);
309 listOfPlotPoints.back().orbitsAdded = orbitsAdded;
310 listOfPlotPoints.back().orbitsRemoved = orbitsRemoved;
311 }
312
313 if(bestOption.nextCell.dim_ == 0) {
315 vpath)) { // Check if the Vpath alternating type 1-2 to 0-1
316 this->dcvf_.reverseAlternatingPath(vpath, triangulation);
317 } else {
318 this->dcvf_.reverseDescendingPath(vpath, triangulation);
319 }
320 numCriticalPoints -= 2;
321 } else {
323 vpath)) { // Check if the Vpath alternating type 0-1 to 1-2
324 this->dcvf_.reverseAlternatingPath(vpath, triangulation);
325 } else {
326 this->dcvf_.reverseAscendingPath(vpath, triangulation);
327 }
328 numCriticalPoints -= 2;
329 }
330 options.pop();
331 }
332 }
333 this->printMsg("Simplified to " + std::to_string(numCriticalPoints)
334 + " critical point(s)",
335 1.0, tm.getElapsedTime(), this->threadNumber_);
336
337 return 0;
338 }
339
351 this->dcvf_ = std::move(dcvf);
352 }
353
354 void setFullOrbitSimplification(bool doFullOrbit) {
355 this->dcvf_.setReverseFullOrbit(doFullOrbit);
356 }
357
359 // WARNING, this function will change the location of the discrete field
360 return std::move(this->dcvf_);
361 }
362
363 protected:
373 template <typename dataType, typename triangulationType>
374 std::vector<std::vector<CandidatePair>>
375 getSaddle1ToDescPair(const std::vector<SimplexId> &criticalEdges,
376 const triangulationType &triangulation) const;
377
394 template <typename dataType,
395 typename triangulationType,
396 typename GFS,
397 typename GFSN,
398 typename OB>
399 std::vector<std::vector<CandidatePair>>
400 getSaddle2ToAscPair(const std::vector<SimplexId> &criticalCells,
401 const GFS &getFaceStar,
402 const GFSN &getFaceStarNumber,
403 const OB &isOnBoundary,
404 const triangulationType &triangulation,
405 const dataType dummyVariable) const;
406
414 template <typename dataType, typename triangulationType>
415 void getDescSaddlePairs(std::vector<CandidatePair> &pairs,
416 const std::vector<SimplexId> &criticalEdges,
417 const triangulationType &triangulation) const;
418
426 template <typename dataType, typename triangulationType>
427 void getAscSaddlePairs(std::vector<CandidatePair> &pairs,
428 const std::vector<SimplexId> &criticalSaddles,
429 const triangulationType &triangulation) const;
430
439 inline bool isAlternatingVpath(std::vector<Cell> &vpath);
440
452 void displayStats(
453 const std::vector<CandidatePair> &pairs,
454 const std::array<std::vector<SimplexId>, 4> &criticalCellsByDim,
455 const std::vector<bool> &pairedMinima,
456 const std::vector<bool> &paired1Saddles,
457 const std::vector<bool> &paired2Saddles,
458 const std::vector<bool> &pairedMaxima) const;
459 };
460} // namespace ttk
461
462bool ttk::VectorSimplification::isAlternatingVpath(std::vector<Cell> &vpath) {
463 int pathSize = vpath.size();
464 // Assume it starts with saddle
465 if(pathSize > 2) {
466 int previousDim = vpath[1].dim_;
467 int previousSecondDim = vpath[2].dim_;
468 for(int i = 1; i < pathSize; i++) {
469 if((i % 2) == 0) {
470 if(vpath[i].dim_ != previousSecondDim) {
471 return true;
472 }
473 } else {
474 if(vpath[i].dim_ != previousDim) {
475 return true;
476 }
477 }
478 }
479 }
480
481 return false;
482}
483
484template <typename dataType, typename triangulationType>
485std::vector<std::vector<ttk::VectorSimplification::CandidatePair>>
487 const std::vector<SimplexId> &criticalEdges,
488 const triangulationType &triangulation) const {
489
490 const auto dim = this->dcvf_.getDimensionality();
491 std::vector<std::vector<CandidatePair>> res(criticalEdges.size());
492
493 // follow vpaths from 1-saddles to descending paths (typically minima)
494#ifdef TTK_ENABLE_OPENMP
495#pragma omp parallel for num_threads(threadNumber_)
496#endif
497 for(size_t i = 0; i < criticalEdges.size(); ++i) {
498 auto &mins = res[i];
499 const auto sid = criticalEdges[i];
500
501 const auto followVPath = [this, dim, sid, &mins,
502 &triangulation](const SimplexId v) {
503 std::vector<Cell> vpath{};
504 vpath.emplace_back(Cell{1, sid});
505 int alternates
506 = this->dcvf_.getDescendingPath<dataType, triangulationType>(
507 Cell{0, v}, vpath, triangulation, false);
508 Cell &lastCell = vpath.back();
509 if(lastCell.dim_ == 0 && this->dcvf_.isCellCritical(lastCell)) {
510 auto weight = this->dcvf_.getPersistence<dataType, triangulationType>(
511 vpath, triangulation);
512 mins.emplace_back(lastCell.id_, Cell(0, v), sid, 0, weight);
513 mins.back().alternations = alternates;
514 } else if(lastCell.dim_ == dim && this->dcvf_.isCellCritical(lastCell)) {
515 // Might change dimensions of end CP
516 auto weight = this->dcvf_.getPersistence<dataType, triangulationType>(
517 vpath, triangulation);
518 mins.emplace_back(sid, Cell{0, v}, lastCell.id_, 2, weight);
519 mins.back().alternations = alternates;
520 }
521 };
522#ifndef TTK_ENABLE_KAMIKAZE
523 // Test for valid sid ()
524 if(sid < 0 || sid > triangulation.getNumberOfEdges()) {
525 std::cout << "[WARNING] Not valid sid " << sid << std::endl;
526 continue;
527 }
528#endif
529 // critical edge vertices
530 SimplexId v0{}, v1{};
531 triangulation.getEdgeVertex(sid, 0, v0);
532 triangulation.getEdgeVertex(sid, 1, v1);
533
534 // follow vpath from each vertex of the critical edge
535 followVPath(v0);
536 followVPath(v1);
537 // Check for same end point (which would make an orbit if simplified)
538 // End point depends on the type (birth(type=0)/death(type=2))
539 if(mins.size() >= 2 && mins[0].type == mins[1].type) {
540 if(mins[0].type == 0 && mins[0].birth == mins[1].birth) {
541 mins[0].generateOrbit = true;
542 mins[1].generateOrbit = true;
543 } else if(mins[0].type == 2 && mins[0].death == mins[1].death) {
544 mins[0].generateOrbit = true;
545 mins[1].generateOrbit = true;
546 }
547 }
548 }
549
550 return res;
551}
552
553template <typename dataType,
554 typename triangulationType,
555 typename GFS,
556 typename GFSN,
557 typename OB>
558std::vector<std::vector<ttk::VectorSimplification::CandidatePair>>
560 const std::vector<SimplexId> &criticalCells,
561 const GFS &getFaceStar,
562 const GFSN &getFaceStarNumber,
563 const OB &isOnBoundary,
564 const triangulationType &triangulation,
565 const dataType dummyVariable) const {
566 // To eliminate unused variable warning
567 TTK_FORCE_USE(dummyVariable);
568 TTK_FORCE_USE(isOnBoundary);
569
570 const auto dim = this->dcvf_.getDimensionality();
571 std::vector<std::vector<CandidatePair>> res(criticalCells.size());
572
573 // follow vpaths from 2-saddles to maxima
574#ifdef TTK_ENABLE_OPENMP
575#pragma omp parallel for num_threads(threadNumber_)
576#endif
577 for(size_t i = 0; i < criticalCells.size(); ++i) {
578 const auto sid = criticalCells[i];
579 auto &maxs = res[i];
580
581 const auto followVPath = [this, dim, sid, &maxs,
582 &triangulation](const SimplexId v) {
583 std::vector<Cell> vpath{};
584 vpath.emplace_back(Cell{dim - 1, sid});
585 int alternates
586 = this->dcvf_.getAscendingPath<dataType, triangulationType>(
587 Cell{dim, v}, vpath, triangulation, false);
588 Cell &lastCell = vpath.back();
589 if(lastCell.dim_ == dim && this->dcvf_.isCellCritical(lastCell)) {
590 auto weight = this->dcvf_.getPersistence<dataType, triangulationType>(
591 vpath, triangulation);
592 maxs.emplace_back(sid, Cell{dim, v}, lastCell.id_, 2, weight);
593 maxs.back().alternations = alternates;
594 } else if(lastCell.dim_ == 0 && this->dcvf_.isCellCritical(lastCell)) {
595 // Might change dimensions of end CP
596 auto weight = this->dcvf_.getPersistence<dataType, triangulationType>(
597 vpath, triangulation);
598 maxs.emplace_back(lastCell.id_, Cell{dim, v}, sid, 0, weight);
599 maxs.back().alternations = alternates;
600 }
601 };
602
603 const auto starNumber = getFaceStarNumber(sid);
604
605 for(SimplexId j = 0; j < starNumber; ++j) {
606 SimplexId cellId{};
607 getFaceStar(sid, j, cellId);
608 followVPath(cellId);
609 }
610
611 // Not sure what this does other than add an extra saddle-max pair:
612 if(!isOnBoundary(sid) && maxs.size() >= 2) {
613 // Check for same end point (which would make an orbit if simplified)
614 // End point depends on the type (birth(type=0)/death(type=2))
615 if(maxs.size() >= 2 && maxs[0].type == maxs[1].type) {
616 if(maxs[0].type == 0 && maxs[0].birth == maxs[1].birth) {
617 maxs[0].generateOrbit = true;
618 maxs[1].generateOrbit = true;
619 } else if(maxs[0].type == 2 && maxs[0].death == maxs[1].death) {
620 maxs[0].generateOrbit = true;
621 maxs[1].generateOrbit = true;
622 }
623 }
624 }
625 }
626
627 return res;
628}
629
630template <typename dataType, typename triangulationType>
632 std::vector<CandidatePair> &pairs,
633 const std::vector<SimplexId> &criticalEdges,
634 const triangulationType &triangulation) const {
635
637 criticalEdges, triangulation);
638
639 for(size_t i = 0; i < saddle1ToMinima.size(); ++i) {
640 auto &mins = saddle1ToMinima[i];
641 // Add to pairs
642 for(size_t j = 0; j < mins.size(); ++j) {
643 pairs.emplace_back(mins[j]);
644 }
645 }
646}
647
648template <typename dataType, typename triangulationType>
650 std::vector<CandidatePair> &pairs,
651 const std::vector<SimplexId> &criticalSaddles,
652 const triangulationType &triangulation) const {
653
654 const auto dim = this->dcvf_.getDimensionality();
655
656 auto saddle2ToMaxima
657 = dim == 3
659 criticalSaddles,
660 [&triangulation](const SimplexId a, const SimplexId i, SimplexId &r) {
661 return triangulation.getTriangleStar(a, i, r);
662 },
663 [&triangulation](const SimplexId a) {
664 return triangulation.getTriangleStarNumber(a);
665 },
666 [&triangulation](const SimplexId a) {
667 return triangulation.isTriangleOnBoundary(a);
668 },
669 triangulation, static_cast<dataType>(0.0))
671 criticalSaddles,
672 [&triangulation](const SimplexId a, const SimplexId i, SimplexId &r) {
673 return triangulation.getEdgeStar(a, i, r);
674 },
675 [&triangulation](const SimplexId a) {
676 return triangulation.getEdgeStarNumber(a);
677 },
678 [&triangulation](const SimplexId a) {
679 return triangulation.isEdgeOnBoundary(a);
680 },
681 triangulation, static_cast<dataType>(0.0));
682
683 for(size_t i = 0; i < saddle2ToMaxima.size(); ++i) {
684 auto &maxs = saddle2ToMaxima[i];
685 // Add to pairs
686 for(size_t j = 0; j < maxs.size(); ++j) {
687 pairs.emplace_back(maxs[j]);
688 }
689 }
690}
#define TTK_FORCE_USE(x)
Force the compiler to use the function/method parameter.
Definition BaseClass.h:57
AbstractTriangulation is an interface class that defines an interface for efficient traversal methods...
int debugLevel_
Definition Debug.h:379
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition Debug.h:149
void setFullOrbitSimplification(bool doFullOrbit)
void getDescSaddlePairs(std::vector< CandidatePair > &pairs, const std::vector< SimplexId > &criticalEdges, const triangulationType &triangulation) const
Compute the candidate pairs from descending paths from saddles.
ttk::dcvf::DiscreteVectorField && getField()
void setField(ttk::dcvf::DiscreteVectorField &&dcvf)
Ugly hack to avoid a call to buildField()
std::vector< std::vector< CandidatePair > > getSaddle2ToAscPair(const std::vector< SimplexId > &criticalCells, const GFS &getFaceStar, const GFSN &getFaceStarNumber, const OB &isOnBoundary, const triangulationType &triangulation, const dataType dummyVariable) const
Follow the ascending 1-separatrices to compute the saddles -> extrema association.
int buildField(const void *const vectors, const size_t vectorsMTime, const triangulationType &triangulation)
bool isAlternatingVpath(std::vector< Cell > &vpath)
Determine if the VPath has 'alternating' behavior of dimension ex: 0-1 to 1-2.
dcvf::DiscreteVectorField dcvf_
int performSimplification(const int criticalThreshold, const bool storePlotPoints, std::vector< PlotPoint > &listOfPlotPoints, const triangulationType &triangulation)
void preconditionTriangulation(AbstractTriangulation *const data)
void getAscSaddlePairs(std::vector< CandidatePair > &pairs, const std::vector< SimplexId > &criticalSaddles, const triangulationType &triangulation) const
Compute the candidate pairs from ascending paths from saddles.
std::vector< std::vector< CandidatePair > > getSaddle1ToDescPair(const std::vector< SimplexId > &criticalEdges, const triangulationType &triangulation) const
Follow the descending 1-separatrices to compute the saddles -> extrema association.
void displayStats(const std::vector< CandidatePair > &pairs, const std::array< std::vector< SimplexId >, 4 > &criticalCellsByDim, const std::vector< bool > &pairedMinima, const std::vector< bool > &paired1Saddles, const std::vector< bool > &paired2Saddles, const std::vector< bool > &pairedMaxima) const
Print number of pairs, critical cells per dimension & unpaired cells.
TTK discreteVectorField processing package.
TTK base package defining the standard types.
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
Candidate pair struct for information of pairs connected by V-Paths We need to know the start and end...
CandidatePair(SimplexId b, Cell next, SimplexId d, int t, float w)
PlotPoint(SimplexId num, SimplexId sinks, SimplexId sources, double w)
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)