TTK
Loading...
Searching...
No Matches
PairCells.cpp
Go to the documentation of this file.
1#include <PairCells.h>
2
3#include <numeric>
4
5using namespace ttk::rpd;
6
7#ifdef TTK_ENABLE_CGAL
8PairCells::PairCells(const std::vector<CGAL::Epick::Point_2> &points,
9 double upperBound,
10 bool parallelSort,
11 bool parallelMatrixConstruction)
12 : n_(points.size()), bound_(upperBound), parallelSort_(parallelSort),
13 parallelMatrixConstruction_(parallelMatrixConstruction) {
14 // inherited from Debug: prefix will be printed at the beginning of every msg
15 this->setDebugMsgPrefix("PairCells");
16
17 for(int i = 1; i < n_; ++i) {
18 for(int j = 0; j < i; ++j)
19 compressedDM_.push_back(
20 sqrt(CGAL::squared_distance(points[i], points[j])));
21 }
22}
23#endif
24
26 bool distanceMatrix,
27 double upperBound,
28 bool parallelSort,
29 bool parallelMatrixConstruction)
30 : n_(distanceMatrix ? (1 + sqrt(1 + 8 * points[0].size())) / 2
31 : points.size()),
32 bound_(upperBound), parallelSort_(parallelSort),
33 parallelMatrixConstruction_(parallelMatrixConstruction) {
34 // inherited from Debug: prefix will be printed at the beginning of every msg
35 this->setDebugMsgPrefix("PairCells");
36
37 if(distanceMatrix)
38 compressedDM_ = points[0];
39 else {
40 const unsigned dim = points[0].size();
41 for(int i = 1; i < n_; ++i) {
42 for(int j = 0; j < i; ++j) {
43 double s = 0.;
44 for(unsigned d = 0; d < dim; ++d)
45 s += (points[i][d] - points[j][d]) * (points[i][d] - points[j][d]);
46 compressedDM_.push_back(sqrt(s));
47 }
48 }
49 }
50}
51
53 int n,
54 int dim,
55 double upperBound,
56 bool parallelSort,
57 bool parallelMatrixConstruction)
58 : n_(n), bound_(upperBound), parallelSort_(parallelSort),
59 parallelMatrixConstruction_(parallelMatrixConstruction) {
60 // inherited from Debug: prefix will be printed at the beginning of every msg
61 this->setDebugMsgPrefix("PairCells");
62
63 for(int i = 1; i < n_; ++i) {
64 for(int j = 0; j < i; ++j) {
65 double s = 0.;
66 for(int d = 0; d < dim; ++d)
67 s += (data[dim * i + d] - data[dim * j + d])
68 * (data[dim * i + d] - data[dim * j + d]);
69 compressedDM_.push_back(sqrt(s));
70 }
71 }
72}
73
75 Timer tm{};
76 if(bound_ == inf || bound_ < 0)
77 initialize();
78 else
79 initializeWithBound();
80 printMsg("Initialized (#p=" + std::to_string(n_)
81 + ", #e=" + std::to_string(nEdges_)
82 + ", #t=" + std::to_string(nTriangles_) + ")",
83 0, tm.getElapsedTime());
84 pairCells();
85 printMsg("Complete", 1, tm.getElapsedTime());
86}
87
88void PairCells::initialize() {
89 // edges
90 for(id_t i = 0; i < n_; ++i) {
91 for(id_t j = i + 1; j < n_; ++j)
92 edges_.push_back({{i, j}, DM(i, j)});
93 }
94 nEdges_ = n_ * (n_ - 1) / 2;
95 executeKruskal();
96
97 // triangles
98 nTriangles_ = n_ * (n_ - 1) * (n_ - 2) / 6;
99 triangles_.resize(nTriangles_);
100 boundaries_.resize(nTriangles_);
101
102#ifdef TTK_ENABLE_OPENMP
103#pragma omp parallel for if(parallelMatrixConstruction_)
104#else
105 TTK_FORCE_USE(parallelMatrixConstruction_);
106#endif // TTK_ENABLE_OPENMP
107 for(id_t i = 0; i < n_ - 2; ++i) {
108 const unsigned index_i
109 = i * (i * i - 3 * i * (n_ - 1) + 3 * n_ * n_ - 6 * n_ + 2) / 6;
110 for(id_t j = i + 1; j < n_ - 1; ++j) {
111 const unsigned index_j = (i - j + 1) * (i + j - 2 * n_ + 2) / 2 + index_i;
112 for(id_t k = j + 1; k < n_; ++k) {
113 const double diameter
114 = std::max(DM(i, j), std::max(DM(i, k), DM(j, k)));
115 triangles_[index_j + (k - j - 1)] = {{i, j, k}, diameter};
116 boundaries_[index_j + (k - j - 1)]
117 = {nEdges_ - 1 - (n_ - i) * (n_ - i - 1) / 2 + (j - i),
118 nEdges_ - 1 - (n_ - i) * (n_ - i - 1) / 2 + (k - i),
119 nEdges_ - 1 - (n_ - j) * (n_ - j - 1) / 2 + (k - j)};
120 }
121 }
122 }
123
124 apparentPairs();
125}
126
127void PairCells::initializeWithBound() {
128 // edges
129 std::unordered_map<Edge, id_t, boost::hash<Edge>> edgeToIndex;
130 std::vector<std::set<id_t>> graph(n_); // std::unordered_set?
131 for(id_t i = 0; i < n_; ++i) {
132 for(id_t j = i + 1; j < n_; ++j) {
133 if(DM(i, j) <= bound_) {
134 edgeToIndex[{i, j}] = edges_.size();
135 edges_.push_back({{i, j}, DM(i, j)});
136 graph[i].insert(j); // only directed graph with edges (ij), i<j
137 }
138 }
139 }
140 nEdges_ = edges_.size();
141 executeKruskal();
142
143 // triangles
144 for(const auto &[e, f] : edges_) {
145 const auto &[i, j] = e;
146 for(auto const &k : graph[j]) {
147 if(graph[i].find(k)
148 != graph[i].end()) { // c++20 -> std::unordered_set::contains
149 triangles_.push_back(
150 {{i, j, k}, std::max(DM(i, j), std::max(DM(i, k), DM(j, k)))});
151 boundaries_.push_back(
152 {edgeToIndex[{i, j}], edgeToIndex[{i, k}], edgeToIndex[{j, k}]});
153 }
154 }
155 }
156 nTriangles_ = triangles_.size();
157
158 apparentPairs();
159}
160
161void PairCells::executeKruskal() {
162 UnionFind UF(n_);
163 edgesIndices_.resize(nEdges_);
164 edgesOrder_.resize(nEdges_);
165 edgesPartner_.resize(nEdges_, -1);
166 std::iota(edgesIndices_.begin(), edgesIndices_.end(), 0);
167 if(parallelSort_)
168 TTK_PSORT(globalThreadNumber_, edgesIndices_.begin(), edgesIndices_.end(),
169 [&](id_t i, id_t j) { return edges_[i] < edges_[j]; })
170 else
171 std::sort(edgesIndices_.begin(), edgesIndices_.end(),
172 [&](id_t i, id_t j) { return edges_[i] < edges_[j]; });
173 symbolicPerturbation();
174 for(unsigned i = 0; i < edgesIndices_.size(); ++i)
175 edgesOrder_[edgesIndices_[i]] = i;
176 for(const id_t &id : edgesIndices_) {
177 const auto &[s, f] = edges_[id];
178 if(UF.find(s.first) != UF.find(s.second)) {
179 UF.merge(s.first, s.second);
180 edgesPartner_[id] = -2;
181 if(++nPairedEdges_ == n_ - 1)
182 break;
183 }
184 }
185}
186
187void PairCells::symbolicPerturbation(
188 double eps) { // could be improved (many calls when lots of collisions)
189 bool generic;
190 do {
191 generic = true;
192 for(unsigned i = 0; i + 1 < edgesIndices_.size(); ++i) {
193 if(edges_[edgesIndices_[i]].d >= edges_[edgesIndices_[i + 1]].d) {
194 generic = false;
195 FiltratedEdge &e = edges_[edgesIndices_[i + 1]];
196 e.d += eps;
197 DM(e.e.first, e.e.second) += eps;
198 }
199 }
200 if(!generic) {
201 if(parallelSort_)
202 TTK_PSORT(globalThreadNumber_, edgesIndices_.begin(),
203 edgesIndices_.end(),
204 [&](id_t i, id_t j) { return edges_[i] < edges_[j]; })
205 else
206 std::sort(edgesIndices_.begin(), edgesIndices_.end(),
207 [&](id_t i, id_t j) { return edges_[i] < edges_[j]; });
208 }
209 } while(!generic);
210}
211
212void PairCells::apparentPairs() {
213 // arrays initializations
214 trianglesIndices_.resize(nTriangles_);
215 trianglesPartner_.resize(nTriangles_, -1);
216 cascadeEdges_.resize(nTriangles_);
217
218 // full order
219 std::iota(trianglesIndices_.begin(), trianglesIndices_.end(), 0);
220 if(parallelSort_)
221 TTK_PSORT(globalThreadNumber_, trianglesIndices_.begin(),
222 trianglesIndices_.end(),
223 [&](id_t i, id_t j) { return triangles_[i] < triangles_[j]; })
224 else
225 std::sort(trianglesIndices_.begin(), trianglesIndices_.end(),
226 [&](id_t i, id_t j) { return triangles_[i] < triangles_[j]; });
227
228 // apparent pairs
229 id_t t = 0;
230 for(const id_t &e : edgesIndices_) {
231 const double f = edges_[e].d;
232 while(triangles_[trianglesIndices_[t]].d < f)
233 ++t;
234 if(triangles_[trianglesIndices_[t]].d == f) {
235 edgesPartner_[e] = trianglesIndices_[t];
236 trianglesPartner_[trianglesIndices_[t]] = e;
237 nPairedEdges_++;
238 }
239 }
240}
241
242void PairCells::pairCells() {
243 for(const id_t &s : trianglesIndices_) {
244 if(trianglesPartner_[s] == -1) {
245 const int e = eliminateBoundaries(s);
246 if(e != -1) {
247 trianglesPartner_[s] = e;
248 edgesPartner_[e] = s;
249 if(++nPairedEdges_ == nEdges_)
250 break;
251 }
252 }
253 }
254}
255
256ttk::rpd::id_t PairCells::eliminateBoundaries(id_t s) {
257 BoundaryContainer bc(boundaries_[s], nEdges_);
258 while(!boundaries_[s].empty()) {
259 const id_t e = *std::max_element(
260 boundaries_[s].begin(), boundaries_[s].end(),
261 [&](id_t i, id_t j) { return edgesOrder_[i] < edgesOrder_[j]; });
262 cascadeEdges_[s].push_back(e);
263 if(edgesPartner_[e] == -1)
264 return e;
265 else
266 bc.exclusiveAddBoundary(boundaries_[edgesPartner_[e]]);
267 }
268 return -1;
269}
270
272 diagrams.resize(2);
273 for(const id_t &e : edgesIndices_) {
274 if(edgesPartner_[e] == -2)
275 diagrams[0].emplace_back(
276 FiltratedSimplex{{0}, 0.},
277 FiltratedSimplex{{edges_[e].e.first, edges_[e].e.second}, edges_[e].d});
278 else if(edgesPartner_[e] > 0
279 && edges_[e].d < triangles_[edgesPartner_[e]].d) {
280 const FiltratedSimplex birth(
281 {edges_[e].e.first, edges_[e].e.second}, edges_[e].d);
282 const FiltratedSimplex death(
283 {std::get<0>(triangles_[edgesPartner_[e]].t),
284 std::get<1>(triangles_[edgesPartner_[e]].t),
285 std::get<2>(triangles_[edgesPartner_[e]].t)},
286 triangles_[edgesPartner_[e]].d);
287 diagrams[1].emplace_back(birth, death);
288 }
289 }
290 diagrams[0].emplace_back(
291 FiltratedSimplex{{0}, 0.}, FiltratedSimplex{{-1}, inf});
292}
293
295 MultidimensionalDiagram &diagrams,
296 std::vector<Generator1> &generators) const {
297 diagrams.resize(2);
298 for(const id_t &e : edgesIndices_) {
299 if(edgesPartner_[e] == -2)
300 diagrams[0].emplace_back(
301 FiltratedSimplex{{0}, 0.},
302 FiltratedSimplex{{edges_[e].e.first, edges_[e].e.second}, edges_[e].d});
303 else if(edgesPartner_[e] > 0
304 && edges_[e].d < triangles_[edgesPartner_[e]].d) {
305 const FiltratedSimplex birth(
306 {edges_[e].e.first, edges_[e].e.second}, edges_[e].d);
307 const FiltratedSimplex death(
308 {std::get<0>(triangles_[edgesPartner_[e]].t),
309 std::get<1>(triangles_[edgesPartner_[e]].t),
310 std::get<2>(triangles_[edgesPartner_[e]].t)},
311 triangles_[edgesPartner_[e]].d);
312 diagrams[1].emplace_back(birth, death);
313
314 EdgeSet boundary;
315 for(const id_t &edge : boundaries_[edgesPartner_[e]])
316 boundary.emplace_back(edges_[edge].e);
317 generators.emplace_back(
318 boundary, std::make_pair(edges_[e].d, triangles_[edgesPartner_[e]].d));
319 }
320 }
321 diagrams[0].emplace_back(
322 FiltratedSimplex{{0}, 0.}, FiltratedSimplex{{-1}, inf});
323}
324
325void PairCells::getCascades(std::vector<Cascade> &cascades,
326 EdgeSets3 &critical) const {
327 for(const id_t &e : edgesIndices_) {
328 if(edgesPartner_[e] == -2) // MST
329 critical[DEATH0].emplace_back(edges_[e].e);
330 else if(edgesPartner_[e] > 0
331 && edges_[e].d < triangles_[edgesPartner_[e]].d) {
332 const Edge &killerEdge = edges_[cascadeEdges_[edgesPartner_[e]][0]].e;
333 critical[BIRTH1].emplace_back(edges_[e].e); // RNG edge
334 critical[DEATH1].emplace_back(killerEdge); // MML edge
335 Cascade cascade = {killerEdge};
336 for(unsigned i = 1; i < cascadeEdges_[edgesPartner_[e]].size() - 1; ++i)
337 cascade.emplace_back(edges_[cascadeEdges_[edgesPartner_[e]][i]].e);
338 cascades.emplace_back(cascade);
339 }
340 }
341}
342
343void PairCells::getCascades(EdgeSets4 &critical) const {
344 std::set<id_t> cascadeSet;
345 for(const id_t &e : edgesIndices_) {
346 if(edgesPartner_[e] == -2) // MST
347 critical[DEATH0].emplace_back(edges_[e].e);
348 else if(edgesPartner_[e] > 0
349 && edges_[e].d < triangles_[edgesPartner_[e]].d) {
350 const Edge &killerEdge = edges_[cascadeEdges_[edgesPartner_[e]][0]].e;
351 critical[BIRTH1].emplace_back(edges_[e].e); // RNG edge
352 critical[DEATH1].emplace_back(killerEdge); // MML edge
353 for(unsigned i = 1; i < cascadeEdges_[edgesPartner_[e]].size() - 1; ++i)
354 cascadeSet.insert(cascadeEdges_[edgesPartner_[e]][i]);
355 }
356 }
357 for(const id_t &e : cascadeSet)
358 critical[CASC1].emplace_back(edges_[e].e);
359}
360
361void PairCells::enrichCascades(std::set<Edge> &cascadeSet,
362 EdgeSets4 &critical,
363 std::vector<int> const &globalIndices) const {
364 for(const id_t &e : edgesIndices_) {
365 if(edgesPartner_[e] > 0 && edges_[e].d < triangles_[edgesPartner_[e]].d) {
366 const Edge &killerEdge = edges_[cascadeEdges_[edgesPartner_[e]][0]].e;
367 critical[DEATH1].emplace_back(
368 globalIndices[killerEdge.first],
369 globalIndices[killerEdge.second]); // MML edge
370 for(unsigned i = 1; i < cascadeEdges_[edgesPartner_[e]].size() - 1; ++i) {
371 const Edge &edge = edges_[cascadeEdges_[edgesPartner_[e]][i]].e;
372 cascadeSet.emplace(
373 globalIndices[edge.first], globalIndices[edge.second]);
374 }
375 }
376 }
377}
#define TTK_FORCE_USE(x)
Force the compiler to use the function/method parameter.
Definition BaseClass.h:57
#define TTK_PSORT(NTHREADS,...)
Parallel sort macro.
Definition OpenMP.h:46
void setDebugMsgPrefix(const std::string &prefix)
Definition Debug.h:364
AtomicUF * find()
Definition FTMAtomicUF.h:38
void getDiagram(MultidimensionalDiagram &diagrams) const
void getCascades(std::vector< Cascade > &cascades, EdgeSets3 &critical) const
void getDiagramAndGenerators(MultidimensionalDiagram &diagrams, std::vector< Generator1 > &generators) const
PairCells(const PointCloud &points, bool distanceMatrix=false, double upperBound=inf, bool parallelSort=false, bool parallelMatrixConstruction=false)
Definition PairCells.cpp:25
void enrichCascades(std::set< Edge > &cascadeSet, EdgeSets4 &critical, std::vector< int > const &globalIndices) const
AtomicUF * UF
Definition FTMTree_MT.h:39
std::pair< id_t, id_t > Edge
std::array< EdgeSet, 4 > EdgeSets4
constexpr value_t inf
std::vector< std::vector< value_t > > PointCloud
std::vector< Edge > EdgeSet
std::vector< Diagram > MultidimensionalDiagram
std::pair< Simplex, value_t > FiltratedSimplex
std::array< EdgeSet, 3 > EdgeSets3
COMMON_EXPORTS int globalThreadNumber_
Definition BaseClass.cpp:6
T end(std::pair< T, T > &p)
Definition ripser.cpp:503
T begin(std::pair< T, T > &p)
Definition ripser.cpp:499
printMsg(debug::output::BOLD+" | | | | | . \\ | | (__| | / __/| |_| / __/| (_) |"+debug::output::ENDCOLOR, debug::Priority::PERFORMANCE, debug::LineMode::NEW, stream)