TTK
Loading...
Searching...
No Matches
JacobiSet_Template.h
Go to the documentation of this file.
1#pragma once
2
3#include <JacobiSet.h>
4
5template <class dataTypeU, class dataTypeV, typename triangulationType>
6int ttk::JacobiSet::execute(std::vector<std::pair<SimplexId, char>> &jacobiSet,
7 const dataTypeU *const uField,
8 const dataTypeV *const vField,
9 const triangulationType &triangulation,
10 std::vector<char> *isPareto) {
11
12 Timer t;
13
14 // check the consistency of the variables -- to adapt
15#ifndef TTK_ENABLE_KAMIKAZE
16 if((triangulation.isEmpty())) {
17 if(vertexNumber_) {
18 return executeLegacy(jacobiSet, uField, vField);
19 }
20 return -1;
21 }
22 if(!uField)
23 return -2;
24 if(!vField)
25 return -3;
26#endif
27
28 jacobiSet.clear();
29
30 SimplexId const edgeNumber = triangulation.getNumberOfEdges();
31
32 std::vector<std::vector<std::pair<SimplexId, char>>> threadedCriticalTypes(
34
35#ifdef TTK_ENABLE_OPENMP
36#pragma omp parallel for num_threads(threadNumber_)
37#endif
38 for(SimplexId i = 0; i < edgeNumber; i++) {
39
40 char const type = getCriticalType(i, uField, vField, triangulation);
41
42 if(type != -2) {
43 // -2: regular vertex
44#ifdef TTK_ENABLE_OPENMP
45 const auto tid = omp_get_thread_num();
46#else
47 const auto tid = 0;
48#endif // TTK_ENABLE_OPENMP
49 threadedCriticalTypes[tid].emplace_back(i, type);
50 }
51 }
52
53 // now merge the threaded lists
54 size_t jacobiSetSize{};
55 for(const auto &vec : threadedCriticalTypes) {
56 jacobiSetSize += vec.size();
57 }
58 jacobiSet.reserve(jacobiSetSize);
59
60 for(SimplexId i = 0; i < threadNumber_; i++) {
61 for(size_t j = 0; j < threadedCriticalTypes[i].size(); j++) {
62 jacobiSet.push_back(threadedCriticalTypes[i][j]);
63 }
64 }
65
66 SimplexId minimumNumber = 0, saddleNumber = 0, maximumNumber = 0,
67 monkeySaddleNumber = 0;
68
69 for(size_t i = 0; i < jacobiSet.size(); i++) {
70 switch(jacobiSet[i].second) {
71 case 0:
72 minimumNumber++;
73 break;
74 case 1:
75 saddleNumber++;
76 break;
77 case 2:
78 maximumNumber++;
79 break;
80 case -1:
81 monkeySaddleNumber++;
82 break;
83 }
84 }
85
86 if(isPareto) {
87 isPareto->resize(jacobiSet.size(), 0);
88#ifdef TTK_ENABLE_OPENMP
89#pragma omp parallel for num_threads(threadNumber_)
90#endif
91 for(int i = 0; i < (int)jacobiSet.size(); i++) {
92 int const edgeId = jacobiSet[i].first;
93 SimplexId vertexId0 = -1, vertexId1 = -1;
94 triangulation.getEdgeVertex(edgeId, 0, vertexId0);
95 triangulation.getEdgeVertex(edgeId, 1, vertexId1);
96 double denominator = vField[vertexId1] - vField[vertexId0];
97 if(fabs(denominator) < Geometry::powIntTen(-DBL_DIG))
98 denominator = 1;
99 if((uField[vertexId1] - uField[vertexId0]) / denominator < 0)
100 (*isPareto)[i] = 1;
101 }
102 }
103
104 this->printMsg(
105 std::vector<std::vector<std::string>>{
106 {"#Minimum edges", std::to_string(minimumNumber)},
107 {"#Saddle edges", std::to_string(saddleNumber)},
108 {"#Maximum edges", std::to_string(maximumNumber)},
109 {"#Multi-saddle edges", std::to_string(monkeySaddleNumber)}},
111
112 this->printMsg(
113 "Data-set processed", 1.0, t.getElapsedTime(), this->threadNumber_);
114 this->printMsg("Jacobi edge rate: "
115 + std::to_string(100.0 * jacobiSet.size() / (double)edgeNumber)
116 + "%");
117
118 return 0;
119}
120
121template <class dataTypeU, class dataTypeV>
123 std::vector<std::pair<SimplexId, char>> &jacobiSet,
124 const dataTypeU *const uField,
125 const dataTypeV *const vField) {
126
127 Timer t;
128
129 this->printWrn("Using legacy implementation...");
130
131 // check the consistency of the variables -- to adapt
132#ifndef TTK_ENABLE_KAMIKAZE
133 if(!vertexNumber_)
134 return -1;
135 if(!uField)
136 return -2;
137 if(!vField)
138 return -3;
139 if(!edgeList_)
140 return -4;
141 if(!edgeFanLinkEdgeLists_)
142 return -5;
143 if(!edgeFans_)
144 return -6;
145 if(!sosOffsetsU_)
146 return -7;
147#endif
148
149 SimplexId count = 0;
150
151 jacobiSet.clear();
152
153 // distance fields (not really memory efficient)
154 // for each thread
155 // for each vertex: distance field map
156 std::vector<std::vector<double>> threadedDistanceField(threadNumber_);
157 for(size_t i = 0; i < threadedDistanceField.size(); i++) {
158 threadedDistanceField[i].resize(vertexNumber_);
159 }
160
161 std::vector<ScalarFieldCriticalPoints> threadedCriticalPoints(threadNumber_);
162 for(ThreadId i = 0; i < threadNumber_; i++) {
163 threadedCriticalPoints[i].setDomainDimension(2);
164 threadedCriticalPoints[i].setVertexNumber(vertexNumber_);
165 }
166
167 std::vector<std::vector<std::pair<SimplexId, char>>> threadedCriticalTypes(
168 threadNumber_);
169
170#ifdef TTK_ENABLE_OPENMP
171#pragma omp parallel for num_threads(threadNumber_)
172#endif
173 for(size_t i = 0; i < edgeList_->size(); i++) {
174
175 // avoid any processing if the abort signal is sent
176 if((!wrapper_) || ((wrapper_) && (!wrapper_->needsToAbort()))) {
177
178 ThreadId threadId = 0;
179#ifdef TTK_ENABLE_OPENMP
180 threadId = omp_get_thread_num();
181#endif
182
183 // processing here!
184 SimplexId const pivotVertexId = (*edgeList_)[i].first;
185 SimplexId const otherExtremityId = (*edgeList_)[i].second;
186
187 // A) compute the distance field
188 double projectedPivotVertex[2];
189 projectedPivotVertex[0] = uField[pivotVertexId];
190 projectedPivotVertex[1] = vField[pivotVertexId];
191
192 double projectedOtherVertex[2];
193 projectedOtherVertex[0] = uField[otherExtremityId];
194 projectedOtherVertex[1] = vField[otherExtremityId];
195
196 double rangeEdge[2];
197 rangeEdge[0] = projectedOtherVertex[0] - projectedPivotVertex[0];
198 rangeEdge[1] = projectedOtherVertex[1] - projectedPivotVertex[1];
199
200 double rangeNormal[2];
201 rangeNormal[0] = -rangeEdge[1];
202 rangeNormal[1] = rangeEdge[0];
203
204 for(size_t j = 0; j < (*edgeFans_)[i].size() / 4; j++) {
205 for(int k = 0; k < 3; k++) {
206
207 SimplexId const vertexId = (*edgeFans_)[i][j * 4 + 1 + k];
208
209 // we can compute the distance field (in the rage)
210 double projectedVertex[2];
211 projectedVertex[0] = uField[vertexId];
212 projectedVertex[1] = vField[vertexId];
213
214 double vertexRangeEdge[2];
215 vertexRangeEdge[0] = projectedVertex[0] - projectedPivotVertex[0];
216 vertexRangeEdge[1] = projectedVertex[1] - projectedPivotVertex[1];
217
218 // signed distance: linear function of the dot product
219 threadedDistanceField[threadId][vertexId]
220 = vertexRangeEdge[0] * rangeNormal[0]
221 + vertexRangeEdge[1] * rangeNormal[1];
222 }
223 }
224
225 // B) compute critical points
226 // watch out between local and global Ids
227 // what I could do is to translate the ids from global to local
228 // also, lots of things in there can be done out of the loop
229
230 // in the loop
231 char const type = threadedCriticalPoints[threadId].getCriticalType(
232 pivotVertexId, sosOffsetsU_, (*edgeFanLinkEdgeLists_)[i]);
233
234 if(type != -2) {
235 // -2: regular vertex
236 threadedCriticalTypes[threadId].emplace_back(i, type);
237 }
238
239 // update the progress bar of the wrapping code -- to adapt
240 if(debugLevel_ > static_cast<int>(debug::Priority::DETAIL)) {
241#ifdef TTK_ENABLE_OPENMP
242#pragma omp critical
243#endif
244 {
245 if((wrapper_) && (!(count % ((vertexNumber_) / 10)))) {
246 wrapper_->updateProgress((count + 1.0) / vertexNumber_);
247 }
248
249 count++;
250 }
251 }
252 }
253 }
254
255 // now merge the threaded lists
256 for(ThreadId i = 0; i < threadNumber_; i++) {
257 for(size_t j = 0; j < threadedCriticalTypes[i].size(); j++) {
258 jacobiSet.push_back(threadedCriticalTypes[i][j]);
259 }
260 }
261
262 SimplexId minimumNumber = 0, saddleNumber = 0, maximumNumber = 0,
263 monkeySaddleNumber = 0;
264
265 for(size_t i = 0; i < jacobiSet.size(); i++) {
266 switch(jacobiSet[i].second) {
267 case 0:
268 minimumNumber++;
269 break;
270 case 1:
271 saddleNumber++;
272 break;
273 case 2:
274 maximumNumber++;
275 break;
276 case -1:
277 monkeySaddleNumber++;
278 break;
279 }
280 }
281
282 this->printMsg(
283 std::vector<std::vector<std::string>>{
284 {"#Minimum edges", std::to_string(minimumNumber)},
285 {"#Saddle edges", std::to_string(saddleNumber)},
286 {"#Maximum edges", std::to_string(maximumNumber)},
287 {"#Multi-saddle edges", std::to_string(monkeySaddleNumber)}},
289
290 this->printMsg(
291 "Data-set processed", 1.0, t.getElapsedTime(), this->threadNumber_);
292 this->printMsg(
293 "Jacobi edge rate: "
294 + std::to_string(100.0 * (jacobiSet.size() / ((double)edgeList_->size())))
295 + "%");
296
297 return 0;
298}
299
300template <class dataTypeU, class dataTypeV, typename triangulationType>
302 const dataTypeU *const uField,
303 const dataTypeV *const vField,
304 const triangulationType &triangulation) {
305
306 SimplexId vertexId0 = -1, vertexId1 = -1;
307 triangulation.getEdgeVertex(edgeId, 0, vertexId0);
308 triangulation.getEdgeVertex(edgeId, 1, vertexId1);
309
310 double projectedPivotVertex[2];
311 projectedPivotVertex[0] = uField[vertexId0];
312 projectedPivotVertex[1] = vField[vertexId0];
313
314 double projectedOtherVertex[2];
315 projectedOtherVertex[0] = uField[vertexId1];
316 projectedOtherVertex[1] = vField[vertexId1];
317
318 double rangeEdge[2];
319 rangeEdge[0] = projectedOtherVertex[0] - projectedPivotVertex[0];
320 rangeEdge[1] = projectedOtherVertex[1] - projectedPivotVertex[1];
321
322 double rangeNormal[2];
323 rangeNormal[0] = -rangeEdge[1];
324 rangeNormal[1] = rangeEdge[0];
325
326 SimplexId const starNumber = triangulation.getEdgeStarNumber(edgeId);
327 std::vector<SimplexId> lowerNeighbors, upperNeighbors;
328
329 SimplexId neighborNumber = 0;
330
331 for(SimplexId i = 0; i < starNumber; i++) {
332
333 SimplexId tetId = -1;
334 triangulation.getEdgeStar(edgeId, i, tetId);
335
336 SimplexId const vertexNumber = triangulation.getCellVertexNumber(tetId);
337 for(SimplexId j = 0; j < vertexNumber; j++) {
338 SimplexId vertexId = -1;
339 triangulation.getCellVertex(tetId, j, vertexId);
340
341 if((vertexId != -1) && (vertexId != vertexId0)
342 && (vertexId != vertexId1)) {
343 // new neighbor
344 bool isIn = false;
345 for(size_t k = 0; k < lowerNeighbors.size(); k++) {
346 if(vertexId == lowerNeighbors[k]) {
347 isIn = true;
348 break;
349 }
350 }
351
352 if(!isIn) {
353 for(size_t k = 0; k < upperNeighbors.size(); k++) {
354 if(vertexId == upperNeighbors[k]) {
355 isIn = true;
356 break;
357 }
358 }
359 }
360
361 if(!isIn) {
362 // compute the actual distance field
363 // A) compute the distance field
364 double projectedVertex[2];
365 projectedVertex[0] = uField[vertexId];
366 projectedVertex[1] = vField[vertexId];
367
368 double vertexRangeEdge[2];
369 vertexRangeEdge[0] = projectedVertex[0] - projectedPivotVertex[0];
370 vertexRangeEdge[1] = projectedVertex[1] - projectedPivotVertex[1];
371
372 // signed distance: linear function of the dot product
373 double distance = vertexRangeEdge[0] * rangeNormal[0]
374 + vertexRangeEdge[1] * rangeNormal[1];
375
376 neighborNumber++;
377
378 if(distance < 0) {
379 lowerNeighbors.push_back(vertexId);
380 } else if(distance > 0) {
381 upperNeighbors.push_back(vertexId);
382 } else {
383 // degenerate
384 // compute the distance field out of the offset positions
385 double offsetProjectedPivotVertex[2];
386 offsetProjectedPivotVertex[0] = sosOffsetsU_[vertexId0];
387 offsetProjectedPivotVertex[1]
388 = sosOffsetsV_[vertexId0] * sosOffsetsV_[vertexId0];
389
390 double offsetProjectedOtherVertex[2];
391 offsetProjectedOtherVertex[0] = sosOffsetsU_[vertexId1];
392 offsetProjectedOtherVertex[1]
393 = sosOffsetsV_[vertexId1] * sosOffsetsV_[vertexId1];
394
395 double offsetRangeEdge[2];
396 offsetRangeEdge[0]
397 = offsetProjectedOtherVertex[0] - offsetProjectedPivotVertex[0];
398 offsetRangeEdge[1]
399 = offsetProjectedOtherVertex[1] - offsetProjectedPivotVertex[1];
400
401 double offsetRangeNormal[2];
402 offsetRangeNormal[0] = -offsetRangeEdge[1];
403 offsetRangeNormal[1] = offsetRangeEdge[0];
404
405 projectedVertex[0] = sosOffsetsU_[vertexId];
406 projectedVertex[1]
407 = sosOffsetsV_[vertexId] * sosOffsetsV_[vertexId];
408
409 vertexRangeEdge[0]
410 = projectedVertex[0] - offsetProjectedPivotVertex[0];
411 vertexRangeEdge[1]
412 = projectedVertex[1] - offsetProjectedPivotVertex[1];
413
414 distance = vertexRangeEdge[0] * offsetRangeNormal[0]
415 + vertexRangeEdge[1] * offsetRangeNormal[1];
416
417 if(distance < 0) {
418 lowerNeighbors.push_back(vertexId);
419 } else if(distance > 0) {
420 upperNeighbors.push_back(vertexId);
421 } else {
422 this->printWrn(
423 "Inconsistent (non-bijective?) offsets for vertex #"
424 + std::to_string(vertexId));
425 }
426 }
427 }
428 }
429 }
430 }
431
432 // at this point, we know if each vertex of the edge link is higher or not.
433 if((SimplexId)(lowerNeighbors.size() + upperNeighbors.size())
434 != neighborNumber) {
435 // Inconsistent offsets (cf above error message)
436 return -2;
437 }
438
439 if(lowerNeighbors.empty()) {
440 if(rangeNormal[0] + rangeNormal[1] > 0)
441 // minimum
442 return 0;
443 // else maximum
444 return triangulation.getDimensionality() - 1;
445 }
446 if(upperNeighbors.empty()) {
447 if(rangeNormal[0] + rangeNormal[1] > 0)
448 // maximum
449 return triangulation.getDimensionality() - 1;
450 // else minimum
451 return 0;
452 }
453
454 // let's check the connectivity now
455 std::vector<UnionFind> lowerSeeds(lowerNeighbors.size());
456 std::vector<UnionFind *> lowerList(lowerNeighbors.size());
457 std::vector<UnionFind> upperSeeds(upperNeighbors.size());
458 std::vector<UnionFind *> upperList(upperNeighbors.size());
459
460 for(size_t i = 0; i < lowerSeeds.size(); i++) {
461 lowerList[i] = &(lowerSeeds[i]);
462 }
463 for(size_t i = 0; i < upperSeeds.size(); i++) {
464 upperList[i] = &(upperSeeds[i]);
465 }
466
467 for(SimplexId i = 0; i < starNumber; i++) {
468
469 SimplexId tetId = -1;
470 triangulation.getEdgeStar(edgeId, i, tetId);
471
472 SimplexId const vertexNumber = triangulation.getCellVertexNumber(tetId);
473 for(SimplexId j = 0; j < vertexNumber; j++) {
474 SimplexId edgeVertexId0 = -1;
475 triangulation.getCellVertex(tetId, j, edgeVertexId0);
476 if((edgeVertexId0 != vertexId0) && (edgeVertexId0 != vertexId1)) {
477 for(SimplexId k = j + 1; k < vertexNumber; k++) {
478 SimplexId edgeVertexId1 = -1;
479 triangulation.getCellVertex(tetId, k, edgeVertexId1);
480 if((edgeVertexId1 != vertexId0) && (edgeVertexId1 != vertexId1)) {
481 // processing the edge (edgeVertexId0, edgeVertexId1)
482
483 // we need to find out if they're lower or not
484 bool lower0 = false;
485 for(size_t l = 0; l < lowerNeighbors.size(); l++) {
486 if(lowerNeighbors[l] == edgeVertexId0) {
487 lower0 = true;
488 break;
489 }
490 }
491 bool lower1 = false;
492 for(size_t l = 0; l < lowerNeighbors.size(); l++) {
493 if(lowerNeighbors[l] == edgeVertexId1) {
494 lower1 = true;
495 break;
496 }
497 }
498
499 auto *neighbors = &lowerNeighbors;
500 auto *seeds = &lowerList;
501
502 if(!lower0) {
503 neighbors = &upperNeighbors;
504 seeds = &upperList;
505 }
506
507 if(lower0 == lower1) {
508 // connect their union-find sets!
509 SimplexId lowerId0 = -1, lowerId1 = -1;
510 for(size_t l = 0; l < neighbors->size(); l++) {
511 if((*neighbors)[l] == edgeVertexId0) {
512 lowerId0 = l;
513 }
514 if((*neighbors)[l] == edgeVertexId1) {
515 lowerId1 = l;
516 }
517 }
518
519 if((lowerId0 != -1) && (lowerId1 != -1)) {
520 (*seeds)[lowerId0] = UnionFind::makeUnion(
521 (*seeds)[lowerId0], (*seeds)[lowerId1]);
522 (*seeds)[lowerId1] = (*seeds)[lowerId0];
523 }
524 }
525
526 break;
527 }
528 }
529 }
530 }
531 }
532
533 // update the UF if necessary
534 for(size_t i = 0; i < lowerList.size(); i++)
535 lowerList[i] = lowerList[i]->find();
536 for(size_t i = 0; i < upperList.size(); i++)
537 upperList[i] = upperList[i]->find();
538
539 {
540 std::sort(lowerList.begin(), lowerList.end());
541 const auto it = std::unique(lowerList.begin(), lowerList.end());
542 lowerList.erase(it, lowerList.end());
543 }
544 {
545 std::sort(upperList.begin(), upperList.end());
546 const auto it = std::unique(upperList.begin(), upperList.end());
547 upperList.erase(it, upperList.end());
548 }
549
550 if((upperList.size() == 1) && (lowerList.size() == 1))
551 return -2;
552
553 return 1;
554}
555
556template <class dataTypeU, class dataTypeV>
557int ttk::JacobiSet::perturb(const dataTypeU *const uField,
558 const dataTypeV *const vField,
559 const dataTypeU uEpsilon,
560 const dataTypeV vEpsilon) const {
561
562#ifndef TTK_ENABLE_KAMIKAZE
563 if(!uField)
564 return -1;
565 if(!vField)
566 return -2;
567 if(!vertexNumber_)
568 return -3;
569#endif
570
571#ifdef TTK_ENABLE_OPENMP
572#pragma omp parallel for num_threads(threadNumber_)
573#endif
574 for(SimplexId i = 0; i < vertexNumber_; i++) {
575 // simulation of simplicity in 2 dimensions, need to use degree 2 polynoms
576
577 uField[i] += i * uEpsilon;
578 vField[i] += (i * vEpsilon) * (i * vEpsilon);
579 }
580
581 return 0;
582}
int executeLegacy(std::vector< std::pair< SimplexId, char > > &jacobiSet, const dataTypeU *const uField, const dataTypeV *const vField)
SimplexId vertexNumber_
Definition JacobiSet.h:131
char getCriticalType(const SimplexId &edgeId, const dataTypeU *const uField, const dataTypeV *const vField, const triangulationType &triangulation)
int perturb(const dataTypeU *const uField, const dataTypeV *const vField, const dataTypeU uEpsilon=Geometry::powIntTen(-DBL_DIG), const dataTypeV vEpsilon=Geometry::powIntTen(-DBL_DIG)) const
int execute(std::vector< std::pair< SimplexId, char > > &jacobiSet, const dataTypeU *const uField, const dataTypeV *const vField, const triangulationType &triangulation, std::vector< char > *isPareto=nullptr)
double getElapsedTime()
Definition Timer.h:15
static UnionFind * makeUnion(UnionFind *uf0, UnionFind *uf1)
Definition UnionFind.h:40
std::string to_string(__int128)
Definition ripserpy.cpp:99
T powIntTen(const int n)
Compute the nth power of ten.
Definition Geometry.h:403
int ThreadId
Identifier type for threads (i.e. with OpenMP).
Definition DataTypes.h:26
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/|__ _|"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)