Blender  V2.93
matrix_util.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is:
17  * GXML/Graphite: Geometry and Graphics Programming Library + Utilities
18  * Copyright (C) 2000 Bruno Levy
19  * Contact: Bruno Levy
20  * <levy@loria.fr>
21  * ISA Project
22  * LORIA, INRIA Lorraine,
23  * Campus Scientifique, BP 239
24  * 54506 VANDOEUVRE LES NANCY CEDEX
25  * FRANCE
26  */
27 
32 #include "matrix_util.h"
33 
34 #include "BLI_math.h"
35 
37 
38 static const double EPS = 0.00001;
39 static int MAX_ITER = 100;
40 
41 void semi_definite_symmetric_eigen(const double *mat, int n, double *eigen_vec, double *eigen_val)
42 {
43  double *a, *v;
44  double a_norm, a_normEPS, thr, thr_nn;
45  int nb_iter = 0;
46  int jj;
47  int i, j, k, ij, ik, l, m, lm, mq, lq, ll, mm, imv, im, iq, ilv, il, nn;
48  int *index;
49  double a_ij, a_lm, a_ll, a_mm, a_im, a_il;
50  double a_lm_2;
51  double v_ilv, v_imv;
52  double x;
53  double sinx, sinx_2, cosx, cosx_2, sincos;
54  double delta;
55 
56  // Number of entries in mat
57  nn = (n * (n + 1)) / 2;
58 
59  // Step 1: Copy mat to a
60  a = new double[nn];
61 
62  for (ij = 0; ij < nn; ij++) {
63  a[ij] = mat[ij];
64  }
65 
66  // Ugly Fortran-porting trick: indices for a are between 1 and n
67  a--;
68 
69  // Step 2 : Init diagonalization matrix as the unit matrix
70  v = new double[n * n];
71 
72  ij = 0;
73  for (i = 0; i < n; i++) {
74  for (j = 0; j < n; j++) {
75  if (i == j) {
76  v[ij++] = 1.0;
77  }
78  else {
79  v[ij++] = 0.0;
80  }
81  }
82  }
83 
84  // Ugly Fortran-porting trick: indices for v are between 1 and n
85  v--;
86 
87  // Step 3 : compute the weight of the non diagonal terms
88  ij = 1;
89  a_norm = 0.0;
90  for (i = 1; i <= n; i++) {
91  for (j = 1; j <= i; j++) {
92  if (i != j) {
93  a_ij = a[ij];
94  a_norm += a_ij * a_ij;
95  }
96  ij++;
97  }
98  }
99 
100  if (a_norm != 0.0) {
101  a_normEPS = a_norm * EPS;
102  thr = a_norm;
103 
104  // Step 4 : rotations
105  while (thr > a_normEPS && nb_iter < MAX_ITER) {
106  nb_iter++;
107  thr_nn = thr / nn;
108 
109  for (l = 1; l < n; l++) {
110  for (m = l + 1; m <= n; m++) {
111  // compute sinx and cosx
112  lq = (l * l - l) / 2;
113  mq = (m * m - m) / 2;
114 
115  lm = l + mq;
116  a_lm = a[lm];
117  a_lm_2 = a_lm * a_lm;
118 
119  if (a_lm_2 < thr_nn) {
120  continue;
121  }
122 
123  ll = l + lq;
124  mm = m + mq;
125  a_ll = a[ll];
126  a_mm = a[mm];
127 
128  delta = a_ll - a_mm;
129 
130  if (delta == 0.0) {
131  x = -M_PI / 4;
132  }
133  else {
134  x = -atan((a_lm + a_lm) / delta) / 2.0;
135  }
136 
137  sinx = sin(x);
138  cosx = cos(x);
139  sinx_2 = sinx * sinx;
140  cosx_2 = cosx * cosx;
141  sincos = sinx * cosx;
142 
143  // rotate L and M columns
144  ilv = n * (l - 1);
145  imv = n * (m - 1);
146 
147  for (i = 1; i <= n; i++) {
148  if (!ELEM(i, l, m)) {
149  iq = (i * i - i) / 2;
150 
151  if (i < m) {
152  im = i + mq;
153  }
154  else {
155  im = m + iq;
156  }
157  a_im = a[im];
158 
159  if (i < l) {
160  il = i + lq;
161  }
162  else {
163  il = l + iq;
164  }
165  a_il = a[il];
166 
167  a[il] = a_il * cosx - a_im * sinx;
168  a[im] = a_il * sinx + a_im * cosx;
169  }
170 
171  ilv++;
172  imv++;
173 
174  v_ilv = v[ilv];
175  v_imv = v[imv];
176 
177  v[ilv] = cosx * v_ilv - sinx * v_imv;
178  v[imv] = sinx * v_ilv + cosx * v_imv;
179  }
180 
181  x = a_lm * sincos;
182  x += x;
183 
184  a[ll] = a_ll * cosx_2 + a_mm * sinx_2 - x;
185  a[mm] = a_ll * sinx_2 + a_mm * cosx_2 + x;
186  a[lm] = 0.0;
187 
188  thr = fabs(thr - a_lm_2);
189  }
190  }
191  }
192  }
193 
194  // Step 5: index conversion and copy eigen values
195 
196  // back from Fortran to C++
197  a++;
198 
199  for (i = 0; i < n; i++) {
200  k = i + (i * (i + 1)) / 2;
201  eigen_val[i] = a[k];
202  }
203 
204  delete[] a;
205 
206  // Step 6: sort the eigen values and eigen vectors
207 
208  index = new int[n];
209  for (i = 0; i < n; i++) {
210  index[i] = i;
211  }
212 
213  for (i = 0; i < (n - 1); i++) {
214  x = eigen_val[i];
215  k = i;
216 
217  for (j = i + 1; j < n; j++) {
218  if (x < eigen_val[j]) {
219  k = j;
220  x = eigen_val[j];
221  }
222  }
223 
224  eigen_val[k] = eigen_val[i];
225  eigen_val[i] = x;
226 
227  jj = index[k];
228  index[k] = index[i];
229  index[i] = jj;
230  }
231 
232  // Step 7: save the eigen vectors
233 
234  // back from Fortran to C++
235  v++;
236 
237  ij = 0;
238  for (k = 0; k < n; k++) {
239  ik = index[k] * n;
240  for (i = 0; i < n; i++) {
241  eigen_vec[ij++] = v[ik++];
242  }
243  }
244 
245  delete[] v;
246  delete[] index;
247 }
248 
249 //_________________________________________________________
250 
251 } // namespace Freestyle::OGF::MatrixUtil
#define M_PI
Definition: BLI_math_base.h:38
#define ELEM(...)
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert * v
static const double EPS
Definition: matrix_util.cpp:38
void semi_definite_symmetric_eigen(const double *mat, int n, double *eigen_vec, double *eigen_val)
Definition: matrix_util.cpp:41
static unsigned x[3]
Definition: RandGen.cpp:87
static unsigned a[3]
Definition: RandGen.cpp:92
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:319
INLINE Rall1d< T, V, S > atan(const Rall1d< T, V, S > &x)
Definition: rall1d.h:375
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
Definition: rall1d.h:311
ccl_device_inline float2 fabs(const float2 &a)