File Coverage

lib/Math/MatrixDecomposition/Eigen.pm
Criterion Covered Total %
statement 440 622 70.7
branch 85 210 40.4
condition 27 82 32.9
subroutine 15 17 88.2
pod 7 7 100.0
total 574 938 61.1


line stmt bran cond sub pod time code
1             ## Math/MatrixDecomposition/Eigen.pm --- eigenvalues and eigenvectors.
2              
3             # Copyright (C) 2010 Ralph Schleicher. All rights reserved.
4              
5             # This program is free software; you can redistribute it and/or
6             # modify it under the same terms as Perl itself.
7              
8             # Commentary:
9             #
10             # Code derived from EiSPACK procedures and
11             # Jama's 'EigenvalueDecomposition' class.
12              
13             ## Code:
14              
15             package Math::MatrixDecomposition::Eigen;
16              
17 1     1   73222 use strict;
  1         11  
  1         31  
18 1     1   5 use warnings;
  1         2  
  1         25  
19 1     1   5 use Carp;
  1         2  
  1         59  
20 1     1   5 use Exporter qw(import);
  1         2  
  1         25  
21 1     1   749 use Math::Complex qw();
  1         13098  
  1         33  
22 1     1   11 use Scalar::Util qw(looks_like_number);
  1         2  
  1         62  
23              
24 1     1   538 use Math::BLAS;
  1         7829  
  1         134  
25 1     1   483 use Math::MatrixDecomposition::Util qw(:all);
  1         2  
  1         184  
