69 template <
typename triangulationType>
71 std::vector<GeneratorType> &generators,
72 std::vector<std::vector<SimplexId>> &connComps,
74 const triangulationType &triangulation);
83 void getConnectedComponents(
84 const std::vector<std::array<SimplexId, 2>> &edgeNeighs,
85 std::vector<SimplexId> &connComp)
const;
94 template <
typename triangulationType>
95 void findGeneratorsConnectedComponents(
96 std::vector<std::vector<SimplexId>> &connComps,
97 const std::vector<GeneratorType> &generators,
98 const triangulationType &triangulation)
const;
110 template <
typename triangulationType>
111 void findHandlesGenerators(std::vector<GeneratorType> &generators,
112 const std::vector<SimplexId> &inf1Saddles,
113 const std::vector<PersistencePair> &minSadPairs,
115 const triangulationType &triangulation)
const;
131 template <
typename triangulationType>
132 void pruneHandleGenerator(std::vector<SimplexId> &generator,
133 std::vector<bool> &genMask,
134 std::vector<bool> &pathVerts,
135 std::vector<SimplexId> &preds,
136 std::vector<size_t> &dists,
138 const triangulationType &triangulation)
const;
145template <
typename triangulationType>
146void ttk::PersistentGenerators::findGeneratorsConnectedComponents(
147 std::vector<std::vector<SimplexId>> &connComps,
148 const std::vector<GeneratorType> &generators,
149 const triangulationType &triangulation)
const {
151 connComps.resize(generators.size());
154 std::vector<SimplexId> isVisited(triangulation.getNumberOfEdges(), -1);
156 for(
size_t i = 0; i < generators.size(); ++i) {
157 const auto &genEdges{generators[i].boundary};
158 auto &connComp{connComps[i]};
159 if(genEdges.empty()) {
162 connComp.resize(genEdges.size(), -1);
165 for(
size_t j = 0; j < genEdges.size(); ++j) {
166 isVisited[genEdges[j]] = j;
171 std::vector<std::array<SimplexId, 2>> edgeNeighs(genEdges.size());
173 for(
size_t j = 0; j < genEdges.size(); ++j) {
174 const auto edge{genEdges[j]};
175 for(
size_t k = 0; k < 2; ++k) {
177 triangulation.getEdgeVertex(edge, k, v);
178 const auto nneighs{triangulation.getVertexEdgeNumber(v)};
181 triangulation.getVertexEdge(v, l, neigh);
182 const auto neighId{isVisited[neigh]};
183 if(neigh == edge || neighId == -1) {
186 edgeNeighs[j][k] = neighId;
192 this->getConnectedComponents(edgeNeighs, connComp);
195 for(
const auto e : genEdges) {
201template <
typename triangulationType>
202void ttk::PersistentGenerators::findHandlesGenerators(
203 std::vector<GeneratorType> &generators,
204 const std::vector<SimplexId> &inf1Saddles,
205 const std::vector<PersistencePair> &minSadPairs,
207 const triangulationType &triangulation)
const {
212 std::vector<SimplexId> min2sad(triangulation.getNumberOfVertices(), -1);
213 for(
const auto &p : minSadPairs) {
214 min2sad[p.birth] = p.death;
218 const auto globMax{std::distance(
220 std::max_element(offsets, offsets + triangulation.getNumberOfVertices()))};
223 std::vector<bool> genMask(triangulation.getNumberOfEdges(),
false);
224 std::vector<size_t> dists(
225 triangulation.getNumberOfVertices(), std::numeric_limits<size_t>::max());
226 std::vector<SimplexId> preds(triangulation.getNumberOfVertices(), -1);
227 std::vector<bool> pathVerts(triangulation.getNumberOfVertices(),
false);
230 for(
const auto s1 : inf1Saddles) {
231 std::vector<SimplexId> generator{};
232 std::set<SimplexId> visited1Sads{};
234 std::queue<SimplexId> toPropagate{};
235 toPropagate.push(s1);
237 while(!toPropagate.empty()) {
239 const auto curr{toPropagate.front()};
241 visited1Sads.emplace(curr);
244 generator.emplace_back(curr);
249 triangulation.getEdgeVertex(curr, i, currVert);
250 std::vector<Cell> vpath{};
252 this->dg_.getDescendingPath(
Cell{0, currVert}, vpath, triangulation);
253 const auto &lastCell = vpath.back();
254 if(lastCell.dim_ == 0) {
264 for(
const auto &c : vpath) {
266 generator.emplace_back(c.id_);
270 if(min2sad[min] == -1) {
275 const auto next{min2sad[min]};
276 if(visited1Sads.find(next) == visited1Sads.end()) {
277 toPropagate.push(next);
283 TTK_PSORT(this->threadNumber_, generator.begin(), generator.end());
284 const auto last = std::unique(generator.begin(), generator.end());
285 generator.erase(last, generator.end());
287 if(this->PruneHandlesGenerators) {
289 this->pruneHandleGenerator(
290 generator, genMask, pathVerts, preds, dists, s1, triangulation);
294 generator.emplace_back(s1);
295 std::swap(generator[0], generator.back());
297 generators.emplace_back(GeneratorType{
299 std::array<SimplexId, 2>{
301 this->dg_.getCellGreaterVertex(
Cell{1, s1}, triangulation),
305 this->
printMsg(
"Computed " + std::to_string(inf1Saddles.size())
306 +
" topological handle generators",
307 1.0, tm.getElapsedTime(), this->threadNumber_);
310template <
typename triangulationType>
311void ttk::PersistentGenerators::pruneHandleGenerator(
312 std::vector<SimplexId> &generator,
313 std::vector<bool> &genMask,
314 std::vector<bool> &pathVerts,
315 std::vector<SimplexId> &preds,
316 std::vector<size_t> &dists,
318 const triangulationType &triangulation)
const {
321 std::vector<SimplexId> genVerts{};
322 genVerts.reserve(2 * generator.size());
324 for(
const auto e : generator) {
328 using PQueueElem = std::pair<size_t, SimplexId>;
329 using PQueue = std::priority_queue<PQueueElem, std::vector<PQueueElem>,
330 std::greater<PQueueElem>>;
336 triangulation.getEdgeVertex(s1, 0, seed0);
337 triangulation.getEdgeVertex(s1, 1, seed1);
338 genVerts.emplace_back(seed0);
339 genVerts.emplace_back(seed1);
340 pq.emplace(0, seed0);
344 const auto curr{pq.top()};
345 genVerts.emplace_back(curr.second);
348 const auto nneighs{triangulation.getVertexNeighborNumber(curr.second)};
349 dists[curr.second] = curr.first;
354 triangulation.getVertexNeighbor(curr.second, i, neigh);
355 triangulation.getVertexEdge(curr.second, i, edge);
361 if(curr.second == seed0 && neigh == seed1) {
364 if(dists[neigh] > curr.first + 1) {
365 dists[neigh] = curr.first + 1;
366 preds[neigh] = curr.second;
367 pq.emplace(dists[neigh], neigh);
374 while(curr != seed0) {
375 pathVerts[curr] =
true;
378 pathVerts[seed0] =
true;
379 pathVerts[seed1] =
true;
381 std::vector<SimplexId> prunedGenerator{};
382 prunedGenerator.reserve(generator.size());
383 for(
const auto e : generator) {
385 triangulation.getEdgeVertex(e, 0, v0);
386 triangulation.getEdgeVertex(e, 1, v1);
387 if(pathVerts[v0] && pathVerts[v1]) {
388 prunedGenerator.emplace_back(e);
391 std::swap(generator, prunedGenerator);
394 for(
const auto e : prunedGenerator) {
397 for(
const auto v : genVerts) {
399 dists[v] = std::numeric_limits<size_t>::max();
400 pathVerts[v] =
false;
404template <
typename triangulationType>
406 std::vector<GeneratorType> &generators,
407 std::vector<std::vector<SimplexId>> &connComps,
409 const triangulationType &triangulation) {
412 this->alloc(triangulation);
415 const auto dim = this->dg_.getDimensionality();
417 this->printWrn(
"Cannot compute cycles for 1D datasets");
424 this->critEdges_.resize(triangulation.getNumberOfEdges());
425 this->edgeTrianglePartner_.resize(triangulation.getNumberOfEdges(), -1);
426 this->onBoundary_.resize(triangulation.getNumberOfEdges(),
false);
427 this->s2Mapping_.resize(triangulation.getNumberOfTriangles(), -1);
428 this->s1Mapping_.resize(triangulation.getNumberOfEdges(), -1);
432 std::array<std::vector<SimplexId>, 4> criticalCellsByDim{};
434 auto &critCellsOrder{this->critCellsOrder_};
436 this->extractCriticalCells(
437 criticalCellsByDim, critCellsOrder, offsets, triangulation,
true);
440 auto &pairedMinima{this->pairedCritCells_[0]};
442 auto &paired1Saddles{this->pairedCritCells_[1]};
444 auto &paired2Saddles{this->pairedCritCells_[dim - 1]};
446 auto &pairedMaxima{this->pairedCritCells_[dim]};
451 std::vector<PersistencePair> minSadPairs{};
452 this->getMinSaddlePairs(minSadPairs, pairedMinima, paired1Saddles,
453 criticalCellsByDim[1], critCellsOrder[1], offsets,
460 std::vector<PersistencePair> sadMaxPairs{};
461 this->getMaxSaddlePairs(
462 sadMaxPairs, pairedMaxima, paired2Saddles, criticalCellsByDim[dim - 1],
463 critCellsOrder[dim - 1], critCellsOrder[dim], triangulation);
466 if(!criticalCellsByDim[1].empty() && !criticalCellsByDim[2].empty()) {
470 std::vector<PersistencePair> sadSadPairs{};
471 this->getSaddleSaddlePairs(
472 sadSadPairs, paired1Saddles, dim == 3 ? paired2Saddles : pairedMaxima,
473 true, generators, criticalCellsByDim[1], criticalCellsByDim[2],
474 critCellsOrder[1], triangulation);
478 std::vector<SimplexId> inf1Saddles{};
479 for(
const auto s1 : criticalCellsByDim[1]) {
480 if(!paired1Saddles[s1]) {
481 inf1Saddles.emplace_back(s1);
485 if(!inf1Saddles.empty()) {
486 this->findHandlesGenerators(
487 generators, inf1Saddles, minSadPairs, offsets, triangulation);
490 if(!generators.empty()) {
492 this->findGeneratorsConnectedComponents(
493 connComps, generators, triangulation);
497 "Computed " + std::to_string(generators.size()) +
" persistent generators",
498 1.0, tm.getElapsedTime(), this->threadNumber_);
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
TTK DiscreteMorseSandwich processing package.
TTK PersistentGenerators processing package.
int computePersistentGenerators(std::vector< GeneratorType > &generators, std::vector< std::vector< SimplexId > > &connComps, const SimplexId *const offsets, const triangulationType &triangulation)
Compute the persistence generators from the discrete gradient.
bool PruneHandlesGenerators
int SimplexId
Identifier type for simplices of any dimension.
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)