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 CHANGED
Binary file
@@ -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
@@ -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.2.3"
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
- 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
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
- alias :old_divition :/
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
- def [](i, j)
416
+ def [](i, j)
416
417
  #{ijwrap}; @rows[i][j]
417
- end
418
+ end
418
419
 
419
- def []=(i, j, v)
420
+ def []=(i, j, v)
420
421
  #{ijwrap}; @rows[i][j] = v
421
- end
422
- end"
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
- case mode
430
- when :torus then eval(wraplate("i %= row_size; j %= column_size"))
431
- when :h_cylinder then eval(wraplate("i %= row_size"))
432
- when :v_cylinder then eval(wraplate("j %= column_size"))
433
- when :nil then eval(wraplate)
434
- end
435
- @wrap = mode
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
- column_collect(j) {|x| x.to_s.length}.max
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
- (0...column_size).collect {|j| max_len_column(j)}
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
- return super if empty?
459
- if mode == :pretty
460
- clen = cols_len
461
- to_a.collect {|r|
462
- i=0
463
- r.map {|x|
464
- l=clen[i]
465
- i+=1
466
- format("%#{l}s ", x.to_s)
467
- } << "\n"
468
- }.join("")
469
- else
470
- i = 0; s = ""; cs = column_size
471
- each do |e|
472
- i = (i + 1) % cs
473
- s += format("%#{len_col}s ", e.to_s)
474
- s += "\n" if i == 0
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
- @rows.each {|x| x.each {|e| yield(e)}}
485
- nil
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
- # Returns:
497
- # 1) the index of row/column and
498
- # 2) the values Vector for changing the row/column and
499
- # 3) the range of changes
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
- def self.id_vect_range(args, l)
502
- i = args[0] # the column(/the row) to be change
503
- vect = args[1] # the values vector
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
- case args.size
506
- when 3 then range = args[2] # the range of the elements to be change
507
- when 4 then range = args[2]..args[3] #the range by borders
508
- else range = 0...l
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
- return i, vect, range
574
+
511
575
  end
512
576
 
