matrix 0.1.0 → 0.4.3
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.
- checksums.yaml +4 -4
- data/{LICENSE.txt → BSDL} +3 -3
- data/COPYING +56 -0
- data/lib/matrix/eigenvalue_decomposition.rb +883 -0
- data/lib/matrix/lup_decomposition.rb +219 -0
- data/lib/matrix/version.rb +5 -0
- data/lib/matrix.rb +302 -181
- data/matrix.gemspec +12 -7
- metadata +14 -45
- data/.gitignore +0 -9
- data/.travis.yml +0 -4
- data/Gemfile +0 -6
- data/README.md +0 -41
- data/Rakefile +0 -10
- data/bin/console +0 -14
- data/bin/setup +0 -8
data/lib/matrix.rb
CHANGED
|
@@ -12,17 +12,44 @@
|
|
|
12
12
|
# Original Documentation:: Gavin Sinclair (sourced from <i>Ruby in a Nutshell</i> (Matsumoto, O'Reilly))
|
|
13
13
|
##
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
require_relative "matrix/version"
|
|
16
16
|
|
|
17
17
|
module ExceptionForMatrix # :nodoc:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
class ErrDimensionMismatch < StandardError
|
|
19
|
+
def initialize(val = nil)
|
|
20
|
+
if val
|
|
21
|
+
super(val)
|
|
22
|
+
else
|
|
23
|
+
super("Dimension mismatch")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class ErrNotRegular < StandardError
|
|
29
|
+
def initialize(val = nil)
|
|
30
|
+
if val
|
|
31
|
+
super(val)
|
|
32
|
+
else
|
|
33
|
+
super("Not Regular Matrix")
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class ErrOperationNotDefined < StandardError
|
|
39
|
+
def initialize(vals)
|
|
40
|
+
if vals.is_a?(Array)
|
|
41
|
+
super("Operation(#{vals[0]}) can't be defined: #{vals[1]} op #{vals[2]}")
|
|
42
|
+
else
|
|
43
|
+
super(vals)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
class ErrOperationNotImplemented < StandardError
|
|
49
|
+
def initialize(vals)
|
|
50
|
+
super("Sorry, Operation(#{vals[0]}) not implemented: #{vals[1]} op #{vals[2]}")
|
|
51
|
+
end
|
|
52
|
+
end
|
|
26
53
|
end
|
|
27
54
|
|
|
28
55
|
#
|
|
@@ -45,8 +72,8 @@ class Matrix
|
|
|
45
72
|
#
|
|
46
73
|
# Creates a matrix where each argument is a row.
|
|
47
74
|
# Matrix[ [25, 93], [-1, 66] ]
|
|
48
|
-
#
|
|
49
|
-
#
|
|
75
|
+
# # => 25 93
|
|
76
|
+
# # -1 66
|
|
50
77
|
#
|
|
51
78
|
def Matrix.[](*rows)
|
|
52
79
|
rows(rows, false)
|
|
@@ -57,8 +84,8 @@ class Matrix
|
|
|
57
84
|
# of the matrix. If the optional argument +copy+ is false, use the given
|
|
58
85
|
# arrays as the internal structure of the matrix without copying.
|
|
59
86
|
# Matrix.rows([[25, 93], [-1, 66]])
|
|
60
|
-
#
|
|
61
|
-
#
|
|
87
|
+
# # => 25 93
|
|
88
|
+
# # -1 66
|
|
62
89
|
#
|
|
63
90
|
def Matrix.rows(rows, copy = true)
|
|
64
91
|
rows = convert_to_array(rows, copy)
|
|
@@ -75,8 +102,8 @@ class Matrix
|
|
|
75
102
|
#
|
|
76
103
|
# Creates a matrix using +columns+ as an array of column vectors.
|
|
77
104
|
# Matrix.columns([[25, 93], [-1, 66]])
|
|
78
|
-
#
|
|
79
|
-
#
|
|
105
|
+
# # => 25 -1
|
|
106
|
+
# # 93 66
|
|
80
107
|
#
|
|
81
108
|
def Matrix.columns(columns)
|
|
82
109
|
rows(columns, false).transpose
|
|
@@ -89,9 +116,9 @@ class Matrix
|
|
|
89
116
|
# Returns an enumerator if no block is given.
|
|
90
117
|
#
|
|
91
118
|
# m = Matrix.build(2, 4) {|row, col| col - row }
|
|
92
|
-
#
|
|
119
|
+
# # => Matrix[[0, 1, 2, 3], [-1, 0, 1, 2]]
|
|
93
120
|
# m = Matrix.build(3) { rand }
|
|
94
|
-
#
|
|
121
|
+
# # => a 3x3 matrix with random elements
|
|
95
122
|
#
|
|
96
123
|
def Matrix.build(row_count, column_count = row_count)
|
|
97
124
|
row_count = CoercionHelper.coerce_to_int(row_count)
|
|
@@ -109,9 +136,9 @@ class Matrix
|
|
|
109
136
|
#
|
|
110
137
|
# Creates a matrix where the diagonal elements are composed of +values+.
|
|
111
138
|
# Matrix.diagonal(9, 5, -3)
|
|
112
|
-
#
|
|
113
|
-
#
|
|
114
|
-
#
|
|
139
|
+
# # => 9 0 0
|
|
140
|
+
# # 0 5 0
|
|
141
|
+
# # 0 0 -3
|
|
115
142
|
#
|
|
116
143
|
def Matrix.diagonal(*values)
|
|
117
144
|
size = values.size
|
|
@@ -128,8 +155,8 @@ class Matrix
|
|
|
128
155
|
# Creates an +n+ by +n+ diagonal matrix where each diagonal element is
|
|
129
156
|
# +value+.
|
|
130
157
|
# Matrix.scalar(2, 5)
|
|
131
|
-
#
|
|
132
|
-
#
|
|
158
|
+
# # => 5 0
|
|
159
|
+
# # 0 5
|
|
133
160
|
#
|
|
134
161
|
def Matrix.scalar(n, value)
|
|
135
162
|
diagonal(*Array.new(n, value))
|
|
@@ -138,8 +165,8 @@ class Matrix
|
|
|
138
165
|
#
|
|
139
166
|
# Creates an +n+ by +n+ identity matrix.
|
|
140
167
|
# Matrix.identity(2)
|
|
141
|
-
#
|
|
142
|
-
#
|
|
168
|
+
# # => 1 0
|
|
169
|
+
# # 0 1
|
|
143
170
|
#
|
|
144
171
|
def Matrix.identity(n)
|
|
145
172
|
scalar(n, 1)
|
|
@@ -152,8 +179,8 @@ class Matrix
|
|
|
152
179
|
#
|
|
153
180
|
# Creates a zero matrix.
|
|
154
181
|
# Matrix.zero(2)
|
|
155
|
-
#
|
|
156
|
-
#
|
|
182
|
+
# # => 0 0
|
|
183
|
+
# # 0 0
|
|
157
184
|
#
|
|
158
185
|
def Matrix.zero(row_count, column_count = row_count)
|
|
159
186
|
rows = Array.new(row_count){Array.new(column_count, 0)}
|
|
@@ -164,7 +191,7 @@ class Matrix
|
|
|
164
191
|
# Creates a single-row matrix where the values of that row are as given in
|
|
165
192
|
# +row+.
|
|
166
193
|
# Matrix.row_vector([4,5,6])
|
|
167
|
-
#
|
|
194
|
+
# # => 4 5 6
|
|
168
195
|
#
|
|
169
196
|
def Matrix.row_vector(row)
|
|
170
197
|
row = convert_to_array(row)
|
|
@@ -175,9 +202,9 @@ class Matrix
|
|
|
175
202
|
# Creates a single-column matrix where the values of that column are as given
|
|
176
203
|
# in +column+.
|
|
177
204
|
# Matrix.column_vector([4,5,6])
|
|
178
|
-
#
|
|
179
|
-
#
|
|
180
|
-
#
|
|
205
|
+
# # => 4
|
|
206
|
+
# # 5
|
|
207
|
+
# # 6
|
|
181
208
|
#
|
|
182
209
|
def Matrix.column_vector(column)
|
|
183
210
|
column = convert_to_array(column)
|
|
@@ -190,12 +217,12 @@ class Matrix
|
|
|
190
217
|
#
|
|
191
218
|
# m = Matrix.empty(2, 0)
|
|
192
219
|
# m == Matrix[ [], [] ]
|
|
193
|
-
#
|
|
220
|
+
# # => true
|
|
194
221
|
# n = Matrix.empty(0, 3)
|
|
195
222
|
# n == Matrix.columns([ [], [], [] ])
|
|
196
|
-
#
|
|
223
|
+
# # => true
|
|
197
224
|
# m * n
|
|
198
|
-
#
|
|
225
|
+
# # => Matrix[[0, 0, 0], [0, 0, 0]]
|
|
199
226
|
#
|
|
200
227
|
def Matrix.empty(row_count = 0, column_count = 0)
|
|
201
228
|
raise ArgumentError, "One size must be 0" if column_count != 0 && row_count != 0
|
|
@@ -249,6 +276,8 @@ class Matrix
|
|
|
249
276
|
new result, total_column_count
|
|
250
277
|
end
|
|
251
278
|
|
|
279
|
+
# :call-seq:
|
|
280
|
+
# Matrix.combine(*matrices) { |*elements| ... }
|
|
252
281
|
#
|
|
253
282
|
# Create a matrix by combining matrices entrywise, using the given block
|
|
254
283
|
#
|
|
@@ -263,7 +292,7 @@ class Matrix
|
|
|
263
292
|
matrices.map!(&CoercionHelper.method(:coerce_to_matrix))
|
|
264
293
|
x = matrices.first
|
|
265
294
|
matrices.each do |m|
|
|
266
|
-
|
|
295
|
+
raise ErrDimensionMismatch unless x.row_count == m.row_count && x.column_count == m.column_count
|
|
267
296
|
end
|
|
268
297
|
|
|
269
298
|
rows = Array.new(x.row_count) do |i|
|
|
@@ -274,12 +303,21 @@ class Matrix
|
|
|
274
303
|
new rows, x.column_count
|
|
275
304
|
end
|
|
276
305
|
|
|
306
|
+
# :call-seq:
|
|
307
|
+
# combine(*other_matrices) { |*elements| ... }
|
|
308
|
+
#
|
|
309
|
+
# Creates new matrix by combining with <i>other_matrices</i> entrywise,
|
|
310
|
+
# using the given block.
|
|
311
|
+
#
|
|
312
|
+
# x = Matrix[[6, 6], [4, 4]]
|
|
313
|
+
# y = Matrix[[1, 2], [3, 4]]
|
|
314
|
+
# x.combine(y) {|a, b| a - b} # => Matrix[[5, 4], [1, 0]]
|
|
277
315
|
def combine(*matrices, &block)
|
|
278
316
|
Matrix.combine(self, *matrices, &block)
|
|
279
317
|
end
|
|
280
318
|
|
|
281
319
|
#
|
|
282
|
-
# Matrix.new is private; use
|
|
320
|
+
# Matrix.new is private; use ::rows, ::columns, ::[], etc... to create.
|
|
283
321
|
#
|
|
284
322
|
def initialize(rows, column_count = rows[0].size)
|
|
285
323
|
# No checking is done at this point. rows must be an Array of Arrays.
|
|
@@ -341,7 +379,7 @@ class Matrix
|
|
|
341
379
|
end
|
|
342
380
|
|
|
343
381
|
private def set_value(row, col, value)
|
|
344
|
-
raise ErrDimensionMismatch, "Expected a
|
|
382
|
+
raise ErrDimensionMismatch, "Expected a value, got a #{value.class}" if value.respond_to?(:to_matrix)
|
|
345
383
|
|
|
346
384
|
@rows[row][col] = value
|
|
347
385
|
end
|
|
@@ -372,12 +410,12 @@ class Matrix
|
|
|
372
410
|
|
|
373
411
|
private def set_row_range(row_range, col, value)
|
|
374
412
|
if value.is_a?(Vector)
|
|
375
|
-
|
|
413
|
+
raise ErrDimensionMismatch unless row_range.size == value.size
|
|
376
414
|
set_column_vector(row_range, col, value)
|
|
377
415
|
elsif value.is_a?(Matrix)
|
|
378
|
-
|
|
416
|
+
raise ErrDimensionMismatch unless value.column_count == 1
|
|
379
417
|
value = value.column(0)
|
|
380
|
-
|
|
418
|
+
raise ErrDimensionMismatch unless row_range.size == value.size
|
|
381
419
|
set_column_vector(row_range, col, value)
|
|
382
420
|
else
|
|
383
421
|
@rows[row_range].each{|e| e[col] = value }
|
|
@@ -395,12 +433,12 @@ class Matrix
|
|
|
395
433
|
value = if value.is_a?(Vector)
|
|
396
434
|
value.to_a
|
|
397
435
|
elsif value.is_a?(Matrix)
|
|
398
|
-
|
|
436
|
+
raise ErrDimensionMismatch unless value.row_count == 1
|
|
399
437
|
value.row(0).to_a
|
|
400
438
|
else
|
|
401
439
|
Array.new(col_range.size, value)
|
|
402
440
|
end
|
|
403
|
-
|
|
441
|
+
raise ErrDimensionMismatch unless col_range.size == value.size
|
|
404
442
|
@rows[row][col_range] = value
|
|
405
443
|
end
|
|
406
444
|
|
|
@@ -464,8 +502,8 @@ class Matrix
|
|
|
464
502
|
# * :strict_upper: yields only elements above the diagonal
|
|
465
503
|
# * :upper: yields only elements on or above the diagonal
|
|
466
504
|
# Matrix[ [1,2], [3,4] ].collect { |e| e**2 }
|
|
467
|
-
#
|
|
468
|
-
#
|
|
505
|
+
# # => 1 4
|
|
506
|
+
# # 9 16
|
|
469
507
|
#
|
|
470
508
|
def collect(which = :all, &block) # :yield: e
|
|
471
509
|
return to_enum(:collect, which) unless block_given?
|
|
@@ -494,7 +532,8 @@ class Matrix
|
|
|
494
532
|
alias map! collect!
|
|
495
533
|
|
|
496
534
|
def freeze
|
|
497
|
-
@rows.freeze
|
|
535
|
+
@rows.each(&:freeze).freeze
|
|
536
|
+
|
|
498
537
|
super
|
|
499
538
|
end
|
|
500
539
|
|
|
@@ -510,16 +549,15 @@ class Matrix
|
|
|
510
549
|
# * :strict_upper: yields only elements above the diagonal
|
|
511
550
|
# * :upper: yields only elements on or above the diagonal
|
|
512
551
|
#
|
|
513
|
-
#
|
|
514
|
-
#
|
|
515
|
-
#
|
|
552
|
+
# Matrix[ [1,2], [3,4] ].each { |e| puts e }
|
|
553
|
+
# # => prints the numbers 1 to 4
|
|
554
|
+
# Matrix[ [1,2], [3,4] ].each(:strict_lower).to_a # => [3]
|
|
516
555
|
#
|
|
517
|
-
def each(which = :all) # :yield: e
|
|
556
|
+
def each(which = :all, &block) # :yield: e
|
|
518
557
|
return to_enum :each, which unless block_given?
|
|
519
558
|
last = column_count - 1
|
|
520
559
|
case which
|
|
521
560
|
when :all
|
|
522
|
-
block = Proc.new
|
|
523
561
|
@rows.each do |row|
|
|
524
562
|
row.each(&block)
|
|
525
563
|
end
|
|
@@ -662,8 +700,8 @@ class Matrix
|
|
|
662
700
|
# * row_range, col_range
|
|
663
701
|
#
|
|
664
702
|
# Matrix.diagonal(9, 5, -3).minor(0..1, 0..2)
|
|
665
|
-
#
|
|
666
|
-
#
|
|
703
|
+
# # => 9 0 0
|
|
704
|
+
# # 0 5 0
|
|
667
705
|
#
|
|
668
706
|
# Like Array#[], negative indices count backward from the end of the
|
|
669
707
|
# row or column (-1 is the last element). Returns nil if the starting
|
|
@@ -706,9 +744,9 @@ class Matrix
|
|
|
706
744
|
# Returns the submatrix obtained by deleting the specified row and column.
|
|
707
745
|
#
|
|
708
746
|
# Matrix.diagonal(9, 5, -3, 4).first_minor(1, 2)
|
|
709
|
-
#
|
|
710
|
-
#
|
|
711
|
-
#
|
|
747
|
+
# # => 9 0 0
|
|
748
|
+
# # 0 0 0
|
|
749
|
+
# # 0 0 4
|
|
712
750
|
#
|
|
713
751
|
def first_minor(row, column)
|
|
714
752
|
raise RuntimeError, "first_minor of empty matrix is not defined" if empty?
|
|
@@ -735,11 +773,11 @@ class Matrix
|
|
|
735
773
|
# the first minor by (-1)**(row + column).
|
|
736
774
|
#
|
|
737
775
|
# Matrix.diagonal(9, 5, -3, 4).cofactor(1, 1)
|
|
738
|
-
#
|
|
776
|
+
# # => -108
|
|
739
777
|
#
|
|
740
778
|
def cofactor(row, column)
|
|
741
779
|
raise RuntimeError, "cofactor of empty matrix is not defined" if empty?
|
|
742
|
-
|
|
780
|
+
raise ErrDimensionMismatch unless square?
|
|
743
781
|
|
|
744
782
|
det_of_minor = first_minor(row, column).determinant
|
|
745
783
|
det_of_minor * (-1) ** (row + column)
|
|
@@ -749,11 +787,11 @@ class Matrix
|
|
|
749
787
|
# Returns the adjugate of the matrix.
|
|
750
788
|
#
|
|
751
789
|
# Matrix[ [7,6],[3,9] ].adjugate
|
|
752
|
-
#
|
|
753
|
-
#
|
|
790
|
+
# # => 9 -6
|
|
791
|
+
# # -3 7
|
|
754
792
|
#
|
|
755
793
|
def adjugate
|
|
756
|
-
|
|
794
|
+
raise ErrDimensionMismatch unless square?
|
|
757
795
|
Matrix.build(row_count, column_count) do |row, column|
|
|
758
796
|
cofactor(column, row)
|
|
759
797
|
end
|
|
@@ -763,10 +801,10 @@ class Matrix
|
|
|
763
801
|
# Returns the Laplace expansion along given row or column.
|
|
764
802
|
#
|
|
765
803
|
# Matrix[[7,6], [3,9]].laplace_expansion(column: 1)
|
|
766
|
-
#
|
|
804
|
+
# # => 45
|
|
767
805
|
#
|
|
768
806
|
# Matrix[[Vector[1, 0], Vector[0, 1]], [2, 3]].laplace_expansion(row: 0)
|
|
769
|
-
#
|
|
807
|
+
# # => Vector[3, -2]
|
|
770
808
|
#
|
|
771
809
|
#
|
|
772
810
|
def laplace_expansion(row: nil, column: nil)
|
|
@@ -776,7 +814,7 @@ class Matrix
|
|
|
776
814
|
raise ArgumentError, "exactly one the row or column arguments must be specified"
|
|
777
815
|
end
|
|
778
816
|
|
|
779
|
-
|
|
817
|
+
raise ErrDimensionMismatch unless square?
|
|
780
818
|
raise RuntimeError, "laplace_expansion of empty matrix is not defined" if empty?
|
|
781
819
|
|
|
782
820
|
unless 0 <= num && num < row_count
|
|
@@ -799,7 +837,7 @@ class Matrix
|
|
|
799
837
|
# Raises an error if matrix is not square.
|
|
800
838
|
#
|
|
801
839
|
def diagonal?
|
|
802
|
-
|
|
840
|
+
raise ErrDimensionMismatch unless square?
|
|
803
841
|
each(:off_diagonal).all?(&:zero?)
|
|
804
842
|
end
|
|
805
843
|
|
|
@@ -816,7 +854,7 @@ class Matrix
|
|
|
816
854
|
# Raises an error if matrix is not square.
|
|
817
855
|
#
|
|
818
856
|
def hermitian?
|
|
819
|
-
|
|
857
|
+
raise ErrDimensionMismatch unless square?
|
|
820
858
|
each_with_index(:upper).all? do |e, row, col|
|
|
821
859
|
e == rows[col][row].conj
|
|
822
860
|
end
|
|
@@ -834,7 +872,7 @@ class Matrix
|
|
|
834
872
|
# Raises an error if matrix is not square.
|
|
835
873
|
#
|
|
836
874
|
def normal?
|
|
837
|
-
|
|
875
|
+
raise ErrDimensionMismatch unless square?
|
|
838
876
|
rows.each_with_index do |row_i, i|
|
|
839
877
|
rows.each_with_index do |row_j, j|
|
|
840
878
|
s = 0
|
|
@@ -852,12 +890,13 @@ class Matrix
|
|
|
852
890
|
# Raises an error if matrix is not square.
|
|
853
891
|
#
|
|
854
892
|
def orthogonal?
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
893
|
+
raise ErrDimensionMismatch unless square?
|
|
894
|
+
|
|
895
|
+
rows.each_with_index do |row_i, i|
|
|
896
|
+
rows.each_with_index do |row_j, j|
|
|
858
897
|
s = 0
|
|
859
898
|
row_count.times do |k|
|
|
860
|
-
s +=
|
|
899
|
+
s += row_i[k] * row_j[k]
|
|
861
900
|
end
|
|
862
901
|
return false unless s == (i == j ? 1 : 0)
|
|
863
902
|
end
|
|
@@ -870,7 +909,7 @@ class Matrix
|
|
|
870
909
|
# Raises an error if matrix is not square.
|
|
871
910
|
#
|
|
872
911
|
def permutation?
|
|
873
|
-
|
|
912
|
+
raise ErrDimensionMismatch unless square?
|
|
874
913
|
cols = Array.new(column_count)
|
|
875
914
|
rows.each_with_index do |row, i|
|
|
876
915
|
found = false
|
|
@@ -920,7 +959,7 @@ class Matrix
|
|
|
920
959
|
# Raises an error if matrix is not square.
|
|
921
960
|
#
|
|
922
961
|
def symmetric?
|
|
923
|
-
|
|
962
|
+
raise ErrDimensionMismatch unless square?
|
|
924
963
|
each_with_index(:strict_upper) do |e, row, col|
|
|
925
964
|
return false if e != rows[col][row]
|
|
926
965
|
end
|
|
@@ -932,7 +971,7 @@ class Matrix
|
|
|
932
971
|
# Raises an error if matrix is not square.
|
|
933
972
|
#
|
|
934
973
|
def antisymmetric?
|
|
935
|
-
|
|
974
|
+
raise ErrDimensionMismatch unless square?
|
|
936
975
|
each_with_index(:upper) do |e, row, col|
|
|
937
976
|
return false unless e == -rows[col][row]
|
|
938
977
|
end
|
|
@@ -945,12 +984,12 @@ class Matrix
|
|
|
945
984
|
# Raises an error if matrix is not square.
|
|
946
985
|
#
|
|
947
986
|
def unitary?
|
|
948
|
-
|
|
949
|
-
rows.each_with_index do |
|
|
950
|
-
|
|
987
|
+
raise ErrDimensionMismatch unless square?
|
|
988
|
+
rows.each_with_index do |row_i, i|
|
|
989
|
+
rows.each_with_index do |row_j, j|
|
|
951
990
|
s = 0
|
|
952
991
|
row_count.times do |k|
|
|
953
|
-
s +=
|
|
992
|
+
s += row_i[k].conj * row_j[k]
|
|
954
993
|
end
|
|
955
994
|
return false unless s == (i == j ? 1 : 0)
|
|
956
995
|
end
|
|
@@ -977,7 +1016,7 @@ class Matrix
|
|
|
977
1016
|
#++
|
|
978
1017
|
|
|
979
1018
|
#
|
|
980
|
-
# Returns
|
|
1019
|
+
# Returns whether the two matrices contain equal elements.
|
|
981
1020
|
#
|
|
982
1021
|
def ==(other)
|
|
983
1022
|
return false unless Matrix === other &&
|
|
@@ -1013,31 +1052,33 @@ class Matrix
|
|
|
1013
1052
|
#
|
|
1014
1053
|
# Matrix multiplication.
|
|
1015
1054
|
# Matrix[[2,4], [6,8]] * Matrix.identity(2)
|
|
1016
|
-
#
|
|
1017
|
-
#
|
|
1055
|
+
# # => 2 4
|
|
1056
|
+
# # 6 8
|
|
1018
1057
|
#
|
|
1019
1058
|
def *(m) # m is matrix or vector or number
|
|
1020
1059
|
case(m)
|
|
1021
1060
|
when Numeric
|
|
1022
|
-
|
|
1061
|
+
new_rows = @rows.collect {|row|
|
|
1023
1062
|
row.collect {|e| e * m }
|
|
1024
1063
|
}
|
|
1025
|
-
return new_matrix
|
|
1064
|
+
return new_matrix new_rows, column_count
|
|
1026
1065
|
when Vector
|
|
1027
1066
|
m = self.class.column_vector(m)
|
|
1028
1067
|
r = self * m
|
|
1029
1068
|
return r.column(0)
|
|
1030
1069
|
when Matrix
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
Array.new(m.column_count)
|
|
1035
|
-
|
|
1036
|
-
|
|
1070
|
+
raise ErrDimensionMismatch if column_count != m.row_count
|
|
1071
|
+
m_rows = m.rows
|
|
1072
|
+
new_rows = rows.map do |row_i|
|
|
1073
|
+
Array.new(m.column_count) do |j|
|
|
1074
|
+
vij = 0
|
|
1075
|
+
column_count.times do |k|
|
|
1076
|
+
vij += row_i[k] * m_rows[k][j]
|
|
1037
1077
|
end
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1078
|
+
vij
|
|
1079
|
+
end
|
|
1080
|
+
end
|
|
1081
|
+
return new_matrix new_rows, m.column_count
|
|
1041
1082
|
else
|
|
1042
1083
|
return apply_through_coercion(m, __method__)
|
|
1043
1084
|
end
|
|
@@ -1046,13 +1087,13 @@ class Matrix
|
|
|
1046
1087
|
#
|
|
1047
1088
|
# Matrix addition.
|
|
1048
1089
|
# Matrix.scalar(2,5) + Matrix[[1,0], [-4,7]]
|
|
1049
|
-
#
|
|
1050
|
-
#
|
|
1090
|
+
# # => 6 0
|
|
1091
|
+
# # -4 12
|
|
1051
1092
|
#
|
|
1052
1093
|
def +(m)
|
|
1053
1094
|
case m
|
|
1054
1095
|
when Numeric
|
|
1055
|
-
|
|
1096
|
+
raise ErrOperationNotDefined, ["+", self.class, m.class]
|
|
1056
1097
|
when Vector
|
|
1057
1098
|
m = self.class.column_vector(m)
|
|
1058
1099
|
when Matrix
|
|
@@ -1060,7 +1101,7 @@ class Matrix
|
|
|
1060
1101
|
return apply_through_coercion(m, __method__)
|
|
1061
1102
|
end
|
|
1062
1103
|
|
|
1063
|
-
|
|
1104
|
+
raise ErrDimensionMismatch unless row_count == m.row_count && column_count == m.column_count
|
|
1064
1105
|
|
|
1065
1106
|
rows = Array.new(row_count) {|i|
|
|
1066
1107
|
Array.new(column_count) {|j|
|
|
@@ -1073,13 +1114,13 @@ class Matrix
|
|
|
1073
1114
|
#
|
|
1074
1115
|
# Matrix subtraction.
|
|
1075
1116
|
# Matrix[[1,5], [4,2]] - Matrix[[9,3], [-4,1]]
|
|
1076
|
-
#
|
|
1077
|
-
#
|
|
1117
|
+
# # => -8 2
|
|
1118
|
+
# # 8 1
|
|
1078
1119
|
#
|
|
1079
1120
|
def -(m)
|
|
1080
1121
|
case m
|
|
1081
1122
|
when Numeric
|
|
1082
|
-
|
|
1123
|
+
raise ErrOperationNotDefined, ["-", self.class, m.class]
|
|
1083
1124
|
when Vector
|
|
1084
1125
|
m = self.class.column_vector(m)
|
|
1085
1126
|
when Matrix
|
|
@@ -1087,7 +1128,7 @@ class Matrix
|
|
|
1087
1128
|
return apply_through_coercion(m, __method__)
|
|
1088
1129
|
end
|
|
1089
1130
|
|
|
1090
|
-
|
|
1131
|
+
raise ErrDimensionMismatch unless row_count == m.row_count && column_count == m.column_count
|
|
1091
1132
|
|
|
1092
1133
|
rows = Array.new(row_count) {|i|
|
|
1093
1134
|
Array.new(column_count) {|j|
|
|
@@ -1100,8 +1141,8 @@ class Matrix
|
|
|
1100
1141
|
#
|
|
1101
1142
|
# Matrix division (multiplication by the inverse).
|
|
1102
1143
|
# Matrix[[7,6], [3,9]] / Matrix[[2,9], [3,1]]
|
|
1103
|
-
#
|
|
1104
|
-
#
|
|
1144
|
+
# # => -7 1
|
|
1145
|
+
# # -3 -6
|
|
1105
1146
|
#
|
|
1106
1147
|
def /(other)
|
|
1107
1148
|
case other
|
|
@@ -1120,8 +1161,8 @@ class Matrix
|
|
|
1120
1161
|
#
|
|
1121
1162
|
# Hadamard product
|
|
1122
1163
|
# Matrix[[1,2], [3,4]].hadamard_product(Matrix[[1,2], [3,2]])
|
|
1123
|
-
#
|
|
1124
|
-
#
|
|
1164
|
+
# # => 1 4
|
|
1165
|
+
# # 9 8
|
|
1125
1166
|
#
|
|
1126
1167
|
def hadamard_product(m)
|
|
1127
1168
|
combine(m){|a, b| a * b}
|
|
@@ -1131,11 +1172,11 @@ class Matrix
|
|
|
1131
1172
|
#
|
|
1132
1173
|
# Returns the inverse of the matrix.
|
|
1133
1174
|
# Matrix[[-1, -1], [0, -1]].inverse
|
|
1134
|
-
#
|
|
1135
|
-
#
|
|
1175
|
+
# # => -1 1
|
|
1176
|
+
# # 0 -1
|
|
1136
1177
|
#
|
|
1137
1178
|
def inverse
|
|
1138
|
-
|
|
1179
|
+
raise ErrDimensionMismatch unless square?
|
|
1139
1180
|
self.class.I(row_count).send(:inverse_from, self)
|
|
1140
1181
|
end
|
|
1141
1182
|
alias_method :inv, :inverse
|
|
@@ -1154,7 +1195,7 @@ class Matrix
|
|
|
1154
1195
|
akk = v
|
|
1155
1196
|
end
|
|
1156
1197
|
end
|
|
1157
|
-
|
|
1198
|
+
raise ErrNotRegular if akk == 0
|
|
1158
1199
|
if i != k
|
|
1159
1200
|
a[i], a[k] = a[k], a[i]
|
|
1160
1201
|
@rows[i], @rows[k] = @rows[k], @rows[i]
|
|
@@ -1187,32 +1228,56 @@ class Matrix
|
|
|
1187
1228
|
#
|
|
1188
1229
|
# Matrix exponentiation.
|
|
1189
1230
|
# Equivalent to multiplying the matrix by itself N times.
|
|
1190
|
-
# Non
|
|
1231
|
+
# Non-integer exponents will be handled by diagonalizing the matrix;
|
|
1232
|
+
# this is not supported for Complex matrices.
|
|
1191
1233
|
#
|
|
1192
1234
|
# Matrix[[7,6], [3,9]] ** 2
|
|
1193
|
-
#
|
|
1194
|
-
#
|
|
1235
|
+
# # => 67 96
|
|
1236
|
+
# # 48 99
|
|
1195
1237
|
#
|
|
1196
|
-
def **(
|
|
1197
|
-
case
|
|
1238
|
+
def **(exp)
|
|
1239
|
+
case exp
|
|
1198
1240
|
when Integer
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
z = z ? z * x : x if other[0] == 1
|
|
1208
|
-
return z if (other >>= 1).zero?
|
|
1209
|
-
x *= x
|
|
1241
|
+
case
|
|
1242
|
+
when exp == 0
|
|
1243
|
+
raise ErrDimensionMismatch unless square?
|
|
1244
|
+
self.class.identity(column_count)
|
|
1245
|
+
when exp < 0
|
|
1246
|
+
inverse.power_int(-exp)
|
|
1247
|
+
else
|
|
1248
|
+
power_int(exp)
|
|
1210
1249
|
end
|
|
1211
1250
|
when Numeric
|
|
1212
1251
|
v, d, v_inv = eigensystem
|
|
1213
|
-
v * self.class.diagonal(*d.each(:diagonal).map{|e| e **
|
|
1252
|
+
v * self.class.diagonal(*d.each(:diagonal).map{|e| e ** exp}) * v_inv
|
|
1253
|
+
else
|
|
1254
|
+
raise ErrOperationNotDefined, ["**", self.class, exp.class]
|
|
1255
|
+
end
|
|
1256
|
+
end
|
|
1257
|
+
|
|
1258
|
+
protected def power_int(exp)
|
|
1259
|
+
# assumes `exp` is an Integer > 0
|
|
1260
|
+
#
|
|
1261
|
+
# Previous algorithm:
|
|
1262
|
+
# build M**2, M**4 = (M**2)**2, M**8, ... and multiplying those you need
|
|
1263
|
+
# e.g. M**0b1011 = M**11 = M * M**2 * M**8
|
|
1264
|
+
# ^ ^
|
|
1265
|
+
# (highlighted the 2 out of 5 multiplications involving `M * x`)
|
|
1266
|
+
#
|
|
1267
|
+
# Current algorithm has same number of multiplications but with lower exponents:
|
|
1268
|
+
# M**11 = M * (M * M**4)**2
|
|
1269
|
+
# ^ ^ ^
|
|
1270
|
+
# (highlighted the 3 out of 5 multiplications involving `M * x`)
|
|
1271
|
+
#
|
|
1272
|
+
# This should be faster for all (non nil-potent) matrices.
|
|
1273
|
+
case
|
|
1274
|
+
when exp == 1
|
|
1275
|
+
self
|
|
1276
|
+
when exp.odd?
|
|
1277
|
+
self * power_int(exp - 1)
|
|
1214
1278
|
else
|
|
1215
|
-
|
|
1279
|
+
sqrt = power_int(exp / 2)
|
|
1280
|
+
sqrt * sqrt
|
|
1216
1281
|
end
|
|
1217
1282
|
end
|
|
1218
1283
|
|
|
@@ -1220,10 +1285,22 @@ class Matrix
|
|
|
1220
1285
|
self
|
|
1221
1286
|
end
|
|
1222
1287
|
|
|
1288
|
+
# Unary matrix negation.
|
|
1289
|
+
#
|
|
1290
|
+
# -Matrix[[1,5], [4,2]]
|
|
1291
|
+
# # => -1 -5
|
|
1292
|
+
# # -4 -2
|
|
1223
1293
|
def -@
|
|
1224
1294
|
collect {|e| -e }
|
|
1225
1295
|
end
|
|
1226
1296
|
|
|
1297
|
+
#
|
|
1298
|
+
# Returns the absolute value elementwise
|
|
1299
|
+
#
|
|
1300
|
+
def abs
|
|
1301
|
+
collect(&:abs)
|
|
1302
|
+
end
|
|
1303
|
+
|
|
1227
1304
|
#--
|
|
1228
1305
|
# MATRIX FUNCTIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
|
1229
1306
|
#++
|
|
@@ -1236,10 +1313,10 @@ class Matrix
|
|
|
1236
1313
|
# Consider using exact types like Rational or BigDecimal instead.
|
|
1237
1314
|
#
|
|
1238
1315
|
# Matrix[[7,6], [3,9]].determinant
|
|
1239
|
-
#
|
|
1316
|
+
# # => 45
|
|
1240
1317
|
#
|
|
1241
1318
|
def determinant
|
|
1242
|
-
|
|
1319
|
+
raise ErrDimensionMismatch unless square?
|
|
1243
1320
|
m = @rows
|
|
1244
1321
|
case row_count
|
|
1245
1322
|
# Up to 4x4, give result using Laplacian expansion by minors.
|
|
@@ -1344,7 +1421,7 @@ class Matrix
|
|
|
1344
1421
|
# Consider using exact types like Rational or BigDecimal instead.
|
|
1345
1422
|
#
|
|
1346
1423
|
# Matrix[[7,6], [3,9]].rank
|
|
1347
|
-
#
|
|
1424
|
+
# # => 2
|
|
1348
1425
|
#
|
|
1349
1426
|
def rank
|
|
1350
1427
|
# We currently use Bareiss' multistep integer-preserving gaussian elimination
|
|
@@ -1382,6 +1459,35 @@ class Matrix
|
|
|
1382
1459
|
rank
|
|
1383
1460
|
end
|
|
1384
1461
|
|
|
1462
|
+
#
|
|
1463
|
+
# Returns a new matrix with rotated elements.
|
|
1464
|
+
# The argument specifies the rotation (defaults to `:clockwise`):
|
|
1465
|
+
# * :clockwise, 1, -3, etc.: "turn right" - first row becomes last column
|
|
1466
|
+
# * :half_turn, 2, -2, etc.: first row becomes last row, elements in reverse order
|
|
1467
|
+
# * :counter_clockwise, -1, 3: "turn left" - first row becomes first column
|
|
1468
|
+
# (but with elements in reverse order)
|
|
1469
|
+
#
|
|
1470
|
+
# m = Matrix[ [1, 2], [3, 4] ]
|
|
1471
|
+
# r = m.rotate_entries(:clockwise)
|
|
1472
|
+
# # => Matrix[[3, 1], [4, 2]]
|
|
1473
|
+
#
|
|
1474
|
+
def rotate_entries(rotation = :clockwise)
|
|
1475
|
+
rotation %= 4 if rotation.respond_to? :to_int
|
|
1476
|
+
|
|
1477
|
+
case rotation
|
|
1478
|
+
when 0
|
|
1479
|
+
dup
|
|
1480
|
+
when 1, :clockwise
|
|
1481
|
+
new_matrix @rows.transpose.each(&:reverse!), row_count
|
|
1482
|
+
when 2, :half_turn
|
|
1483
|
+
new_matrix @rows.map(&:reverse).reverse!, column_count
|
|
1484
|
+
when 3, :counter_clockwise
|
|
1485
|
+
new_matrix @rows.transpose.reverse!, row_count
|
|
1486
|
+
else
|
|
1487
|
+
raise ArgumentError, "expected #{rotation.inspect} to be one of :clockwise, :counter_clockwise, :half_turn or an integer"
|
|
1488
|
+
end
|
|
1489
|
+
end
|
|
1490
|
+
|
|
1385
1491
|
# Returns a matrix with entries rounded to the given precision
|
|
1386
1492
|
# (see Float#round)
|
|
1387
1493
|
#
|
|
@@ -1392,10 +1498,10 @@ class Matrix
|
|
|
1392
1498
|
#
|
|
1393
1499
|
# Returns the trace (sum of diagonal elements) of the matrix.
|
|
1394
1500
|
# Matrix[[7,6], [3,9]].trace
|
|
1395
|
-
#
|
|
1501
|
+
# # => 16
|
|
1396
1502
|
#
|
|
1397
1503
|
def trace
|
|
1398
|
-
|
|
1504
|
+
raise ErrDimensionMismatch unless square?
|
|
1399
1505
|
(0...column_count).inject(0) do |tr, i|
|
|
1400
1506
|
tr + @rows[i][i]
|
|
1401
1507
|
end
|
|
@@ -1405,12 +1511,12 @@ class Matrix
|
|
|
1405
1511
|
#
|
|
1406
1512
|
# Returns the transpose of the matrix.
|
|
1407
1513
|
# Matrix[[1,2], [3,4], [5,6]]
|
|
1408
|
-
#
|
|
1409
|
-
#
|
|
1410
|
-
#
|
|
1514
|
+
# # => 1 2
|
|
1515
|
+
# # 3 4
|
|
1516
|
+
# # 5 6
|
|
1411
1517
|
# Matrix[[1,2], [3,4], [5,6]].transpose
|
|
1412
|
-
#
|
|
1413
|
-
#
|
|
1518
|
+
# # => 1 3 5
|
|
1519
|
+
# # 2 4 6
|
|
1414
1520
|
#
|
|
1415
1521
|
def transpose
|
|
1416
1522
|
return self.class.empty(column_count, 0) if row_count.zero?
|
|
@@ -1442,6 +1548,7 @@ class Matrix
|
|
|
1442
1548
|
# v.inv == v_inv # => true
|
|
1443
1549
|
# (v * d * v_inv).round(5) == m # => true
|
|
1444
1550
|
#
|
|
1551
|
+
# This is not supported for Complex matrices
|
|
1445
1552
|
def eigensystem
|
|
1446
1553
|
EigenvalueDecomposition.new(self)
|
|
1447
1554
|
end
|
|
@@ -1469,25 +1576,36 @@ class Matrix
|
|
|
1469
1576
|
#
|
|
1470
1577
|
# Returns the conjugate of the matrix.
|
|
1471
1578
|
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
|
|
1472
|
-
#
|
|
1473
|
-
#
|
|
1579
|
+
# # => 1+2i i 0
|
|
1580
|
+
# # 1 2 3
|
|
1474
1581
|
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].conjugate
|
|
1475
|
-
#
|
|
1476
|
-
#
|
|
1582
|
+
# # => 1-2i -i 0
|
|
1583
|
+
# # 1 2 3
|
|
1477
1584
|
#
|
|
1478
1585
|
def conjugate
|
|
1479
1586
|
collect(&:conjugate)
|
|
1480
1587
|
end
|
|
1481
1588
|
alias_method :conj, :conjugate
|
|
1482
1589
|
|
|
1590
|
+
#
|
|
1591
|
+
# Returns the adjoint of the matrix.
|
|
1592
|
+
#
|
|
1593
|
+
# Matrix[ [i,1],[2,-i] ].adjoint
|
|
1594
|
+
# # => -i 2
|
|
1595
|
+
# # 1 i
|
|
1596
|
+
#
|
|
1597
|
+
def adjoint
|
|
1598
|
+
conjugate.transpose
|
|
1599
|
+
end
|
|
1600
|
+
|
|
1483
1601
|
#
|
|
1484
1602
|
# Returns the imaginary part of the matrix.
|
|
1485
1603
|
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
|
|
1486
|
-
#
|
|
1487
|
-
#
|
|
1604
|
+
# # => 1+2i i 0
|
|
1605
|
+
# # 1 2 3
|
|
1488
1606
|
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].imaginary
|
|
1489
|
-
#
|
|
1490
|
-
#
|
|
1607
|
+
# # => 2i i 0
|
|
1608
|
+
# # 0 0 0
|
|
1491
1609
|
#
|
|
1492
1610
|
def imaginary
|
|
1493
1611
|
collect(&:imaginary)
|
|
@@ -1497,11 +1615,11 @@ class Matrix
|
|
|
1497
1615
|
#
|
|
1498
1616
|
# Returns the real part of the matrix.
|
|
1499
1617
|
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]]
|
|
1500
|
-
#
|
|
1501
|
-
#
|
|
1618
|
+
# # => 1+2i i 0
|
|
1619
|
+
# # 1 2 3
|
|
1502
1620
|
# Matrix[[Complex(1,2), Complex(0,1), 0], [1, 2, 3]].real
|
|
1503
|
-
#
|
|
1504
|
-
#
|
|
1621
|
+
# # => 1 0 0
|
|
1622
|
+
# # 1 2 3
|
|
1505
1623
|
#
|
|
1506
1624
|
def real
|
|
1507
1625
|
collect(&:real)
|
|
@@ -1511,7 +1629,7 @@ class Matrix
|
|
|
1511
1629
|
# Returns an array containing matrices corresponding to the real and imaginary
|
|
1512
1630
|
# parts of the matrix
|
|
1513
1631
|
#
|
|
1514
|
-
#
|
|
1632
|
+
# m.rect == [m.real, m.imag] # ==> true for all matrices m
|
|
1515
1633
|
#
|
|
1516
1634
|
def rect
|
|
1517
1635
|
[real, imag]
|
|
@@ -1572,7 +1690,7 @@ class Matrix
|
|
|
1572
1690
|
|
|
1573
1691
|
# Deprecated.
|
|
1574
1692
|
#
|
|
1575
|
-
# Use map(&:to_f)
|
|
1693
|
+
# Use <code>map(&:to_f)</code>
|
|
1576
1694
|
def elements_to_f
|
|
1577
1695
|
warn "Matrix#elements_to_f is deprecated, use map(&:to_f)", uplevel: 1
|
|
1578
1696
|
map(&:to_f)
|
|
@@ -1580,7 +1698,7 @@ class Matrix
|
|
|
1580
1698
|
|
|
1581
1699
|
# Deprecated.
|
|
1582
1700
|
#
|
|
1583
|
-
# Use map(&:to_i)
|
|
1701
|
+
# Use <code>map(&:to_i)</code>
|
|
1584
1702
|
def elements_to_i
|
|
1585
1703
|
warn "Matrix#elements_to_i is deprecated, use map(&:to_i)", uplevel: 1
|
|
1586
1704
|
map(&:to_i)
|
|
@@ -1588,7 +1706,7 @@ class Matrix
|
|
|
1588
1706
|
|
|
1589
1707
|
# Deprecated.
|
|
1590
1708
|
#
|
|
1591
|
-
# Use map(&:to_r)
|
|
1709
|
+
# Use <code>map(&:to_r)</code>
|
|
1592
1710
|
def elements_to_r
|
|
1593
1711
|
warn "Matrix#elements_to_r is deprecated, use map(&:to_r)", uplevel: 1
|
|
1594
1712
|
map(&:to_r)
|
|
@@ -1728,7 +1846,7 @@ class Matrix
|
|
|
1728
1846
|
when Numeric
|
|
1729
1847
|
Scalar.new(@value + other)
|
|
1730
1848
|
when Vector, Matrix
|
|
1731
|
-
|
|
1849
|
+
raise ErrOperationNotDefined, ["+", @value.class, other.class]
|
|
1732
1850
|
else
|
|
1733
1851
|
apply_through_coercion(other, __method__)
|
|
1734
1852
|
end
|
|
@@ -1739,7 +1857,7 @@ class Matrix
|
|
|
1739
1857
|
when Numeric
|
|
1740
1858
|
Scalar.new(@value - other)
|
|
1741
1859
|
when Vector, Matrix
|
|
1742
|
-
|
|
1860
|
+
raise ErrOperationNotDefined, ["-", @value.class, other.class]
|
|
1743
1861
|
else
|
|
1744
1862
|
apply_through_coercion(other, __method__)
|
|
1745
1863
|
end
|
|
@@ -1761,7 +1879,7 @@ class Matrix
|
|
|
1761
1879
|
when Numeric
|
|
1762
1880
|
Scalar.new(@value / other)
|
|
1763
1881
|
when Vector
|
|
1764
|
-
|
|
1882
|
+
raise ErrOperationNotDefined, ["/", @value.class, other.class]
|
|
1765
1883
|
when Matrix
|
|
1766
1884
|
self * other.inverse
|
|
1767
1885
|
else
|
|
@@ -1774,10 +1892,10 @@ class Matrix
|
|
|
1774
1892
|
when Numeric
|
|
1775
1893
|
Scalar.new(@value ** other)
|
|
1776
1894
|
when Vector
|
|
1777
|
-
|
|
1895
|
+
raise ErrOperationNotDefined, ["**", @value.class, other.class]
|
|
1778
1896
|
when Matrix
|
|
1779
1897
|
#other.powered_by(self)
|
|
1780
|
-
|
|
1898
|
+
raise ErrOperationNotImplemented, ["**", @value.class, other.class]
|
|
1781
1899
|
else
|
|
1782
1900
|
apply_through_coercion(other, __method__)
|
|
1783
1901
|
end
|
|
@@ -1824,8 +1942,8 @@ end
|
|
|
1824
1942
|
# * #-@
|
|
1825
1943
|
#
|
|
1826
1944
|
# Vector functions:
|
|
1827
|
-
# * #inner_product(v), dot(v)
|
|
1828
|
-
# * #cross_product(v), cross(v)
|
|
1945
|
+
# * #inner_product(v), #dot(v)
|
|
1946
|
+
# * #cross_product(v), #cross(v)
|
|
1829
1947
|
# * #collect
|
|
1830
1948
|
# * #collect!
|
|
1831
1949
|
# * #magnitude
|
|
@@ -1890,7 +2008,7 @@ class Vector
|
|
|
1890
2008
|
#
|
|
1891
2009
|
# Return a zero vector.
|
|
1892
2010
|
#
|
|
1893
|
-
# Vector.zero(3) => Vector[0, 0, 0]
|
|
2011
|
+
# Vector.zero(3) # => Vector[0, 0, 0]
|
|
1894
2012
|
#
|
|
1895
2013
|
def Vector.zero(size)
|
|
1896
2014
|
raise ArgumentError, "invalid size (#{size} for 0..)" if size < 0
|
|
@@ -1953,7 +2071,7 @@ class Vector
|
|
|
1953
2071
|
raise ArgumentError, "vector to be set has wrong size" unless range.size == value.size
|
|
1954
2072
|
@elements[range] = value.elements
|
|
1955
2073
|
elsif value.is_a?(Matrix)
|
|
1956
|
-
|
|
2074
|
+
raise ErrDimensionMismatch unless value.row_count == 1
|
|
1957
2075
|
@elements[range] = value.row(0).elements
|
|
1958
2076
|
else
|
|
1959
2077
|
@elements[range] = Array.new(range.size, value)
|
|
@@ -1992,7 +2110,7 @@ class Vector
|
|
|
1992
2110
|
#
|
|
1993
2111
|
def each2(v) # :yield: e1, e2
|
|
1994
2112
|
raise TypeError, "Integer is not like Vector" if v.kind_of?(Integer)
|
|
1995
|
-
|
|
2113
|
+
raise ErrDimensionMismatch if size != v.size
|
|
1996
2114
|
return to_enum(:each2, v) unless block_given?
|
|
1997
2115
|
size.times do |i|
|
|
1998
2116
|
yield @elements[i], v[i]
|
|
@@ -2006,7 +2124,7 @@ class Vector
|
|
|
2006
2124
|
#
|
|
2007
2125
|
def collect2(v) # :yield: e1, e2
|
|
2008
2126
|
raise TypeError, "Integer is not like Vector" if v.kind_of?(Integer)
|
|
2009
|
-
|
|
2127
|
+
raise ErrDimensionMismatch if size != v.size
|
|
2010
2128
|
return to_enum(:collect2, v) unless block_given?
|
|
2011
2129
|
Array.new(size) do |i|
|
|
2012
2130
|
yield @elements[i], v[i]
|
|
@@ -2018,43 +2136,46 @@ class Vector
|
|
|
2018
2136
|
#++
|
|
2019
2137
|
|
|
2020
2138
|
#
|
|
2021
|
-
# Returns
|
|
2139
|
+
# Returns whether all of vectors are linearly independent.
|
|
2022
2140
|
#
|
|
2023
2141
|
# Vector.independent?(Vector[1,0], Vector[0,1])
|
|
2024
|
-
#
|
|
2142
|
+
# # => true
|
|
2025
2143
|
#
|
|
2026
2144
|
# Vector.independent?(Vector[1,2], Vector[2,4])
|
|
2027
|
-
#
|
|
2145
|
+
# # => false
|
|
2028
2146
|
#
|
|
2029
2147
|
def Vector.independent?(*vs)
|
|
2030
2148
|
vs.each do |v|
|
|
2031
2149
|
raise TypeError, "expected Vector, got #{v.class}" unless v.is_a?(Vector)
|
|
2032
|
-
|
|
2150
|
+
raise ErrDimensionMismatch unless v.size == vs.first.size
|
|
2033
2151
|
end
|
|
2034
2152
|
return false if vs.count > vs.first.size
|
|
2035
2153
|
Matrix[*vs].rank.eql?(vs.count)
|
|
2036
2154
|
end
|
|
2037
2155
|
|
|
2038
2156
|
#
|
|
2039
|
-
# Returns
|
|
2157
|
+
# Returns whether all of vectors are linearly independent.
|
|
2040
2158
|
#
|
|
2041
2159
|
# Vector[1,0].independent?(Vector[0,1])
|
|
2042
|
-
#
|
|
2160
|
+
# # => true
|
|
2043
2161
|
#
|
|
2044
2162
|
# Vector[1,2].independent?(Vector[2,4])
|
|
2045
|
-
#
|
|
2163
|
+
# # => false
|
|
2046
2164
|
#
|
|
2047
2165
|
def independent?(*vs)
|
|
2048
2166
|
self.class.independent?(self, *vs)
|
|
2049
2167
|
end
|
|
2050
2168
|
|
|
2051
2169
|
#
|
|
2052
|
-
# Returns
|
|
2170
|
+
# Returns whether all elements are zero.
|
|
2053
2171
|
#
|
|
2054
2172
|
def zero?
|
|
2055
2173
|
all?(&:zero?)
|
|
2056
2174
|
end
|
|
2057
2175
|
|
|
2176
|
+
#
|
|
2177
|
+
# Makes the matrix frozen and Ractor-shareable
|
|
2178
|
+
#
|
|
2058
2179
|
def freeze
|
|
2059
2180
|
@elements.freeze
|
|
2060
2181
|
super
|
|
@@ -2074,7 +2195,7 @@ class Vector
|
|
|
2074
2195
|
#++
|
|
2075
2196
|
|
|
2076
2197
|
#
|
|
2077
|
-
# Returns
|
|
2198
|
+
# Returns whether the two vectors have the same elements in the same order.
|
|
2078
2199
|
#
|
|
2079
2200
|
def ==(other)
|
|
2080
2201
|
return false unless Vector === other
|
|
@@ -2108,7 +2229,7 @@ class Vector
|
|
|
2108
2229
|
when Matrix
|
|
2109
2230
|
Matrix.column_vector(self) * x
|
|
2110
2231
|
when Vector
|
|
2111
|
-
|
|
2232
|
+
raise ErrOperationNotDefined, ["*", self.class, x.class]
|
|
2112
2233
|
else
|
|
2113
2234
|
apply_through_coercion(x, __method__)
|
|
2114
2235
|
end
|
|
@@ -2120,7 +2241,7 @@ class Vector
|
|
|
2120
2241
|
def +(v)
|
|
2121
2242
|
case v
|
|
2122
2243
|
when Vector
|
|
2123
|
-
|
|
2244
|
+
raise ErrDimensionMismatch if size != v.size
|
|
2124
2245
|
els = collect2(v) {|v1, v2|
|
|
2125
2246
|
v1 + v2
|
|
2126
2247
|
}
|
|
@@ -2138,7 +2259,7 @@ class Vector
|
|
|
2138
2259
|
def -(v)
|
|
2139
2260
|
case v
|
|
2140
2261
|
when Vector
|
|
2141
|
-
|
|
2262
|
+
raise ErrDimensionMismatch if size != v.size
|
|
2142
2263
|
els = collect2(v) {|v1, v2|
|
|
2143
2264
|
v1 - v2
|
|
2144
2265
|
}
|
|
@@ -2159,7 +2280,7 @@ class Vector
|
|
|
2159
2280
|
els = @elements.collect{|e| e / x}
|
|
2160
2281
|
self.class.elements(els, false)
|
|
2161
2282
|
when Matrix, Vector
|
|
2162
|
-
|
|
2283
|
+
raise ErrOperationNotDefined, ["/", self.class, x.class]
|
|
2163
2284
|
else
|
|
2164
2285
|
apply_through_coercion(x, __method__)
|
|
2165
2286
|
end
|
|
@@ -2179,10 +2300,10 @@ class Vector
|
|
|
2179
2300
|
|
|
2180
2301
|
#
|
|
2181
2302
|
# Returns the inner product of this vector with the other.
|
|
2182
|
-
# Vector[4,7].inner_product Vector[10,1]
|
|
2303
|
+
# Vector[4,7].inner_product Vector[10,1] # => 47
|
|
2183
2304
|
#
|
|
2184
2305
|
def inner_product(v)
|
|
2185
|
-
|
|
2306
|
+
raise ErrDimensionMismatch if size != v.size
|
|
2186
2307
|
|
|
2187
2308
|
p = 0
|
|
2188
2309
|
each2(v) {|v1, v2|
|
|
@@ -2194,7 +2315,7 @@ class Vector
|
|
|
2194
2315
|
|
|
2195
2316
|
#
|
|
2196
2317
|
# Returns the cross product of this vector with the others.
|
|
2197
|
-
# Vector[1, 0, 0].cross_product Vector[0, 1, 0]
|
|
2318
|
+
# Vector[1, 0, 0].cross_product Vector[0, 1, 0] # => Vector[0, 0, 1]
|
|
2198
2319
|
#
|
|
2199
2320
|
# It is generalized to other dimensions to return a vector perpendicular
|
|
2200
2321
|
# to the arguments.
|
|
@@ -2209,7 +2330,7 @@ class Vector
|
|
|
2209
2330
|
raise ArgumentError, "wrong number of arguments (#{vs.size} for #{size - 2})" unless vs.size == size - 2
|
|
2210
2331
|
vs.each do |v|
|
|
2211
2332
|
raise TypeError, "expected Vector, got #{v.class}" unless v.is_a? Vector
|
|
2212
|
-
|
|
2333
|
+
raise ErrDimensionMismatch unless v.size == size
|
|
2213
2334
|
end
|
|
2214
2335
|
case size
|
|
2215
2336
|
when 2
|
|
@@ -2249,7 +2370,7 @@ class Vector
|
|
|
2249
2370
|
|
|
2250
2371
|
#
|
|
2251
2372
|
# Returns the modulus (Pythagorean distance) of the vector.
|
|
2252
|
-
# Vector[5,8,2].r => 9.643650761
|
|
2373
|
+
# Vector[5,8,2].r # => 9.643650761
|
|
2253
2374
|
#
|
|
2254
2375
|
def magnitude
|
|
2255
2376
|
Math.sqrt(@elements.inject(0) {|v, e| v + e.abs2})
|
|
@@ -2272,7 +2393,7 @@ class Vector
|
|
|
2272
2393
|
# Returns a new vector with the same direction but with norm 1.
|
|
2273
2394
|
# v = Vector[5,8,2].normalize
|
|
2274
2395
|
# # => Vector[0.5184758473652127, 0.8295613557843402, 0.20739033894608505]
|
|
2275
|
-
# v.norm => 1.0
|
|
2396
|
+
# v.norm # => 1.0
|
|
2276
2397
|
#
|
|
2277
2398
|
def normalize
|
|
2278
2399
|
n = magnitude
|
|
@@ -2287,7 +2408,7 @@ class Vector
|
|
|
2287
2408
|
#
|
|
2288
2409
|
def angle_with(v)
|
|
2289
2410
|
raise TypeError, "Expected a Vector, got a #{v.class}" unless v.is_a?(Vector)
|
|
2290
|
-
|
|
2411
|
+
raise ErrDimensionMismatch if size != v.size
|
|
2291
2412
|
prod = magnitude * v.magnitude
|
|
2292
2413
|
raise ZeroVectorError, "Can't get angle of zero vector" if prod == 0
|
|
2293
2414
|
dot = inner_product(v)
|