Blender V4.3
matrix_util.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2000 `Bruno Levy <levy@loria.fr>`
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later
4 *
5 * The Original Code is:
6 * - GXML/Graphite: Geometry and Graphics Programming Library + Utilities.
7 */
8
13#include "matrix_util.h"
14
15#include "BLI_math_base.h"
16#include "BLI_utildefines.h"
17
19
20static const double EPS = 0.00001;
21static int MAX_ITER = 100;
22
23void semi_definite_symmetric_eigen(const double *mat, int n, double *eigen_vec, double *eigen_val)
24{
25 double *a, *v;
26 double a_norm, a_normEPS, thr, thr_nn;
27 int nb_iter = 0;
28 int jj;
29 int i, j, k, ij, ik, l, m, lm, mq, lq, ll, mm, imv, im, iq, ilv, il, nn;
30 int *index;
31 double a_ij, a_lm, a_ll, a_mm, a_im, a_il;
32 double a_lm_2;
33 double v_ilv, v_imv;
34 double x;
35 double sinx, sinx_2, cosx, cosx_2, sincos;
36 double delta;
37
38 // Number of entries in mat
39 nn = (n * (n + 1)) / 2;
40
41 // Step 1: Copy mat to a
42 a = new double[nn];
43
44 for (ij = 0; ij < nn; ij++) {
45 a[ij] = mat[ij];
46 }
47
48 // Ugly Fortran-porting trick: indices for a are between 1 and n
49 a--;
50
51 // Step 2 : Init diagonalization matrix as the unit matrix
52 v = new double[n * n];
53
54 ij = 0;
55 for (i = 0; i < n; i++) {
56 for (j = 0; j < n; j++) {
57 if (i == j) {
58 v[ij++] = 1.0;
59 }
60 else {
61 v[ij++] = 0.0;
62 }
63 }
64 }
65
66 // Ugly Fortran-porting trick: indices for v are between 1 and n
67 v--;
68
69 // Step 3 : compute the weight of the non diagonal terms
70 ij = 1;
71 a_norm = 0.0;
72 for (i = 1; i <= n; i++) {
73 for (j = 1; j <= i; j++) {
74 if (i != j) {
75 a_ij = a[ij];
76 a_norm += a_ij * a_ij;
77 }
78 ij++;
79 }
80 }
81
82 if (a_norm != 0.0) {
83 a_normEPS = a_norm * EPS;
84 thr = a_norm;
85
86 // Step 4 : rotations
87 while (thr > a_normEPS && nb_iter < MAX_ITER) {
88 nb_iter++;
89 thr_nn = thr / nn;
90
91 for (l = 1; l < n; l++) {
92 for (m = l + 1; m <= n; m++) {
93 // compute sinx and cosx
94 lq = (l * l - l) / 2;
95 mq = (m * m - m) / 2;
96
97 lm = l + mq;
98 a_lm = a[lm];
99 a_lm_2 = a_lm * a_lm;
100
101 if (a_lm_2 < thr_nn) {
102 continue;
103 }
104
105 ll = l + lq;
106 mm = m + mq;
107 a_ll = a[ll];
108 a_mm = a[mm];
109
110 delta = a_ll - a_mm;
111
112 if (delta == 0.0) {
113 x = -M_PI_4;
114 }
115 else {
116 x = -atan((a_lm + a_lm) / delta) / 2.0;
117 }
118
119 sinx = sin(x);
120 cosx = cos(x);
121 sinx_2 = sinx * sinx;
122 cosx_2 = cosx * cosx;
123 sincos = sinx * cosx;
124
125 // rotate L and M columns
126 ilv = n * (l - 1);
127 imv = n * (m - 1);
128
129 for (i = 1; i <= n; i++) {
130 if (!ELEM(i, l, m)) {
131 iq = (i * i - i) / 2;
132
133 if (i < m) {
134 im = i + mq;
135 }
136 else {
137 im = m + iq;
138 }
139 a_im = a[im];
140
141 if (i < l) {
142 il = i + lq;
143 }
144 else {
145 il = l + iq;
146 }
147 a_il = a[il];
148
149 a[il] = a_il * cosx - a_im * sinx;
150 a[im] = a_il * sinx + a_im * cosx;
151 }
152
153 ilv++;
154 imv++;
155
156 v_ilv = v[ilv];
157 v_imv = v[imv];
158
159 v[ilv] = cosx * v_ilv - sinx * v_imv;
160 v[imv] = sinx * v_ilv + cosx * v_imv;
161 }
162
163 x = a_lm * sincos;
164 x += x;
165
166 a[ll] = a_ll * cosx_2 + a_mm * sinx_2 - x;
167 a[mm] = a_ll * sinx_2 + a_mm * cosx_2 + x;
168 a[lm] = 0.0;
169
170 thr = fabs(thr - a_lm_2);
171 }
172 }
173 }
174 }
175
176 // Step 5: index conversion and copy eigen values
177
178 // back from Fortran to C++
179 a++;
180
181 for (i = 0; i < n; i++) {
182 k = i + (i * (i + 1)) / 2;
183 eigen_val[i] = a[k];
184 }
185
186 delete[] a;
187
188 // Step 6: sort the eigen values and eigen vectors
189
190 index = new int[n];
191 for (i = 0; i < n; i++) {
192 index[i] = i;
193 }
194
195 for (i = 0; i < (n - 1); i++) {
196 x = eigen_val[i];
197 k = i;
198
199 for (j = i + 1; j < n; j++) {
200 if (x < eigen_val[j]) {
201 k = j;
202 x = eigen_val[j];
203 }
204 }
205
206 eigen_val[k] = eigen_val[i];
207 eigen_val[i] = x;
208
209 jj = index[k];
210 index[k] = index[i];
211 index[i] = jj;
212 }
213
214 // Step 7: save the eigen vectors
215
216 // back from Fortran to C++
217 v++;
218
219 ij = 0;
220 for (k = 0; k < n; k++) {
221 ik = index[k] * n;
222 for (i = 0; i < n; i++) {
223 eigen_vec[ij++] = v[ik++];
224 }
225 }
226
227 delete[] v;
228 delete[] index;
229}
230
231//_________________________________________________________
232
233} // namespace Freestyle::OGF::MatrixUtil
#define M_PI_4
#define ELEM(...)
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
ccl_device_inline float2 fabs(const float2 a)
ccl_device_inline float3 cos(float3 v)
static const double EPS
void semi_definite_symmetric_eigen(const double *mat, int n, double *eigen_vec, double *eigen_val)
static uint a[3]
Definition RandGen.cpp:82
static uint x[3]
Definition RandGen.cpp:77