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 works with very small numbers and can trigger invalid float exceptions when not flushing denormals
32 FPFlushDenormals flush_denormals;
33 (void)flush_denormals;
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
74 // Normal return, convergence to machine underflow
75 if (sm == 0.0f)
76 {
77 // Sanity checks
78 #ifdef JPH_ENABLE_ASSERTS
79 for (uint c = 0; c < n; ++c)
80 {
81 // Check if the eigenvector is normalized
82 JPH_ASSERT(outEigVec.GetColumn(c).IsNormalized());
83
84 // Check if inMatrix * eigen_vector = eigen_value * eigen_vector
85 Vector mat_eigvec = inMatrix * outEigVec.GetColumn(c);
86 Vector eigval_eigvec = outEigVal[c] * outEigVec.GetColumn(c);
87 JPH_ASSERT(mat_eigvec.IsClose(eigval_eigvec, max(mat_eigvec.LengthSq(), eigval_eigvec.LengthSq()) * 1.0e-6f));
88 }
89 #endif
90
91 // Success
92 return true;
93 }
94
95 // On the first three sweeps use a fraction of the sum of the off diagonal elements as threshold
96 float tresh = sweep < 4? 0.2f * sm / Square(n) : 0.0f;
97
98 for (uint ip = 0; ip < n - 1; ++ip)
99 for (uint iq = ip + 1; iq < n; ++iq)
100 {
101 float g = 100.0f * abs(a(ip, iq));
102
103 // After four sweeps, skip the rotation if the off-diagonal element is small
104 if (sweep > 4
105 && abs(outEigVal[ip]) + g == abs(outEigVal[ip])
106 && abs(outEigVal[iq]) + g == abs(outEigVal[iq]))
107 {
108 a(ip, iq) = 0.0f;
109 }
110 else if (abs(a(ip, iq)) > tresh)
111 {
112 float h = outEigVal[iq] - outEigVal[ip];
113
114 float t;
115 if (abs(h) + g == abs(h))
116 {
117 t = a(ip, iq) / h;
118 }
119 else
120 {
121 float theta = 0.5f * h / a(ip, iq); // Warning: Can become inf if a(ip, iq) too small
122 t = 1.0f / (abs(theta) + sqrt(1.0f + theta * theta)); // Warning: Squaring large value can make it inf
123 if (theta < 0.0f) t = -t;
124 }
125
126 float c = 1.0f / sqrt(1.0f + t * t);
127 float s = t * c;
128 float tau = s / (1.0f + c);
129 h = t * a(ip, iq);
130
131 a(ip, iq) = 0.0f;
132
133 // !Modification from Numerical Recipes!
134 // h can become infinite due to numerical overflow, this only happens when a(ip, iq) is very small
135 // so we can safely set a(ip, iq) to zero and skip the rotation, see lines marked with 'Warning' above.
136 if (!isnan(h))
137 {
138 z[ip] -= h;
139 z[iq] += h;
140
141 outEigVal[ip] -= h;
142 outEigVal[iq] += h;
143
144 #define JPH_EVS_ROTATE(a, i, j, k, l) \
145 g = a(i, j), \
146 h = a(k, l), \
147 a(i, j) = g - s * (h + g * tau), \
148 a(k, l) = h + s * (g - h * tau)
149
150 uint j;
151 for (j = 0; j < ip; ++j) JPH_EVS_ROTATE(a, j, ip, j, iq);
152 for (j = ip + 1; j < iq; ++j) JPH_EVS_ROTATE(a, ip, j, j, iq);
153 for (j = iq + 1; j < n; ++j) JPH_EVS_ROTATE(a, ip, j, iq, j);
154 for (j = 0; j < n; ++j) JPH_EVS_ROTATE(outEigVec, j, ip, j, iq);
155
156 #undef JPH_EVS_ROTATE
157 }
158 }
159 }
160
161 // Update eigenvalues with the sum of ta_pq and reinitialize z
162 for (uint ip = 0; ip < n; ++ip)
163 {
164 b[ip] += z[ip];
165 outEigVal[ip] = b[ip];
166 z[ip] = 0.0f;
167 }
168 }
169
170 // Failure
171 JPH_ASSERT(false, "Too many iterations");
172 return false;
173}
174
unsigned int uint
Definition: Core.h:439
#define JPH_NAMESPACE_END
Definition: Core.h:367
#define JPH_NAMESPACE_BEGIN
Definition: Core.h:361
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
constexpr T Square(T inV)
Square a value.
Definition: Math.h:52
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:180