26              
27             BEGIN
28             {
29 1     1   4 our $VERSION = '1.04';
30 1         6853 our @EXPORT_OK = qw(eig);
31             }
32              
33             # Calculate eigenvalues and eigenvectors (convenience function).
34             sub eig
35             {
36 0     0 1 0 __PACKAGE__->new (@_);
37             }
38              
39             # Standard constructor.
40             sub new
41             {
42 1     1 1 9 my $class = shift;
43 1         4 my $self =
44             {
45             # Eigenvalues (a vector).
46             value => undef,
47              
48             # Eigenvectors (an array of vectors).
49             vector => undef,
50             };
51              
52 1   33     7 bless ($self, ref ($class) || $class);
53              
54             # Process arguments.
55 1 50       4 $self->decompose (@_)
56             if @_ > 0;
57              
58             # Return object.
59 1         3 $self;
60             }
61              
62             # Calculate eigenvalues and eigenvectors.
63             sub decompose
64             {
65 2     2 1 319 my $self = shift;
66              
67             # Check arguments.
68 2         3 my $a = shift;
69              
70 2 50 33     25 my $m = @_ > 0 && looks_like_number ($_[0]) ? shift : sqrt (@$a);
71 2 50 33     10 my $n = @_ > 0 && looks_like_number ($_[0]) ? shift : $m ? @$a / $m : 0;
    50          
72              
73 2 50 33     31 croak ('Invalid argument')
      33        
      33        
      33        
74             if (@$a != ($m * $n)
75             || mod ($m, 1) != 0 || $m < 1
76             || mod ($n, 1) != 0 || $n < 1);
77              
78 2 50       4 croak ('Matrix has to be square')
79             if $m != $n;
80              
81             # Get properties.
82 2         5 my %prop = @_;
83              
84 2   50     8 $prop{balance} //= 1;
85 2   50     8 $prop{normalize} //= 1;
86 2   50     7 $prop{positive} //= 1;
87              
88             # Index of last row/column.
89 2         5 my $end = $n - 1;
90              
91             # Eigenvalues (a vector).
92 2   100     11 $$self{value} //= [];
93              
94             # Vector $d contains the real part of the eigenvalues and vector $e
95             # contains the imaginary part of the eigenvalues.
96 2         3 my $d = $$self{value};
97 2         3 my $e = [];
98              
99 2 50       5 splice (@$d, $n)
100             if @$d > $n;
101              
102             # Eigenvectors (an array of vectors).
103 2   100     6 $$self{vector} //= [];
104              
105             # Matrix $Z contains the eigenvectors.
106 2         3 my $Z = $$self{vector};
107              
108 2 50       5 splice (@$Z, $n)
109             if @$Z > $n;
110              
111 2         6 for my $v (@$Z)
112             {
113 3 50       7 splice (@$v, $n)
114             if @$v > $n;
115             }
116              
117             # True if matrix is symmetric.
118 2         4 my $sym = 1;
119              
120             SYM:
121              
122 2         4 for my $i (0 .. $end)
123             {
124 4         8 for my $j ($i + 1 .. $end)
125             {
126 4 100       12 if ($$a[$i * $n + $j] != $$a[$j * $n + $i])
127             {
128 1         2 $sym = 0;
129 1         3 last SYM;
130             }
131             }
132             }
133              
134 2 100       4 if ($sym)
135             {
136             # Copy matrix elements.
137 1         2 for my $j (0 .. $end)
138             {
139 3   50     12 $$Z[$j] //= [];
140              
141 3         5 for my $i (0 .. $end)
142             {
143 9         14 $$Z[$j][$i] = $$a[$i * $n + $j];
144             }
145             }
146              
147             # Reduce a real symmetric matrix to a symmetric tridiagonal matrix
148             # using and accumulating orthogonal similarity transformations.
149             #
150             # See EiSPACK procedure 'tred2'.
151 1         2 if (1)
152             {
153 1         2 my ($i, $j, $k, $l,
154             $f, $g, $h, $hh, $scale);
155              
156 1         3 for $i (0 .. $end)
157             {
158 3         5 $$d[$i] = $$Z[$i][$end];
159             }
160              
161 1         4 for $i (reverse (1 .. $end))
162             {
163 2         4 $l = $i - 1;
164 2         2 $h = 0;
165              
166             # Scale row.
167 2         3 $scale = 0;
168              
169 2         3 for $k (0 .. $l)
170             {
171 3         6 $scale += abs ($$d[$k]);
172             }
173              
174 2 50       5 if ($scale == 0)
175             {
176 0         0 $$e[$i] = $$d[$l];
177              
178 0         0 for $j (0 .. $l)
179             {
180 0         0 $$d[$j] = $$Z[$j][$l];
181              
182 0         0 $$Z[$j][$i] = 0;
183 0         0 $$Z[$i][$j] = 0;
184             }
185             }
186             else
187             {
188 2         5 for $k (0 .. $l)
189             {
190 3         5 $$d[$k] /= $scale;
191 3         8 $h += $$d[$k] ** 2;
192             }
193              
194 2         3 $f = $$d[$l];
195 2         7 $g = - sign (sqrt ($h), $f);
196 2         4 $h -= $f * $g;
197              
198 2         3 $$d[$l] = $f - $g;
199 2         3 $$e[$i] = $scale * $g;
200              
201             # Form a*u.
202 2         4 for $j (0 .. $l)
203             {
204 3         5 $$e[$j] = 0;
205             }
206              
207 2         3 for $j (0 .. $l)
208             {
209 3         7 $f = $$d[$j];
210 3         13 $$Z[$i][$j] = $f;
211 3         8 $g = $$e[$j] + $$Z[$j][$j] * $f;
212              
213 3         7 for $k ($j + 1 .. $l)
214             {
215 1         2 $g += $$Z[$j][$k] * $$d[$k];
216 1         3 $$e[$k] += $$Z[$j][$k] * $f;
217             }
218              
219 3         9 $$e[$j] = $g;
220             }
221              
222             # Form p.
223 2         3 $f = 0;
224              
225 2         4 for $j (0 .. $l)
226             {
227 3         5 $$e[$j] /= $h;
228 3         5 $f += $$e[$j] * $$d[$j];
229             }
230              
231             # Form q.
232 2         3 $hh = $f / ($h + $h);
233              
234 2         5 for $j (0 .. $l)
235             {
236 3         5 $$e[$j] -= $hh * $$d[$j];
237             }
238              
239             # Form reduced a.
240 2         3 for $j (0 .. $l)
241             {
242 3         4 $f = $$d[$j];
243 3         4 $g = $$e[$j];
244              
245 3         5 for $k ($j .. $l)
246             {
247 4         9 $$Z[$j][$k] -= ($f * $$e[$k] + $g * $$d[$k]);
248             }
249              
250 3         5 $$d[$j] = $$Z[$j][$l];
251 3         4 $$Z[$j][$i] = 0;
252             }
253             }
254              
255 2         4 $$d[$i] = $h;
256             }
257              
258             # Accumulation of transformation matrices.
259 1         2 for $i (1 .. $end)
260             {
261 2         3 $l = $i - 1;
262              
263 2         5 $$Z[$l][$end] = $$Z[$l][$l];
264 2         3 $$Z[$l][$l] = 1;
265              
266 2         3 $h = $$d[$i];
267 2 50       5 if ($h != 0)
268             {
269 2         4 for $k (0 .. $l)
270             {
271 3         6 $$d[$k] = $$Z[$i][$k] / $h;
272             }
273              
274 2         4 for $j (0 .. $l)
275             {
276 3         4 $g = 0;
277              
278 3         5 for $k (0 .. $l)
279             {
280 5         7 $g += $$Z[$i][$k] * $$Z[$j][$k];
281             }
282              
283 3         6 for $k (0 .. $l)
284             {
285 5         7 $$Z[$j][$k] -= $g * $$d[$k];
286             }
287             }
288             }
289              
290 2         3 for $k (0 .. $l)
291             {
292 3         5 $$Z[$i][$k] = 0;
293             }
294             }
295              
296 1         4 for $j (0 .. $end)
297             {
298 3         4 $$d[$j] = $$Z[$j][$end];
299 3         13 $$Z[$j][$end] = 0;
300             }
301              
302 1         2 $$Z[$end][$end] = 1;
303 1         2 $$e[0] = 0;
304             }
305              
306             # Find the eigenvalues and eigenvectors of a symmetric tridiagonal
307             # matrix by the QL method.
308             #
309             # See EiSPACK procedure 'tql2'.
310 1         2 if (1)
311             {
312 1         2 my ($i, $j, $k, $l, $m,
313             $c, $c2, $c3, $dl1, $el1, $f, $g, $h, $p, $r, $s, $s2, $t, $t2);
314              
315 1         3 for $i (1 .. $end)
316             {
317 2         6 $$e[$i - 1] = $$e[$i];
318             }
319              
320 1         1 $$e[$end] = 0;
321              
322 1         3 $f = 0;
323 1         1 $t = 0;
324              
325 1         3 for $l (0 .. $end)
326             {
327 3         5 $t2 = abs ($$d[$l]) + abs ($$e[$l]);
328 3 100       8 $t = $t2 if $t2 > $t;
329              
330 3         9 for ($m = $l; $m < $n; ++$m)
331             {
332 6 100       17 last if abs ($$e[$m]) <= eps * $t;
333             }
334              
335 3 100       8 if ($m > $l)
336             {
337 2         3 while (1)
338             {
339 5         6 $g = $$d[$l];
340 5         12 $p = ($$d[$l + 1] - $g) / (2 * $$e[$l]);
341 5         10 $r = sign (hypot ($p, 1), $p);
342              
343 5         8 $$d[$l] = $$e[$l] / ($p + $r);
344 5         8 $$d[$l + 1] = $$e[$l] * ($p + $r);
345 5         8 $dl1 = $$d[$l + 1];
346 5         7 $h = $g - $$d[$l];
347              
348 5         7 for $i ($l + 2 .. $end)
349             {
350 4         9 $$d[$i] -= $h;
351             }
352              
353 5         6 $f += $h;
354              
355 5         7 $p = $$d[$m];
356 5         6 $c = 1;
357 5         14 $c2 = $c;
358 5         6 $el1 = $$e[$l + 1];
359 5         6 $s = 0;
360              
361 5         9 for $i (reverse ($l .. $m - 1))
362             {
363 9         12 $c3 = $c2;
364 9         10 $c2 = $c;
365 9         9 $s2 = $s;
366 9         12 $g = $c * $$e[$i];
367 9         9 $h = $c * $p;
368 9         20 $r = hypot ($p, $$e[$i]);
369 9         18 $$e[$i + 1] = $s * $r;
370 9         11 $s = $$e[$i] / $r;
371 9         10 $c = $p / $r;
372 9         10 $p = $c * $$d[$i] - $s * $g;
373 9         17 $$d[$i + 1] = $h + $s * ($c * $g + $s * $$d[$i]);
374              
375 9         12 for $k (0 .. $end)
376             {
377 27         35 $h = $$Z[$i + 1][$k];
378 27         32 $$Z[$i + 1][$k] = $s * $$Z[$i][$k] + $c * $h;
379 27         44 $$Z[$i][$k] = $c * $$Z[$i][$k] - $s * $h;
380             }
381             }
382              
383 5         9 $p = 0 - $s * $s2 * $c3 * $el1 * $$e[$l] / $dl1;
384              
385 5         6 $$e[$l] = $s * $p;
386 5         8 $$d[$l] = $c * $p;
387              
388             # Check convergence.
389 5 100       9 last if abs ($$e[$l]) <= eps * $t;
390             }
391             }
392              
393 3         5 $$d[$l] = $$d[$l] + $f;
394 3         6 $$e[$l] = 0;
395             }
396             }
397             }
398             else
399             {
400             # Hessenberg matrix (an array of row vectors).
401 1         6 my @H = map ([], 0 .. $end);
402              
403 1         3 for my $i (0 .. $end)
404             {
405 3         6 for my $j (0 .. $end)
406             {
407 9         25 $H[$i][$j] = $$a[$i * $n + $j];
408             }
409             }
410              
411             # Row and column indices of the beginning and end
412             # of the principal sub-matrix.
413 1         2 my $lo = 0;
414 1         1 my $hi = $end;
415              
416             # Permutation vector.
417 1         11 my @perm = ();
418              
419             # Scaling vector.
420 1         2 my @scale = ();
421              
422             # Balance a real matrix and isolate eigenvalues whenever possible.
423             #
424             # See EiSPACK procedure 'balanc' and LAPACK procedure 'dgebal'.
425 1 50       4 if ($prop{balance})
426             {
427 1         3 @perm = (0 .. $end);
428 1         5 @scale = map (1, 0 .. $end);
429              
430             # Work variables.
431 1         2 my ($i, $j, $k, $l,
432             $b, $b2, $c, $f, $g, $r, $s, $no_conv);
433              
434             # Scale factors are powers of two.
435 1         3 $b = 2;
436 1         2 $b2 = $b ** 2;
437              
438 1         1 $k = 0;
439 1         2 $l = $end;
440              
441 1 50       4 if ($l > 0)
442             {
443             # Search for rows isolating an eigenvalue
444             # and push them down.
445             L:
446             {
447 1         2 for $j (reverse (0 .. $l))
  1         2  
448             {
449 3         5 $r = 0;
450              
451 3         4 for $i (0 .. $l)
452             {
453 9 100 66     26 $r = 1 if $i != $j && $H[$j][$i] != 0;
454             }
455              
456 3 50       5 next if $r != 0;
457              
458 0 0       0 if ($j != $l)
459             {
460             # Exchange row and column.
461 0         0 @perm[$j, $l] = @perm[$l, $j];
462              
463 0         0 for $i (0 .. $l)
464             {
465 0         0 ($H[$i][$j], $H[$i][$l])
466             = ($H[$i][$l], $H[$i][$j]);
467             }
468              
469 0         0 for $i ($k .. $end)
470             {
471 0         0 ($H[$j][$i], $H[$l][$i])
472             = ($H[$l][$i], $H[$j][$i]);
473             }
474             }
475              
476 0         0 $l -= 1;
477 0         0 next L;
478             }
479             }
480              
481             # Search for columns isolating an eigenvalue
482             # and push them left.
483             K:
484             {
485 1         2 for $j ($k .. $l)
  1         2  
486             {
487 3         4 $c = 0;
488              
489 3         5 for $i ($k .. $l)
490             {
491 9 100 66     25 $c = 1 if $i != $j && $H[$i][$j] != 0;
492             }
493              
494 3 50       6 next if $c != 0;
495              
496 0 0       0 if ($j != $k)
497             {
498             # Exchange row and column.
499 0         0 @perm[$j, $k] = @perm[$k, $j];
500              
501 0         0 for $i (0 .. $l)
502             {
503 0         0 ($H[$i][$j], $H[$i][$k])
504             = ($H[$i][$k], $H[$i][$j]);
505             }
506              
507 0         0 for $i ($k .. $end)
508             {
509 0         0 ($H[$j][$i], $H[$k][$i])
510             = ($H[$k][$i], $H[$j][$i]);
511             }
512             }
513              
514 0         0 $k += 1;
515 0         0 next K;
516             }
517             }
518              
519             ## Now balance the sub-matrix in rows k to l.
520              
521             # Iterative loop for norm reduction.
522 1         10 while (1)
523             {
524 1         3 $no_conv = 0;
525              
526 1         3 for $i ($k .. $l)
527             {
528 3         3 $c = 0;
529 3         5 $r = 0;
530              
531 3         4 for $j ($k .. $l)
532             {
533 9 100       16 next if $j == $i;
534              
535 6         8 $c += abs ($H[$j][$i]);
536 6         7 $r += abs ($H[$i][$j]);
537             }
538              
539             # Guard against zero c or r due to underflow.
540 3 50 33     11 next if $c == 0 || $r == 0;
541              
542 3         4 $s = $c + $r;
543 3         3 $f = 1;
544              
545 3         11 $g = $r / $b;
546 3         7 while ($c < $g)
547             {
548 0         0 $f *= $b;
549 0         0 $c *= $b2;
550             }
551              
552 3         5 $g = $r * $b;
553 3         6 while ($c >= $g)
554             {
555 1         1 $f /= $b;
556 1         3 $c /= $b2;
557             }
558              
559             # Now balance.
560 3 50       9 if (($c + $r) / $f < 0.95 * $s)
561             {
562 0         0 $g = 1 / $f;
563              
564 0         0 for $j ($k .. $end)
565             {
566 0         0 $H[$i][$j] *= $g;
567             }
568              
569 0         0 for $j (0 .. $l)
570             {
571 0         0 $H[$j][$i] *= $f;
572             }
573              
574 0         0 $scale[$i] *= $f;
575 0         0 $no_conv = 1;
576             }
577             }
578              
579 1 50       4 last unless $no_conv;
580             }
581             }
582              
583 1         2 $lo = $k;
584 1         2 $hi = $l;
585             }
586              
587             # Reduce matrix to upper Hessenberg form by orthogonal similarity
588             # transformations.
589             #
590             # See EiSPACK procedure 'orthes'.
591 1         3 my @ort = ();
592              
593 1         1 if (1)
594             {
595 1         3 my ($i, $j, $k, $m,
596             $f, $g, $h, $scale);
597              
598 1         12 for $m ($lo + 1 .. $hi - 1)
599             {
600 1         3 $scale = 0;
601              
602 1         2 for $i ($m .. $hi)
603             {
604 2         5 $scale += abs ($H[$i][$m - 1]);
605             }
606              
607 1 50       3 next if $scale == 0;
608              
609 1         2 $h = 0;
610              
611 1         3 for $i (reverse ($m .. $hi))
612             {
613 2         5 $ort[$i] = $H[$i][$m - 1] / $scale;
614 2         5 $h += $ort[$i] ** 2;
615             }
616              
617 1         5 $g = - sign (sqrt ($h), $ort[$m]);
618 1         2 $h -= $ort[$m] * $g;
619 1         2 $ort[$m] -= $g;
620              
621 1         3 for $j ($m .. $end)
622             {
623 2         8 $f = 0;
624              
625 2         7 for $i (reverse ($m .. $hi))
626             {
627 4         8 $f += $ort[$i] * $H[$i][$j];
628             }
629              
630 2         3 $f /= $h;
631              
632 2         4 for $i ($m .. $hi)
633             {
634 4         8 $H[$i][$j] -= $f * $ort[$i];
635             }
636             }
637              
638 1         2 for $i (0 .. $hi)
639             {
640 3         4 $f = 0;
641              
642 3         4 for $j (reverse ($m .. $hi))
643             {
644 6         10 $f += $ort[$j] * $H[$i][$j];
645             }
646              
647 3         4 $f /= $h;
648              
649 3         3 for $j ($m .. $hi)
650             {
651 6         11 $H[$i][$j] -= $f * $ort[$j];
652             }
653             }
654              
655 1         2 $ort[$m] *= $scale;
656 1         3 $H[$m][$m - 1] = $scale * $g;
657             }
658             }
659              
660             # Accumulate the orthogonal similarity transformations.
661             #
662             # See EiSPACK procedure 'ortran'.
663 1         2 if (1)
664             {
665 1         2 my ($i, $j, $k, $m,
666             $g);
667              
668 1         3 for $j (0 .. $end)
669             {
670 3   50     7 $$Z[$j] //= [];
671              
672 3         11 for $i (0 .. $end)
673             {
674 9 100       16 $$Z[$j][$i] = ($i == $j ? 1 : 0);
675             }
676             }
677              
678 1         4 for $m (reverse ($lo + 1 .. $hi - 1))
679             {
680 1 50       13 next if $H[$m][$m - 1] == 0;
681              
682 1         4 for $i ($m + 1 .. $hi)
683             {
684 1         4 $ort[$i] = $H[$i][$m - 1];
685             }
686              
687 1         2 for $j ($m .. $hi)
688             {
689 2         3 $g = 0;
690              
691 2         4 for $i ($m .. $hi)
692             {
693 4         7 $g += $ort[$i] * $$Z[$j][$i];
694             }
695              
696 2         4 $g = ($g / $ort[$m]) / $H[$m][$m - 1];
697              
698 2         3 for $i ($m .. $hi)
699             {
700 4         8 $$Z[$j][$i] += $g * $ort[$i];
701             }
702             }
703             }
704             }
705              
706             # Find the eigenvalues and eigenvectors of a real upper Hessenberg
707             # matrix by the QR method.
708             #
709             # See EiSPACK procedure 'hqr2'.
710 1         2 if (1)
711             {
712 1         3 my ($i, $j, $k, $l, $m,
713             $h, $g, $f, $p, $q, $r, $s, $t, $w, $x, $y, $z,
714             $norm, $iter, $not_last, $vr, $vi, $ra, $sa);
715              
716 1         1 $t = 0;
717              
718             # Store isolated roots.
719 1         2 for $i (0 .. $end)
720             {
721 3 50 33     19 if ($i < $lo || $i > $hi)
722             {
723 0         0 $$d[$i] = $H[$i][$i];
724 0         0 $$e[$i] = 0;
725             }
726             }
727              
728             # Compute matrix norm.
729 1         2 $norm = 0;
730              
731 1         2 for $i (0 .. $end)
732             {
733 3         4 for $j ($i .. $end)
734             {
735 6         11 $norm += abs ($H[$i][$j]);
736             }
737             }
738              
739             # Search for next eigenvalue.
740 1         1 $iter = 0;
741              
742 1         4 for ($n = $end; $n >= $lo; )
743             {
744             # Look for single small sub-diagonal element.
745 10         20 for ($l = $n; $l > $lo; --$l)
746             {
747 16         26 $s = abs ($H[$l - 1][$l - 1]) + abs ($H[$l][$l]);
748 16 50       29 $s = $norm
749             if $s == 0;
750              
751 16 100       32 last if abs ($H[$l][$l - 1]) < eps * $s;
752             }
753              
754 10         13 $x = $H[$n][$n];
755              
756 10 100       21 if ($l == $n)
    50          
757             {
758             # One root found,
759 3         6 $H[$n][$n] = $x + $t;
760              
761 3         3 $$d[$n] = $H[$n][$n];
762 3         4 $$e[$n] = 0;
763              
764 3         4 $n -= 1;
765 3         6 $iter = 0;
766             }
767             elsif ($l == $n - 1)
768             {
769             # Two roots found.
770 0         0 $y = $H[$n - 1][$n - 1];
771 0         0 $w = $H[$n][$n - 1] * $H[$n - 1][$n];
772              
773 0         0 $p = ($y - $x) / 2;
774 0         0 $q = $p * $p + $w;
775 0         0 $z = sqrt (abs ($q));
776              
777 0         0 $H[$n][$n] = $x + $t;
778 0         0 $H[$n - 1][$n - 1] = $y + $t;
779 0         0 $x = $H[$n][$n];
780              
781 0 0       0 if ($q >= 0)
782             {
783             # Real pair.
784 0         0 $z = $p + sign ($z, $p);
785              
786 0         0 $$d[$n - 1] = $x + $z;
787 0         0 $$d[$n] = $$d[$n - 1];
788 0 0       0 $$d[$n] = $x - $w / $z
789             if $z != 0;
790              
791 0         0 $$e[$n - 1] = 0;
792 0         0 $$e[$n] = 0;
793              
794 0         0 $x = $H[$n][$n - 1];
795 0         0 $s = abs ($x) + abs ($z);
796 0         0 $p = $x / $s;
797 0         0 $q = $z / $s;
798 0         0 $r = sqrt ($p * $p + $q * $q);
799 0         0 $p = $p / $r;
800 0         0 $q = $q / $r;
801              
802             # Row modification.
803 0         0 for $j ($n - 1 .. $end)
804             {
805 0         0 $z = $H[$n - 1][$j];
806 0         0 $H[$n - 1][$j] = $q * $z + $p * $H[$n][$j];
807 0         0 $H[$n][$j] = $q * $H[$n][$j] - $p * $z;
808             }
809              
810             # Column modification.
811 0         0 for $i (0 .. $n)
812             {
813 0         0 $z = $H[$i][$n - 1];
814 0         0 $H[$i][$n - 1] = $q * $z + $p * $H[$i][$n];
815 0         0 $H[$i][$n] = $q * $H[$i][$n] - $p * $z;
816             }
817              
818             # Accumulate transformations.
819 0         0 for $i ($lo .. $hi)
820             {
821 0         0 $z = $$Z[$n - 1][$i];
822 0         0 $$Z[$n - 1][$i] = $q * $z + $p * $$Z[$n][$i];
823 0         0 $$Z[$n][$i] = $q * $$Z[$n][$i] - $p * $z;
824             }
825             }
826             else
827             {
828             # Complex pair.
829 0         0 $$d[$n - 1] = $x + $p;
830 0         0 $$d[$n] = $x + $p;
831 0         0 $$e[$n - 1] = $z;
832 0         0 $$e[$n] = 0 - $z;
833             }
834              
835 0         0 $n -= 2;
836 0         0 $iter = 0;
837             }
838             else
839             {
840             # Form shift.
841 7         11 $y = $H[$n - 1][$n - 1];
842 7         13 $w = $H[$n][$n - 1] * $H[$n - 1][$n];
843              
844             # Wilkinson's original ad hoc shift.
845 7 50 33     21 if ($iter == 10 || $iter == 20)
846             {
847 0         0 $t += $x;
848              
849 0         0 for $i ($lo .. $n)
850             {
851 0         0 $H[$i][$i] -= $x;
852             }
853              
854 0         0 $s = abs ($H[$n][$n - 1]) + abs ($H[$n - 1][$n - 2]);
855 0         0 $x = 0.75 * $s;
856 0         0 $y = $x;
857 0         0 $w = -0.4375 * $s * $s;
858             }
859              
860             # Matlab's new ad hoc shift.
861 7 50       14 if ($iter == 30)
862             {
863 0         0 $s = ($y - $x) / 2;
864 0         0 $s = $s * $s + $w;
865 0 0       0 if ($s > 0)
866             {
867 0         0 $s = sqrt ($s);
868 0 0       0 $s = - $s if $y < $x;
869 0         0 $s = $x - $w / (($y - $x) / 2 + $s);
870              
871 0         0 for $i ($lo .. $n)
872             {
873 0         0 $H[$i][$i] -= $s;
874             }
875              
876 0         0 $t += $s;
877 0         0 $x = 0.964;
878 0         0 $w = $y = $x;
879             }
880             }
881              
882 7         8 ++$iter;
883              
884             # Look for two consecutive small sub-diagonal elements.
885 7         12 for ($m = $n - 2; $m >= $l; --$m)
886             {
887 7         11 $z = $H[$m][$m];
888 7         9 $r = $x - $z;
889 7         8 $s = $y - $z;
890 7         11 $p = ($r * $s - $w) / $H[$m + 1][$m] + $H[$m][$m + 1];
891 7         10 $q = $H[$m + 1][$m + 1] - $z - $r - $s;
892 7         11 $r = $H[$m + 2][$m + 1];
893 7         9 $s = abs ($p) + abs ($q) + abs ($r);
894 7         9 $p = $p / $s;
895 7         8 $q = $q / $s;
896 7         8 $r = $r / $s;
897              
898 7 50       14 last if $m == $l;
899 0 0       0 last if abs ($H[$m][$m - 1]) * (abs ($q) + abs ($r)) < eps * (abs ($p) * (abs ($H[$m - 1][$m - 1]) + abs ($z) + abs ($H[$m + 1][$m + 1])));
900             }
901              
902 7         11 for $i ($m + 2 .. $n)
903             {
904 7         11 $H[$i][$i - 2] = 0;
905 7 50       20 $H[$i][$i - 3] = 0
906             if $i > $m + 2;
907             }
908              
909             # Double QR step.
910 7         13 for $k ($m .. $n - 1)
911             {
912 14         17 $not_last = ($k != $n - 1);
913              
914 14 100       24 if ($k != $m)
915             {
916 7         11 $p = $H[$k][$k - 1];
917 7         11 $q = $H[$k + 1][$k - 1];
918 7 50       11 $r = $not_last ? $H[$k + 2][$k - 1] : 0;
919 7         13 $x = abs ($p) + abs ($q) + abs ($r);
920              
921 7 50       11 next if $x == 0;
922              
923 7         9 $p = $p / $x;
924 7         8 $q = $q / $x;
925 7         8 $r = $r / $x;
926             }
927              
928 14         34 $s = sign (sqrt ($p * $p + $q * $q + $r * $r), $p);
929 14 50       31 if ($s != 0)
930             {
931 14 100       36 if ($k != $m)
    50          
932             {
933 7         11 $H[$k][$k - 1] = 0 - $s * $x;
934             }
935             elsif ($l != $m)
936             {
937 0         0 $H[$k][$k - 1] = - $H[$k][$k - 1];
938             }
939              
940 14         17 $p = $p + $s;
941 14         17 $x = $p / $s;
942 14         15 $y = $q / $s;
943 14         16 $z = $r / $s;
944 14         17 $q = $q / $p;
945 14         15 $r = $r / $p;
946              
947 14 100       21 if ($not_last)
948             {
949             # Row modification.
950 7         10 for $j ($k .. $end)
951             {
952 21         34 $p = $H[$k][$j] + $q * $H[$k + 1][$j] + $r * $H[$k + 2][$j];
953              
954 21         49 $H[$k][$j] -= $p * $x;
955 21         28 $H[$k + 1][$j] -= $p * $y;
956 21         33 $H[$k + 2][$j] -= $p * $z;
957             }
958              
959             # Column modification.
960 7         13 for $i (0 .. min ($n, $k + 3))
961             {
962 21         35 $p = $x * $H[$i][$k] + $y * $H[$i][$k + 1] + $z * $H[$i][$k + 2];
963              
964 21         25 $H[$i][$k] -= $p;
965 21         25 $H[$i][$k + 1] -= $p * $q;
966 21         25 $H[$i][$k + 2] -= $p * $r;
967             }
968              
969             # Accumulate transformations.
970 7         22 for $i ($lo .. $hi)
971             {
972 21         85 $p = $x * $$Z[$k][$i] + $y * $$Z[$k + 1][$i] + $z * $$Z[$k + 2][$i];
973              
974 21         25 $$Z[$k][$i] -= $p;
975 21         75 $$Z[$k + 1][$i] -= $p * $q;
976 21         34 $$Z[$k + 2][$i] -= $p * $r;
977             }
978             }
979             else
980             {
981             # Row modification.
982 7         10 for $j ($k .. $end)
983             {
984 14         19 $p = $H[$k][$j] + $q * $H[$k + 1][$j];
985              
986 14         21 $H[$k][$j] -= $p * $x;
987 14         21 $H[$k + 1][$j] -= $p * $y;
988             }
989              
990             # Column modification.
991 7         12 for $i (0 .. min ($n, $k + 3))
992             {
993 21         28 $p = $x * $H[$i][$k] + $y * $H[$i][$k + 1];
994              
995 21         23 $H[$i][$k] -= $p;
996 21         30 $H[$i][$k + 1] -= $p * $q;
997             }
998              
999             # Accumulate transformations.
1000 7         11 for $i (0 .. $end)
1001             {
1002 21         32 $p = $x * $$Z[$k][$i] + $y * $$Z[$k + 1][$i];
1003              
1004 21         25 $$Z[$k][$i] -= $p;
1005 21         32 $$Z[$k + 1][$i] -= $p * $q;
1006             }
1007             }
1008             }
1009             }
1010             }
1011             }
1012              
1013             # Backsubstitute to find vectors of upper triangular form.
1014 1 50       3 return if $norm == 0;
1015              
1016 1         4 for $n (reverse (0 .. $end))
1017             {
1018 3         5 $p = $$d[$n];
1019 3         3 $q = $$e[$n];
1020              
1021 3 50       15 if ($q == 0)
    0          
1022             {
1023             # Real vector.
1024 3         6 $m = $n;
1025 3         5 $H[$n][$n] = 1;
1026              
1027 3         7 for $i (reverse (0 .. $n - 1))
1028             {
1029 3         5 $w = $H[$i][$i] - $p;
1030 3         4 $r = 0;
1031              
1032 3         5 for $j ($m .. $n)
1033             {
1034 4         9 $r += $H[$i][$j] * $H[$j][$n];
1035             }
1036              
1037 3 50       6 if ($$e[$i] < 0)
1038             {
1039 0         0 $z = $w;
1040 0         0 $s = $r;
1041             }
1042             else
1043             {
1044 3         3 $m = $i;
1045              
1046 3 50       8 if ($$e[$i] == 0)
1047             {
1048 3 50       7 $H[$i][$n] = ($w != 0 ?
1049             0 - $r / $w :
1050             0 - $r / (eps * $norm));
1051             }
1052             else
1053             {
1054             # Solve real equations.
1055 0         0 $x = $H[$i][$i + 1];
1056 0         0 $y = $H[$i + 1][$i];
1057 0         0 $q = ($$d[$i] - $p) ** 2 + $$e[$i] ** 2;
1058 0         0 $t = ($x * $s - $z * $r) / $q;
1059              
1060 0         0 $H[$i][$n] = $t;
1061 0 0       0 $H[$i + 1][$n] = (abs ($x) > abs ($z) ?
1062             (0 - $r - $w * $t) / $x :
1063             (0 - $s - $y * $t) / $z);
1064             }
1065              
1066             # Overflow control.
1067 3         5 $t = abs ($H[$i][$n]);
1068 3 50       6 if ((eps * $t) * $t > 1)
1069             {
1070 0         0 for $j ($i .. $n)
1071             {
1072 0         0 $H[$j][$n] /= $t;
1073             }
1074             }
1075             }
1076             }
1077             }
1078             elsif ($q < 0)
1079             {
1080             # Complex vector.
1081 0         0 $m = $n - 1;
1082              
1083             # Last vector component chosen imaginary so that
1084             # eigenvector matrix is triangular.
1085 0 0       0 if (abs ($H[$n][$n - 1]) > abs ($H[$n - 1][$n]))
1086             {
1087 0         0 $H[$n - 1][$n - 1] = $q / $H[$n][$n - 1];
1088 0         0 $H[$n - 1][$n] = ($p - $H[$n][$n]) / $H[$n][$n - 1];
1089             }
1090             else
1091             {
1092 0         0 ($H[$n - 1][$n - 1], $H[$n - 1][$n])
1093             = cdiv (0, - $H[$n - 1][$n],
1094             $H[$n - 1][$n - 1] - $p, $q);
1095             }
1096              
1097 0         0 $H[$n][$n - 1] = 0;
1098 0         0 $H[$n][$n] = 1;
1099              
1100 0         0 for $i (reverse (0 .. $n - 2))
1101             {
1102 0         0 $w = $H[$i][$i] - $p;
1103              
1104 0         0 $ra = 0;
1105 0         0 $sa = 0;
1106              
1107 0         0 for $j ($m .. $n)
1108             {
1109 0         0 $ra += $H[$i][$j] * $H[$j][$n - 1];
1110 0         0 $sa += $H[$i][$j] * $H[$j][$n];
1111             }
1112              
1113 0 0       0 if ($$e[$i] < 0)
1114             {
1115 0         0 $z = $w;
1116 0         0 $r = $ra;
1117 0         0 $s = $sa;
1118             }
1119             else
1120             {
1121 0         0 $m = $i;
1122              
1123 0 0       0 if ($$e[$i] == 0)
1124             {
1125 0         0 ($H[$i][$n - 1], $H[$i][$n])
1126             = cdiv (- $ra, - $sa, $w, $q);
1127             }
1128             else
1129             {
1130             # Solve complex equations.
1131 0         0 $x = $H[$i][$i + 1];
1132 0         0 $y = $H[$i + 1][$i];
1133              
1134 0         0 $vr = ($$d[$i] - $p) ** 2 + $$e[$i] ** 2 - $q ** 2;
1135 0         0 $vi = ($$d[$i] - $p) * 2 * $q;
1136              
1137 0 0 0     0 if ($vr == 0 && $vi == 0)
1138             {
1139 0         0 $vr = eps * $norm * (abs ($w) + abs ($q) + abs ($x) + abs ($y) + abs ($z));
1140             }
1141              
1142 0         0 ($H[$i][$n - 1], $H[$i][$n])
1143             = cdiv ($x * $r - $z * $ra + $q * $sa,
1144             $x * $s - $z * $sa - $q * $ra,
1145             $vr,
1146             $vi);
1147              
1148 0 0       0 if (abs ($x) > (abs ($z) + abs ($q)))
1149             {
1150 0         0 $H[$i + 1][$n - 1] = (0 - $ra - $w * $H[$i][$n - 1] + $q * $H[$i][$n]) / $x;
1151 0         0 $H[$i + 1][$n] = (0 - $sa - $w * $H[$i][$n] - $q * $H[$i][$n - 1]) / $x;
1152             }
1153             else
1154             {
1155 0         0 ($H[$i + 1][$n - 1], $H[$i + 1][$n])
1156             = cdiv (0 - $r - $y * $H[$i][$n - 1],
1157             0 - $s - $y * $H[$i][$n],
1158             $z,
1159             $q);
1160             }
1161             }
1162              
1163             # Overflow control.
1164 0         0 $t = max (abs ($H[$i][$n - 1]), abs ($H[$i][$n]));
1165 0 0       0 if ((eps * $t) * $t > 1)
1166             {
1167 0         0 for $j ($i .. $n)
1168             {
1169 0         0 $H[$j][$n - 1] /= $t;
1170 0         0 $H[$j][$n] /= $t;
1171             }
1172             }
1173             }
1174             }
1175             }
1176             }
1177              
1178             # Vectors of isolated roots.
1179 1         3 for $i (0 .. $end)
1180             {
1181 3 50 33     10 if ($i < $lo || $i > $hi)
1182             {
1183 0         0 for $j ($i .. $end)
1184             {
1185 0         0 $$Z[$j][$i] = $H[$i][$j];
1186             }
1187             }
1188             }
1189              
1190             # Multiply by transformation matrix to give
1191             # vectors of original full matrix.
1192 1         3 for $j (reverse ($lo .. $end))
1193             {
1194 3         7 $m = min ($j, $hi);
1195              
1196 3         6 for $i ($lo .. $hi)
1197             {
1198 9         10 $z = 0;
1199              
1200 9         19 for $k ($lo .. $m)
1201             {
1202 18         30 $z += $$Z[$k][$i] * $H[$k][$j];
1203             }
1204              
1205 9         11 $$Z[$j][$i] = $z;
1206             }
1207             }
1208             }
1209              
1210             # Form the eigenvectors of a real general matrix by back
1211             # transforming those of the corresponding balanced matrix
1212             # determined by 'balance'.
1213             #
1214             # See EiSPACK procedure 'balbak'.
1215 1 50       4 if ($prop{balance})
1216             {
1217 1         2 my ($i, $j, $k);
1218              
1219             # Undo permutations.
1220 1         4 for $i (reverse (0 .. $lo - 1))
1221             {
1222 0         0 $k = $perm[$i];
1223 0 0       0 if ($k != $i)
1224             {
1225 0         0 for $j (0 .. $end)
1226             {
1227 0         0 ($$Z[$j][$i], $$Z[$j][$k])
1228             = ($$Z[$j][$k], $$Z[$j][$i]);
1229             }
1230             }
1231             }
1232              
1233 1         4 for $i ($hi + 1 .. $end)
1234             {
1235 0         0 $k = $perm[$i];
1236 0 0       0 if ($k != $i)
1237             {
1238 0         0 for $j (0 .. $end)
1239             {
1240 0         0 ($$Z[$j][$i], $$Z[$j][$k])
1241             = ($$Z[$j][$k], $$Z[$j][$i]);
1242             }
1243             }
1244             }
1245             }
1246             }
1247              
1248             # Create complex eigenvalues.
1249 2         5 for my $i (0 .. $end)
1250             {
1251 6 50       12 $$d[$i] = Math::Complex->make ($$d[$i], $$e[$i])
1252             if $$e[$i] != 0;
1253             }
1254              
1255             # Normalize eigenvectors.
1256             $self->normalize
1257 2 50       8 if $prop{normalize};
1258              
1259             # Make first non-zero vector element a positive number.
1260 2 50       5 if ($prop{positive})
1261             {
1262 2         5 my ($i, $j, $k);
1263              
1264 2         4 for $j (0 .. $end)
1265             {
1266 6         10 for $i (0 .. $end)
1267             {
1268 6 50       14 next if $$Z[$j][$i] == 0;
1269              
1270 6 100       11 if ($$Z[$j][$i] < 0)
1271             {
1272 4         6 for $k ($i .. $end)
1273             {
1274 12         16 $$Z[$j][$k] = - $$Z[$j][$k];
1275             }
1276             }
1277              
1278 6         16 last;
1279             }
1280             }
1281             }
1282              
1283             # Return object.
1284 2         11 $self;
1285             }
1286              
1287             # Normalize eigenvectors.
1288             sub normalize
1289             {
1290 2     2 1 4 my $self = shift;
1291              
1292             # Work variables.
1293 2         3 my $len;
1294              
1295 2         3 for my $v (@{ $$self{vector} })
  2         5  
1296             {
1297 6         139 $len = blas_norm (@$v, $v, norm => BLAS_TWO_NORM);
1298 6 50       287 blas_rscale (@$v, $v, alpha => $len) if $len != 0;
1299             }
1300              
1301             # Return object.
1302 2         64 $self;
1303             }
1304              
1305             # Sort eigenvalues and corresponding eigenvectors.
1306             sub sort
1307             {
1308 2     2 1 3 my $self = shift;
1309 2   50     5 my $order = shift // 'abs_desc';
1310              
1311             # Permutation vector.
1312 2         5 my @p = ();
1313              
1314 2 50       5 if ($order =~ m/\Avec_/)
    50          
1315             {
1316 0         0 my $Z = $$self{vector};
1317              
1318             @p = ($order eq 'vec_desc' ?
1319 0         0 sort { _cmp_vec ($$Z[$b], $$Z[$a]) } 0 .. $#$Z :
1320             ($order eq 'vec_asc' ?
1321 0 0       0 sort { _cmp_vec ($$Z[$a], $$Z[$b]) } 0 .. $#$Z :
  0 0       0  
1322             croak ("Invalid argument")));
1323             }
1324 2         10 elsif (grep (ref ($_), @{ $$self{value} }))
1325             {
1326             # Consider complex eigenvalues.
1327 0         0 my (@d, @e, @m) = ();
1328              
1329 0         0 for (@{ $$self{value} })
  0         0  
1330             {
1331 0 0       0 if (ref ($_))
1332             {
1333 0         0 push (@d, $_->Re);
1334 0         0 push (@e, $_->Im);
1335 0         0 push (@m, abs ($_));
1336             }
1337             else
1338             {
1339 0         0 push (@d, $_);
1340 0         0 push (@e, 0);
1341 0         0 push (@m, abs ($_));
1342             }
1343             }
1344              
1345             @p = ($order eq 'abs_desc' ?
1346 0 0 0     0 sort { abs ($d[$b]) <=> abs ($d[$a]) || $m[$b] <=> $m[$a] || $d[$b] <=> $d[$a] || $e[$b] <=> $e[$a] } 0 .. $#d :
      0        
1347             ($order eq 'abs_asc' ?
1348 0 0 0     0 sort { abs ($d[$a]) <=> abs ($d[$b]) || $m[$a] <=> $m[$b] || $d[$a] <=> $d[$b] || $e[$a] <=> $e[$b] } 0 .. $#d :
      0        
1349             ($order eq 'norm_desc' ?
1350 0 0 0     0 sort { $m[$b] <=> $m[$a] || $d[$b] <=> $d[$a] || $e[$b] <=> $e[$a] } 0 .. $#d :
1351             ($order eq 'norm_asc' ?
1352 0 0 0     0 sort { $m[$a] <=> $m[$b] || $d[$a] <=> $d[$b] || $e[$a] <=> $e[$b] } 0 .. $#d :
1353             ($order eq 'desc' ?
1354 0 0       0 sort { $d[$b] <=> $d[$a] || $e[$b] <=> $e[$a] } 0 .. $#d :
1355             ($order eq 'asc' ?
1356 0 0       0 sort { $d[$a] <=> $d[$b] || $e[$a] <=> $e[$b] } 0 .. $#d :
  0 0       0  
    0          
    0          
    0          
    0          
    0          
1357             croak ("Invalid argument")))))));
1358             }
1359             else
1360             {
1361             # Only real eigenvalues.
1362 2         4 my $d = $$self{value};
1363              
1364             @p = ($order eq 'abs_desc' || $order eq 'norm_desc' ?
1365 0 0       0 sort { abs ($$d[$b]) <=> abs ($$d[$a]) || $$d[$b] <=> $$d[$a] } 0 .. $#$d :
1366             ($order eq 'abs_asc' || $order eq 'norm_asc' ?
1367 0 0       0 sort { abs ($$d[$a]) <=> abs ($$d[$b]) || $$d[$a] <=> $$d[$b] } 0 .. $#$d :
1368             ($order eq 'desc' ?
1369 0         0 sort { $$d[$b] <=> $$d[$a] } 0 .. $#$d :
1370             ($order eq 'asc' ?
1371 2 50 33     31 sort { $$d[$a] <=> $$d[$b] } 0 .. $#$d :
  5 50 33     14  
    50          
    50          
1372             croak ("Invalid argument")))));
1373             }
1374              
1375             # Reorder eigenvalues and corresponding eigenvectors.
1376 2 50       6 if (@p > 0)
1377             {
1378 2         3 my $ref;
1379              
1380 2         3 $ref = $$self{value};
1381 2         7 @$ref = @$ref[@p];
1382              
1383 2         3 $ref = $$self{vector};
1384 2         4 @$ref = @$ref[@p];
1385             }
1386              
1387             # Return object.
1388 2         6 $self;
1389             }
1390              
1391             # Compare two eigenvectors.
1392             sub _cmp_vec
1393             {
1394 0     0   0 my ($u, $v) = @_;
1395              
1396 0 0       0 croak ("Invalid argument")
1397             if $#$u != $#$v;
1398              
1399 0         0 my $d = 0;
1400              
1401 0         0 for my $i (0 .. $#$u)
1402             {
1403 0 0       0 last if $d = ($$u[$i] <=> $$v[$i]);
1404             }
1405              
1406 0         0 $d;
1407             }
1408              
1409             # Return one or more eigenvalues.
1410             sub value
1411             {
1412 2     2 1 4 my $self = shift;
1413              
1414 2 50       12 @_ > 0 ? @{ $$self{value} }[@_] : @{ $$self{value} };
  0         0  
  2         8  
1415             }
1416              
1417             *values = \&value;
1418              
1419             # Return one or more eigenvectors.
1420             sub vector
1421             {
1422 6     6 1 2035 my $self = shift;
1423              
1424 6 50       17 @_ > 0 ? @{ $$self{vector} }[@_] : @{ $$self{vector} };
  6         16  
  0            
1425             }
1426              
1427             *vectors = \&vector;
1428              
1429             1;
1430              
1431             __END__