rubysl-matrix 1.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4352b6d39cda8317bc801f0ba18351a8bbfeb9c9
4
- data.tar.gz: c99b10ce12cf8eefa06d89b89efe129d85ea63ce
3
+ metadata.gz: d6755874e753b4736c6d210443580375656a602b
4
+ data.tar.gz: 4dbf6dfbf4cf58473d4632be499ce3b5e2b71737
5
5
  SHA512:
6
- metadata.gz: 9ff15f3449d2db32e3af7236d5a0b8b78bbfda777af44d1cc82d9a1c6bce6e6dbe7dc835bf9669f920a59519431f5e2b6e72efd12242f2258adccd35fe0b97c0
7
- data.tar.gz: d0fe7fb661a965e5b9cf18357ebbb6b67af266cdb21f45445aca5bac276107f4427e6275679431c0b043d2906d25f85a52233cad6be8a7e196b2e779298fc2d7
6
+ metadata.gz: 882328b70fa7941f5d4b7e680a5e5f10e8fb2b360ab4d6ab329b2022d2a5bc660df50dd8886c48c61cb9a2b44f5c436d40a264c35548a4dd6b86d8672890e18e
7
+ data.tar.gz: 21489946f1015972fe6e005e4ec30b5e8340568e17651948ba526fd564f3bc9f1a6a37f4883b62d69abd1ccafe5fd546e3d11ccce42bcb26ff30860c53a54ded
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
2
  before_install:
3
+ - rvm use $RVM --install --binary --fuzzy
3
4
  - gem update --system
4
5
  - gem --version
5
6
  - gem install rubysl-bundler
7
+ env:
8
+ - RVM=rbx-nightly-d21 RUBYLIB=lib
6
9
  script: bundle exec mspec spec
