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 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