513
- end
514
-
515
- #
516
- # Returns an array with the elements collected from the row "i".
517
- # When a block is given, the elements of that vector are iterated.
518
- #
519
- def row_collect(i, &block)
520
- f = MMatrix.default_block(block)
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
- if block_given?
530
- @rows[i].collect! {|e| yield e }
531
- else
532
- Vector.elements(@rows[i], false)
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
- end
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
- f = MMatrix.default_block(block)
543
- (0...row_size).collect {|r| f.call(self[r, j])}
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 of columns
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
- # a QR factorization that uses Householder transformation
714
- # Q^T * A = R
715
- # MC, Golub & van Loan, pg 224, 5.2.1 Householder QR
716
- #
717
- def self.QR(mat)
718
- h = []
719
- a = mat.clone
720
- m = a.row_size
721
- n = a.column_size
722
- n.times do |j|
723
- v, beta = a[j..m - 1, j].house
724
- h[j] = Matrix.diag(Matrix.robust_I(j), Matrix.I(m-j)- beta * (v * v.t))
725
-
726
- a[j..m-1, j..n-1] = (Matrix.I(m-j) - beta * (v * v.t)) * a[j..m-1, j..n-1]
727
- a[(j+1)..m-1,j] = v[2..(m-j)] if j < m - 1
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
- h
730
- end
731
- #
732
- # From the essential part of Householder vector
733
- # it returns the coresponding upper(U_j)/lower(V_j) matrix
734
- #
735
- def self.bidiagUV(essential, dim, beta)
736
- v = Vector.concat(Vector[1], essential)
737
- dimv = v.size
738
-
739
-
740
- Matrix.diag(Matrix.robust_I(dim - dimv), Matrix.I(dimv) - beta * (v * v.t) )
741
- end
742
-
743
- #
744
- # Householder Bidiagonalization algorithm. MC, Golub, pg 252, Algorithm 5.4.2
745
- # Returns the matrices U_B and V_B such that: U_B^T * A * V_B = B,
746
- # where B is upper bidiagonal.
747
- #
748
- def self.bidiag(mat)
749
- a = mat.clone
750
- m = a.row_size
751
- n = a.column_size
752
- ub = Matrix.I(m)
753
- vb = Matrix.I(n)
754
- n.times{|j|
755
- v, beta = a[j..m-1,j].house
756
- a[j..m-1, j..n-1] = (Matrix.I(m-j) - beta * (v * v.t)) * a[j..m-1, j..n-1]
757
- a[j+1..m-1, j] = v[1..(m-j-1)]
758
- ub *= bidiagUV(a[j+1..m-1,j], m, beta) #Ub = U_1 * U_2 * ... * U_n
759
- if j < n - 2
760
- v, beta = (a[j, j+1..n-1]).house
761
- 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))
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
- return r, q
890
- end
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
- # Returns the upper triunghiular matrix R of a Givens QR factorization
896
- #
897
- def givensR
898
- Givens.QR(self)[0]
899
- end
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
- # Returns the orthogonal matrix Q of Givens QR factorization.
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 givensQ
907
- Givens.QR(self)[1]
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
- module Hessenberg
911
- #
912
- # the matrix must be an upper R^(n x n) Hessenberg matrix
913
- #
914
- def self.QR(mat)
915
- r = mat.clone
916
- n = r.row_size
917
- q = Matrix.I(n)
918
- for j in (0...n-1)
919
- c, s = Givens.givens(r[j,j], r[j+1, j])
920
- cs = Matrix[[c, s], [-s, c]]
921
- q *= Matrix.diag(Matrix.robust_I(j), cs, Matrix.robust_I(n - j - 2))
922
- r[j..j+1, j..n-1] = cs.t * r[j..j+1, j..n-1]
923
- end
924
- return q, r
925
- end
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 orthogonal matrix Q of Hessenberg QR factorization
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 hessenbergQ
933
- Hessenberg.QR(self)[0]
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 upper triunghiular matrix R of a Hessenberg QR factorization
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 hessenbergR
940
- Hessenberg.QR(self)[1]
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
- # Return an upper Hessenberg matrix obtained with Householder reduction to Hessenberg Form algorithm
1062
+ # Compute the cosine-sine pair (c, s) for the element A[p, q]
945
1063
  #
946
- def hessenberg_form_H
947
- Householder.toHessenberg(self)[0]
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
- # The real Schur decomposition.
952
- # The eigenvalues are aproximated in diagonal elements of the real Schur decomposition matrix
1082
+ # Returns the Jacobi rotation matrix
953
1083
  #
954
- def realSchur(eps = 1.0e-10, steps = 100)
955
- h = self.hessenberg_form_H
956
- h1 = Matrix[]
957
- i = 0
958
- loop do
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
- module Jacobi
970
- #
971
- # Returns the nurm of the off-diagonal element
972
- #
973
- def self.off(a)
974
- n = a.row_size
975
- sum = 0
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
- # Returns a Vector with the eigenvalues aproximated values.
1059
- # The eigenvalues are computed with the Classic Jacobi Algorithm.
1060
- #
1061
- def eigenvaluesJacobi
1062
- a = cJacobiA
1063
- Vector[*(0...row_size).collect{|i| a[i, i]}]
1064
- end
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
- # Returns the orthogonal matrix obtained with the Jacobi eigenvalue algorithm.
1068
- # The columns of V are the eigenvector.
1069
- #
1070
- def cJacobiV(tol = 1.0e-10)
1071
- cJacobi(tol)[1]
1072
- end
1073
- end
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
 
@@ -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
- version: 0.2.3
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-05-07 00:00:00 -04:00
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
- - 0
66
- version: 2.6.0
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