extendmatrix 0.2.3 → 0.3.1
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.
- 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
|