TTK
Loading...
Searching...
No Matches
Laplacian.cpp
Go to the documentation of this file.
1#include <Geometry.h>
2#include <Laplacian.h>
3
4#ifdef TTK_ENABLE_EIGEN
5#include <Eigen/Sparse>
6
7#include <array>
8
9template <typename T,
10 class TriangulationType,
11 typename SparseMatrixType = Eigen::SparseMatrix<T>>
12int ttk::Laplacian::discreteLaplacian(SparseMatrixType &output,
13 const Debug &dbg,
14 const TriangulationType &triangulation) {
15
16 Timer tm{};
17
18 using Triplet = Eigen::Triplet<T>;
19 const auto vertexNumber = triangulation.getNumberOfVertices();
20 const auto edgeNumber = triangulation.getNumberOfEdges();
21
22 // early return when input graph is empty
23 if(vertexNumber <= 0) {
24 return -1;
25 }
26
27 const auto threadNumber = dbg.getThreadNumber();
28
29 // clear output
30 output.resize(vertexNumber, vertexNumber);
31 output.setZero();
32
33 // number of triplets to insert into laplacian matrix: vertexNumber_
34 // values on the diagonal + 2 values per edge
35 std::vector<Triplet> triplets(vertexNumber + 2 * edgeNumber);
36
37 // on the diagonal: number of neighbors
38#ifdef TTK_ENABLE_OPENMP
39#pragma omp parallel for num_threads(threadNumber)
40#endif // TTK_ENABLE_OPENMP
41 for(SimplexId i = 0; i < vertexNumber; ++i) {
42 const auto nneigh = triangulation.getVertexNeighborNumber(i);
43 triplets[i] = Triplet(i, i, T(nneigh));
44 }
45
46 // neighbors mapping: loop over edges
47#ifdef TTK_ENABLE_OPENMP
48#pragma omp parallel for num_threads(threadNumber)
49#endif // TTK_ENABLE_OPENMP
50 for(SimplexId i = 0; i < edgeNumber; ++i) {
51 // the two vertices of the current edge
52 std::vector<SimplexId> edgeVertices(2);
53 for(SimplexId j = 0; j < 2; ++j) {
54 triangulation.getEdgeVertex(i, j, edgeVertices[j]);
55 }
56 // fill triplets for both vertices of current edge
57 triplets[vertexNumber + 2 * i]
58 = Triplet(edgeVertices[0], edgeVertices[1], T(-1.0));
59 triplets[vertexNumber + 2 * i + 1]
60 = Triplet(edgeVertices[1], edgeVertices[0], T(-1.0));
61 }
62
63#ifndef __clang_analyzer__
64 output.setFromTriplets(triplets.begin(), triplets.end());
65#endif // __clang_analyzer__
66
67 dbg.printMsg(
68 "Computed Discrete Laplacian", 1.0, tm.getElapsedTime(), threadNumber);
69
70 return 0;
71}
72
73template <typename T,
74 class TriangulationType,
75 typename SparseMatrixType = Eigen::SparseMatrix<T>>
76int ttk::Laplacian::cotanWeights(SparseMatrixType &output,
77 const Debug &dbg,
78 const TriangulationType &triangulation) {
79
80 Timer tm{};
81
82 using Triplet = Eigen::Triplet<T>;
83 const auto vertexNumber = triangulation.getNumberOfVertices();
84 const auto edgeNumber = triangulation.getNumberOfEdges();
85
86 const auto threadNumber = dbg.getThreadNumber();
87
88 // early return when input graph is empty
89 if(vertexNumber <= 0) {
90 return -1;
91 }
92
93 // clear output
94 output.resize(vertexNumber, vertexNumber);
95 output.setZero();
96
97 // number of triplets to insert into laplacian matrix: vertexNumber_
98 // values on the diagonal + 2 values per edge
99 std::vector<Triplet> triplets(vertexNumber + 2 * edgeNumber);
100
101 std::vector<SimplexId> edgeTriangles{};
102 std::vector<T> angles{};
103
104 // iterate over all edges
105#ifdef TTK_ENABLE_OPENMP
106#pragma omp parallel for num_threads(threadNumber) \
107 firstprivate(edgeTriangles, angles)
108#endif // TTK_ENABLE_OPENMP
109 for(SimplexId i = 0; i < edgeNumber; ++i) {
110
111 edgeTriangles.clear();
112 angles.clear();
113
114 // the two vertices of the current edge (+ a third)
115 std::array<SimplexId, 3> edgeVertices{};
116 for(SimplexId j = 0; j < 2; ++j) {
117 triangulation.getEdgeVertex(i, j, edgeVertices[j]);
118 }
119
120 // get the triangles that share the current edge
121 // in 2D only 2, in 3D, maybe more...
122 const auto trianglesNumber = triangulation.getEdgeTriangleNumber(i);
123 // stores the triangles ID for every triangle around the current edge
124 edgeTriangles.resize(trianglesNumber);
125 for(SimplexId j = 0; j < trianglesNumber; ++j) {
126 triangulation.getEdgeTriangle(i, j, edgeTriangles[j]);
127 }
128
129 // iterate over current edge triangles
130 angles.reserve(trianglesNumber);
131
132 for(const auto &j : edgeTriangles) {
133
134 // get the third vertex of the triangle
135 SimplexId thirdNeigh;
136 // a triangle has only three vertices
137 for(SimplexId k = 0; k < 3; ++k) {
138 triangulation.getTriangleVertex(j, k, thirdNeigh);
139 if(thirdNeigh != edgeVertices[0] && thirdNeigh != edgeVertices[1]) {
140 // store the third vertex ID into the edgeVertices array to
141 // be more easily handled
142 edgeVertices[2] = thirdNeigh;
143 break;
144 }
145 }
146 // compute the 3D coords of the three vertices
147 std::array<float, 9> coords{};
148 for(SimplexId k = 0; k < 3; ++k) {
149 triangulation.getVertexPoint(
150 edgeVertices[k], coords[3 * k], coords[3 * k + 1], coords[3 * k + 2]);
151 }
152 angles.emplace_back(ttk::Geometry::angle(&coords[6], // edgeVertices[2]
153 &coords[0], // edgeVertices[0]
154 &coords[6], // edgeVertices[2]
155 &coords[3]) // edgeVertices[1]
156 );
157 }
158
159 // cotan weights for every triangle around the current edge
160 T cotan_weight{0.0};
161 // C++ has no map statement until C++17 (std::transform)
162 for(auto &angle : angles) {
163 cotan_weight += T(1.0) / std::tan(angle);
164 }
165
166 // since we iterate over the edges, fill the laplacian matrix
167 // symmetrically for the two vertices
168 triplets[2 * i] = Triplet(edgeVertices[0], edgeVertices[1], -cotan_weight);
169 triplets[2 * i + 1]
170 = Triplet(edgeVertices[1], edgeVertices[0], -cotan_weight);
171 }
172
173 // on the diagonal: sum of cotan weights for every vertex
174#ifdef TTK_ENABLE_OPENMP
175#pragma omp parallel for num_threads(threadNumber)
176#endif // TTK_ENABLE_OPENMP
177 for(SimplexId i = 0; i < vertexNumber; ++i) {
178 T vertWeightSum{};
179 const auto nEdges{triangulation.getVertexEdgeNumber(i)};
180 // get the (-) cotan weights from the edges triplets
181 for(SimplexId j = 0; j < nEdges; ++j) {
182 SimplexId e{};
183 triangulation.getVertexEdge(i, j, e);
184 vertWeightSum += triplets[2 * e].value();
185 }
186
187 triplets[2 * edgeNumber + i] = Triplet(i, i, -vertWeightSum);
188 }
189
190#ifndef __clang_analyzer__
191 output.setFromTriplets(triplets.begin(), triplets.end());
192#endif // __clang_analyzer__
193
194 dbg.printMsg("Computed Laplacian with Cotan Weights", 1.0,
195 tm.getElapsedTime(), threadNumber);
196
197 return 0;
198}
199
200#define LAPLACIAN_SPECIALIZE(TYPE) \
201 template int ttk::Laplacian::discreteLaplacian<TYPE>( \
202 Eigen::SparseMatrix<TYPE> &, const Debug &dbg, const Triangulation &); \
203 template int ttk::Laplacian::cotanWeights<TYPE>( \
204 Eigen::SparseMatrix<TYPE> &, const Debug &dbg, const Triangulation &)
205
206// explicit intantiations for floating-point types
207LAPLACIAN_SPECIALIZE(float);
208LAPLACIAN_SPECIALIZE(double);
209
210#endif // TTK_ENABLE_EIGEN
T angle(const T *vA0, const T *vA1, const T *vB0, const T *vB1)
Definition Geometry.cpp:13
int discreteLaplacian(SparseMatrixType &output, const Debug &dbg, const TriangulationType &triangulation)
Compute the Laplacian matrix of the graph.
int cotanWeights(SparseMatrixType &output, const Debug &dbg, const TriangulationType &triangulation)
Compute the Laplacian matrix of the graph using the cotangente weights method.
int SimplexId
Identifier type for simplices of any dimension.
Definition DataTypes.h:22