Little fix after the last commit (mostly a git fail)
[eigenmath-fx.git] / eigen.cpp
blob197435f99cad8d94db7d30609ccede091a13044f
1 //-----------------------------------------------------------------------------
2 //
3 // Compute eigenvalues and eigenvectors
4 //
5 // Input: stack[tos - 1] symmetric matrix
6 //
7 // Output: D diagnonal matrix
8 //
9 // Q eigenvector matrix
11 // D and Q have the property that
13 // A == dot(transpose(Q),D,Q)
15 // where A is the original matrix.
17 // The eigenvalues are on the diagonal of D.
19 // The eigenvectors are row vectors in Q.
21 // The eigenvalue relation
23 // A X = lambda X
25 // can be checked as follows:
27 // lambda = D[1,1]
29 // X = Q[1]
31 // dot(A,X) - lambda X
33 //-----------------------------------------------------------------------------
35 #include "stdafx.h"
36 #include "defs.h"
38 #define D(i, j) yydd[n * (i) + (j)]
39 #define Q(i, j) yyqq[n * (i) + (j)]
41 extern void copy_tensor(void);
42 static void eigen(int);
43 static int check_arg(void);
44 static int step(void);
45 static void step2(int, int);
46 static int n;
47 static double *yydd, *yyqq;
49 void
50 eval_eigen(void)
52 if (check_arg() == 0)
53 stop("eigen: argument is not a square matrix");
55 eigen(EIGEN);
57 p1 = usr_symbol("D");
58 set_binding(p1, p2);
60 p1 = usr_symbol("Q");
61 set_binding(p1, p3);
63 push(symbol(NIL));
66 void
67 eval_eigenval(void)
69 if (check_arg() == 0) {
70 push_symbol(EIGENVAL);
71 push(p1);
72 list(2);
73 return;
76 eigen(EIGENVAL);
78 push(p2);
81 void
82 eval_eigenvec(void)
84 if (check_arg() == 0) {
85 push_symbol(EIGENVEC);
86 push(p1);
87 list(2);
88 return;
91 eigen(EIGENVEC);
93 push(p3);
96 static int
97 check_arg(void)
99 int i, j;
101 push(cadr(p1));
102 eval();
103 yyfloat();
104 eval();
105 p1 = pop();
107 if (!istensor(p1))
108 return 0;
110 if (p1->u.tensor->ndim != 2 || p1->u.tensor->dim[0] != p1->u.tensor->dim[1])
111 stop("eigen: argument is not a square matrix");
113 n = p1->u.tensor->dim[0];
115 for (i = 0; i < n; i++)
116 for (j = 0; j < n; j++)
117 if (!isdouble(p1->u.tensor->elem[n * i + j]))
118 stop("eigen: matrix is not numerical");
120 for (i = 0; i < n - 1; i++)
121 for (j = i + 1; j < n; j++)
122 if (fabs(p1->u.tensor->elem[n * i + j]->u.d - p1->u.tensor->elem[n * j + i]->u.d) > 1e-10)
123 stop("eigen: matrix is not symmetrical");
125 return 1;
128 //-----------------------------------------------------------------------------
130 // Input: p1 matrix
132 // Output: p2 eigenvalues
134 // p3 eigenvectors
136 //-----------------------------------------------------------------------------
138 static void
139 eigen(int op)
141 int i, j;
143 // malloc working vars
145 yydd = (double *) malloc(n * n * sizeof (double));
147 if (yydd == NULL)
148 stop("malloc failure");
150 yyqq = (double *) malloc(n * n * sizeof (double));
152 if (yyqq == NULL)
153 stop("malloc failure");
155 // initialize D
157 for (i = 0; i < n; i++) {
158 D(i, i) = p1->u.tensor->elem[n * i + i]->u.d;
159 for (j = i + 1; j < n; j++) {
160 D(i, j) = p1->u.tensor->elem[n * i + j]->u.d;
161 D(j, i) = p1->u.tensor->elem[n * i + j]->u.d;
165 // initialize Q
167 for (i = 0; i < n; i++) {
168 Q(i, i) = 1.0;
169 for (j = i + 1; j < n; j++) {
170 Q(i, j) = 0.0;
171 Q(j, i) = 0.0;
175 // step up to 100 times
177 for (i = 0; i < 100; i++)
178 if (step() == 0)
179 break;
181 if (i == 100)
182 printstr("\nnote: eigen did not converge");
184 // p2 = D
186 if (op == EIGEN || op == EIGENVAL) {
188 push(p1);
189 copy_tensor();
190 p2 = pop();
192 for (i = 0; i < n; i++) {
193 for (j = 0; j < n; j++) {
194 push_double(D(i, j));
195 p2->u.tensor->elem[n * i + j] = pop();
200 // p3 = Q
202 if (op == EIGEN || op == EIGENVEC) {
204 push(p1);
205 copy_tensor();
206 p3 = pop();
208 for (i = 0; i < n; i++) {
209 for (j = 0; j < n; j++) {
210 push_double(Q(i, j));
211 p3->u.tensor->elem[n * i + j] = pop();
216 // free working vars
218 free(yydd);
219 free(yyqq);
222 //-----------------------------------------------------------------------------
224 // Example: p = 1, q = 3
226 // c 0 s 0
228 // 0 1 0 0
229 // G =
230 // -s 0 c 0
232 // 0 0 0 1
234 // The effect of multiplying G times A is...
236 // row 1 of A = c (row 1 of A ) + s (row 3 of A )
237 // n+1 n n
239 // row 3 of A = c (row 3 of A ) - s (row 1 of A )
240 // n+1 n n
242 // In terms of components the overall effect is...
244 // row 1 = c row 1 + s row 3
246 // A[1,1] = c A[1,1] + s A[3,1]
248 // A[1,2] = c A[1,2] + s A[3,2]
250 // A[1,3] = c A[1,3] + s A[3,3]
252 // A[1,4] = c A[1,4] + s A[3,4]
254 // row 3 = c row 3 - s row 1
256 // A[3,1] = c A[3,1] - s A[1,1]
258 // A[3,2] = c A[3,2] - s A[1,2]
260 // A[3,3] = c A[3,3] - s A[1,3]
262 // A[3,4] = c A[3,4] - s A[1,4]
264 // T
265 // The effect of multiplying A times G is...
267 // col 1 of A = c (col 1 of A ) + s (col 3 of A )
268 // n+1 n n
270 // col 3 of A = c (col 3 of A ) - s (col 1 of A )
271 // n+1 n n
273 // In terms of components the overall effect is...
275 // col 1 = c col 1 + s col 3
277 // A[1,1] = c A[1,1] + s A[1,3]
279 // A[2,1] = c A[2,1] + s A[2,3]
281 // A[3,1] = c A[3,1] + s A[3,3]
283 // A[4,1] = c A[4,1] + s A[4,3]
285 // col 3 = c col 3 - s col 1
287 // A[1,3] = c A[1,3] - s A[1,1]
289 // A[2,3] = c A[2,3] - s A[2,1]
291 // A[3,3] = c A[3,3] - s A[3,1]
293 // A[4,3] = c A[4,3] - s A[4,1]
295 // What we want to do is just compute the upper triangle of A since we
296 // know the lower triangle is identical.
298 // In other words, we just want to update components A[i,j] where i < j.
300 //-----------------------------------------------------------------------------
302 // Example: p = 2, q = 5
304 // p q
306 // j=1 j=2 j=3 j=4 j=5 j=6
308 // i=1 . A[1,2] . . A[1,5] .
310 // p i=2 A[2,1] A[2,2] A[2,3] A[2,4] A[2,5] A[2,6]
312 // i=3 . A[3,2] . . A[3,5] .
314 // i=4 . A[4,2] . . A[4,5] .
316 // q i=5 A[5,1] A[5,2] A[5,3] A[5,4] A[5,5] A[5,6]
318 // i=6 . A[6,2] . . A[6,5] .
320 //-----------------------------------------------------------------------------
322 // This is what B = GA does:
324 // row 2 = c row 2 + s row 5
326 // B[2,1] = c * A[2,1] + s * A[5,1]
327 // B[2,2] = c * A[2,2] + s * A[5,2]
328 // B[2,3] = c * A[2,3] + s * A[5,3]
329 // B[2,4] = c * A[2,4] + s * A[5,4]
330 // B[2,5] = c * A[2,5] + s * A[5,5]
331 // B[2,6] = c * A[2,6] + s * A[5,6]
333 // row 5 = c row 5 - s row 2
335 // B[5,1] = c * A[5,1] + s * A[2,1]
336 // B[5,2] = c * A[5,2] + s * A[2,2]
337 // B[5,3] = c * A[5,3] + s * A[2,3]
338 // B[5,4] = c * A[5,4] + s * A[2,4]
339 // B[5,5] = c * A[5,5] + s * A[2,5]
340 // B[5,6] = c * A[5,6] + s * A[2,6]
342 // T
343 // This is what BG does:
345 // col 2 = c col 2 + s col 5
347 // B[1,2] = c * A[1,2] + s * A[1,5]
348 // B[2,2] = c * A[2,2] + s * A[2,5]
349 // B[3,2] = c * A[3,2] + s * A[3,5]
350 // B[4,2] = c * A[4,2] + s * A[4,5]
351 // B[5,2] = c * A[5,2] + s * A[5,5]
352 // B[6,2] = c * A[6,2] + s * A[6,5]
354 // col 5 = c col 5 - s col 2
356 // B[1,5] = c * A[1,5] - s * A[1,2]
357 // B[2,5] = c * A[2,5] - s * A[2,2]
358 // B[3,5] = c * A[3,5] - s * A[3,2]
359 // B[4,5] = c * A[4,5] - s * A[4,2]
360 // B[5,5] = c * A[5,5] - s * A[5,2]
361 // B[6,5] = c * A[6,5] - s * A[6,2]
363 //-----------------------------------------------------------------------------
365 // Step 1: Just do upper triangle (i < j), B[2,5] = 0
367 // B[1,2] = c * A[1,2] + s * A[1,5]
369 // B[2,3] = c * A[2,3] + s * A[5,3]
370 // B[2,4] = c * A[2,4] + s * A[5,4]
371 // B[2,6] = c * A[2,6] + s * A[5,6]
373 // B[1,5] = c * A[1,5] - s * A[1,2]
374 // B[3,5] = c * A[3,5] - s * A[3,2]
375 // B[4,5] = c * A[4,5] - s * A[4,2]
377 // B[5,6] = c * A[5,6] + s * A[2,6]
379 //-----------------------------------------------------------------------------
381 // Step 2: Transpose where i > j since A[i,j] == A[j,i]
383 // B[1,2] = c * A[1,2] + s * A[1,5]
385 // B[2,3] = c * A[2,3] + s * A[3,5]
386 // B[2,4] = c * A[2,4] + s * A[4,5]
387 // B[2,6] = c * A[2,6] + s * A[5,6]
389 // B[1,5] = c * A[1,5] - s * A[1,2]
390 // B[3,5] = c * A[3,5] - s * A[2,3]
391 // B[4,5] = c * A[4,5] - s * A[2,4]
393 // B[5,6] = c * A[5,6] + s * A[2,6]
395 //-----------------------------------------------------------------------------
397 // Step 3: Same as above except reorder
399 // k < p (k = 1)
401 // A[1,2] = c * A[1,2] + s * A[1,5]
402 // A[1,5] = c * A[1,5] - s * A[1,2]
404 // p < k < q (k = 3..4)
406 // A[2,3] = c * A[2,3] + s * A[3,5]
407 // A[3,5] = c * A[3,5] - s * A[2,3]
409 // A[2,4] = c * A[2,4] + s * A[4,5]
410 // A[4,5] = c * A[4,5] - s * A[2,4]
412 // q < k (k = 6)
414 // A[2,6] = c * A[2,6] + s * A[5,6]
415 // A[5,6] = c * A[5,6] - s * A[2,6]
417 //-----------------------------------------------------------------------------
419 static int
420 step(void)
422 int count, i, j;
424 count = 0;
426 // for each upper triangle "off-diagonal" component do step2
428 for (i = 0; i < n - 1; i++) {
429 for (j = i + 1; j < n; j++) {
430 if (D(i, j) != 0.0) {
431 step2(i, j);
432 count++;
437 return count;
440 static void
441 step2(int p, int q)
443 int k;
444 double t, theta;
445 double c, cc, s, ss;
447 // compute c and s
449 // from Numerical Recipes (except they have a_qq - a_pp)
451 theta = 0.5 * (D(p, p) - D(q, q)) / D(p, q);
453 t = 1.0 / (fabs(theta) + sqrt(theta * theta + 1.0));
455 if (theta < 0.0)
456 t = -t;
458 c = 1.0 / sqrt(t * t + 1.0);
460 s = t * c;
462 // D = GD
464 // which means "add rows"
466 for (k = 0; k < n; k++) {
467 cc = D(p, k);
468 ss = D(q, k);
469 D(p, k) = c * cc + s * ss;
470 D(q, k) = c * ss - s * cc;
473 // D = D transpose(G)
475 // which means "add columns"
477 for (k = 0; k < n; k++) {
478 cc = D(k, p);
479 ss = D(k, q);
480 D(k, p) = c * cc + s * ss;
481 D(k, q) = c * ss - s * cc;
484 // Q = GQ
486 // which means "add rows"
488 for (k = 0; k < n; k++) {
489 cc = Q(p, k);
490 ss = Q(q, k);
491 Q(p, k) = c * cc + s * ss;
492 Q(q, k) = c * ss - s * cc;
495 D(p, q) = 0.0;
496 D(q, p) = 0.0;