1 /************************************************************************/
2 /* */
3 /* Copyright 2004 by Ullrich Koethe */
4 /* Cognitive Systems Group, University of Hamburg, Germany */
5 /* */
6 /* This file is part of the VIGRA computer vision library. */
7 /* ( Version 1.5.0, Dec 07 2006 ) */
8 /* The VIGRA Website is */
9 /* http://kogs-www.informatik.uni-hamburg.de/~koethe/vigra/ */
10 /* Please direct questions, bug reports, and contributions to */
11 /* koethe@informatik.uni-hamburg.de or */
12 /* vigra@kogs1.informatik.uni-hamburg.de */
13 /* */
14 /* Permission is hereby granted, free of charge, to any person */
15 /* obtaining a copy of this software and associated documentation */
16 /* files (the "Software"), to deal in the Software without */
17 /* restriction, including without limitation the rights to use, */
18 /* copy, modify, merge, publish, distribute, sublicense, and/or */
19 /* sell copies of the Software, and to permit persons to whom the */
20 /* Software is furnished to do so, subject to the following */
21 /* conditions: */
22 /* */
23 /* The above copyright notice and this permission notice shall be */
24 /* included in all copies or substantial portions of the */
25 /* Software. */
26 /* */
27 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
28 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
29 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
30 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
31 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
32 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
33 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
34 /* OTHER DEALINGS IN THE SOFTWARE. */
35 /* */
36 /************************************************************************/
37
38
39 #ifndef VIGRA_EIGENSYSTEM_HXX
40 #define VIGRA_EIGENSYSTEM_HXX
41
42 #include <algorithm>
43 #include <complex>
44 #include "matrix.hxx"
45 #include "array_vector.hxx"
46 #include "polynomial.hxx"
47
48 namespace vigra
49 {
50
51 namespace linalg
52 {
53
54 namespace detail
55 {
56
57 // code adapted from JAMA
58 // a and b will be overwritten
59 template <class T, class C1, class C2>
60 void
housholderTridiagonalization(MultiArrayView<2,T,C1> & a,MultiArrayView<2,T,C2> & b)61 housholderTridiagonalization(MultiArrayView<2, T, C1> &a, MultiArrayView<2, T, C2> &b)
62 {
63 const unsigned int n = rowCount(a);
64 vigra_precondition(n == columnCount(a),
65 "housholderTridiagonalization(): matrix must be square.");
66 vigra_precondition(n == rowCount(b) && 2 <= columnCount(b),
67 "housholderTridiagonalization(): matrix size mismatch.");
68
69 MultiArrayView<1, T, C2> d = b.bindOuter(0);
70 MultiArrayView<1, T, C2> e = b.bindOuter(1);
71
72 for(unsigned int j = 0; j < n; ++j)
73 {
74 d(j) = a(n-1, j);
75 }
76
77 // Householder reduction to tridiagonalMatrix form.
78
79 for(int i = n-1; i > 0; --i)
80 {
81 // Scale to avoid under/overflow.
82
83 T scale = 0.0;
84 T h = 0.0;
85 for(int k = 0; k < i; ++k)
86 {
87 scale = scale + abs(d(k));
88 }
89 if(scale == 0.0)
90 {
91 e(i) = d(i-1);
92 for(int j = 0; j < i; ++j)
93 {
94 d(j) = a(i-1, j);
95 a(i, j) = 0.0;
96 a(j, i) = 0.0;
97 }
98 }
99 else
100 {
101 // Generate Householder vector.
102
103 for(int k = 0; k < i; ++k)
104 {
105 d(k) /= scale;
106 h += sq(d(k));
107 }
108 T f = d(i-1);
109 T g = VIGRA_CSTD::sqrt(h);
110 if(f > 0) {
111 g = -g;
112 }
113 e(i) = scale * g;
114 h -= f * g;
115 d(i-1) = f - g;
116 for(int j = 0; j < i; ++j)
117 {
118 e(j) = 0.0;
119 }
120
121 // Apply similarity transformation to remaining columns.
122
123 for(int j = 0; j < i; ++j)
124 {
125 f = d(j);
126 a(j, i) = f;
127 g = e(j) + a(j, j) * f;
128 for(int k = j+1; k <= i-1; ++k)
129 {
130 g += a(k, j) * d(k);
131 e(k) += a(k, j) * f;
132 }
133 e(j) = g;
134 }
135 f = 0.0;
136 for(int j = 0; j < i; ++j)
137 {
138 e(j) /= h;
139 f += e(j) * d(j);
140 }
141 T hh = f / (h + h);
142 for(int j = 0; j < i; ++j)
143 {
144 e(j) -= hh * d(j);
145 }
146 for(int j = 0; j < i; ++j)
147 {
148 f = d(j);
149 g = e(j);
150 for(int k = j; k <= i-1; ++k)
151 {
152 a(k, j) -= (f * e(k) + g * d(k));
153 }
154 d(j) = a(i-1, j);
155 a(i, j) = 0.0;
156 }
157 }
158 d(i) = h;
159 }
160
161 // Accumulate transformations.
162
163 for(unsigned int i = 0; i < n-1; ++i)
164 {
165 a(n-1, i) = a(i, i);
166 a(i, i) = 1.0;
167 T h = d(i+1);
168 if(h != 0.0)
169 {
170 for(unsigned int k = 0; k <= i; ++k)
171 {
172 d(k) = a(k, i+1) / h;
173 }
174 for(unsigned int j = 0; j <= i; ++j)
175 {
176 T g = 0.0;
177 for(unsigned int k = 0; k <= i; ++k)
178 {
179 g += a(k, i+1) * a(k, j);
180 }
181 for(unsigned int k = 0; k <= i; ++k)
182 {
183 a(k, j) -= g * d(k);
184 }
185 }
186 }
187 for(unsigned int k = 0; k <= i; ++k)
188 {
189 a(k, i+1) = 0.0;
190 }
191 }
192 for(unsigned int j = 0; j < n; ++j)
193 {
194 d(j) = a(n-1, j);
195 a(n-1, j) = 0.0;
196 }
197 a(n-1, n-1) = 1.0;
198 e(0) = 0.0;
199 }
200
201 // code adapted from JAMA
202 // de and z will be overwritten
203 template <class T, class C1, class C2>
204 bool
tridiagonalMatrixEigensystem(MultiArrayView<2,T,C1> & de,MultiArrayView<2,T,C2> & z)205 tridiagonalMatrixEigensystem(MultiArrayView<2, T, C1> &de, MultiArrayView<2, T, C2> &z)
206 {
207 const unsigned int n = rowCount(z);
208 vigra_precondition(n == columnCount(z),
209 "tridiagonalMatrixEigensystem(): matrix must be square.");
210 vigra_precondition(n == rowCount(de) && 2 <= columnCount(de),
211 "tridiagonalMatrixEigensystem(): matrix size mismatch.");
212
213 MultiArrayView<1, T, C2> d = de.bindOuter(0);
214 MultiArrayView<1, T, C2> e = de.bindOuter(1);
215
216 for(unsigned int i = 1; i < n; i++) {
217 e(i-1) = e(i);
218 }
219 e(n-1) = 0.0;
220
221 T f = 0.0;
222 T tst1 = 0.0;
223 T eps = VIGRA_CSTD::pow(2.0,-52.0);
224 for(unsigned int l = 0; l < n; ++l)
225 {
226 // Find small subdiagonalMatrix element
227
228 tst1 = std::max(tst1, abs(d(l)) + abs(e(l)));
229 unsigned int m = l;
230
231 // Original while-loop from Java code
232 while(m < n)
233 {
234 if(abs(e(m)) <= eps*tst1)
235 break;
236 ++m;
237 }
238
239 // If m == l, d(l) is an eigenvalue,
240 // otherwise, iterate.
241
242 if(m > l)
243 {
244 int iter = 0;
245 do
246 {
247 if(++iter > 50)
248 return false; // too many iterations
249
250 // Compute implicit shift
251
252 T g = d(l);
253 T p = (d(l+1) - g) / (2.0 * e(l));
254 T r = hypot(p,1.0);
255 if(p < 0)
256 {
257 r = -r;
258 }
259 d(l) = e(l) / (p + r);
260 d(l+1) = e(l) * (p + r);
261 T dl1 = d(l+1);
262 T h = g - d(l);
263 for(unsigned int i = l+2; i < n; ++i)
264 {
265 d(i) -= h;
266 }
267 f = f + h;
268
269 // Implicit QL transformation.
270
271 p = d(m);
272 T c = 1.0;
273 T c2 = c;
274 T c3 = c;
275 T el1 = e(l+1);
276 T s = 0.0;
277 T s2 = 0.0;
278 for(int i = m-1; i >= (int)l; --i)
279 {
280 c3 = c2;
281 c2 = c;
282 s2 = s;
283 g = c * e(i);
284 h = c * p;
285 r = hypot(p,e(i));
286 e(i+1) = s * r;
287 s = e(i) / r;
288 c = p / r;
289 p = c * d(i) - s * g;
290 d(i+1) = h + s * (c * g + s * d(i));
291
292 // Accumulate transformation.
293
294 for(unsigned int k = 0; k < n; ++k)
295 {
296 h = z(k, i+1);
297 z(k, i+1) = s * z(k, i) + c * h;
298 z(k, i) = c * z(k, i) - s * h;
299 }
300 }
301 p = -s * s2 * c3 * el1 * e(l) / dl1;
302 e(l) = s * p;
303 d(l) = c * p;
304
305 // Check for convergence.
306
307 } while(abs(e(l)) > eps*tst1);
308 }
309 d(l) = d(l) + f;
310 e(l) = 0.0;
311 }
312
313 // Sort eigenvalues and corresponding vectors.
314
315 for(unsigned int i = 0; i < n-1; ++i)
316 {
317 int k = i;
318 T p = abs(d(i));
319 for(unsigned int j = i+1; j < n; ++j)
320 {
321 T p1 = abs(d(j));
322 if(p < p1)
323 {
324 k = j;
325 p = p1;
326 }
327 }
328 if(k != i)
329 {
330 std::swap(d(k), d(i));
331 for(unsigned int j = 0; j < n; ++j)
332 {
333 std::swap(z(j, i), z(j, k));
334 }
335 }
336 }
337 return true;
338 }
339
340 // Nonsymmetric reduction to Hessenberg form.
341
342 template <class T, class C1, class C2>
nonsymmetricHessenbergReduction(MultiArrayView<2,T,C1> & H,MultiArrayView<2,T,C2> & V)343 void nonsymmetricHessenbergReduction(MultiArrayView<2, T, C1> & H, MultiArrayView<2, T, C2> & V)
344 {
345 // This is derived from the Algol procedures orthes and ortran,
346 // by Martin and Wilkinson, Handbook for Auto. Comp.,
347 // Vol.ii-Linear Algebra, and the corresponding
348 // Fortran subroutines in EISPACK.
349
350 int n = rowCount(H);
351 int low = 0;
352 int high = n-1;
353 ArrayVector<T> ort(n);
354
355 for(int m = low+1; m <= high-1; ++m)
356 {
357 // Scale column.
358
359 T scale = 0.0;
360 for(int i = m; i <= high; ++i)
361 {
362 scale = scale + abs(H(i, m-1));
363 }
364 if(scale != 0.0)
365 {
366
367 // Compute Householder transformation.
368
369 T h = 0.0;
370 for(int i = high; i >= m; --i)
371 {
372 ort[i] = H(i, m-1)/scale;
373 h += sq(ort[i]);
374 }
375 T g = VIGRA_CSTD::sqrt(h);
376 if(ort[m] > 0)
377 {
378 g = -g;
379 }
380 h = h - ort[m] * g;
381 ort[m] = ort[m] - g;
382
383 // Apply Householder similarity transformation
384 // H = (I-u*u'/h)*H*(I-u*u')/h)
385
386 for(int j = m; j < n; ++j)
387 {
388 T f = 0.0;
389 for(int i = high; i >= m; --i)
390 {
391 f += ort[i]*H(i, j);
392 }
393 f = f/h;
394 for(int i = m; i <= high; ++i)
395 {
396 H(i, j) -= f*ort[i];
397 }
398 }
399
400 for(int i = 0; i <= high; ++i)
401 {
402 T f = 0.0;
403 for(int j = high; j >= m; --j)
404 {
405 f += ort[j]*H(i, j);
406 }
407 f = f/h;
408 for(int j = m; j <= high; ++j)
409 {
410 H(i, j) -= f*ort[j];
411 }
412 }
413 ort[m] = scale*ort[m];
414 H(m, m-1) = scale*g;
415 }
416 }
417
418 // Accumulate transformations (Algol's ortran).
419
420 for(int i = 0; i < n; ++i)
421 {
422 for(int j = 0; j < n; ++j)
423 {
424 V(i, j) = (i == j ? 1.0 : 0.0);
425 }
426 }
427
428 for(int m = high-1; m >= low+1; --m)
429 {
430 if(H(m, m-1) != 0.0)
431 {
432 for(int i = m+1; i <= high; ++i)
433 {
434 ort[i] = H(i, m-1);
435 }
436 for(int j = m; j <= high; ++j)
437 {
438 T g = 0.0;
439 for(int i = m; i <= high; ++i)
440 {
441 g += ort[i] * V(i, j);
442 }
443 // Double division avoids possible underflow
444 g = (g / ort[m]) / H(m, m-1);
445 for(int i = m; i <= high; ++i)
446 {
447 V(i, j) += g * ort[i];
448 }
449 }
450 }
451 }
452 }
453
454
455 // Complex scalar division.
456
457 template <class T>
cdiv(T xr,T xi,T yr,T yi,T & cdivr,T & cdivi)458 void cdiv(T xr, T xi, T yr, T yi, T & cdivr, T & cdivi)
459 {
460 T r,d;
461 if(abs(yr) > abs(yi))
462 {
463 r = yi/yr;
464 d = yr + r*yi;
465 cdivr = (xr + r*xi)/d;
466 cdivi = (xi - r*xr)/d;
467 }
468 else
469 {
470 r = yr/yi;
471 d = yi + r*yr;
472 cdivr = (r*xr + xi)/d;
473 cdivi = (r*xi - xr)/d;
474 }
475 }
476
477 template <class T, class C>
hessenbergQrDecompositionHelper(MultiArrayView<2,T,C> & H,int n,int l,double eps,T & p,T & q,T & r,T & s,T & w,T & x,T & y,T & z)478 int hessenbergQrDecompositionHelper(MultiArrayView<2, T, C> & H, int n, int l, double eps,
479 T & p, T & q, T & r, T & s, T & w, T & x, T & y, T & z)
480 {
481 int m = n-2;
482 while(m >= l)
483 {
484 z = H(m, m);
485 r = x - z;
486 s = y - z;
487 p = (r * s - w) / H(m+1, m) + H(m, m+1);
488 q = H(m+1, m+1) - z - r - s;
489 r = H(m+2, m+1);
490 s = abs(p) + abs(q) + abs(r);
491 p = p / s;
492 q = q / s;
493 r = r / s;
494 if(m == l)
495 {
496 break;
497 }
498 if(abs(H(m, m-1)) * (abs(q) + abs(r)) <
499 eps * (abs(p) * (abs(H(m-1, m-1)) + abs(z) +
500 abs(H(m+1, m+1)))))
501 {
502 break;
503 }
504 --m;
505 }
506 return m;
507 }
508
509
510
511 // Nonsymmetric reduction from Hessenberg to real Schur form.
512
513 template <class T, class C1, class C2, class C3>
hessenbergQrDecomposition(MultiArrayView<2,T,C1> & H,MultiArrayView<2,T,C2> & V,MultiArrayView<2,T,C3> & de)514 bool hessenbergQrDecomposition(MultiArrayView<2, T, C1> & H, MultiArrayView<2, T, C2> & V,
515 MultiArrayView<2, T, C3> & de)
516 {
517
518 // This is derived from the Algol procedure hqr2,
519 // by Martin and Wilkinson, Handbook for Auto. Comp.,
520 // Vol.ii-Linear Algebra, and the corresponding
521 // Fortran subroutine in EISPACK.
522
523 // Initialize
524 MultiArrayView<1, T, C3> d = de.bindOuter(0);
525 MultiArrayView<1, T, C3> e = de.bindOuter(1);
526
527 int nn = rowCount(H);
528 int n = nn-1;
529 int low = 0;
530 int high = nn-1;
531 T eps = VIGRA_CSTD::pow(2.0, sizeof(T) == sizeof(float)
532 ? -23.0
533 : -52.0);
534 T exshift = 0.0;
535 T p=0,q=0,r=0,s=0,z=0,t,w,x,y;
536 T norm = vigra::norm(H);
537
538 // Outer loop over eigenvalue index
539 int iter = 0;
540 while(n >= low)
541 {
542
543 // Look for single small sub-diagonal element
544 int l = n;
545 while (l > low)
546 {
547 s = abs(H(l-1, l-1)) + abs(H(l, l));
548 if(s == 0.0)
549 {
550 s = norm;
551 }
552 if(abs(H(l, l-1)) < eps * s)
553 {
554 break;
555 }
556 --l;
557 }
558
559 // Check for convergence
560 // One root found
561 if(l == n)
562 {
563 H(n, n) = H(n, n) + exshift;
564 d(n) = H(n, n);
565 e(n) = 0.0;
566 --n;
567 iter = 0;
568
569 // Two roots found
570
571 }
572 else if(l == n-1)
573 {
574 w = H(n, n-1) * H(n-1, n);
575 p = (H(n-1, n-1) - H(n, n)) / 2.0;
576 q = p * p + w;
577 z = VIGRA_CSTD::sqrt(abs(q));
578 H(n, n) = H(n, n) + exshift;
579 H(n-1, n-1) = H(n-1, n-1) + exshift;
580 x = H(n, n);
581
582 // Real pair
583
584 if(q >= 0)
585 {
586 if(p >= 0)
587 {
588 z = p + z;
589 }
590 else
591 {
592 z = p - z;
593 }
594 d(n-1) = x + z;
595 d(n) = d(n-1);
596 if(z != 0.0)
597 {
598 d(n) = x - w / z;
599 }
600 e(n-1) = 0.0;
601 e(n) = 0.0;
602 x = H(n, n-1);
603 s = abs(x) + abs(z);
604 p = x / s;
605 q = z / s;
606 r = VIGRA_CSTD::sqrt(p * p+q * q);
607 p = p / r;
608 q = q / r;
609
610 // Row modification
611
612 for(int j = n-1; j < nn; ++j)
613 {
614 z = H(n-1, j);
615 H(n-1, j) = q * z + p * H(n, j);
616 H(n, j) = q * H(n, j) - p * z;
617 }
618
619 // Column modification
620
621 for(int i = 0; i <= n; ++i)
622 {
623 z = H(i, n-1);
624 H(i, n-1) = q * z + p * H(i, n);
625 H(i, n) = q * H(i, n) - p * z;
626 }
627
628 // Accumulate transformations
629
630 for(int i = low; i <= high; ++i)
631 {
632 z = V(i, n-1);
633 V(i, n-1) = q * z + p * V(i, n);
634 V(i, n) = q * V(i, n) - p * z;
635 }
636
637 // Complex pair
638
639 }
640 else
641 {
642 d(n-1) = x + p;
643 d(n) = x + p;
644 e(n-1) = z;
645 e(n) = -z;
646 }
647 n = n - 2;
648 iter = 0;
649
650 // No convergence yet
651
652 }
653 else
654 {
655
656 // Form shift
657
658 x = H(n, n);
659 y = 0.0;
660 w = 0.0;
661 if(l < n)
662 {
663 y = H(n-1, n-1);
664 w = H(n, n-1) * H(n-1, n);
665 }
666
667 // Wilkinson's original ad hoc shift
668
669 if(iter == 10)
670 {
671 exshift += x;
672 for(int i = low; i <= n; ++i)
673 {
674 H(i, i) -= x;
675 }
676 s = abs(H(n, n-1)) + abs(H(n-1, n-2));
677 x = y = 0.75 * s;
678 w = -0.4375 * s * s;
679 }
680
681 // MATLAB's new ad hoc shift
682
683 if(iter == 30)
684 {
685 s = (y - x) / 2.0;
686 s = s * s + w;
687 if(s > 0)
688 {
689 s = VIGRA_CSTD::sqrt(s);
690 if(y < x)
691 {
692 s = -s;
693 }
694 s = x - w / ((y - x) / 2.0 + s);
695 for(int i = low; i <= n; ++i)
696 {
697 H(i, i) -= s;
698 }
699 exshift += s;
700 x = y = w = 0.964;
701 }
702 }
703
704 iter = iter + 1;
705 if(iter > 60)
706 return false;
707
708 // Look for two consecutive small sub-diagonal elements
709 int m = hessenbergQrDecompositionHelper(H, n, l, eps, p, q, r, s, w, x, y, z);
710 for(int i = m+2; i <= n; ++i)
711 {
712 H(i, i-2) = 0.0;
713 if(i > m+2)
714 {
715 H(i, i-3) = 0.0;
716 }
717 }
718
719 // Double QR step involving rows l:n and columns m:n
720
721 for(int k = m; k <= n-1; ++k)
722 {
723 int notlast = (k != n-1);
724 if(k != m) {
725 p = H(k, k-1);
726 q = H(k+1, k-1);
727 r = (notlast ? H(k+2, k-1) : 0.0);
728 x = abs(p) + abs(q) + abs(r);
729 if(x != 0.0)
730 {
731 p = p / x;
732 q = q / x;
733 r = r / x;
734 }
735 }
736 if(x == 0.0)
737 {
738 break;
739 }
740 s = VIGRA_CSTD::sqrt(p * p + q * q + r * r);
741 if(p < 0)
742 {
743 s = -s;
744 }
745 if(s != 0)
746 {
747 if(k != m)
748 {
749 H(k, k-1) = -s * x;
750 }
751 else if(l != m)
752 {
753 H(k, k-1) = -H(k, k-1);
754 }
755 p = p + s;
756 x = p / s;
757 y = q / s;
758 z = r / s;
759 q = q / p;
760 r = r / p;
761
762 // Row modification
763
764 for(int j = k; j < nn; ++j)
765 {
766 p = H(k, j) + q * H(k+1, j);
767 if(notlast)
768 {
769 p = p + r * H(k+2, j);
770 H(k+2, j) = H(k+2, j) - p * z;
771 }
772 H(k, j) = H(k, j) - p * x;
773 H(k+1, j) = H(k+1, j) - p * y;
774 }
775
776 // Column modification
777
778 for(int i = 0; i <= std::min(n,k+3); ++i)
779 {
780 p = x * H(i, k) + y * H(i, k+1);
781 if(notlast)
782 {
783 p = p + z * H(i, k+2);
784 H(i, k+2) = H(i, k+2) - p * r;
785 }
786 H(i, k) = H(i, k) - p;
787 H(i, k+1) = H(i, k+1) - p * q;
788 }
789
790 // Accumulate transformations
791
792 for(int i = low; i <= high; ++i)
793 {
794 p = x * V(i, k) + y * V(i, k+1);
795 if(notlast)
796 {
797 p = p + z * V(i, k+2);
798 V(i, k+2) = V(i, k+2) - p * r;
799 }
800 V(i, k) = V(i, k) - p;
801 V(i, k+1) = V(i, k+1) - p * q;
802 }
803 } // (s != 0)
804 } // k loop
805 } // check convergence
806 } // while (n >= low)
807
808 // Backsubstitute to find vectors of upper triangular form
809
810 if(norm == 0.0)
811 {
812 return false;
813 }
814
815 for(n = nn-1; n >= 0; --n)
816 {
817 p = d(n);
818 q = e(n);
819
820 // Real vector
821
822 if(q == 0)
823 {
824 int l = n;
825 H(n, n) = 1.0;
826 for(int i = n-1; i >= 0; --i)
827 {
828 w = H(i, i) - p;
829 r = 0.0;
830 for(int j = l; j <= n; ++j)
831 {
832 r = r + H(i, j) * H(j, n);
833 }
834 if(e(i) < 0.0)
835 {
836 z = w;
837 s = r;
838 }
839 else
840 {
841 l = i;
842 if(e(i) == 0.0)
843 {
844 if(w != 0.0)
845 {
846 H(i, n) = -r / w;
847 }
848 else
849 {
850 H(i, n) = -r / (eps * norm);
851 }
852
853 // Solve real equations
854
855 }
856 else
857 {
858 x = H(i, i+1);
859 y = H(i+1, i);
860 q = (d(i) - p) * (d(i) - p) + e(i) * e(i);
861 t = (x * s - z * r) / q;
862 H(i, n) = t;
863 if(abs(x) > abs(z))
864 {
865 H(i+1, n) = (-r - w * t) / x;
866 }
867 else
868 {
869 H(i+1, n) = (-s - y * t) / z;
870 }
871 }
872
873 // Overflow control
874
875 t = abs(H(i, n));
876 if((eps * t) * t > 1)
877 {
878 for(int j = i; j <= n; ++j)
879 {
880 H(j, n) = H(j, n) / t;
881 }
882 }
883 }
884 }
885
886 // Complex vector
887
888 }
889 else if(q < 0)
890 {
891 int l = n-1;
892
893 // Last vector component imaginary so matrix is triangular
894
895 if(abs(H(n, n-1)) > abs(H(n-1, n)))
896 {
897 H(n-1, n-1) = q / H(n, n-1);
898 H(n-1, n) = -(H(n, n) - p) / H(n, n-1);
899 }
900 else
901 {
902 cdiv(0.0,-H(n-1, n),H(n-1, n-1)-p,q, H(n-1, n-1), H(n-1, n));
903 }
904 H(n, n-1) = 0.0;
905 H(n, n) = 1.0;
906 for(int i = n-2; i >= 0; --i)
907 {
908 T ra,sa,vr,vi;
909 ra = 0.0;
910 sa = 0.0;
911 for(int j = l; j <= n; ++j)
912 {
913 ra = ra + H(j, n-1) * H(i, j);
914 sa = sa + H(j, n) * H(i, j);
915 }
916 w = H(i, i) - p;
917
918 if(e(i) < 0.0)
919 {
920 z = w;
921 r = ra;
922 s = sa;
923 }
924 else
925 {
926 l = i;
927 if(e(i) == 0)
928 {
929 cdiv(-ra,-sa,w,q, H(i, n-1), H(i, n));
930 }
931 else
932 {
933 // Solve complex equations
934
935 x = H(i, i+1);
936 y = H(i+1, i);
937 vr = (d(i) - p) * (d(i) - p) + e(i) * e(i) - q * q;
938 vi = (d(i) - p) * 2.0 * q;
939 if((vr == 0.0) && (vi == 0.0))
940 {
941 vr = eps * norm * (abs(w) + abs(q) +
942 abs(x) + abs(y) + abs(z));
943 }
944 cdiv(x*r-z*ra+q*sa,x*s-z*sa-q*ra,vr,vi, H(i, n-1), H(i, n));
945 if(abs(x) > (abs(z) + abs(q)))
946 {
947 H(i+1, n-1) = (-ra - w * H(i, n-1) + q * H(i, n)) / x;
948 H(i+1, n) = (-sa - w * H(i, n) - q * H(i, n-1)) / x;
949 }
950 else
951 {
952 cdiv(-r-y*H(i, n-1),-s-y*H(i, n),z,q, H(i+1, n-1), H(i+1, n));
953 }
954 }
955
956 // Overflow control
957
958 t = std::max(abs(H(i, n-1)),abs(H(i, n)));
959 if((eps * t) * t > 1)
960 {
961 for(int j = i; j <= n; ++j)
962 {
963 H(j, n-1) = H(j, n-1) / t;
964 H(j, n) = H(j, n) / t;
965 }
966 }
967 }
968 }
969 }
970 }
971
972 // Back transformation to get eigenvectors of original matrix
973
974 for(int j = nn-1; j >= low; --j)
975 {
976 for(int i = low; i <= high; ++i)
977 {
978 z = 0.0;
979 for(int k = low; k <= std::min(j,high); ++k)
980 {
981 z = z + V(i, k) * H(k, j);
982 }
983 V(i, j) = z;
984 }
985 }
986 return true;
987 }
988
989 } // namespace detail
990
991 /** \addtogroup LinearAlgebraFunctions Matrix functions
992 */
993 //@{
994 /** Compute the eigensystem of a symmetric matrix.
995
996 \a a is a real symmetric matrix, \a ew is a single-column matrix
997 holding the eigenvalues, and \a ev is a matrix of the same size as
998 \a a whose columns are the corresponding eigenvectors. Eigenvalues
999 will be sorted from largest to smallest magnitude.
1000 The algorithm returns <tt>false</tt> when it doesn't
1001 converge. It can be applied in-place, i.e. <tt>&a == &ev</tt> is allowed.
1002 The code of this function was adapted from JAMA.
1003
1004 <b>\#include</b> "<a href="eigensystem_8hxx-source.html">vigra/eigensystem.hxx</a>" or<br>
1005 <b>\#include</b> "<a href="linear__algebra_8hxx-source.html">vigra/linear_algebra.hxx</a>"<br>
1006 Namespaces: vigra and vigra::linalg
1007 */
1008 template <class T, class C1, class C2, class C3>
1009 bool
symmetricEigensystem(MultiArrayView<2,T,C1> const & a,MultiArrayView<2,T,C2> & ew,MultiArrayView<2,T,C3> & ev)1010 symmetricEigensystem(MultiArrayView<2, T, C1> const & a,
1011 MultiArrayView<2, T, C2> & ew, MultiArrayView<2, T, C3> & ev)
1012 {
1013 vigra_precondition(isSymmetric(a),
1014 "symmetricEigensystem(): symmetric input matrix required.");
1015 unsigned int acols = columnCount(a);
1016 vigra_precondition(1 == columnCount(ew) && acols == rowCount(ew) &&
1017 acols == columnCount(ev) && acols == rowCount(ev),
1018 "symmetricEigensystem(): matrix shape mismatch.");
1019
1020 ev.copy(a); // does nothing if &ev == &a
1021 Matrix<T> de(acols, 2);
1022 detail::housholderTridiagonalization(ev, de);
1023 if(!detail::tridiagonalMatrixEigensystem(de, ev))
1024 return false;
1025
1026 ew.copy(columnVector(de, 0));
1027 return true;
1028 }
1029
1030 /** Compute the eigensystem of a square, but
1031 not necessarily symmetric matrix.
1032
1033 \a a is a real square matrix, \a ew is a single-column matrix
1034 holding the possibly complex eigenvalues, and \a ev is a matrix of
1035 the same size as \a a whose columns are the corresponding eigenvectors.
1036 Eigenvalues will be sorted from largest to smallest magnitude.
1037 The algorithm returns <tt>false</tt> when it doesn't
1038 converge. It can be applied in-place, i.e. <tt>&a == &ev</tt> is allowed.
1039 The code of this function was adapted from JAMA.
1040
1041 <b>\#include</b> "<a href="eigensystem_8hxx-source.html">vigra/eigensystem.hxx</a>" or<br>
1042 <b>\#include</b> "<a href="linear__algebra_8hxx-source.html">vigra/linear_algebra.hxx</a>"<br>
1043 Namespaces: vigra and vigra::linalg
1044 */
1045 template <class T, class C1, class C2, class C3>
1046 bool
nonsymmetricEigensystem(MultiArrayView<2,T,C1> const & a,MultiArrayView<2,std::complex<T>,C2> & ew,MultiArrayView<2,T,C3> & ev)1047 nonsymmetricEigensystem(MultiArrayView<2, T, C1> const & a,
1048 MultiArrayView<2, std::complex<T>, C2> & ew, MultiArrayView<2, T, C3> & ev)
1049 {
1050 unsigned int acols = columnCount(a);
1051 vigra_precondition(acols == rowCount(a),
1052 "nonsymmetricEigensystem(): square input matrix required.");
1053 vigra_precondition(1 == columnCount(ew) && acols == rowCount(ew) &&
1054 acols == columnCount(ev) && acols == rowCount(ev),
1055 "nonsymmetricEigensystem(): matrix shape mismatch.");
1056
1057 Matrix<T> H(a);
1058 Matrix<T> de(acols, 2);
1059 detail::nonsymmetricHessenbergReduction(H, ev);
1060 if(!detail::hessenbergQrDecomposition(H, ev, de))
1061 return false;
1062
1063 for(unsigned int i=0; i < acols; ++i)
1064 {
1065 ew(i,0) = std::complex<T>(de(i, 0), de(i, 1));
1066 }
1067 return true;
1068 }
1069
1070 /** Compute the roots of a polynomial using the eigenvalue method.
1071
1072 \a poly is a real polynomial (compatible to \ref vigra::PolynomialView),
1073 and \a roots a complex valued vector (compatible to <tt>std::vector</tt>
1074 with a <tt>value_type</tt> compatible to the type <tt>POLYNOMIAL::Complex</tt>) to which
1075 the roots are appended. The function calls \ref nonsymmetricEigensystem() with the standard
1076 companion matrix yielding the roots as eigenvalues. It returns <tt>false</tt> if
1077 it fails to converge.
1078
1079 <b>\#include</b> "<a href="eigensystem_8hxx-source.html">vigra/eigensystem.hxx</a>" or<br>
1080 <b>\#include</b> "<a href="linear__algebra_8hxx-source.html">vigra/linear_algebra.hxx</a>"<br>
1081 Namespaces: vigra and vigra::linalg
1082
1083 \see polynomialRoots(), vigra::Polynomial
1084 */
1085 template <class POLYNOMIAL, class VECTOR>
polynomialRootsEigenvalueMethod(POLYNOMIAL const & poly,VECTOR & roots,bool polishRoots)1086 bool polynomialRootsEigenvalueMethod(POLYNOMIAL const & poly, VECTOR & roots, bool polishRoots)
1087 {
1088 typedef typename POLYNOMIAL::value_type T;
1089 typedef typename POLYNOMIAL::Real Real;
1090 typedef typename POLYNOMIAL::Complex Complex;
1091 typedef Matrix<T> TMatrix;
1092 typedef Matrix<Complex> ComplexMatrix;
1093
1094 int const degree = poly.order();
1095 double const eps = poly.epsilon();
1096
1097 TMatrix inMatrix(degree, degree);
1098 for(int i = 0; i < degree; ++i)
1099 inMatrix(0, i) = -poly[degree - i - 1] / poly[degree];
1100 for(int i = 0; i < degree - 1; ++i)
1101 inMatrix(i + 1, i) = NumericTraits<T>::one();
1102 ComplexMatrix ew(degree, 1);
1103 TMatrix ev(degree, degree);
1104 bool success = nonsymmetricEigensystem(inMatrix, ew, ev);
1105 if(!success)
1106 return false;
1107 for(int i = 0; i < degree; ++i)
1108 {
1109 if(polishRoots)
1110 vigra::detail::laguerre1Root(poly, ew(i,0), 1);
1111 roots.push_back(vigra::detail::deleteBelowEpsilon(ew(i,0), eps));
1112 }
1113 std::sort(roots.begin(), roots.end(), vigra::detail::PolynomialRootCompare<Real>(eps));
1114 return true;
1115 }
1116
1117 template <class POLYNOMIAL, class VECTOR>
polynomialRootsEigenvalueMethod(POLYNOMIAL const & poly,VECTOR & roots)1118 bool polynomialRootsEigenvalueMethod(POLYNOMIAL const & poly, VECTOR & roots)
1119 {
1120 return polynomialRootsEigenvalueMethod(poly, roots, true);
1121 }
1122
1123 /** Compute the real roots of a real polynomial using the eigenvalue method.
1124
1125 \a poly is a real polynomial (compatible to \ref vigra::PolynomialView),
1126 and \a roots a real valued vector (compatible to <tt>std::vector</tt>
1127 with a <tt>value_type</tt> compatible to the type <tt>POLYNOMIAL::Real</tt>) to which
1128 the roots are appended. The function calls \ref polynomialRootsEigenvalueMethod() and
1129 throws away all complex roots. It returns <tt>false</tt> if it fails to converge.
1130
1131 <b>\#include</b> "<a href="eigensystem_8hxx-source.html">vigra/eigensystem.hxx</a>" or<br>
1132 <b>\#include</b> "<a href="linear__algebra_8hxx-source.html">vigra/linear_algebra.hxx</a>"<br>
1133 Namespaces: vigra and vigra::linalg
1134
1135 \see polynomialRealRoots(), vigra::Polynomial
1136 */
1137 template <class POLYNOMIAL, class VECTOR>
polynomialRealRootsEigenvalueMethod(POLYNOMIAL const & p,VECTOR & roots,bool polishRoots)1138 bool polynomialRealRootsEigenvalueMethod(POLYNOMIAL const & p, VECTOR & roots, bool polishRoots)
1139 {
1140 typedef typename NumericTraits<typename VECTOR::value_type>::ComplexPromote Complex;
1141 ArrayVector<Complex> croots;
1142 if(!polynomialRootsEigenvalueMethod(p, croots))
1143 return false;
1144 for(unsigned int i = 0; i < croots.size(); ++i)
1145 if(croots[i].imag() == 0.0)
1146 roots.push_back(croots[i].real());
1147 return true;
1148 }
1149
1150 template <class POLYNOMIAL, class VECTOR>
polynomialRealRootsEigenvalueMethod(POLYNOMIAL const & p,VECTOR & roots)1151 bool polynomialRealRootsEigenvalueMethod(POLYNOMIAL const & p, VECTOR & roots)
1152 {
1153 return polynomialRealRootsEigenvalueMethod(p, roots, true);
1154 }
1155
1156
1157 //@}
1158
1159 } // namespace linalg
1160
1161 using linalg::symmetricEigensystem;
1162 using linalg::nonsymmetricEigensystem;
1163 using linalg::polynomialRootsEigenvalueMethod;
1164 using linalg::polynomialRealRootsEigenvalueMethod;
1165
1166 } // namespace vigra
1167
1168 #endif // VIGRA_EIGENSYSTEM_HXX
1169