7
- rvm:
8
- - rbx-nightly-18mode
@@ -0,0 +1,882 @@
1
+ class Matrix
2
+ # Adapted from JAMA: http://math.nist.gov/javanumerics/jama/
3
+
4
+ # Eigenvalues and eigenvectors of a real matrix.
5
+ #
6
+ # Computes the eigenvalues and eigenvectors of a matrix A.
7
+ #
8
+ # If A is diagonalizable, this provides matrices V and D
9
+ # such that A = V*D*V.inv, where D is the diagonal matrix with entries
10
+ # equal to the eigenvalues and V is formed by the eigenvectors.
11
+ #
12
+ # If A is symmetric, then V is orthogonal and thus A = V*D*V.t
13
+
14
+ class EigenvalueDecomposition
15
+
16
+ # Constructs the eigenvalue decomposition for a square matrix +A+
17
+ #
18
+ def initialize(a)
19
+ # @d, @e: Arrays for internal storage of eigenvalues.
20
+ # @v: Array for internal storage of eigenvectors.
21
+ # @h: Array for internal storage of nonsymmetric Hessenberg form.
22
+ raise TypeError, "Expected Matrix but got #{a.class}" unless a.is_a?(Matrix)
23
+ @size = a.row_count
24
+ @d = Array.new(@size, 0)
25
+ @e = Array.new(@size, 0)
26
+
27
+ if (@symmetric = a.symmetric?)
28
+ @v = a.to_a
29
+ tridiagonalize
30
+ diagonalize
31
+ else
32
+ @v = Array.new(@size) { Array.new(@size, 0) }
33
+ @h = a.to_a
34
+ @ort = Array.new(@size, 0)
35
+ reduce_to_hessenberg
36
+ hessenberg_to_real_schur
37
+ end
38
+ end
39
+
40
+ # Returns the eigenvector matrix +V+
41
+ #
42
+ def eigenvector_matrix
43
+ Matrix.send :new, build_eigenvectors.transpose
44
+ end
45
+ alias v eigenvector_matrix
46
+
47
+ # Returns the inverse of the eigenvector matrix +V+
48
+ #
49
+ def eigenvector_matrix_inv
50
+ r = Matrix.send :new, build_eigenvectors
51
+ r = r.transpose.inverse unless @symmetric
52
+ r
53
+ end
54
+ alias v_inv eigenvector_matrix_inv
55
+
56
+ # Returns the eigenvalues in an array
57
+ #
58
+ def eigenvalues
59
+ values = @d.dup
60
+ @e.each_with_index{|imag, i| values[i] = Complex(values[i], imag) unless imag == 0}
61
+ values
62
+ end
63
+
64
+ # Returns an array of the eigenvectors
65
+ #
66
+ def eigenvectors
67
+ build_eigenvectors.map{|ev| Vector.send :new, ev}
68
+ end
69
+
70
+ # Returns the block diagonal eigenvalue matrix +D+
71
+ #
72
+ def eigenvalue_matrix
73
+ Matrix.diagonal(*eigenvalues)
74
+ end
75
+ alias d eigenvalue_matrix
76
+
77
+ # Returns [eigenvector_matrix, eigenvalue_matrix, eigenvector_matrix_inv]
78
+ #
79
+ def to_ary
80
+ [v, d, v_inv]
81
+ end
82
+ alias_method :to_a, :to_ary
83
+
84
+ private
85
+ def build_eigenvectors
86
+ # JAMA stores complex eigenvectors in a strange way
87
+ # See http://web.archive.org/web/20111016032731/http://cio.nist.gov/esd/emaildir/lists/jama/msg01021.html
88
+ @e.each_with_index.map do |imag, i|
89
+ if imag == 0
90
+ Array.new(@size){|j| @v[j][i]}
91
+ elsif imag > 0
92
+ Array.new(@size){|j| Complex(@v[j][i], @v[j][i+1])}
93
+ else
94
+ Array.new(@size){|j| Complex(@v[j][i-1], -@v[j][i])}
95
+ end
96
+ end
97
+ end
98
+ # Complex scalar division.
99
+
100
+ def cdiv(xr, xi, yr, yi)
101
+ if (yr.abs > yi.abs)
102
+ r = yi/yr
103
+ d = yr + r*yi
104
+ [(xr + r*xi)/d, (xi - r*xr)/d]
105
+ else
106
+ r = yr/yi
107
+ d = yi + r*yr
108
+ [(r*xr + xi)/d, (r*xi - xr)/d]
109
+ end
110
+ end
111
+
112
+
113
+ # Symmetric Householder reduction to tridiagonal form.
114
+
115
+ def tridiagonalize
116
+
117
+ # This is derived from the Algol procedures tred2 by
118
+ # Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
119
+ # Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
120
+ # Fortran subroutine in EISPACK.
121
+
122
+ @size.times do |j|
123
+ @d[j] = @v[@size-1][j]
124
+ end
125
+
126
+ # Householder reduction to tridiagonal form.
127
+
128
+ (@size-1).downto(0+1) do |i|
129
+
130
+ # Scale to avoid under/overflow.
131
+
132
+ scale = 0.0
133
+ h = 0.0
134
+ i.times do |k|
135
+ scale = scale + @d[k].abs
136
+ end
137
+ if (scale == 0.0)
138
+ @e[i] = @d[i-1]
139
+ i.times do |j|
140
+ @d[j] = @v[i-1][j]
141
+ @v[i][j] = 0.0
142
+ @v[j][i] = 0.0
143
+ end
144
+ else
145
+
146
+ # Generate Householder vector.
147
+
148
+ i.times do |k|
149
+ @d[k] /= scale
150
+ h += @d[k] * @d[k]
151
+ end
152
+ f = @d[i-1]
153
+ g = Math.sqrt(h)
154
+ if (f > 0)
155
+ g = -g
156
+ end
157
+ @e[i] = scale * g
158
+ h -= f * g
159
+ @d[i-1] = f - g
160
+ i.times do |j|
161
+ @e[j] = 0.0
162
+ end
163
+
164
+ # Apply similarity transformation to remaining columns.
165
+
166
+ i.times do |j|
167
+ f = @d[j]
168
+ @v[j][i] = f
169
+ g = @e[j] + @v[j][j] * f
170
+ (j+1).upto(i-1) do |k|
171
+ g += @v[k][j] * @d[k]
172
+ @e[k] += @v[k][j] * f
173
+ end
174
+ @e[j] = g
175
+ end
176
+ f = 0.0
177
+ i.times do |j|
178
+ @e[j] /= h
179
+ f += @e[j] * @d[j]
180
+ end
181
+ hh = f / (h + h)
182
+ i.times do |j|
183
+ @e[j] -= hh * @d[j]
184
+ end
185
+ i.times do |j|
186
+ f = @d[j]
187
+ g = @e[j]
188
+ j.upto(i-1) do |k|
189
+ @v[k][j] -= (f * @e[k] + g * @d[k])
190
+ end
191
+ @d[j] = @v[i-1][j]
192
+ @v[i][j] = 0.0
193
+ end
194
+ end
195
+ @d[i] = h
196
+ end
197
+
198
+ # Accumulate transformations.
199
+
200
+ 0.upto(@size-1-1) do |i|
201
+ @v[@size-1][i] = @v[i][i]
202
+ @v[i][i] = 1.0
203
+ h = @d[i+1]
204
+ if (h != 0.0)
205
+ 0.upto(i) do |k|
206
+ @d[k] = @v[k][i+1] / h
207
+ end
208
+ 0.upto(i) do |j|
209
+ g = 0.0
210
+ 0.upto(i) do |k|
211
+ g += @v[k][i+1] * @v[k][j]
212
+ end
213
+ 0.upto(i) do |k|
214
+ @v[k][j] -= g * @d[k]
215
+ end
216
+ end
217
+ end
218
+ 0.upto(i) do |k|
219
+ @v[k][i+1] = 0.0
220
+ end
221
+ end
222
+ @size.times do |j|
223
+ @d[j] = @v[@size-1][j]
224
+ @v[@size-1][j] = 0.0
225
+ end
226
+ @v[@size-1][@size-1] = 1.0
227
+ @e[0] = 0.0
228
+ end
229
+
230
+
231
+ # Symmetric tridiagonal QL algorithm.
232
+
233
+ def diagonalize
234
+ # This is derived from the Algol procedures tql2, by
235
+ # Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
236
+ # Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
237
+ # Fortran subroutine in EISPACK.
238
+
239
+ 1.upto(@size-1) do |i|
240
+ @e[i-1] = @e[i]
241
+ end
242
+ @e[@size-1] = 0.0
243
+
244
+ f = 0.0
245
+ tst1 = 0.0
246
+ eps = Float::EPSILON
247
+ @size.times do |l|
248
+
249
+ # Find small subdiagonal element
250
+
251
+ tst1 = [tst1, @d[l].abs + @e[l].abs].max
252
+ m = l
253
+ while (m < @size) do
254
+ if (@e[m].abs <= eps*tst1)
255
+ break
256
+ end
257
+ m+=1
258
+ end
259
+
260
+ # If m == l, @d[l] is an eigenvalue,
261
+ # otherwise, iterate.
262
+
263
+ if (m > l)
264
+ iter = 0
265
+ begin
266
+ iter = iter + 1 # (Could check iteration count here.)
267
+
268
+ # Compute implicit shift
269
+
270
+ g = @d[l]
271
+ p = (@d[l+1] - g) / (2.0 * @e[l])
272
+ r = Math.hypot(p, 1.0)
273
+ if (p < 0)
274
+ r = -r
275
+ end
276
+ @d[l] = @e[l] / (p + r)
277
+ @d[l+1] = @e[l] * (p + r)
278
+ dl1 = @d[l+1]
279
+ h = g - @d[l]
280
+ (l+2).upto(@size-1) do |i|
281
+ @d[i] -= h
282
+ end
283
+ f += h
284
+
285
+ # Implicit QL transformation.
286
+
287
+ p = @d[m]
288
+ c = 1.0
289
+ c2 = c
290
+ c3 = c
291
+ el1 = @e[l+1]
292
+ s = 0.0
293
+ s2 = 0.0
294
+ (m-1).downto(l) do |i|
295
+ c3 = c2
296
+ c2 = c
297
+ s2 = s
298
+ g = c * @e[i]
299
+ h = c * p
300
+ r = Math.hypot(p, @e[i])
301
+ @e[i+1] = s * r
302
+ s = @e[i] / r
303
+ c = p / r
304
+ p = c * @d[i] - s * g
305
+ @d[i+1] = h + s * (c * g + s * @d[i])
306
+
307
+ # Accumulate transformation.
308
+
309
+ @size.times do |k|
310
+ h = @v[k][i+1]
311
+ @v[k][i+1] = s * @v[k][i] + c * h
312
+ @v[k][i] = c * @v[k][i] - s * h
313
+ end
314
+ end
315
+ p = -s * s2 * c3 * el1 * @e[l] / dl1
316
+ @e[l] = s * p
317
+ @d[l] = c * p
318
+
319
+ # Check for convergence.
320
+
321
+ end while (@e[l].abs > eps*tst1)
322
+ end
323
+ @d[l] = @d[l] + f
324
+ @e[l] = 0.0
325
+ end
326
+
327
+ # Sort eigenvalues and corresponding vectors.
328
+
329
+ 0.upto(@size-2) do |i|
330
+ k = i
331
+ p = @d[i]
332
+ (i+1).upto(@size-1) do |j|
333
+ if (@d[j] < p)
334
+ k = j
335
+ p = @d[j]
336
+ end
337
+ end
338
+ if (k != i)
339
+ @d[k] = @d[i]
340
+ @d[i] = p
341
+ @size.times do |j|
342
+ p = @v[j][i]
343
+ @v[j][i] = @v[j][k]
344
+ @v[j][k] = p
345
+ end
346
+ end
347
+ end
348
+ end
349
+
350
+ # Nonsymmetric reduction to Hessenberg form.
351
+
352
+ def reduce_to_hessenberg
353
+ # This is derived from the Algol procedures orthes and ortran,
354
+ # by Martin and Wilkinson, Handbook for Auto. Comp.,
355
+ # Vol.ii-Linear Algebra, and the corresponding
356
+ # Fortran subroutines in EISPACK.
357
+
358
+ low = 0
359
+ high = @size-1
360
+
361
+ (low+1).upto(high-1) do |m|
362
+
363
+ # Scale column.
364
+
365
+ scale = 0.0
366
+ m.upto(high) do |i|
367
+ scale = scale + @h[i][m-1].abs
368
+ end
369
+ if (scale != 0.0)
370
+
371
+ # Compute Householder transformation.
372
+
373
+ h = 0.0
374
+ high.downto(m) do |i|
375
+ @ort[i] = @h[i][m-1]/scale
376
+ h += @ort[i] * @ort[i]
377
+ end
378
+ g = Math.sqrt(h)
379
+ if (@ort[m] > 0)
380
+ g = -g
381
+ end
382
+ h -= @ort[m] * g
383
+ @ort[m] = @ort[m] - g
384
+
385
+ # Apply Householder similarity transformation
386
+ # @h = (I-u*u'/h)*@h*(I-u*u')/h)
387
+
388
+ m.upto(@size-1) do |j|
389
+ f = 0.0
390
+ high.downto(m) do |i|
391
+ f += @ort[i]*@h[i][j]
392
+ end
393
+ f = f/h
394
+ m.upto(high) do |i|
395
+ @h[i][j] -= f*@ort[i]
396
+ end
397
+ end
398
+
399
+ 0.upto(high) do |i|
400
+ f = 0.0
401
+ high.downto(m) do |j|
402
+ f += @ort[j]*@h[i][j]
403
+ end
404
+ f = f/h
405
+ m.upto(high) do |j|
406
+ @h[i][j] -= f*@ort[j]
407
+ end
408
+ end
409
+ @ort[m] = scale*@ort[m]
410
+ @h[m][m-1] = scale*g
411
+ end
412
+ end
413
+
414
+ # Accumulate transformations (Algol's ortran).
415
+
416
+ @size.times do |i|
417
+ @size.times do |j|
418
+ @v[i][j] = (i == j ? 1.0 : 0.0)
419
+ end
420
+ end
421
+
422
+ (high-1).downto(low+1) do |m|
423
+ if (@h[m][m-1] != 0.0)
424
+ (m+1).upto(high) do |i|
425
+ @ort[i] = @h[i][m-1]
426
+ end
427
+ m.upto(high) do |j|
428
+ g = 0.0
429
+ m.upto(high) do |i|
430
+ g += @ort[i] * @v[i][j]
431
+ end
432
+ # Double division avoids possible underflow
433
+ g = (g / @ort[m]) / @h[m][m-1]
434
+ m.upto(high) do |i|
435
+ @v[i][j] += g * @ort[i]
436
+ end
437
+ end
438
+ end
439
+ end
440
+ end
441
+
442
+
443
+
444
+ # Nonsymmetric reduction from Hessenberg to real Schur form.
445
+
446
+ def hessenberg_to_real_schur
447
+
448
+ # This is derived from the Algol procedure hqr2,
449
+ # by Martin and Wilkinson, Handbook for Auto. Comp.,
450
+ # Vol.ii-Linear Algebra, and the corresponding
451
+ # Fortran subroutine in EISPACK.
452
+
453
+ # Initialize
454
+
455
+ nn = @size
456
+ n = nn-1
457
+ low = 0
458
+ high = nn-1
459
+ eps = Float::EPSILON
460
+ exshift = 0.0
461
+ p=q=r=s=z=0
462
+
463
+ # Store roots isolated by balanc and compute matrix norm
464
+
465
+ norm = 0.0
466
+ nn.times do |i|
467
+ if (i < low || i > high)
468
+ @d[i] = @h[i][i]
469
+ @e[i] = 0.0
470
+ end
471
+ ([i-1, 0].max).upto(nn-1) do |j|
472
+ norm = norm + @h[i][j].abs
473
+ end
474
+ end
475
+
476
+ # Outer loop over eigenvalue index
477
+
478
+ iter = 0
479
+ while (n >= low) do
480
+
481
+ # Look for single small sub-diagonal element
482
+
483
+ l = n
484
+ while (l > low) do
485
+ s = @h[l-1][l-1].abs + @h[l][l].abs
486
+ if (s == 0.0)
487
+ s = norm
488
+ end
489
+ if (@h[l][l-1].abs < eps * s)
490
+ break
491
+ end
492
+ l-=1
493
+ end
494
+
495
+ # Check for convergence
496
+ # One root found
497
+
498
+ if (l == n)
499
+ @h[n][n] = @h[n][n] + exshift
500
+ @d[n] = @h[n][n]
501
+ @e[n] = 0.0
502
+ n-=1
503
+ iter = 0
504
+
505
+ # Two roots found
506
+
507
+ elsif (l == n-1)
508
+ w = @h[n][n-1] * @h[n-1][n]
509
+ p = (@h[n-1][n-1] - @h[n][n]) / 2.0
510
+ q = p * p + w
511
+ z = Math.sqrt(q.abs)
512
+ @h[n][n] = @h[n][n] + exshift
513
+ @h[n-1][n-1] = @h[n-1][n-1] + exshift
514
+ x = @h[n][n]
515
+
516
+ # Real pair
517
+
518
+ if (q >= 0)
519
+ if (p >= 0)
520
+ z = p + z
521
+ else
522
+ z = p - z
523
+ end
524
+ @d[n-1] = x + z
525
+ @d[n] = @d[n-1]
526
+ if (z != 0.0)
527
+ @d[n] = x - w / z
528
+ end
529
+ @e[n-1] = 0.0
530
+ @e[n] = 0.0
531
+ x = @h[n][n-1]
532
+ s = x.abs + z.abs
533
+ p = x / s
534
+ q = z / s
535
+ r = Math.sqrt(p * p+q * q)
536
+ p /= r
537
+ q /= r
538
+
539
+ # Row modification
540
+
541
+ (n-1).upto(nn-1) do |j|
542
+ z = @h[n-1][j]
543
+ @h[n-1][j] = q * z + p * @h[n][j]
544
+ @h[n][j] = q * @h[n][j] - p * z
545
+ end
546
+
547
+ # Column modification
548
+
549
+ 0.upto(n) do |i|
550
+ z = @h[i][n-1]
551
+ @h[i][n-1] = q * z + p * @h[i][n]
552
+ @h[i][n] = q * @h[i][n] - p * z
553
+ end
554
+
555
+ # Accumulate transformations
556
+
557
+ low.upto(high) do |i|
558
+ z = @v[i][n-1]
559
+ @v[i][n-1] = q * z + p * @v[i][n]
560
+ @v[i][n] = q * @v[i][n] - p * z
561
+ end
562
+
563
+ # Complex pair
564
+
565
+ else
566
+ @d[n-1] = x + p
567
+ @d[n] = x + p
568
+ @e[n-1] = z
569
+ @e[n] = -z
570
+ end
571
+ n -= 2
572
+ iter = 0
573
+
574
+ # No convergence yet
575
+
576
+ else
577
+
578
+ # Form shift
579
+
580
+ x = @h[n][n]
581
+ y = 0.0
582
+ w = 0.0
583
+ if (l < n)
584
+ y = @h[n-1][n-1]
585
+ w = @h[n][n-1] * @h[n-1][n]
586
+ end
587
+
588
+ # Wilkinson's original ad hoc shift
589
+
590
+ if (iter == 10)
591
+ exshift += x
592
+ low.upto(n) do |i|
593
+ @h[i][i] -= x
594
+ end
595
+ s = @h[n][n-1].abs + @h[n-1][n-2].abs
596
+ x = y = 0.75 * s
597
+ w = -0.4375 * s * s
598
+ end
599
+
600
+ # MATLAB's new ad hoc shift
601
+
602
+ if (iter == 30)
603
+ s = (y - x) / 2.0
604
+ s *= s + w
605
+ if (s > 0)
606
+ s = Math.sqrt(s)
607
+ if (y < x)
608
+ s = -s
609
+ end
610
+ s = x - w / ((y - x) / 2.0 + s)
611
+ low.upto(n) do |i|
612
+ @h[i][i] -= s
613
+ end
614
+ exshift += s
615
+ x = y = w = 0.964
616
+ end
617
+ end
618
+
619
+ iter = iter + 1 # (Could check iteration count here.)
620
+
621
+ # Look for two consecutive small sub-diagonal elements
622
+
623
+ m = n-2
624
+ while (m >= l) do
625
+ z = @h[m][m]
626
+ r = x - z
627
+ s = y - z
628
+ p = (r * s - w) / @h[m+1][m] + @h[m][m+1]
629
+ q = @h[m+1][m+1] - z - r - s
630
+ r = @h[m+2][m+1]
631
+ s = p.abs + q.abs + r.abs
632
+ p /= s
633
+ q /= s
634
+ r /= s
635
+ if (m == l)
636
+ break
637
+ end
638
+ if (@h[m][m-1].abs * (q.abs + r.abs) <
639
+ eps * (p.abs * (@h[m-1][m-1].abs + z.abs +
640
+ @h[m+1][m+1].abs)))
641
+ break
642
+ end
643
+ m-=1
644
+ end
645
+
646
+ (m+2).upto(n) do |i|
647
+ @h[i][i-2] = 0.0
648
+ if (i > m+2)
649
+ @h[i][i-3] = 0.0
650
+ end
651
+ end
652
+
653
+ # Double QR step involving rows l:n and columns m:n
654
+
655
+ m.upto(n-1) do |k|
656
+ notlast = (k != n-1)
657
+ if (k != m)
658
+ p = @h[k][k-1]
659
+ q = @h[k+1][k-1]
660
+ r = (notlast ? @h[k+2][k-1] : 0.0)
661
+ x = p.abs + q.abs + r.abs
662
+ next if x == 0
663
+ p /= x
664
+ q /= x
665
+ r /= x
666
+ end
667
+ s = Math.sqrt(p * p + q * q + r * r)
668
+ if (p < 0)
669
+ s = -s
670
+ end
671
+ if (s != 0)
672
+ if (k != m)
673
+ @h[k][k-1] = -s * x
674
+ elsif (l != m)
675
+ @h[k][k-1] = -@h[k][k-1]
676
+ end
677
+ p += s
678
+ x = p / s
679
+ y = q / s
680
+ z = r / s
681
+ q /= p
682
+ r /= p
683
+
684
+ # Row modification
685
+
686
+ k.upto(nn-1) do |j|
687
+ p = @h[k][j] + q * @h[k+1][j]
688
+ if (notlast)
689
+ p += r * @h[k+2][j]
690
+ @h[k+2][j] = @h[k+2][j] - p * z
691
+ end
692
+ @h[k][j] = @h[k][j] - p * x
693
+ @h[k+1][j] = @h[k+1][j] - p * y
694
+ end
695
+
696
+ # Column modification
697
+
698
+ 0.upto([n, k+3].min) do |i|
699
+ p = x * @h[i][k] + y * @h[i][k+1]
700
+ if (notlast)
701
+ p += z * @h[i][k+2]
702
+ @h[i][k+2] = @h[i][k+2] - p * r
703
+ end
704
+ @h[i][k] = @h[i][k] - p
705
+ @h[i][k+1] = @h[i][k+1] - p * q
706
+ end
707
+
708
+ # Accumulate transformations
709
+
710
+ low.upto(high) do |i|
711
+ p = x * @v[i][k] + y * @v[i][k+1]
712
+ if (notlast)
713
+ p += z * @v[i][k+2]
714
+ @v[i][k+2] = @v[i][k+2] - p * r
715
+ end
716
+ @v[i][k] = @v[i][k] - p
717
+ @v[i][k+1] = @v[i][k+1] - p * q
718
+ end
719
+ end # (s != 0)
720
+ end # k loop
721
+ end # check convergence
722
+ end # while (n >= low)
723
+
724
+ # Backsubstitute to find vectors of upper triangular form
725
+
726
+ if (norm == 0.0)
727
+ return
728
+ end
729
+
730
+ (nn-1).downto(0) do |n|
731
+ p = @d[n]
732
+ q = @e[n]
733
+
734
+ # Real vector
735
+
736
+ if (q == 0)
737
+ l = n
738
+ @h[n][n] = 1.0
739
+ (n-1).downto(0) do |i|
740
+ w = @h[i][i] - p
741
+ r = 0.0
742
+ l.upto(n) do |j|
743
+ r += @h[i][j] * @h[j][n]
744
+ end
745
+ if (@e[i] < 0.0)
746
+ z = w
747
+ s = r
748
+ else
749
+ l = i
750
+ if (@e[i] == 0.0)
751
+ if (w != 0.0)
752
+ @h[i][n] = -r / w
753
+ else
754
+ @h[i][n] = -r / (eps * norm)
755
+ end
756
+
757
+ # Solve real equations
758
+
759
+ else
760
+ x = @h[i][i+1]
761
+ y = @h[i+1][i]
762
+ q = (@d[i] - p) * (@d[i] - p) + @e[i] * @e[i]
763
+ t = (x * s - z * r) / q
764
+ @h[i][n] = t
765
+ if (x.abs > z.abs)
766
+ @h[i+1][n] = (-r - w * t) / x
767
+ else
768
+ @h[i+1][n] = (-s - y * t) / z
769
+ end
770
+ end
771
+
772
+ # Overflow control
773
+
774
+ t = @h[i][n].abs
775
+ if ((eps * t) * t > 1)
776
+ i.upto(n) do |j|
777
+ @h[j][n] = @h[j][n] / t
778
+ end
779
+ end
780
+ end
781
+ end
782
+
783
+ # Complex vector
784
+
785
+ elsif (q < 0)
786
+ l = n-1
787
+
788
+ # Last vector component imaginary so matrix is triangular
789
+
790
+ if (@h[n][n-1].abs > @h[n-1][n].abs)
791
+ @h[n-1][n-1] = q / @h[n][n-1]
792
+ @h[n-1][n] = -(@h[n][n] - p) / @h[n][n-1]
793
+ else
794
+ cdivr, cdivi = cdiv(0.0, -@h[n-1][n], @h[n-1][n-1]-p, q)
795
+ @h[n-1][n-1] = cdivr
796
+ @h[n-1][n] = cdivi
797
+ end
798
+ @h[n][n-1] = 0.0
799
+ @h[n][n] = 1.0
800
+ (n-2).downto(0) do |i|
801
+ ra = 0.0
802
+ sa = 0.0
803
+ l.upto(n) do |j|
804
+ ra = ra + @h[i][j] * @h[j][n-1]
805
+ sa = sa + @h[i][j] * @h[j][n]
806
+ end
807
+ w = @h[i][i] - p
808
+
809
+ if (@e[i] < 0.0)
810
+ z = w
811
+ r = ra
812
+ s = sa
813
+ else
814
+ l = i
815
+ if (@e[i] == 0)
816
+ cdivr, cdivi = cdiv(-ra, -sa, w, q)
817
+ @h[i][n-1] = cdivr
818
+ @h[i][n] = cdivi
819
+ else
820
+
821
+ # Solve complex equations
822
+
823
+ x = @h[i][i+1]
824
+ y = @h[i+1][i]
825
+ vr = (@d[i] - p) * (@d[i] - p) + @e[i] * @e[i] - q * q
826
+ vi = (@d[i] - p) * 2.0 * q
827
+ if (vr == 0.0 && vi == 0.0)
828
+ vr = eps * norm * (w.abs + q.abs +
829
+ x.abs + y.abs + z.abs)
830
+ end
831
+ cdivr, cdivi = cdiv(x*r-z*ra+q*sa, x*s-z*sa-q*ra, vr, vi)
832
+ @h[i][n-1] = cdivr
833
+ @h[i][n] = cdivi
834
+ if (x.abs > (z.abs + q.abs))
835
+ @h[i+1][n-1] = (-ra - w * @h[i][n-1] + q * @h[i][n]) / x
836
+ @h[i+1][n] = (-sa - w * @h[i][n] - q * @h[i][n-1]) / x
837
+ else
838
+ cdivr, cdivi = cdiv(-r-y*@h[i][n-1], -s-y*@h[i][n], z, q)
839
+ @h[i+1][n-1] = cdivr
840
+ @h[i+1][n] = cdivi
841
+ end
842
+ end
843
+
844
+ # Overflow control
845
+
846
+ t = [@h[i][n-1].abs, @h[i][n].abs].max
847
+ if ((eps * t) * t > 1)
848
+ i.upto(n) do |j|
849
+ @h[j][n-1] = @h[j][n-1] / t
850
+ @h[j][n] = @h[j][n] / t
851
+ end
852
+ end
853
+ end
854
+ end
855
+ end
856
+ end
857
+
858
+ # Vectors of isolated roots
859
+
860
+ nn.times do |i|
861
+ if (i < low || i > high)
862
+ i.upto(nn-1) do |j|
863
+ @v[i][j] = @h[i][j]
864
+ end
865
+ end
866
+ end
867
+
868
+ # Back transformation to get eigenvectors of original matrix
869
+
870
+ (nn-1).downto(low) do |j|
871
+ low.upto(high) do |i|
872
+ z = 0.0
873
+ low.upto([j, high].min) do |k|
874
+ z += @v[i][k] * @h[k][j]
875
+ end
876
+ @v[i][j] = z
877
+ end
878
+ end
879
+ end
880
+
881
+ end
882
+ end