rubysl-matrix 1.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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