Jpp 19.3.0-rc.2
the software that should make you happy
Loading...
Searching...
No Matches
JSVD3D.hh
Go to the documentation of this file.
1#ifndef __JMATH_JSVD3D__
2#define __JMATH_JSVD3D__
3
4#include <algorithm>
5#include <cmath>
6#include <array>
7
8#include "JMath/JConstants.hh"
9#include "JMath/JMatrix3D.hh"
10#include "JMath/JMatrix3S.hh"
11
12
13namespace JMATH {}
14namespace JPP { using namespace JMATH; }
15
16namespace JMATH {
17
18 /**
19 * Singular value decomposition.
20 *
21 * See:
22 * "Computing the Singular Value Decomposition of 3x3 matrices
23 * with minimal branching and elementary floating point operations",\n
24 * A.\ McAdams, A.\ Selle, R.\ Tamstorf, J.\ Teran and E.\ Sifakis,\n
25 * University of Wisconsin - Madison technical report TR1690, May 2011
26 */
27 class JSVD3D {
28 public:
29 /**
30 * Default constructor.
31 */
33 {}
34
35
36 /**
37 * Constructor.
38 *
39 * \param A matrix
40 */
41 JSVD3D(const JMatrix3D& A)
42 {
43 decompose(A);
44 }
45
46
47 /**
48 * Decompose given matrix.
49 *
50 * \param A matrix
51 */
52 void decompose(const JMatrix3D& A)
53 {
54 using namespace std;
55
56 // B = A^T * A
57
58 B.a00 = A.a00 * A.a00 + A.a10 * A.a10 + A.a20 * A.a20;
59
60 B.a10 = A.a01 * A.a00 + A.a11 * A.a10 + A.a21 * A.a20;
61 B.a11 = A.a01 * A.a01 + A.a11 * A.a11 + A.a21 * A.a21;
62
63 B.a20 = A.a02 * A.a00 + A.a12 * A.a10 + A.a22 * A.a20;
64 B.a21 = A.a02 * A.a01 + A.a12 * A.a11 + A.a22 * A.a21;
65 B.a22 = A.a02 * A.a02 + A.a12 * A.a12 + A.a22 * A.a22;
66
67 B.a01 = B.a10;
68 B.a02 = B.a20;
69 B.a12 = B.a21;
70
71
72 // symmetric eigen alysis
73
74 const JQuaternion q(B);
75
76 const double w = q[3];
77 const double x = q[0];
78 const double y = q[1];
79 const double z = q[2];
80
81 const double xx = x*x;
82 const double yy = y*y;
83 const double zz = z*z;
84 const double xz = x*z;
85 const double xy = x*y;
86 const double yz = y*z;
87 const double wx = w*x;
88 const double wy = w*y;
89 const double wz = w*z;
90
91 V.a00 = 1.0 - 2.0*(yy + zz); V.a01 = 2.0*(xy - wz); V.a02 = 2.0*(xz + wy);
92 V.a10 = 2.0*(xy + wz); V.a11 = 1.0 - 2.0*(xx + zz); V.a12 = 2.0*(yz - wx);
93 V.a20 = 2.0*(xz - wy); V.a21 = 2.0*(yz + wx); V.a22 = 1.0 - 2.0*(xx + yy);
94
95
96 B.mul(A, V);
97
98
99 // sort singular values.
100
101 double rho1 = getLengthSquared(B.a00, B.a10, B.a20);
102 double rho2 = getLengthSquared(B.a01, B.a11, B.a21);
103 double rho3 = getLengthSquared(B.a02, B.a12, B.a22);
104
105 if (rho1 < rho2) {
106 swap(B.a00,B.a01); B.a01 = -B.a01; swap(V.a00,V.a01); V.a01 = -V.a01;
107 swap(B.a10,B.a11); B.a11 = -B.a11; swap(V.a10,V.a11); V.a11 = -V.a11;
108 swap(B.a20,B.a21); B.a21 = -B.a21; swap(V.a20,V.a21); V.a21 = -V.a21;
109 swap(rho1,rho2);
110 }
111
112 if (rho1 < rho3) {
113 swap(B.a00,B.a02); B.a02 = -B.a02; swap(V.a00,V.a02); V.a02 = -V.a02;
114 swap(B.a10,B.a12); B.a12 = -B.a12; swap(V.a10,V.a12); V.a12 = -V.a12;
115 swap(B.a20,B.a22); B.a22 = -B.a22; swap(V.a20,V.a22); V.a22 = -V.a22;
116 swap(rho1,rho3);
117 }
118
119 if (rho2 < rho3) {
120 swap(B.a01,B.a02); B.a02 = -B.a02; swap(V.a01,V.a02); V.a02 = -V.a02;
121 swap(B.a11,B.a12); B.a12 = -B.a12; swap(V.a11,V.a12); V.a12 = -V.a12;
122 swap(B.a21,B.a22); B.a22 = -B.a22; swap(V.a21,V.a22); V.a22 = -V.a22;
123 }
124
125
126 // QR decomposition
127
128 JMatrix3D& Q = U;
129 JMatrix3D& R = S;
130
131 double a, b;
132
133 const JGivens q1(B.a00, B.a10); // 1st Givens rotation (ch,0,0,sh)
134
135 a = q1.geta();
136 b = q1.getb();
137
138 // apply B = Q' * B
139
140 R.a00 = a*B.a00 + b*B.a10; R.a01 = a*B.a01 + b*B.a11; R.a02 = a*B.a02 + b*B.a12;
141 R.a10 = -b*B.a00 + a*B.a10; R.a11 = -b*B.a01 + a*B.a11; R.a12 = -b*B.a02 + a*B.a12;
142 R.a20 = B.a20; R.a21 = B.a21; R.a22 = B.a22;
143
144 const JGivens q2(R.a00, R.a20); // 2nd Givens rotation (ch,0,-sh,0)
145
146 a = q2.geta();
147 b = q2.getb();
148
149 // apply B = Q' * B;
150
151 B.a00 = a*R.a00 + b*R.a20; B.a01 = a*R.a01 + b*R.a21; B.a02 = a*R.a02 + b*R.a22;
152 B.a10 = R.a10; B.a11 = R.a11; B.a12 = R.a12;
153 B.a20 = -b*R.a00 + a*R.a20; B.a21 = -b*R.a01 + a*R.a21; B.a22 = -b*R.a02 + a*R.a22;
154
155 const JGivens q3(B.a11, B.a21); // 3rd Givens rotation (ch,sh,0,0)
156
157 a = q3.geta();
158 b = q3.getb();
159
160 // R is now set to desired value
161
162 R.a00 = B.a00; R.a01 = B.a01; R.a02 = B.a02;
163 R.a10 = a*B.a10+b*B.a20; R.a11 = a*B.a11 + b*B.a21; R.a12 = a*B.a12 + b*B.a22;
164 R.a20 = -b*B.a10+a*B.a20; R.a21 = -b*B.a11 + a*B.a21; R.a22 = -b*B.a12 + a*B.a22;
165
166 // Q = Q1 * Q2 * Q3
167
168 const double sp1 = -1.0 + 2.0*q1.sh*q1.sh;
169 const double sp2 = -1.0 + 2.0*q2.sh*q2.sh;
170 const double sp3 = -1.0 + 2.0*q3.sh*q3.sh;
171
172 Q.a00 = sp1*sp2;
173 Q.a01 = 4.0*q2.ch*q3.ch*sp1*q2.sh*q3.sh + 2.0*q1.ch*q1.sh*sp3;
174 Q.a02 = 4.0*q1.ch*q3.ch*q1.sh*q3.sh - 2.0*q2.ch*sp1*q2.sh*sp3;
175
176 Q.a10 = -2.0*q1.ch*q1.sh*sp2;
177 Q.a11 = -8.0*q1.ch*q2.ch*q3.ch*q1.sh*q2.sh*q3.sh + sp1*sp3;
178 Q.a12 = -2.0*q3.ch*q3.sh + 4.0*q1.sh*(q3.ch*q1.sh*q3.sh + q1.ch*q2.ch*q2.sh*sp3);
179
180 Q.a20 = 2.0*q2.ch*q2.sh;
181 Q.a21 = -2.0*q3.ch*sp2*q3.sh;
182 Q.a22 = sp2*sp3;
183 }
184
185
186 /**
187 * Get inverted matrix.
188 *
189 * \param precision precision
190 * \return inverted matrix
191 */
192 const JMatrix3D& invert(const double precision = 1.0e-12) const
193 {
194 double w = fabs(S.a00);
195
196 if (fabs(S.a11) > w) { w = fabs(S.a11); }
197 if (fabs(S.a22) > w) { w = fabs(S.a22); }
198
199 w *= precision;
200
201 const double s00 = (fabs(S.a00) >= w ? 1.0 / S.a00 : 0.0);
202 const double s11 = (fabs(S.a11) >= w ? 1.0 / S.a11 : 0.0);
203 const double s22 = (fabs(S.a22) >= w ? 1.0 / S.a22 : 0.0);
204
205 // B = V * S^-1
206
207 B.a00 = V.a00*s00; B.a01 = V.a01*s11; B.a02 = V.a02*s22;
208 B.a10 = V.a10*s00; B.a11 = V.a11*s11; B.a12 = V.a12*s22;
209 B.a20 = V.a20*s00; B.a21 = V.a21*s11; B.a22 = V.a22*s22;
210
211 U.transpose();
212
213 // B = V * S^-1 * U^T
214
215 B.mul(U);
216
217 U.transpose();
218
219 return B;
220 }
221
222 mutable JMatrix3D U;
223 mutable JMatrix3D S;
224 mutable JMatrix3D V;
225
226 private:
227
228 mutable JMatrix3S B; // work space
229
230 /**
231 * Auxiliary class for quaternion computation.
232 */
233 struct JQuaternion :
234 public std::array<double, 4>
235 {
236 /**
237 * Constructor.
238 *
239 * Finds transformation that diagonalizes the given symmetric matrix by using Jacobi eigen analysis.
240 *
241 * \param S matrix
242 */
244 {
245 // follow same indexing convention as GLM
246
247 (*this)[3] = 1.0;
248 (*this)[0] = 0.0;
249 (*this)[1] = 0.0;
250 (*this)[2] = 0.0;
251
252 for (int i = 0; i != 4; ++i) {
253
254 // eliminate maximum off-diagonal element on every iteration,
255 // while cycling over all 3 possible rotations in fixed order.
256 // (p,q) = (0,1), (1,2), (0,2) still retains asymptotic convergence.
257
258 conjugate(0, 1, 2, S); // (p,q) = (0,1)
259 conjugate(1, 2, 0, S); // (p,q) = (1,2)
260 conjugate(2, 0, 1, S); // (p,q) = (0,2)
261 }
262 }
263
264 private:
265
266 /**
267 * Get conjugate of symmetric matrix for given order.
268 *
269 * \param x 1st index
270 * \param y 2nd index
271 * \param z 3rd index
272 * \param S matrix
273 */
274 inline void conjugate(const int x,
275 const int y,
276 const int z,
277 JMatrix3S& S)
278 {
279 static const double GAMMA = sqrt(8.0) + 3.0;
280 static const double CSTAR = cos(PI/8.0);
281 static const double SSTAR = sin(PI/8.0);
282
283 // approximate Givens quaternion
284
285 double ch = 2.0*(S.a00 - S.a11);
286 double sh = S.a10;
287
288 if (GAMMA*sh*sh < ch*ch) {
289
290 const double w = 1.0 / sqrt(ch*ch+sh*sh);
291
292 ch *= w;
293 sh *= w;
294
295 } else {
296
297 ch = CSTAR;
298 sh = SSTAR;
299 }
300
301 const double scale = ch*ch + sh*sh;
302 const double a = (ch+sh)*(ch-sh) / scale;
303 const double b = (2.0*sh*ch) / scale;
304
305 // make temporary copy of S
306
307 double s00 = S.a00;
308 double s10 = S.a10; double s11 = S.a11;
309 double s20 = S.a20; double s21 = S.a21; double s22 = S.a22;
310
311 // perform conjugation S = Q' * S * Q
312 // where Q is already implicitly solved from a and b
313
314 S.a00 = a*( a*s00 + b*s10) + b*( a*s10 + b*s11);
315 S.a10 = a*(-b*s00 + a*s10) + b*(-b*s10 + a*s11);
316 S.a11 = -b*(-b*s00 + a*s10) + a*(-b*s10 + a*s11);
317 S.a20 = a*s20 + b*s21;
318 S.a21 = -b*s20 + a*s21;
319 S.a22 = s22;
320
321 // update cumulative rotation
322
323 double tmp[] = {
324 (*this)[0]*sh,
325 (*this)[1]*sh,
326 (*this)[2]*sh
327 };
328
329 sh *= (*this)[3];
330
331 (*this)[0] *= ch;
332 (*this)[1] *= ch;
333 (*this)[2] *= ch;
334 (*this)[3] *= ch;
335
336 // (x,y,z) corresponds to {(0,1,2), (1,2,0), (2,0,1)}
337 // for (p,q) = {(0,1), (1,2), (0,2)}, respectively.
338
339 (*this)[z] += sh;
340 (*this)[3] -= tmp[z]; // w
341 (*this)[x] += tmp[y];
342 (*this)[y] -= tmp[x];
343
344 // re-arrange matrix for next iteration
345
346 s00 = S.a11;
347 s10 = S.a21; s11 = S.a22;
348 s20 = S.a10; s21 = S.a20; s22 = S.a00;
349
350 S.a00 = s00;
351 S.a10 = s10; S.a11 = s11;
352 S.a20 = s20; S.a21 = s21; S.a22 = s22;
353 }
354 };
355
356
357 /**
358 * Get length squared.
359 *
360 * \param x x
361 * \param y y
362 * \param z z
363 * \return square of length
364 */
365 static inline double getLengthSquared(const double x, const double y, const double z)
366 {
367 return x*x + y*y + z*z;
368 }
369
370
371 /**
372 * Givens quaternion.
373 */
374 struct JGivens {
375
376 static constexpr double EPSILON = 1.0e-6;
377
378 /**
379 * Constructor.
380 *
381 * \param a pivot point on diagonal
382 * \param b lower triangular entry we want to annihilate
383 */
384 JGivens(const double a,
385 const double b)
386 {
387 using namespace std;
388
389 const double rho = sqrt(a*a + b*b);
390
391 sh = rho > EPSILON ? b : 0.0;
392 ch = fabs(a) + max(rho,EPSILON);
393
394 if (a < 0.0) {
395 swap(sh,ch);
396 }
397
398 const double w = 1.0 / sqrt(ch*ch + sh*sh);
399
400 ch *= w;
401 sh *= w;
402 }
403
404 inline double geta() const { return 1.0 - 2.0*sh*sh; } //!< get new a value
405 inline double getb() const { return 2.0*ch*sh; } //!< get new b value
406
407 double ch; //!< cosine rotation angle
408 double sh; //!< sine rotation angle
409 };
410 };
411}
412
413#endif
void scale(vector< double > &v, double c)
scale vector content
Mathematical constants.
JMatrix3D & mul(const double factor)
Scale matrix.
JMatrix3D & transpose()
Transpose.
3 x 3 symmetric matrix
Definition JMatrix3S.hh:31
Singular value decomposition.
Definition JSVD3D.hh:27
const JMatrix3D & invert(const double precision=1.0e-12) const
Get inverted matrix.
Definition JSVD3D.hh:192
void decompose(const JMatrix3D &A)
Decompose given matrix.
Definition JSVD3D.hh:52
JMatrix3D S
Definition JSVD3D.hh:223
static double getLengthSquared(const double x, const double y, const double z)
Get length squared.
Definition JSVD3D.hh:365
JMatrix3D V
Definition JSVD3D.hh:224
JSVD3D()
Default constructor.
Definition JSVD3D.hh:32
JMatrix3S B
Definition JSVD3D.hh:228
JSVD3D(const JMatrix3D &A)
Constructor.
Definition JSVD3D.hh:41
JMatrix3D U
Definition JSVD3D.hh:222
Auxiliary classes and methods for mathematical operations.
Definition JEigen3D.hh:88
This name space includes all other name spaces (except KM3NETDAQ, KM3NET and ANTARES).
Givens quaternion.
Definition JSVD3D.hh:374
JGivens(const double a, const double b)
Constructor.
Definition JSVD3D.hh:384
double ch
cosine rotation angle
Definition JSVD3D.hh:407
double sh
sine rotation angle
Definition JSVD3D.hh:408
double getb() const
get new b value
Definition JSVD3D.hh:405
double geta() const
get new a value
Definition JSVD3D.hh:404
static constexpr double EPSILON
Definition JSVD3D.hh:376
Auxiliary class for quaternion computation.
Definition JSVD3D.hh:235
JQuaternion(JMatrix3S &S)
Constructor.
Definition JSVD3D.hh:243
void conjugate(const int x, const int y, const int z, JMatrix3S &S)
Get conjugate of symmetric matrix for given order.
Definition JSVD3D.hh:274