Jolt Physics
A multi core friendly Game Physics Engine
Loading...
Searching...
No Matches
EigenValueSymmetric.h
Go to the documentation of this file.
1// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
2// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
8
10
28template <class Vector, class Matrix>
29bool EigenValueSymmetric(const Matrix &inMatrix, Matrix &outEigVec, Vector &outEigVal)
30{
31 // This algorithm can generate infinite values, see comment below
32 FPExceptionDisableInvalid disable_invalid;
33 (void)disable_invalid;
34
35 // Maximum number of sweeps to make
36 const int cMaxSweeps = 50;
37
38 // Get problem dimension
39 const uint n = inMatrix.GetRows();
40
41 // Make sure the dimensions are right
42 JPH_ASSERT(inMatrix.GetRows() == n);
43 JPH_ASSERT(inMatrix.GetCols() == n);
44 JPH_ASSERT(outEigVec.GetRows() == n);
45 JPH_ASSERT(outEigVec.GetCols() == n);
46 JPH_ASSERT(outEigVal.GetRows() == n);
47 JPH_ASSERT(outEigVec.IsIdentity());
48
49 // Get the matrix in a so we can mess with it
50 Matrix a = inMatrix;
51
52 Vector b, z;
53
54 for (uint ip = 0; ip < n; ++ip)
55 {
56 // Initialize b to diagonal of a
57 b[ip] = a(ip, ip);
58
59 // Initialize output to diagonal of a
60 outEigVal[ip] = a(ip, ip);
61
62 // Reset z
63 z[ip] = 0.0f;
64 }
65
66 for (int sweep = 0; sweep < cMaxSweeps; ++sweep)
67 {
68 // Get the sum of the off-diagonal elements of a
69 float sm = 0.0f;
70 for (uint ip = 0; ip < n - 1; ++ip)
71 for (uint iq = ip + 1; iq < n; ++iq)
72 sm += abs(a(ip, iq));
73 float avg_sm = sm / Square(n);
74
75 // Normal return, convergence to machine underflow
76 if (avg_sm < FLT_MIN) // Original code: sm == 0.0f, when the average is denormal, we also consider it machine underflow
77 {
78 // Sanity checks
79 #ifdef JPH_ENABLE_ASSERTS
80 for (uint c = 0; c < n; ++c)
81 {
82 // Check if the eigenvector is normalized
83 JPH_ASSERT(outEigVec.GetColumn(c).IsNormalized());
84
85 // Check if inMatrix * eigen_vector = eigen_value * eigen_vector
86 Vector mat_eigvec = inMatrix * outEigVec.GetColumn(c);
87 Vector eigval_eigvec = outEigVal[c] * outEigVec.GetColumn(c);
88 JPH_ASSERT(mat_eigvec.IsClose(eigval_eigvec, max(mat_eigvec.LengthSq(), eigval_eigvec.LengthSq()) * 1.0e-6f));
89 }
90 #endif
91
92 // Success
93 return true;
94 }
95
96 // On the first three sweeps use a fraction of the sum of the off diagonal elements as threshold
97 // Note that we pick a minimum threshold of FLT_MIN because dividing by a denormalized number is likely to result in infinity.
98 float tresh = sweep < 4? 0.2f * avg_sm : FLT_MIN; // Original code: 0.0f instead of FLT_MIN
99
100 for (uint ip = 0; ip < n - 1; ++ip)
101 for (uint iq = ip + 1; iq < n; ++iq)
102 {
103 float &a_pq = a(ip, iq);
104 float &eigval_p = outEigVal[ip];
105 float &eigval_q = outEigVal[iq];
106
107 float abs_a_pq = abs(a_pq);
108 float g = 100.0f * abs_a_pq;
109
110 // After four sweeps, skip the rotation if the off-diagonal element is small
111 if (sweep > 4
112 && abs(eigval_p) + g == abs(eigval_p)
113 && abs(eigval_q) + g == abs(eigval_q))
114 {
115 a_pq = 0.0f;
116 }
117 else if (abs_a_pq > tresh)
118 {
119 float h = eigval_q - eigval_p;
120 float abs_h = abs(h);
121
122 float t;
123 if (abs_h + g == abs_h)
124 {
125 t = a_pq / h;
126 }
127 else
128 {
129 float theta = 0.5f * h / a_pq; // Warning: Can become infinite if a(ip, iq) is very small which may trigger an invalid float exception
130 t = 1.0f / (abs(theta) + sqrt(1.0f + theta * theta)); // If theta becomes inf, t will be 0 so the infinite is not a problem for the algorithm
131 if (theta < 0.0f) t = -t;
132 }
133
134 float c = 1.0f / sqrt(1.0f + t * t);
135 float s = t * c;
136 float tau = s / (1.0f + c);
137 h = t * a_pq;
138
139 a_pq = 0.0f;
140
141 z[ip] -= h;
142 z[iq] += h;
143
144 eigval_p -= h;
145 eigval_q += h;
146
147 #define JPH_EVS_ROTATE(a, i, j, k, l) \
148 g = a(i, j), \
149 h = a(k, l), \
150 a(i, j) = g - s * (h + g * tau), \
151 a(k, l) = h + s * (g - h * tau)
152
153 uint j;
154 for (j = 0; j < ip; ++j) JPH_EVS_ROTATE(a, j, ip, j, iq);
155 for (j = ip + 1; j < iq; ++j) JPH_EVS_ROTATE(a, ip, j, j, iq);
156 for (j = iq + 1; j < n; ++j) JPH_EVS_ROTATE(a, ip, j, iq, j);
157 for (j = 0; j < n; ++j) JPH_EVS_ROTATE(outEigVec, j, ip, j, iq);
158
159 #undef JPH_EVS_ROTATE
160 }
161 }
162
163 // Update eigenvalues with the sum of ta_pq and reinitialize z
164 for (uint ip = 0; ip < n; ++ip)
165 {
166 b[ip] += z[ip];
167 outEigVal[ip] = b[ip];
168 z[ip] = 0.0f;
169 }
170 }
171
172 // Failure
173 JPH_ASSERT(false, "Too many iterations");
174 return false;
175}
176
unsigned int uint
Definition: Core.h:452
#define JPH_NAMESPACE_END
Definition: Core.h:378
#define JPH_NAMESPACE_BEGIN
Definition: Core.h:372
JPH_NAMESPACE_BEGIN bool EigenValueSymmetric(const Matrix &inMatrix, Matrix &outEigVec, Vector &outEigVal)
Definition: EigenValueSymmetric.h:29
#define JPH_EVS_ROTATE(a, i, j, k, l)
#define JPH_ASSERT(...)
Definition: IssueReporting.h:33
JPH_INLINE constexpr T Square(T inV)
Square a value.
Definition: Math.h:52
Definition: FPException.h:69
Templatized matrix class.
Definition: Matrix.h:15
uint GetRows() const
Dimensions.
Definition: Matrix.h:22
bool IsIdentity() const
Check if this matrix is identity.
Definition: Matrix.h:58
uint GetCols() const
Definition: Matrix.h:23
const Vector< Rows > & GetColumn(int inIdx) const
Column access.
Definition: Matrix.h:225
Templatized vector class.
Definition: Vector.h:12
uint GetRows() const
Dimensions.
Definition: Vector.h:19
bool IsClose(const Vector &inV2, float inMaxDistSq=1.0e-12f)
Test if two vectors are close to each other.
Definition: Vector.h:78
float LengthSq() const
Squared length of vector.
Definition: Vector.h:175