extendmatrix 0.2.3 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/History.txt +7 -0
- data/lib/extendmatrix.rb +402 -340
- data/spec/extendmatrix_spec.rb +41 -2
- metadata +5 -5
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/History.txt
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
=== 0.3.1 / 2010-08-16
|
2
|
+
* Bug fix: method mssq returns only the last row squares
|
3
|
+
* Added method diagonal
|
4
|
+
|
5
|
+
=== 0.3.0 / 2010-08-16
|
6
|
+
* Added new methods, inspired on SPSS matrix methods: e_mult, e_quo, mssq, eigen, eigenpairs, ln, sqrt, sscp
|
7
|
+
|
1
8
|
=== 0.2.3 / 2010-05-05
|
2
9
|
* Added LICENSE.txt
|
3
10
|
* Complete README.txt
|
data/lib/extendmatrix.rb
CHANGED
@@ -42,9 +42,9 @@ class Vector
|
|
42
42
|
# Magnitude or length of the vector
|
43
43
|
# Equal to sqrt(sum(x_i^2))
|
44
44
|
def magnitude
|
45
|
-
Math::sqrt(to_a.inject(0) {|ac,v| ac+(v**2)})
|
45
|
+
Math::sqrt(to_a.inject(0) {|ac, v| ac+(v**2)})
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
#
|
49
49
|
# Sets a vector value/(range of values) with a new value/(values from a vector)
|
50
50
|
# v = Vector[1, 2, 3]
|
@@ -65,7 +65,7 @@ class Vector
|
|
65
65
|
class << self
|
66
66
|
#
|
67
67
|
# Returns a concatenated Vector
|
68
|
-
#
|
68
|
+
#
|
69
69
|
# Vector.concat(Vector[1,2,3], Vector[4,5,6])
|
70
70
|
# => Vector[1, 2, 3, 4, 5, 6]
|
71
71
|
#
|
@@ -105,7 +105,7 @@ class Vector
|
|
105
105
|
end
|
106
106
|
#
|
107
107
|
# Returns the sum of values of the vector
|
108
|
-
#
|
108
|
+
#
|
109
109
|
def sum
|
110
110
|
to_a.inject(&:+)
|
111
111
|
end
|
@@ -222,7 +222,7 @@ end
|
|
222
222
|
|
223
223
|
class Matrix
|
224
224
|
|
225
|
-
EXTENSION_VERSION="0.
|
225
|
+
EXTENSION_VERSION="0.3.1"
|
226
226
|
include Enumerable
|
227
227
|
|
228
228
|
attr_reader :rows, :wrap
|
@@ -330,17 +330,17 @@ class Matrix
|
|
330
330
|
|
331
331
|
|
332
332
|
class << self
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
333
|
+
if !self.respond_to? :build
|
334
|
+
#
|
335
|
+
# Creates a matrix <tt>n</tt> x <tt>m</tt>
|
336
|
+
# If you provide a block, it will be used to set the values.
|
337
|
+
# If not, <tt>val</tt> will be used
|
338
|
+
#
|
339
|
+
|
340
|
+
def build(n,m,val=0)
|
341
|
+
f = (block_given?)? lambda {|i,j| yield(i, j)} : lambda {|i,j| val}
|
342
|
+
Matrix.rows((0...n).collect {|i| (0...m).collect {|j| f.call(i,j)}})
|
343
|
+
end
|
344
344
|
end
|
345
345
|
#
|
346
346
|
# Creates a matrix with the given matrices as diagonal blocks
|
@@ -392,7 +392,8 @@ class Matrix
|
|
392
392
|
def quo(v)
|
393
393
|
map {|e| e.quo(v)}
|
394
394
|
end
|
395
|
-
|
395
|
+
|
396
|
+
alias :old_divition :/
|
396
397
|
#
|
397
398
|
# quo seems always desirable
|
398
399
|
#
|
@@ -412,136 +413,197 @@ class Matrix
|
|
412
413
|
|
413
414
|
def wraplate(ijwrap = "")
|
414
415
|
"class << self
|
415
|
-
|
416
|
+
def [](i, j)
|
416
417
|
#{ijwrap}; @rows[i][j]
|
417
|
-
|
418
|
+
end
|
418
419
|
|
419
|
-
|
420
|
+
def []=(i, j, v)
|
420
421
|
#{ijwrap}; @rows[i][j] = v
|
421
|
-
|
422
|
-
|
423
|
-
end
|
422
|
+
end
|
423
|
+
end"
|
424
|
+
end
|
424
425
|
|
425
|
-
#
|
426
|
-
# Set wrap feature of a matrix
|
427
|
-
#
|
428
|
-
def wrap=(mode = :torus)
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
end
|
426
|
+
#
|
427
|
+
# Set wrap feature of a matrix
|
428
|
+
#
|
429
|
+
def wrap=(mode = :torus)
|
430
|
+
case mode
|
431
|
+
when :torus then eval(wraplate("i %= row_size; j %= column_size"))
|
432
|
+
when :h_cylinder then eval(wraplate("i %= row_size"))
|
433
|
+
when :v_cylinder then eval(wraplate("j %= column_size"))
|
434
|
+
when :nil then eval(wraplate)
|
435
|
+
end
|
436
|
+
@wrap = mode
|
437
|
+
end
|
437
438
|
|
438
|
-
#
|
439
|
-
# Returns the maximum length of column elements
|
440
|
-
#
|
441
|
-
def max_len_column(j)
|
442
|
-
|
443
|
-
end
|
439
|
+
#
|
440
|
+
# Returns the maximum length of column elements
|
441
|
+
#
|
442
|
+
def max_len_column(j)
|
443
|
+
column_collect(j) {|x| x.to_s.length}.max
|
444
|
+
end
|
444
445
|
|
445
|
-
#
|
446
|
-
# Returns a list with the maximum lengths
|
447
|
-
#
|
448
|
-
def cols_len
|
449
|
-
|
450
|
-
end
|
446
|
+
#
|
447
|
+
# Returns a list with the maximum lengths
|
448
|
+
#
|
449
|
+
def cols_len
|
450
|
+
(0...column_size).collect {|j| max_len_column(j)}
|
451
|
+
end
|
451
452
|
|
452
|
-
alias :to_s_old :to_s
|
453
|
+
alias :to_s_old :to_s
|
453
454
|
|
454
|
-
#
|
455
|
-
# Returns a string for nice printing matrix
|
456
|
-
#
|
457
|
-
def to_s(mode = :pretty, len_col = 3)
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
455
|
+
#
|
456
|
+
# Returns a string for nice printing matrix
|
457
|
+
#
|
458
|
+
def to_s(mode = :pretty, len_col = 3)
|
459
|
+
return "Matrix[]" if empty?
|
460
|
+
if mode == :pretty
|
461
|
+
clen = cols_len
|
462
|
+
to_a.collect {|r|
|
463
|
+
i=0
|
464
|
+
r.map {|x|
|
465
|
+
l=clen[i]
|
466
|
+
i+=1
|
467
|
+
format("%#{l}s ", x.to_s)
|
468
|
+
} << "\n"
|
469
|
+
}.join("")
|
470
|
+
else
|
471
|
+
i = 0; s = ""; cs = column_size
|
472
|
+
each do |e|
|
473
|
+
i = (i + 1) % cs
|
474
|
+
s += format("%#{len_col}s ", e.to_s)
|
475
|
+
s += "\n" if i == 0
|
476
|
+
end
|
477
|
+
s
|
475
478
|
end
|
476
|
-
s
|
477
479
|
end
|
478
|
-
end
|
479
480
|
|
480
|
-
#
|
481
|
-
# Iterate the elements of a matrix
|
482
|
-
#
|
483
|
-
def each
|
484
|
-
|
485
|
-
|
486
|
-
end
|
487
|
-
|
488
|
-
#
|
489
|
-
# a hided module of Matrix
|
490
|
-
module MMatrix
|
491
|
-
def self.default_block(block)
|
492
|
-
block ? lambda { |i| block.call(i) } : lambda {|i| i }
|
481
|
+
#
|
482
|
+
# Iterate the elements of a matrix
|
483
|
+
#
|
484
|
+
def each
|
485
|
+
@rows.each {|x| x.each {|e| yield(e)}}
|
486
|
+
nil
|
493
487
|
end
|
494
|
-
|
495
488
|
#
|
496
|
-
#
|
497
|
-
#
|
498
|
-
|
499
|
-
#
|
489
|
+
# :section: SPSS like methods
|
490
|
+
#
|
491
|
+
|
492
|
+
# Element wise operation
|
493
|
+
def elementwise_operation(op,other)
|
494
|
+
Matrix.build(row_size,column_size) do |row, column|
|
495
|
+
self[row,column].send(op,other[row,column])
|
496
|
+
end
|
497
|
+
end
|
498
|
+
# Element wise multiplication
|
499
|
+
def e_mult(other)
|
500
|
+
elementwise_operation(:*,other)
|
501
|
+
end
|
502
|
+
# Element wise multiplication
|
503
|
+
def e_quo(other)
|
504
|
+
elementwise_operation(:quo,other)
|
505
|
+
end
|
506
|
+
# Matrix sum of squares
|
507
|
+
def mssq
|
508
|
+
@rows.inject(0){|ac,row| ac+(row.inject(0) {|acr,i| acr+(i**2)})}
|
509
|
+
end
|
510
|
+
def eigenpairs
|
511
|
+
eigval, eigvec= eigenvaluesJacobi, cJacobiV
|
512
|
+
eigenpairs=eigval.size.times.map {|i|
|
513
|
+
[eigval[i], eigvec.column(i)]
|
514
|
+
}
|
515
|
+
eigenpairs=eigenpairs.sort{|a,b| a[0]<=>b[0]}.reverse
|
516
|
+
end
|
517
|
+
# Returns eigenvalues and eigenvectors of a matrix on a Hash
|
518
|
+
# like CALL EIGEN on SPSS.
|
519
|
+
# * _:eigenvectors_: contains the eigenvectors as columns of a new Matrix, ordered in descendent order
|
520
|
+
# * _:eigenvalues_: contains an array with the eigenvalues, ordered in descendent order
|
521
|
+
def eigen
|
522
|
+
ep=eigenpairs
|
523
|
+
{ :eigenvalues=>ep.map {|a| a[0]} ,
|
524
|
+
:eigenvectors=>Matrix.columns(ep.map{|a| a[1]})
|
525
|
+
}
|
526
|
+
end
|
527
|
+
# Returns a new matrix with the natural logarithm of initial values
|
528
|
+
def ln
|
529
|
+
map {|v| Math::log(v)}
|
530
|
+
end
|
531
|
+
# Returns a new matrix, with the square root of initial values
|
532
|
+
def sqrt
|
533
|
+
map {|v| Math::sqrt(v)}
|
534
|
+
end
|
535
|
+
# Sum of squares and cross-products. Equal to m.t * m
|
536
|
+
def sscp
|
537
|
+
transpose*self
|
538
|
+
end
|
539
|
+
# Diagonal of matrix.
|
540
|
+
# Returns an array with as many elements as the minimum of the number of rows
|
541
|
+
# and the number of columns in the argument
|
542
|
+
def diagonal
|
543
|
+
min = (row_size<column_size) ? row_size : column_size
|
544
|
+
min.times.map {|i| self[i,i]}
|
545
|
+
end
|
500
546
|
#
|
501
|
-
|
502
|
-
|
503
|
-
|
547
|
+
# :section: Advanced methods
|
548
|
+
#
|
549
|
+
|
550
|
+
#
|
551
|
+
# a hided module of Matrix
|
552
|
+
module MMatrix
|
553
|
+
def self.default_block(block)
|
554
|
+
block ? lambda { |i| block.call(i) } : lambda {|i| i }
|
555
|
+
end
|
504
556
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
557
|
+
#
|
558
|
+
# Returns:
|
559
|
+
# 1) the index of row/column and
|
560
|
+
# 2) the values Vector for changing the row/column and
|
561
|
+
# 3) the range of changes
|
562
|
+
#
|
563
|
+
def self.id_vect_range(args, l)
|
564
|
+
i = args[0] # the column(/the row) to be change
|
565
|
+
vect = args[1] # the values vector
|
566
|
+
|
567
|
+
case args.size
|
568
|
+
when 3 then range = args[2] # the range of the elements to be change
|
569
|
+
when 4 then range = args[2]..args[3] #the range by borders
|
570
|
+
else range = 0...l
|
571
|
+
end
|
572
|
+
return i, vect, range
|
509
573
|
end
|
510
|
-
|
574
|
+
|
511
575
|
end
|
512
576
|
|
513
|
-
|
514
|
-
|
515
|
-
#
|
516
|
-
#
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
@rows[i].collect {|e| f.call(e)}
|
522
|
-
end
|
577
|
+
#
|
578
|
+
# Returns an array with the elements collected from the row "i".
|
579
|
+
# When a block is given, the elements of that vector are iterated.
|
580
|
+
#
|
581
|
+
def row_collect(i, &block)
|
582
|
+
f = MMatrix.default_block(block)
|
583
|
+
@rows[i].collect {|e| f.call(e)}
|
584
|
+
end
|
523
585
|
|
524
|
-
#
|
525
|
-
# Returns row vector number "i" like Matrix.row as a Vector.
|
526
|
-
# When the block is given, the elements of row "i" are modified
|
527
|
-
#
|
528
|
-
def row!(i)
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
586
|
+
#
|
587
|
+
# Returns row vector number "i" like Matrix.row as a Vector.
|
588
|
+
# When the block is given, the elements of row "i" are modified
|
589
|
+
#
|
590
|
+
def row!(i)
|
591
|
+
if block_given?
|
592
|
+
@rows[i].collect! {|e| yield e }
|
593
|
+
else
|
594
|
+
Vector.elements(@rows[i], false)
|
595
|
+
end
|
533
596
|
end
|
534
|
-
|
535
|
-
alias :row_collect! :row!
|
597
|
+
alias :row_collect! :row!
|
536
598
|
|
537
|
-
#
|
538
|
-
# Returns an array with the elements collected from the column "j".
|
539
|
-
# When a block is given, the elements of that vector are iterated.
|
540
|
-
#
|
541
|
-
def column_collect(j, &block)
|
542
|
-
|
543
|
-
|
544
|
-
end
|
599
|
+
#
|
600
|
+
# Returns an array with the elements collected from the column "j".
|
601
|
+
# When a block is given, the elements of that vector are iterated.
|
602
|
+
#
|
603
|
+
def column_collect(j, &block)
|
604
|
+
f = MMatrix.default_block(block)
|
605
|
+
(0...row_size).collect {|r| f.call(self[r, j])}
|
606
|
+
end
|
545
607
|
|
546
608
|
#
|
547
609
|
# Returns column vector number "j" as a Vector.
|
@@ -640,7 +702,7 @@ def row_sum
|
|
640
702
|
row(i).sum
|
641
703
|
}
|
642
704
|
end
|
643
|
-
# Returns the
|
705
|
+
# Returns the marginal of columns
|
644
706
|
def column_sum
|
645
707
|
(0...column_size).collect {|i|
|
646
708
|
column(i).sum
|
@@ -708,60 +770,59 @@ def L
|
|
708
770
|
LU.factorization(self)[0]
|
709
771
|
end
|
710
772
|
|
711
|
-
module Householder
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
773
|
+
module Householder
|
774
|
+
#
|
775
|
+
# a QR factorization that uses Householder transformation
|
776
|
+
# Q^T * A = R
|
777
|
+
# MC, Golub & van Loan, pg 224, 5.2.1 Householder QR
|
778
|
+
#
|
779
|
+
def self.QR(mat)
|
780
|
+
h = []
|
781
|
+
a = mat.clone
|
782
|
+
m = a.row_size
|
783
|
+
n = a.column_size
|
784
|
+
n.times do |j|
|
785
|
+
v, beta = a[j..m - 1, j].house
|
786
|
+
h[j] = Matrix.diag(Matrix.robust_I(j), Matrix.I(m-j)- beta * (v * v.t))
|
787
|
+
|
788
|
+
a[j..m-1, j..n-1] = (Matrix.I(m-j) - beta * (v * v.t)) * a[j..m-1, j..n-1]
|
789
|
+
a[(j+1)..m-1,j] = v[2..(m-j)] if j < m - 1
|
790
|
+
end
|
791
|
+
h
|
728
792
|
end
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
a[j, j+2..n-1] = v[1..n-j-2]
|
763
|
-
vb *= bidiagUV(a[j, j+2..n-1], n, beta) #Vb = V_1 * U_2 * ... * V_n-2
|
764
|
-
end }
|
793
|
+
#
|
794
|
+
# From the essential part of Householder vector
|
795
|
+
# it returns the coresponding upper(U_j)/lower(V_j) matrix
|
796
|
+
#
|
797
|
+
def self.bidiagUV(essential, dim, beta)
|
798
|
+
v = Vector.concat(Vector[1], essential)
|
799
|
+
dimv = v.size
|
800
|
+
Matrix.diag(Matrix.robust_I(dim - dimv), Matrix.I(dimv) - beta * (v * v.t) )
|
801
|
+
end
|
802
|
+
|
803
|
+
#
|
804
|
+
# Householder Bidiagonalization algorithm. MC, Golub, pg 252, Algorithm 5.4.2
|
805
|
+
# Returns the matrices U_B and V_B such that: U_B^T * A * V_B = B,
|
806
|
+
# where B is upper bidiagonal.
|
807
|
+
#
|
808
|
+
def self.bidiag(mat)
|
809
|
+
a = mat.clone
|
810
|
+
m = a.row_size
|
811
|
+
n = a.column_size
|
812
|
+
ub = Matrix.I(m)
|
813
|
+
vb = Matrix.I(n)
|
814
|
+
n.times do |j|
|
815
|
+
v, beta = a[j..m-1,j].house
|
816
|
+
a[j..m-1, j..n-1] = (Matrix.I(m-j) - beta * (v * v.t)) * a[j..m-1, j..n-1]
|
817
|
+
a[j+1..m-1, j] = v[1..(m-j-1)]
|
818
|
+
ub *= bidiagUV(a[j+1..m-1,j], m, beta) #Ub = U_1 * U_2 * ... * U_n
|
819
|
+
if j < n - 2
|
820
|
+
v, beta = (a[j, j+1..n-1]).house
|
821
|
+
a[j..m-1, j+1..n-1] = a[j..m-1, j+1..n-1] * (Matrix.I(n-j-1) - beta * (v * v.t))
|
822
|
+
a[j, j+2..n-1] = v[1..n-j-2]
|
823
|
+
vb *= bidiagUV(a[j, j+2..n-1], n, beta) #Vb = V_1 * U_2 * ... * V_n-2
|
824
|
+
end
|
825
|
+
end
|
765
826
|
return ub, vb
|
766
827
|
end
|
767
828
|
|
@@ -781,8 +842,6 @@ module Householder
|
|
781
842
|
end
|
782
843
|
return h, u0
|
783
844
|
end
|
784
|
-
|
785
|
-
|
786
845
|
end #end of Householder module
|
787
846
|
|
788
847
|
#
|
@@ -885,191 +944,194 @@ module Householder
|
|
885
944
|
c, s = givens(r[i - 1, j], r[i, j])
|
886
945
|
qt = Matrix.I(m); qt[i-1..i, i-1..i] = Matrix[[c, s],[-s, c]]
|
887
946
|
q *= qt
|
888
|
-
r[i-1..i, j..n-1] = Matrix[[c, -s],[s, c]] * r[i-1..i, j..n-1]
|
889
|
-
|
890
|
-
|
891
|
-
|
947
|
+
r[i-1..i, j..n-1] = Matrix[[c, -s],[s, c]] * r[i-1..i, j..n-1]
|
948
|
+
}
|
949
|
+
}
|
950
|
+
return r, q
|
892
951
|
end
|
952
|
+
end
|
893
953
|
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
954
|
+
#
|
955
|
+
# Returns the upper triunghiular matrix R of a Givens QR factorization
|
956
|
+
#
|
957
|
+
def givensR
|
958
|
+
Givens.QR(self)[0]
|
959
|
+
end
|
960
|
+
|
961
|
+
#
|
962
|
+
# Returns the orthogonal matrix Q of Givens QR factorization.
|
963
|
+
# Q = G_1 * ... * G_t where G_j is the j'th Givens rotation
|
964
|
+
# and 't' is the total number of rotations
|
965
|
+
#
|
966
|
+
def givensQ
|
967
|
+
Givens.QR(self)[1]
|
968
|
+
end
|
900
969
|
|
970
|
+
module Hessenberg
|
901
971
|
#
|
902
|
-
#
|
903
|
-
# Q = G_1 * ... * G_t where G_j is the j'th Givens rotation
|
904
|
-
# and 't' is the total number of rotations
|
972
|
+
# the matrix must be an upper R^(n x n) Hessenberg matrix
|
905
973
|
#
|
906
|
-
def
|
907
|
-
|
974
|
+
def self.QR(mat)
|
975
|
+
r = mat.clone
|
976
|
+
n = r.row_size
|
977
|
+
q = Matrix.I(n)
|
978
|
+
for j in (0...n-1)
|
979
|
+
c, s = Givens.givens(r[j,j], r[j+1, j])
|
980
|
+
cs = Matrix[[c, s], [-s, c]]
|
981
|
+
q *= Matrix.diag(Matrix.robust_I(j), cs, Matrix.robust_I(n - j - 2))
|
982
|
+
r[j..j+1, j..n-1] = cs.t * r[j..j+1, j..n-1]
|
983
|
+
end
|
984
|
+
return q, r
|
908
985
|
end
|
986
|
+
end
|
909
987
|
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
988
|
+
#
|
989
|
+
# Returns the orthogonal matrix Q of Hessenberg QR factorization
|
990
|
+
# Q = G_1 *...* G_(n-1) where G_j is the Givens rotation G_j = G(j, j+1, omega_j)
|
991
|
+
#
|
992
|
+
def hessenbergQ
|
993
|
+
Hessenberg.QR(self)[0]
|
994
|
+
end
|
995
|
+
|
996
|
+
#
|
997
|
+
# Returns the upper triunghiular matrix R of a Hessenberg QR factorization
|
998
|
+
#
|
999
|
+
def hessenbergR
|
1000
|
+
Hessenberg.QR(self)[1]
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
#
|
1004
|
+
# Return an upper Hessenberg matrix obtained with Householder reduction to Hessenberg Form algorithm
|
1005
|
+
#
|
1006
|
+
def hessenberg_form_H
|
1007
|
+
Householder.toHessenberg(self)[0]
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
#
|
1011
|
+
# The real Schur decomposition.
|
1012
|
+
# The eigenvalues are aproximated in diagonal elements of the real Schur decomposition matrix
|
1013
|
+
#
|
1014
|
+
def realSchur(eps = 1.0e-10, steps = 100)
|
1015
|
+
h = self.hessenberg_form_H
|
1016
|
+
h1 = Matrix[]
|
1017
|
+
i = 0
|
1018
|
+
loop do
|
1019
|
+
h1 = h.hessenbergR * h.hessenbergQ
|
1020
|
+
break if Matrix.diag_in_delta?(h1, h, eps) or steps <= 0
|
1021
|
+
h = h1.clone
|
1022
|
+
steps -= 1
|
1023
|
+
i += 1
|
926
1024
|
end
|
1025
|
+
h1
|
1026
|
+
end
|
1027
|
+
|
927
1028
|
|
1029
|
+
module Jacobi
|
928
1030
|
#
|
929
|
-
# Returns the
|
930
|
-
# Q = G_1 *...* G_(n-1) where G_j is the Givens rotation G_j = G(j, j+1, omega_j)
|
1031
|
+
# Returns the nurm of the off-diagonal element
|
931
1032
|
#
|
932
|
-
def
|
933
|
-
|
1033
|
+
def self.off(a)
|
1034
|
+
n = a.row_size
|
1035
|
+
sum = 0
|
1036
|
+
n.times{|i| n.times{|j| sum += a[i, j]**2 if j != i}}
|
1037
|
+
Math.sqrt(sum)
|
934
1038
|
end
|
935
1039
|
|
936
1040
|
#
|
937
|
-
# Returns the
|
1041
|
+
# Returns the index pair (p, q) with 1<= p < q <= n and A[p, q] is the maximum in absolute value
|
938
1042
|
#
|
939
|
-
def
|
940
|
-
|
1043
|
+
def self.max(a)
|
1044
|
+
n = a.row_size
|
1045
|
+
max = 0
|
1046
|
+
p = 0
|
1047
|
+
q = 0
|
1048
|
+
n.times{|i|
|
1049
|
+
((i+1)...n).each{|j|
|
1050
|
+
val = a[i, j].abs
|
1051
|
+
if val > max
|
1052
|
+
max = val
|
1053
|
+
p = i
|
1054
|
+
q = j
|
1055
|
+
end
|
1056
|
+
}
|
1057
|
+
}
|
1058
|
+
return p, q
|
941
1059
|
end
|
942
1060
|
|
943
1061
|
#
|
944
|
-
#
|
1062
|
+
# Compute the cosine-sine pair (c, s) for the element A[p, q]
|
945
1063
|
#
|
946
|
-
def
|
947
|
-
|
1064
|
+
def self.sym_schur2(a, p, q)
|
1065
|
+
if a[p, q] != 0
|
1066
|
+
tau = Float(a[q, q] - a[p, p])/(2 * a[p, q])
|
1067
|
+
if tau >= 0
|
1068
|
+
t = 1./(tau + Math.sqrt(1 + tau ** 2))
|
1069
|
+
else
|
1070
|
+
t = -1./(-tau + Math.sqrt(1 + tau ** 2))
|
1071
|
+
end
|
1072
|
+
c = 1./Math.sqrt(1 + t ** 2)
|
1073
|
+
s = t * c
|
1074
|
+
else
|
1075
|
+
c = 1
|
1076
|
+
s = 0
|
1077
|
+
end
|
1078
|
+
return c, s
|
948
1079
|
end
|
949
1080
|
|
950
1081
|
#
|
951
|
-
#
|
952
|
-
# The eigenvalues are aproximated in diagonal elements of the real Schur decomposition matrix
|
1082
|
+
# Returns the Jacobi rotation matrix
|
953
1083
|
#
|
954
|
-
def
|
955
|
-
|
956
|
-
|
957
|
-
|
958
|
-
|
959
|
-
h1 = h.hessenbergR * h.hessenbergQ
|
960
|
-
break if Matrix.diag_in_delta?(h1, h, eps) or steps <= 0
|
961
|
-
h = h1.clone
|
962
|
-
steps -= 1
|
963
|
-
i += 1
|
964
|
-
end
|
965
|
-
h1
|
1084
|
+
def self.J(p, q, c, s, n)
|
1085
|
+
j = Matrix.I(n)
|
1086
|
+
j[p,p] = c; j[p, q] = s
|
1087
|
+
j[q,p] = -s; j[q, q] = c
|
1088
|
+
j
|
966
1089
|
end
|
1090
|
+
end
|
967
1091
|
|
1092
|
+
#
|
1093
|
+
# Classical Jacobi 8.4.3 Golub & van Loan
|
1094
|
+
#
|
1095
|
+
def cJacobi(tol = 1.0e-10)
|
1096
|
+
a = self.clone
|
1097
|
+
n = row_size
|
1098
|
+
v = Matrix.I(n)
|
1099
|
+
eps = tol * a.normF
|
1100
|
+
while Jacobi.off(a) > eps
|
1101
|
+
p, q = Jacobi.max(a)
|
1102
|
+
c, s = Jacobi.sym_schur2(a, p, q)
|
1103
|
+
#print "\np:#{p} q:#{q} c:#{c} s:#{s}\n"
|
1104
|
+
j = Jacobi.J(p, q, c, s, n)
|
1105
|
+
a = j.t * a * j
|
1106
|
+
v = v * j
|
1107
|
+
end
|
1108
|
+
return a, v
|
1109
|
+
end
|
968
1110
|
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
n.times{|i| n.times{|j| sum += a[i, j]**2 if j != i}}
|
977
|
-
Math.sqrt(sum)
|
978
|
-
end
|
979
|
-
|
980
|
-
#
|
981
|
-
# Returns the index pair (p, q) with 1<= p < q <= n and A[p, q] is the maximum in absolute value
|
982
|
-
#
|
983
|
-
def self.max(a)
|
984
|
-
n = a.row_size
|
985
|
-
max = 0
|
986
|
-
p = 0
|
987
|
-
q = 0
|
988
|
-
n.times{|i|
|
989
|
-
((i+1)...n).each{|j|
|
990
|
-
val = a[i, j].abs
|
991
|
-
if val > max
|
992
|
-
max = val
|
993
|
-
p = i
|
994
|
-
q = j
|
995
|
-
end }}
|
996
|
-
return p, q
|
997
|
-
end
|
998
|
-
|
999
|
-
#
|
1000
|
-
# Compute the cosine-sine pair (c, s) for the element A[p, q]
|
1001
|
-
#
|
1002
|
-
def self.sym_schur2(a, p, q)
|
1003
|
-
if a[p, q] != 0
|
1004
|
-
tau = Float(a[q, q] - a[p, p])/(2 * a[p, q])
|
1005
|
-
if tau >= 0
|
1006
|
-
t = 1./(tau + Math.sqrt(1 + tau ** 2))
|
1007
|
-
else
|
1008
|
-
t = -1./(-tau + Math.sqrt(1 + tau ** 2))
|
1009
|
-
end
|
1010
|
-
c = 1./Math.sqrt(1 + t ** 2)
|
1011
|
-
s = t * c
|
1012
|
-
else
|
1013
|
-
c = 1
|
1014
|
-
s = 0
|
1015
|
-
end
|
1016
|
-
return c, s
|
1017
|
-
end
|
1018
|
-
|
1019
|
-
#
|
1020
|
-
# Returns the Jacobi rotation matrix
|
1021
|
-
#
|
1022
|
-
def self.J(p, q, c, s, n)
|
1023
|
-
j = Matrix.I(n)
|
1024
|
-
j[p,p] = c; j[p, q] = s
|
1025
|
-
j[q,p] = -s; j[q, q] = c
|
1026
|
-
j
|
1027
|
-
end
|
1028
|
-
end
|
1029
|
-
|
1030
|
-
#
|
1031
|
-
# Classical Jacobi 8.4.3 Golub & van Loan
|
1032
|
-
#
|
1033
|
-
def cJacobi(tol = 1.0e-10)
|
1034
|
-
a = self.clone
|
1035
|
-
n = row_size
|
1036
|
-
v = Matrix.I(n)
|
1037
|
-
eps = tol * a.normF
|
1038
|
-
while Jacobi.off(a) > eps
|
1039
|
-
p, q = Jacobi.max(a)
|
1040
|
-
c, s = Jacobi.sym_schur2(a, p, q)
|
1041
|
-
#print "\np:#{p} q:#{q} c:#{c} s:#{s}\n"
|
1042
|
-
j = Jacobi.J(p, q, c, s, n)
|
1043
|
-
a = j.t * a * j
|
1044
|
-
v = v * j
|
1045
|
-
end
|
1046
|
-
return a, v
|
1047
|
-
end
|
1048
|
-
|
1049
|
-
#
|
1050
|
-
# Returns the aproximation matrix computed with Classical Jacobi algorithm.
|
1051
|
-
# The aproximate eigenvalues values are in the diagonal of the matrix A.
|
1052
|
-
#
|
1053
|
-
def cJacobiA(tol = 1.0e-10)
|
1054
|
-
cJacobi(tol)[0]
|
1055
|
-
end
|
1111
|
+
#
|
1112
|
+
# Returns the aproximation matrix computed with Classical Jacobi algorithm.
|
1113
|
+
# The aproximate eigenvalues values are in the diagonal of the matrix A.
|
1114
|
+
#
|
1115
|
+
def cJacobiA(tol = 1.0e-10)
|
1116
|
+
cJacobi(tol)[0]
|
1117
|
+
end
|
1056
1118
|
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1119
|
+
#
|
1120
|
+
# Returns a Vector with the eigenvalues aproximated values.
|
1121
|
+
# The eigenvalues are computed with the Classic Jacobi Algorithm.
|
1122
|
+
#
|
1123
|
+
def eigenvaluesJacobi
|
1124
|
+
a = cJacobiA
|
1125
|
+
Vector[*(0...row_size).collect{|i| a[i, i]}]
|
1126
|
+
end
|
1065
1127
|
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
|
1072
|
-
|
1073
|
-
|
1128
|
+
#
|
1129
|
+
# Returns the orthogonal matrix obtained with the Jacobi eigenvalue algorithm.
|
1130
|
+
# The columns of V are the eigenvector.
|
1131
|
+
#
|
1132
|
+
def cJacobiV(tol = 1.0e-10)
|
1133
|
+
cJacobi(tol)[1]
|
1134
|
+
end
|
1135
|
+
end
|
1074
1136
|
|
1075
1137
|
|
data/spec/extendmatrix_spec.rb
CHANGED
@@ -266,7 +266,46 @@ describe "Matrix class extension:" do
|
|
266
266
|
m = Matrix.build(5, 5){|i, j| i == j ? 1 + 0.001 * (i+1) : i + j}
|
267
267
|
Matrix.diag_in_delta?(Matrix.I(5), m, 0.01).should == true
|
268
268
|
end
|
269
|
-
|
269
|
+
|
270
|
+
|
271
|
+
it "e_mult method" do
|
272
|
+
m = Matrix.build(4, 3){|i, j| i * 3 + j +1}
|
273
|
+
n = Matrix.build(4, 3){|i, j| i * 2 + j +2}
|
274
|
+
m.e_mult(n).should==Matrix.build(4,3){|i,j| (i*3+j+1)*(i*2+j+2)}
|
275
|
+
end
|
276
|
+
it "e_mult method" do
|
277
|
+
m = Matrix.build(4, 3){|i, j| i * 3 + j +1}
|
278
|
+
n = Matrix.build(4, 3){|i, j| i * 2 + j +2}
|
279
|
+
m.e_quo(n).should==Matrix.build(4,3){|i,j| (i*3+j+1).quo(i*2+j+2)}
|
280
|
+
end
|
281
|
+
it "mssq method" do
|
282
|
+
m = Matrix[[1,2,3],[4,5,6],[7,8,9]]
|
283
|
+
m.mssq.should==(1..9).each.inject(0) {|ac,v| ac+v**2}
|
284
|
+
end
|
285
|
+
it "eigen" do
|
286
|
+
m=Matrix[[0.95,0.95,0.01,0.01],[0.95,0.95,0.01,0.01],[0.01, 0.01,0.95,0.95], [0.01, 0.01, 0.95, 0.95]]
|
287
|
+
eigenvalues=[1.92,1.88,0.0,0.0]
|
288
|
+
eigen=m.eigen
|
289
|
+
eigen[:eigenvalues].each_with_index do |v,i|
|
290
|
+
v.should be_close(eigenvalues[i],0.01)
|
291
|
+
end
|
292
|
+
eigenvectors=Matrix[[0.5, 0.5, 0.0, 0.707106781186547], [0.5, 0.5, 0.0, -0.707106781186547], [0.5, -0.5, 0.707106781186547, 0.0], [0.5, -0.5, -0.707106781186547, 0.0]]
|
293
|
+
Matrix.equal_in_delta?(eigen[:eigenvectors], eigenvectors).should be_true
|
294
|
+
end
|
295
|
+
it "sqrt" do
|
296
|
+
m=Matrix[[1,4,9],[16,25,36]]
|
297
|
+
m.sqrt.should==Matrix[[1,2,3],[4,5,6]]
|
298
|
+
end
|
299
|
+
it "sscp" do
|
300
|
+
m=Matrix[[1,4,9],[16,25,36]]
|
301
|
+
m.sscp.should== m.t*m
|
302
|
+
end
|
303
|
+
it "diagonal" do
|
304
|
+
m=Matrix.diag(1,4,5,6,7)
|
305
|
+
m.diagonal.should==[1,4,5,6,7]
|
306
|
+
m=Matrix[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]
|
307
|
+
m.diagonal.should==[1,5,9]
|
308
|
+
end
|
270
309
|
it "LU " do
|
271
310
|
m = Matrix[[1, 4, 7],
|
272
311
|
[2, 5, 8],
|
@@ -379,6 +418,6 @@ describe "Matrix class extension:" do
|
|
379
418
|
[4, 5, 4, 1]]
|
380
419
|
e = Matrix[[-0.26828, 0, 0, 0], [0, -5.97550, 0, 0], [0, 0, 1.01373, 0], [0, 0, 0, 9.23004]]
|
381
420
|
Matrix.diag_in_delta?(e, a.cJacobiA, 1.0e-5).should == true
|
382
|
-
end
|
421
|
+
end
|
383
422
|
end
|
384
423
|
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 2
|
8
7
|
- 3
|
9
|
-
|
8
|
+
- 1
|
9
|
+
version: 0.3.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Cosmin Bonchis
|
@@ -35,7 +35,7 @@ cert_chain:
|
|
35
35
|
rpP0jjs0
|
36
36
|
-----END CERTIFICATE-----
|
37
37
|
|
38
|
-
date: 2010-
|
38
|
+
date: 2010-08-16 00:00:00 -04:00
|
39
39
|
default_executable:
|
40
40
|
dependencies:
|
41
41
|
- !ruby/object:Gem::Dependency
|
@@ -62,8 +62,8 @@ dependencies:
|
|
62
62
|
segments:
|
63
63
|
- 2
|
64
64
|
- 6
|
65
|
-
-
|
66
|
-
version: 2.6.
|
65
|
+
- 1
|
66
|
+
version: 2.6.1
|
67
67
|
type: :development
|
68
68
|
version_requirements: *id002
|
69
69
|
description: |-
|
metadata.gz.sig
CHANGED
Binary file
|