rubysl-matrix 1.0.0 → 2.1.0
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/.travis.yml +3 -2
- data/lib/matrix/eigenvalue_decomposition.rb +882 -0
- data/lib/matrix/lup_decomposition.rb +218 -0
- data/lib/rubysl/matrix/matrix.rb +547 -203
- data/lib/rubysl/matrix/version.rb +1 -1
- data/rubysl-matrix.gemspec +3 -3
- metadata +10 -7
@@ -0,0 +1,218 @@
|
|
1
|
+
class Matrix
|
2
|
+
# Adapted from JAMA: http://math.nist.gov/javanumerics/jama/
|
3
|
+
|
4
|
+
#
|
5
|
+
# For an m-by-n matrix A with m >= n, the LU decomposition is an m-by-n
|
6
|
+
# unit lower triangular matrix L, an n-by-n upper triangular matrix U,
|
7
|
+
# and a m-by-m permutation matrix P so that L*U = P*A.
|
8
|
+
# If m < n, then L is m-by-m and U is m-by-n.
|
9
|
+
#
|
10
|
+
# The LUP decomposition with pivoting always exists, even if the matrix is
|
11
|
+
# singular, so the constructor will never fail. The primary use of the
|
12
|
+
# LU decomposition is in the solution of square systems of simultaneous
|
13
|
+
# linear equations. This will fail if singular? returns true.
|
14
|
+
#
|
15
|
+
|
16
|
+
class LUPDecomposition
|
17
|
+
# Returns the lower triangular factor +L+
|
18
|
+
|
19
|
+
include Matrix::ConversionHelper
|
20
|
+
|
21
|
+
def l
|
22
|
+
Matrix.build(@row_count, [@column_count, @row_count].min) do |i, j|
|
23
|
+
if (i > j)
|
24
|
+
@lu[i][j]
|
25
|
+
elsif (i == j)
|
26
|
+
1
|
27
|
+
else
|
28
|
+
0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the upper triangular factor +U+
|
34
|
+
|
35
|
+
def u
|
36
|
+
Matrix.build([@column_count, @row_count].min, @column_count) do |i, j|
|
37
|
+
if (i <= j)
|
38
|
+
@lu[i][j]
|
39
|
+
else
|
40
|
+
0
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the permutation matrix +P+
|
46
|
+
|
47
|
+
def p
|
48
|
+
rows = Array.new(@row_count){Array.new(@row_count, 0)}
|
49
|
+
@pivots.each_with_index{|p, i| rows[i][p] = 1}
|
50
|
+
Matrix.send :new, rows, @row_count
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns +L+, +U+, +P+ in an array
|
54
|
+
|
55
|
+
def to_ary
|
56
|
+
[l, u, p]
|
57
|
+
end
|
58
|
+
alias_method :to_a, :to_ary
|
59
|
+
|
60
|
+
# Returns the pivoting indices
|
61
|
+
|
62
|
+
attr_reader :pivots
|
63
|
+
|
64
|
+
# Returns +true+ if +U+, and hence +A+, is singular.
|
65
|
+
|
66
|
+
def singular? ()
|
67
|
+
@column_count.times do |j|
|
68
|
+
if (@lu[j][j] == 0)
|
69
|
+
return true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
false
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns the determinant of +A+, calculated efficiently
|
76
|
+
# from the factorization.
|
77
|
+
|
78
|
+
def det
|
79
|
+
if (@row_count != @column_count)
|
80
|
+
Matrix.Raise Matrix::ErrDimensionMismatch
|
81
|
+
end
|
82
|
+
d = @pivot_sign
|
83
|
+
@column_count.times do |j|
|
84
|
+
d *= @lu[j][j]
|
85
|
+
end
|
86
|
+
d
|
87
|
+
end
|
88
|
+
alias_method :determinant, :det
|
89
|
+
|
90
|
+
# Returns +m+ so that <tt>A*m = b</tt>,
|
91
|
+
# or equivalently so that <tt>L*U*m = P*b</tt>
|
92
|
+
# +b+ can be a Matrix or a Vector
|
93
|
+
|
94
|
+
def solve b
|
95
|
+
if (singular?)
|
96
|
+
Matrix.Raise Matrix::ErrNotRegular, "Matrix is singular."
|
97
|
+
end
|
98
|
+
if b.is_a? Matrix
|
99
|
+
if (b.row_count != @row_count)
|
100
|
+
Matrix.Raise Matrix::ErrDimensionMismatch
|
101
|
+
end
|
102
|
+
|
103
|
+
# Copy right hand side with pivoting
|
104
|
+
nx = b.column_count
|
105
|
+
m = @pivots.map{|row| b.row(row).to_a}
|
106
|
+
|
107
|
+
# Solve L*Y = P*b
|
108
|
+
@column_count.times do |k|
|
109
|
+
(k+1).upto(@column_count-1) do |i|
|
110
|
+
nx.times do |j|
|
111
|
+
m[i][j] -= m[k][j]*@lu[i][k]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
# Solve U*m = Y
|
116
|
+
(@column_count-1).downto(0) do |k|
|
117
|
+
nx.times do |j|
|
118
|
+
m[k][j] = m[k][j].quo(@lu[k][k])
|
119
|
+
end
|
120
|
+
k.times do |i|
|
121
|
+
nx.times do |j|
|
122
|
+
m[i][j] -= m[k][j]*@lu[i][k]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
Matrix.send :new, m, nx
|
127
|
+
else # same algorithm, specialized for simpler case of a vector
|
128
|
+
b = convert_to_array(b)
|
129
|
+
if (b.size != @row_count)
|
130
|
+
Matrix.Raise Matrix::ErrDimensionMismatch
|
131
|
+
end
|
132
|
+
|
133
|
+
# Copy right hand side with pivoting
|
134
|
+
m = b.values_at(*@pivots)
|
135
|
+
|
136
|
+
# Solve L*Y = P*b
|
137
|
+
@column_count.times do |k|
|
138
|
+
(k+1).upto(@column_count-1) do |i|
|
139
|
+
m[i] -= m[k]*@lu[i][k]
|
140
|
+
end
|
141
|
+
end
|
142
|
+
# Solve U*m = Y
|
143
|
+
(@column_count-1).downto(0) do |k|
|
144
|
+
m[k] = m[k].quo(@lu[k][k])
|
145
|
+
k.times do |i|
|
146
|
+
m[i] -= m[k]*@lu[i][k]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
Vector.elements(m, false)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def initialize a
|
154
|
+
raise TypeError, "Expected Matrix but got #{a.class}" unless a.is_a?(Matrix)
|
155
|
+
# Use a "left-looking", dot-product, Crout/Doolittle algorithm.
|
156
|
+
@lu = a.to_a
|
157
|
+
@row_count = a.row_count
|
158
|
+
@column_count = a.column_count
|
159
|
+
@pivots = Array.new(@row_count)
|
160
|
+
@row_count.times do |i|
|
161
|
+
@pivots[i] = i
|
162
|
+
end
|
163
|
+
@pivot_sign = 1
|
164
|
+
lu_col_j = Array.new(@row_count)
|
165
|
+
|
166
|
+
# Outer loop.
|
167
|
+
|
168
|
+
@column_count.times do |j|
|
169
|
+
|
170
|
+
# Make a copy of the j-th column to localize references.
|
171
|
+
|
172
|
+
@row_count.times do |i|
|
173
|
+
lu_col_j[i] = @lu[i][j]
|
174
|
+
end
|
175
|
+
|
176
|
+
# Apply previous transformations.
|
177
|
+
|
178
|
+
@row_count.times do |i|
|
179
|
+
lu_row_i = @lu[i]
|
180
|
+
|
181
|
+
# Most of the time is spent in the following dot product.
|
182
|
+
|
183
|
+
kmax = [i, j].min
|
184
|
+
s = 0
|
185
|
+
kmax.times do |k|
|
186
|
+
s += lu_row_i[k]*lu_col_j[k]
|
187
|
+
end
|
188
|
+
|
189
|
+
lu_row_i[j] = lu_col_j[i] -= s
|
190
|
+
end
|
191
|
+
|
192
|
+
# Find pivot and exchange if necessary.
|
193
|
+
|
194
|
+
p = j
|
195
|
+
(j+1).upto(@row_count-1) do |i|
|
196
|
+
if (lu_col_j[i].abs > lu_col_j[p].abs)
|
197
|
+
p = i
|
198
|
+
end
|
199
|
+
end
|
200
|
+
if (p != j)
|
201
|
+
@column_count.times do |k|
|
202
|
+
t = @lu[p][k]; @lu[p][k] = @lu[j][k]; @lu[j][k] = t
|
203
|
+
end
|
204
|
+
k = @pivots[p]; @pivots[p] = @pivots[j]; @pivots[j] = k
|
205
|
+
@pivot_sign = -@pivot_sign
|
206
|
+
end
|
207
|
+
|
208
|
+
# Compute multipliers.
|
209
|
+
|
210
|
+
if (j < @row_count && @lu[j][j] != 0)
|
211
|
+
(j+1).upto(@row_count-1) do |i|
|
212
|
+
@lu[i][j] = @lu[i][j].quo(@lu[j][j])
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
data/lib/rubysl/matrix/matrix.rb
CHANGED
@@ -32,79 +32,99 @@ end
|
|
32
32
|
# == Method Catalogue
|
33
33
|
#
|
34
34
|
# To create a matrix:
|
35
|
-
# *
|
36
|
-
# *
|
37
|
-
# *
|
38
|
-
# *
|
39
|
-
# *
|
40
|
-
# *
|
41
|
-
# *
|
42
|
-
# *
|
43
|
-
# *
|
44
|
-
# *
|
45
|
-
# *
|
46
|
-
# *
|
47
|
-
# *
|
35
|
+
# * Matrix[*rows]
|
36
|
+
# * Matrix.[](*rows)
|
37
|
+
# * Matrix.rows(rows, copy = true)
|
38
|
+
# * Matrix.columns(columns)
|
39
|
+
# * Matrix.build(row_count, column_count, &block)
|
40
|
+
# * Matrix.diagonal(*values)
|
41
|
+
# * Matrix.scalar(n, value)
|
42
|
+
# * Matrix.identity(n)
|
43
|
+
# * Matrix.unit(n)
|
44
|
+
# * Matrix.I(n)
|
45
|
+
# * Matrix.zero(n)
|
46
|
+
# * Matrix.row_vector(row)
|
47
|
+
# * Matrix.column_vector(column)
|
48
48
|
#
|
49
49
|
# To access Matrix elements/columns/rows/submatrices/properties:
|
50
|
-
# *
|
51
|
-
# *
|
52
|
-
# *
|
53
|
-
# *
|
54
|
-
# *
|
55
|
-
# *
|
56
|
-
# *
|
57
|
-
# *
|
58
|
-
# *
|
59
|
-
# *
|
50
|
+
# * #[](i, j)
|
51
|
+
# * #row_count (row_size)
|
52
|
+
# * #column_count (column_size)
|
53
|
+
# * #row(i)
|
54
|
+
# * #column(j)
|
55
|
+
# * #collect
|
56
|
+
# * #map
|
57
|
+
# * #each
|
58
|
+
# * #each_with_index
|
59
|
+
# * #find_index
|
60
|
+
# * #minor(*param)
|
60
61
|
#
|
61
62
|
# Properties of a matrix:
|
62
|
-
# *
|
63
|
-
# *
|
64
|
-
# *
|
65
|
-
# *
|
66
|
-
# *
|
63
|
+
# * #diagonal?
|
64
|
+
# * #empty?
|
65
|
+
# * #hermitian?
|
66
|
+
# * #lower_triangular?
|
67
|
+
# * #normal?
|
68
|
+
# * #orthogonal?
|
69
|
+
# * #permutation?
|
70
|
+
# * #real?
|
71
|
+
# * #regular?
|
72
|
+
# * #singular?
|
73
|
+
# * #square?
|
74
|
+
# * #symmetric?
|
75
|
+
# * #unitary?
|
76
|
+
# * #upper_triangular?
|
77
|
+
# * #zero?
|
67
78
|
#
|
68
79
|
# Matrix arithmetic:
|
69
|
-
# *
|
70
|
-
# *
|
71
|
-
# *
|
72
|
-
# *
|
73
|
-
# *
|
74
|
-
# *
|
75
|
-
# *
|
80
|
+
# * #*(m)
|
81
|
+
# * #+(m)
|
82
|
+
# * #-(m)
|
83
|
+
# * #/(m)
|
84
|
+
# * #inverse
|
85
|
+
# * #inv
|
86
|
+
# * #**
|
76
87
|
#
|
77
88
|
# Matrix functions:
|
78
|
-
# *
|
79
|
-
# *
|
80
|
-
# *
|
81
|
-
# *
|
82
|
-
# *
|
83
|
-
# *
|
84
|
-
# *
|
89
|
+
# * #determinant
|
90
|
+
# * #det
|
91
|
+
# * #rank
|
92
|
+
# * #round
|
93
|
+
# * #trace
|
94
|
+
# * #tr
|
95
|
+
# * #transpose
|
96
|
+
# * #t
|
97
|
+
#
|
98
|
+
# Matrix decompositions:
|
99
|
+
# * #eigen
|
100
|
+
# * #eigensystem
|
101
|
+
# * #lup
|
102
|
+
# * #lup_decomposition
|
85
103
|
#
|
86
104
|
# Complex arithmetic:
|
87
|
-
# *
|
88
|
-
# *
|
89
|
-
# *
|
90
|
-
# *
|
91
|
-
# *
|
92
|
-
# *
|
93
|
-
# *
|
105
|
+
# * conj
|
106
|
+
# * conjugate
|
107
|
+
# * imag
|
108
|
+
# * imaginary
|
109
|
+
# * real
|
110
|
+
# * rect
|
111
|
+
# * rectangular
|
94
112
|
#
|
95
113
|
# Conversion to other data types:
|
96
|
-
# *
|
97
|
-
# *
|
98
|
-
# *
|
99
|
-
# *
|
114
|
+
# * #coerce(other)
|
115
|
+
# * #row_vectors
|
116
|
+
# * #column_vectors
|
117
|
+
# * #to_a
|
100
118
|
#
|
101
119
|
# String representations:
|
102
|
-
# *
|
103
|
-
# *
|
120
|
+
# * #to_s
|
121
|
+
# * #inspect
|
104
122
|
#
|
105
123
|
class Matrix
|
106
124
|
include Enumerable
|
107
125
|
include ExceptionForMatrix
|
126
|
+
autoload :EigenvalueDecomposition, "matrix/eigenvalue_decomposition"
|
127
|
+
autoload :LUPDecomposition, "matrix/lup_decomposition"
|
108
128
|
|
109
129
|
# instance creations
|
110
130
|
private_class_method :new
|
@@ -118,7 +138,7 @@ class Matrix
|
|
118
138
|
# -1 66
|
119
139
|
#
|
120
140
|
def Matrix.[](*rows)
|
121
|
-
|
141
|
+
rows(rows, false)
|
122
142
|
end
|
123
143
|
|
124
144
|
#
|
@@ -136,7 +156,7 @@ class Matrix
|
|
136
156
|
end
|
137
157
|
size = (rows[0] || []).size
|
138
158
|
rows.each do |row|
|
139
|
-
|
159
|
+
raise ErrDimensionMismatch, "row size differs (#{row.size} should be #{size})" unless row.size == size
|
140
160
|
end
|
141
161
|
new rows, size
|
142
162
|
end
|
@@ -148,11 +168,11 @@ class Matrix
|
|
148
168
|
# 93 66
|
149
169
|
#
|
150
170
|
def Matrix.columns(columns)
|
151
|
-
|
171
|
+
rows(columns, false).transpose
|
152
172
|
end
|
153
173
|
|
154
174
|
#
|
155
|
-
# Creates a matrix of size +
|
175
|
+
# Creates a matrix of size +row_count+ x +column_count+.
|
156
176
|
# It fills the values by calling the given block,
|
157
177
|
# passing the current row and column.
|
158
178
|
# Returns an enumerator if no block is given.
|
@@ -162,17 +182,17 @@ class Matrix
|
|
162
182
|
# m = Matrix.build(3) { rand }
|
163
183
|
# => a 3x3 matrix with random elements
|
164
184
|
#
|
165
|
-
def Matrix.build(
|
166
|
-
|
167
|
-
|
168
|
-
raise ArgumentError if
|
169
|
-
return to_enum :build,
|
170
|
-
rows =
|
171
|
-
|
185
|
+
def Matrix.build(row_count, column_count = row_count)
|
186
|
+
row_count = CoercionHelper.coerce_to_int(row_count)
|
187
|
+
column_count = CoercionHelper.coerce_to_int(column_count)
|
188
|
+
raise ArgumentError if row_count < 0 || column_count < 0
|
189
|
+
return to_enum :build, row_count, column_count unless block_given?
|
190
|
+
rows = Array.new(row_count) do |i|
|
191
|
+
Array.new(column_count) do |j|
|
172
192
|
yield i, j
|
173
193
|
end
|
174
194
|
end
|
175
|
-
new rows,
|
195
|
+
new rows, column_count
|
176
196
|
end
|
177
197
|
|
178
198
|
#
|
@@ -184,7 +204,7 @@ class Matrix
|
|
184
204
|
#
|
185
205
|
def Matrix.diagonal(*values)
|
186
206
|
size = values.size
|
187
|
-
rows = (
|
207
|
+
rows = Array.new(size) {|j|
|
188
208
|
row = Array.new(size, 0)
|
189
209
|
row[j] = values[j]
|
190
210
|
row
|
@@ -200,7 +220,7 @@ class Matrix
|
|
200
220
|
# 0 5
|
201
221
|
#
|
202
222
|
def Matrix.scalar(n, value)
|
203
|
-
|
223
|
+
diagonal(*Array.new(n, value))
|
204
224
|
end
|
205
225
|
|
206
226
|
#
|
@@ -210,7 +230,7 @@ class Matrix
|
|
210
230
|
# 0 1
|
211
231
|
#
|
212
232
|
def Matrix.identity(n)
|
213
|
-
|
233
|
+
scalar(n, 1)
|
214
234
|
end
|
215
235
|
class << Matrix
|
216
236
|
alias unit identity
|
@@ -218,13 +238,14 @@ class Matrix
|
|
218
238
|
end
|
219
239
|
|
220
240
|
#
|
221
|
-
# Creates
|
241
|
+
# Creates a zero matrix.
|
222
242
|
# Matrix.zero(2)
|
223
243
|
# => 0 0
|
224
244
|
# 0 0
|
225
245
|
#
|
226
|
-
def Matrix.zero(
|
227
|
-
|
246
|
+
def Matrix.zero(row_count, column_count = row_count)
|
247
|
+
rows = Array.new(row_count){Array.new(column_count, 0)}
|
248
|
+
new rows, column_count
|
228
249
|
end
|
229
250
|
|
230
251
|
#
|
@@ -252,8 +273,8 @@ class Matrix
|
|
252
273
|
end
|
253
274
|
|
254
275
|
#
|
255
|
-
# Creates a empty matrix of +
|
256
|
-
# At least one of +
|
276
|
+
# Creates a empty matrix of +row_count+ x +column_count+.
|
277
|
+
# At least one of +row_count+ or +column_count+ must be 0.
|
257
278
|
#
|
258
279
|
# m = Matrix.empty(2, 0)
|
259
280
|
# m == Matrix[ [], [] ]
|
@@ -264,26 +285,26 @@ class Matrix
|
|
264
285
|
# m * n
|
265
286
|
# => Matrix[[0, 0, 0], [0, 0, 0]]
|
266
287
|
#
|
267
|
-
def Matrix.empty(
|
268
|
-
|
269
|
-
|
288
|
+
def Matrix.empty(row_count = 0, column_count = 0)
|
289
|
+
raise ArgumentError, "One size must be 0" if column_count != 0 && row_count != 0
|
290
|
+
raise ArgumentError, "Negative size" if column_count < 0 || row_count < 0
|
270
291
|
|
271
|
-
new([[]]*
|
292
|
+
new([[]]*row_count, column_count)
|
272
293
|
end
|
273
294
|
|
274
295
|
#
|
275
296
|
# Matrix.new is private; use Matrix.rows, columns, [], etc... to create.
|
276
297
|
#
|
277
|
-
def initialize(rows,
|
298
|
+
def initialize(rows, column_count = rows[0].size)
|
278
299
|
# No checking is done at this point. rows must be an Array of Arrays.
|
279
|
-
#
|
300
|
+
# column_count must be the size of the first row, if there is one,
|
280
301
|
# otherwise it *must* be specified and can be any integer >= 0
|
281
302
|
@rows = rows
|
282
|
-
@
|
303
|
+
@column_count = column_count
|
283
304
|
end
|
284
305
|
|
285
|
-
def new_matrix(rows,
|
286
|
-
|
306
|
+
def new_matrix(rows, column_count = rows[0].size) # :nodoc:
|
307
|
+
self.class.send(:new, rows, column_count) # bypass privacy of Matrix.new
|
287
308
|
end
|
288
309
|
private :new_matrix
|
289
310
|
|
@@ -306,14 +327,16 @@ class Matrix
|
|
306
327
|
#
|
307
328
|
# Returns the number of rows.
|
308
329
|
#
|
309
|
-
def
|
330
|
+
def row_count
|
310
331
|
@rows.size
|
311
332
|
end
|
312
333
|
|
334
|
+
alias_method :row_size, :row_count
|
313
335
|
#
|
314
336
|
# Returns the number of columns.
|
315
337
|
#
|
316
|
-
attr_reader :
|
338
|
+
attr_reader :column_count
|
339
|
+
alias_method :column_size, :column_count
|
317
340
|
|
318
341
|
#
|
319
342
|
# Returns row vector number +i+ of the matrix as a Vector (starting at 0 like
|
@@ -335,14 +358,14 @@ class Matrix
|
|
335
358
|
#
|
336
359
|
def column(j) # :yield: e
|
337
360
|
if block_given?
|
338
|
-
return self if j >=
|
339
|
-
|
361
|
+
return self if j >= column_count || j < -column_count
|
362
|
+
row_count.times do |i|
|
340
363
|
yield @rows[i][j]
|
341
364
|
end
|
342
365
|
self
|
343
366
|
else
|
344
|
-
return nil if j >=
|
345
|
-
col = (
|
367
|
+
return nil if j >= column_count || j < -column_count
|
368
|
+
col = Array.new(row_count) {|i|
|
346
369
|
@rows[i][j]
|
347
370
|
}
|
348
371
|
Vector.elements(col, false)
|
@@ -359,46 +382,167 @@ class Matrix
|
|
359
382
|
def collect(&block) # :yield: e
|
360
383
|
return to_enum(:collect) unless block_given?
|
361
384
|
rows = @rows.collect{|row| row.collect(&block)}
|
362
|
-
new_matrix rows,
|
385
|
+
new_matrix rows, column_count
|
363
386
|
end
|
364
387
|
alias map collect
|
365
388
|
|
366
389
|
#
|
367
390
|
# Yields all elements of the matrix, starting with those of the first row,
|
368
|
-
# or returns an Enumerator is no block given
|
391
|
+
# or returns an Enumerator is no block given.
|
392
|
+
# Elements can be restricted by passing an argument:
|
393
|
+
# * :all (default): yields all elements
|
394
|
+
# * :diagonal: yields only elements on the diagonal
|
395
|
+
# * :off_diagonal: yields all elements except on the diagonal
|
396
|
+
# * :lower: yields only elements on or below the diagonal
|
397
|
+
# * :strict_lower: yields only elements below the diagonal
|
398
|
+
# * :strict_upper: yields only elements above the diagonal
|
399
|
+
# * :upper: yields only elements on or above the diagonal
|
400
|
+
#
|
369
401
|
# Matrix[ [1,2], [3,4] ].each { |e| puts e }
|
370
402
|
# # => prints the numbers 1 to 4
|
371
|
-
#
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
403
|
+
# Matrix[ [1,2], [3,4] ].each(:strict_lower).to_a # => [3]
|
404
|
+
#
|
405
|
+
def each(which = :all) # :yield: e
|
406
|
+
return to_enum :each, which unless block_given?
|
407
|
+
last = column_count - 1
|
408
|
+
case which
|
409
|
+
when :all
|
410
|
+
block = Proc.new
|
411
|
+
@rows.each do |row|
|
412
|
+
row.each(&block)
|
413
|
+
end
|
414
|
+
when :diagonal
|
415
|
+
@rows.each_with_index do |row, row_index|
|
416
|
+
yield row.fetch(row_index){return self}
|
417
|
+
end
|
418
|
+
when :off_diagonal
|
419
|
+
@rows.each_with_index do |row, row_index|
|
420
|
+
column_count.times do |col_index|
|
421
|
+
yield row[col_index] unless row_index == col_index
|
422
|
+
end
|
423
|
+
end
|
424
|
+
when :lower
|
425
|
+
@rows.each_with_index do |row, row_index|
|
426
|
+
0.upto([row_index, last].min) do |col_index|
|
427
|
+
yield row[col_index]
|
428
|
+
end
|
429
|
+
end
|
430
|
+
when :strict_lower
|
431
|
+
@rows.each_with_index do |row, row_index|
|
432
|
+
[row_index, column_count].min.times do |col_index|
|
433
|
+
yield row[col_index]
|
434
|
+
end
|
435
|
+
end
|
436
|
+
when :strict_upper
|
437
|
+
@rows.each_with_index do |row, row_index|
|
438
|
+
(row_index+1).upto(last) do |col_index|
|
439
|
+
yield row[col_index]
|
440
|
+
end
|
441
|
+
end
|
442
|
+
when :upper
|
443
|
+
@rows.each_with_index do |row, row_index|
|
444
|
+
row_index.upto(last) do |col_index|
|
445
|
+
yield row[col_index]
|
446
|
+
end
|
447
|
+
end
|
448
|
+
else
|
449
|
+
raise ArgumentError, "expected #{which.inspect} to be one of :all, :diagonal, :off_diagonal, :lower, :strict_lower, :strict_upper or :upper"
|
376
450
|
end
|
377
451
|
self
|
378
452
|
end
|
379
453
|
|
380
454
|
#
|
381
|
-
#
|
382
|
-
#
|
383
|
-
# or returns an Enumerator is no block given
|
455
|
+
# Same as #each, but the row index and column index in addition to the element
|
456
|
+
#
|
384
457
|
# Matrix[ [1,2], [3,4] ].each_with_index do |e, row, col|
|
385
458
|
# puts "#{e} at #{row}, #{col}"
|
386
459
|
# end
|
387
|
-
# # =>
|
388
|
-
# #
|
389
|
-
# #
|
390
|
-
# #
|
391
|
-
#
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
460
|
+
# # => Prints:
|
461
|
+
# # 1 at 0, 0
|
462
|
+
# # 2 at 0, 1
|
463
|
+
# # 3 at 1, 0
|
464
|
+
# # 4 at 1, 1
|
465
|
+
#
|
466
|
+
def each_with_index(which = :all) # :yield: e, row, column
|
467
|
+
return to_enum :each_with_index, which unless block_given?
|
468
|
+
last = column_count - 1
|
469
|
+
case which
|
470
|
+
when :all
|
471
|
+
@rows.each_with_index do |row, row_index|
|
472
|
+
row.each_with_index do |e, col_index|
|
473
|
+
yield e, row_index, col_index
|
474
|
+
end
|
397
475
|
end
|
476
|
+
when :diagonal
|
477
|
+
@rows.each_with_index do |row, row_index|
|
478
|
+
yield row.fetch(row_index){return self}, row_index, row_index
|
479
|
+
end
|
480
|
+
when :off_diagonal
|
481
|
+
@rows.each_with_index do |row, row_index|
|
482
|
+
column_count.times do |col_index|
|
483
|
+
yield row[col_index], row_index, col_index unless row_index == col_index
|
484
|
+
end
|
485
|
+
end
|
486
|
+
when :lower
|
487
|
+
@rows.each_with_index do |row, row_index|
|
488
|
+
0.upto([row_index, last].min) do |col_index|
|
489
|
+
yield row[col_index], row_index, col_index
|
490
|
+
end
|
491
|
+
end
|
492
|
+
when :strict_lower
|
493
|
+
@rows.each_with_index do |row, row_index|
|
494
|
+
[row_index, column_count].min.times do |col_index|
|
495
|
+
yield row[col_index], row_index, col_index
|
496
|
+
end
|
497
|
+
end
|
498
|
+
when :strict_upper
|
499
|
+
@rows.each_with_index do |row, row_index|
|
500
|
+
(row_index+1).upto(last) do |col_index|
|
501
|
+
yield row[col_index], row_index, col_index
|
502
|
+
end
|
503
|
+
end
|
504
|
+
when :upper
|
505
|
+
@rows.each_with_index do |row, row_index|
|
506
|
+
row_index.upto(last) do |col_index|
|
507
|
+
yield row[col_index], row_index, col_index
|
508
|
+
end
|
509
|
+
end
|
510
|
+
else
|
511
|
+
raise ArgumentError, "expected #{which.inspect} to be one of :all, :diagonal, :off_diagonal, :lower, :strict_lower, :strict_upper or :upper"
|
398
512
|
end
|
399
513
|
self
|
400
514
|
end
|
401
515
|
|
516
|
+
SELECTORS = {all: true, diagonal: true, off_diagonal: true, lower: true, strict_lower: true, strict_upper: true, upper: true}.freeze
|
517
|
+
#
|
518
|
+
# :call-seq:
|
519
|
+
# index(value, selector = :all) -> [row, column]
|
520
|
+
# index(selector = :all){ block } -> [row, column]
|
521
|
+
# index(selector = :all) -> an_enumerator
|
522
|
+
#
|
523
|
+
# The index method is specialized to return the index as [row, column]
|
524
|
+
# It also accepts an optional +selector+ argument, see #each for details.
|
525
|
+
#
|
526
|
+
# Matrix[ [1,2], [3,4] ].index(&:even?) # => [0, 1]
|
527
|
+
# Matrix[ [1,1], [1,1] ].index(1, :strict_lower) # => [1, 0]
|
528
|
+
#
|
529
|
+
def index(*args)
|
530
|
+
raise ArgumentError, "wrong number of arguments(#{args.size} for 0-2)" if args.size > 2
|
531
|
+
which = (args.size == 2 || SELECTORS.include?(args.last)) ? args.pop : :all
|
532
|
+
return to_enum :find_index, which, *args unless block_given? || args.size == 1
|
533
|
+
if args.size == 1
|
534
|
+
value = args.first
|
535
|
+
each_with_index(which) do |e, row_index, col_index|
|
536
|
+
return row_index, col_index if e == value
|
537
|
+
end
|
538
|
+
else
|
539
|
+
each_with_index(which) do |e, row_index, col_index|
|
540
|
+
return row_index, col_index if yield e
|
541
|
+
end
|
542
|
+
end
|
543
|
+
nil
|
544
|
+
end
|
545
|
+
alias_method :find_index, :index
|
402
546
|
#
|
403
547
|
# Returns a section of the matrix. The parameters are either:
|
404
548
|
# * start_row, nrows, start_col, ncols; OR
|
@@ -410,51 +554,136 @@ class Matrix
|
|
410
554
|
#
|
411
555
|
# Like Array#[], negative indices count backward from the end of the
|
412
556
|
# row or column (-1 is the last element). Returns nil if the starting
|
413
|
-
# row or column is greater than
|
557
|
+
# row or column is greater than row_count or column_count respectively.
|
414
558
|
#
|
415
559
|
def minor(*param)
|
416
560
|
case param.size
|
417
561
|
when 2
|
418
562
|
row_range, col_range = param
|
419
563
|
from_row = row_range.first
|
420
|
-
from_row +=
|
564
|
+
from_row += row_count if from_row < 0
|
421
565
|
to_row = row_range.end
|
422
|
-
to_row +=
|
566
|
+
to_row += row_count if to_row < 0
|
423
567
|
to_row += 1 unless row_range.exclude_end?
|
424
568
|
size_row = to_row - from_row
|
425
569
|
|
426
570
|
from_col = col_range.first
|
427
|
-
from_col +=
|
571
|
+
from_col += column_count if from_col < 0
|
428
572
|
to_col = col_range.end
|
429
|
-
to_col +=
|
573
|
+
to_col += column_count if to_col < 0
|
430
574
|
to_col += 1 unless col_range.exclude_end?
|
431
575
|
size_col = to_col - from_col
|
432
576
|
when 4
|
433
577
|
from_row, size_row, from_col, size_col = param
|
434
578
|
return nil if size_row < 0 || size_col < 0
|
435
|
-
from_row +=
|
436
|
-
from_col +=
|
579
|
+
from_row += row_count if from_row < 0
|
580
|
+
from_col += column_count if from_col < 0
|
437
581
|
else
|
438
|
-
|
582
|
+
raise ArgumentError, param.inspect
|
439
583
|
end
|
440
584
|
|
441
|
-
return nil if from_row >
|
585
|
+
return nil if from_row > row_count || from_col > column_count || from_row < 0 || from_col < 0
|
442
586
|
rows = @rows[from_row, size_row].collect{|row|
|
443
587
|
row[from_col, size_col]
|
444
588
|
}
|
445
|
-
new_matrix rows,
|
589
|
+
new_matrix rows, [column_count - from_col, size_col].min
|
446
590
|
end
|
447
591
|
|
448
592
|
#--
|
449
593
|
# TESTING -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
450
594
|
#++
|
451
595
|
|
596
|
+
#
|
597
|
+
# Returns +true+ is this is a diagonal matrix.
|
598
|
+
# Raises an error if matrix is not square.
|
599
|
+
#
|
600
|
+
def diagonal?
|
601
|
+
Matrix.Raise ErrDimensionMismatch unless square?
|
602
|
+
each(:off_diagonal).all?(&:zero?)
|
603
|
+
end
|
604
|
+
|
452
605
|
#
|
453
606
|
# Returns +true+ if this is an empty matrix, i.e. if the number of rows
|
454
607
|
# or the number of columns is 0.
|
455
608
|
#
|
456
609
|
def empty?
|
457
|
-
|
610
|
+
column_count == 0 || row_count == 0
|
611
|
+
end
|
612
|
+
|
613
|
+
#
|
614
|
+
# Returns +true+ is this is an hermitian matrix.
|
615
|
+
# Raises an error if matrix is not square.
|
616
|
+
#
|
617
|
+
def hermitian?
|
618
|
+
Matrix.Raise ErrDimensionMismatch unless square?
|
619
|
+
each_with_index(:upper).all? do |e, row, col|
|
620
|
+
e == rows[col][row].conj
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
#
|
625
|
+
# Returns +true+ is this is a lower triangular matrix.
|
626
|
+
#
|
627
|
+
def lower_triangular?
|
628
|
+
each(:strict_upper).all?(&:zero?)
|
629
|
+
end
|
630
|
+
|
631
|
+
#
|
632
|
+
# Returns +true+ is this is a normal matrix.
|
633
|
+
# Raises an error if matrix is not square.
|
634
|
+
#
|
635
|
+
def normal?
|
636
|
+
Matrix.Raise ErrDimensionMismatch unless square?
|
637
|
+
rows.each_with_index do |row_i, i|
|
638
|
+
rows.each_with_index do |row_j, j|
|
639
|
+
s = 0
|
640
|
+
rows.each_with_index do |row_k, k|
|
641
|
+
s += row_i[k] * row_j[k].conj - row_k[i].conj * row_k[j]
|
642
|
+
end
|
643
|
+
return false unless s == 0
|
644
|
+
end
|
645
|
+
end
|
646
|
+
true
|
647
|
+
end
|
648
|
+
|
649
|
+
#
|
650
|
+
# Returns +true+ is this is an orthogonal matrix
|
651
|
+
# Raises an error if matrix is not square.
|
652
|
+
#
|
653
|
+
def orthogonal?
|
654
|
+
Matrix.Raise ErrDimensionMismatch unless square?
|
655
|
+
rows.each_with_index do |row, i|
|
656
|
+
column_count.times do |j|
|
657
|
+
s = 0
|
658
|
+
row_count.times do |k|
|
659
|
+
s += row[k] * rows[k][j]
|
660
|
+
end
|
661
|
+
return false unless s == (i == j ? 1 : 0)
|
662
|
+
end
|
663
|
+
end
|
664
|
+
true
|
665
|
+
end
|
666
|
+
|
667
|
+
#
|
668
|
+
# Returns +true+ is this is a permutation matrix
|
669
|
+
# Raises an error if matrix is not square.
|
670
|
+
#
|
671
|
+
def permutation?
|
672
|
+
Matrix.Raise ErrDimensionMismatch unless square?
|
673
|
+
cols = Array.new(column_count)
|
674
|
+
rows.each_with_index do |row, i|
|
675
|
+
found = false
|
676
|
+
row.each_with_index do |e, j|
|
677
|
+
if e == 1
|
678
|
+
return false if found || cols[j]
|
679
|
+
found = cols[j] = true
|
680
|
+
elsif e != 0
|
681
|
+
return false
|
682
|
+
end
|
683
|
+
end
|
684
|
+
return false unless found
|
685
|
+
end
|
686
|
+
true
|
458
687
|
end
|
459
688
|
|
460
689
|
#
|
@@ -482,7 +711,51 @@ class Matrix
|
|
482
711
|
# Returns +true+ is this is a square matrix.
|
483
712
|
#
|
484
713
|
def square?
|
485
|
-
|
714
|
+
column_count == row_count
|
715
|
+
end
|
716
|
+
|
717
|
+
#
|
718
|
+
# Returns +true+ is this is a symmetric matrix.
|
719
|
+
# Raises an error if matrix is not square.
|
720
|
+
#
|
721
|
+
def symmetric?
|
722
|
+
Matrix.Raise ErrDimensionMismatch unless square?
|
723
|
+
each_with_index(:strict_upper) do |e, row, col|
|
724
|
+
return false if e != rows[col][row]
|
725
|
+
end
|
726
|
+
true
|
727
|
+
end
|
728
|
+
|
729
|
+
#
|
730
|
+
# Returns +true+ is this is a unitary matrix
|
731
|
+
# Raises an error if matrix is not square.
|
732
|
+
#
|
733
|
+
def unitary?
|
734
|
+
Matrix.Raise ErrDimensionMismatch unless square?
|
735
|
+
rows.each_with_index do |row, i|
|
736
|
+
column_count.times do |j|
|
737
|
+
s = 0
|
738
|
+
row_count.times do |k|
|
739
|
+
s += row[k].conj * rows[k][j]
|
740
|
+
end
|
741
|
+
return false unless s == (i == j ? 1 : 0)
|
742
|
+
end
|
743
|
+
end
|
744
|
+
true
|
745
|
+
end
|
746
|
+
|
747
|
+
#
|
748
|
+
# Returns +true+ is this is an upper triangular matrix.
|
749
|
+
#
|
750
|
+
def upper_triangular?
|
751
|
+
each(:strict_lower).all?(&:zero?)
|
752
|
+
end
|
753
|
+
|
754
|
+
#
|
755
|
+
# Returns +true+ is this is a matrix with only zero elements
|
756
|
+
#
|
757
|
+
def zero?
|
758
|
+
all?(&:zero?)
|
486
759
|
end
|
487
760
|
|
488
761
|
#--
|
@@ -493,12 +766,14 @@ class Matrix
|
|
493
766
|
# Returns +true+ if and only if the two matrices contain equal elements.
|
494
767
|
#
|
495
768
|
def ==(other)
|
496
|
-
return false unless Matrix === other
|
769
|
+
return false unless Matrix === other &&
|
770
|
+
column_count == other.column_count # necessary for empty matrices
|
497
771
|
rows == other.rows
|
498
772
|
end
|
499
773
|
|
500
774
|
def eql?(other)
|
501
|
-
return false unless Matrix === other
|
775
|
+
return false unless Matrix === other &&
|
776
|
+
column_count == other.column_count # necessary for empty matrices
|
502
777
|
rows.eql? other.rows
|
503
778
|
end
|
504
779
|
|
@@ -508,7 +783,7 @@ class Matrix
|
|
508
783
|
# There should be no good reason to do this since Matrices are immutable.
|
509
784
|
#
|
510
785
|
def clone
|
511
|
-
new_matrix @rows.map(&:dup),
|
786
|
+
new_matrix @rows.map(&:dup), column_count
|
512
787
|
end
|
513
788
|
|
514
789
|
#
|
@@ -532,26 +807,24 @@ class Matrix
|
|
532
807
|
case(m)
|
533
808
|
when Numeric
|
534
809
|
rows = @rows.collect {|row|
|
535
|
-
row.collect {|e|
|
536
|
-
e * m
|
537
|
-
}
|
810
|
+
row.collect {|e| e * m }
|
538
811
|
}
|
539
|
-
return new_matrix rows,
|
812
|
+
return new_matrix rows, column_count
|
540
813
|
when Vector
|
541
|
-
m =
|
814
|
+
m = self.class.column_vector(m)
|
542
815
|
r = self * m
|
543
816
|
return r.column(0)
|
544
817
|
when Matrix
|
545
|
-
Matrix.Raise ErrDimensionMismatch if
|
818
|
+
Matrix.Raise ErrDimensionMismatch if column_count != m.row_count
|
546
819
|
|
547
|
-
rows = (
|
548
|
-
(
|
549
|
-
(0 ...
|
820
|
+
rows = Array.new(row_count) {|i|
|
821
|
+
Array.new(m.column_count) {|j|
|
822
|
+
(0 ... column_count).inject(0) do |vij, k|
|
550
823
|
vij + self[i, k] * m[k, j]
|
551
824
|
end
|
552
825
|
}
|
553
826
|
}
|
554
|
-
return new_matrix rows, m.
|
827
|
+
return new_matrix rows, m.column_count
|
555
828
|
else
|
556
829
|
return apply_through_coercion(m, __method__)
|
557
830
|
end
|
@@ -568,20 +841,20 @@ class Matrix
|
|
568
841
|
when Numeric
|
569
842
|
Matrix.Raise ErrOperationNotDefined, "+", self.class, m.class
|
570
843
|
when Vector
|
571
|
-
m =
|
844
|
+
m = self.class.column_vector(m)
|
572
845
|
when Matrix
|
573
846
|
else
|
574
847
|
return apply_through_coercion(m, __method__)
|
575
848
|
end
|
576
849
|
|
577
|
-
Matrix.Raise ErrDimensionMismatch unless
|
850
|
+
Matrix.Raise ErrDimensionMismatch unless row_count == m.row_count and column_count == m.column_count
|
578
851
|
|
579
|
-
rows = (
|
580
|
-
(
|
852
|
+
rows = Array.new(row_count) {|i|
|
853
|
+
Array.new(column_count) {|j|
|
581
854
|
self[i, j] + m[i, j]
|
582
855
|
}
|
583
856
|
}
|
584
|
-
new_matrix rows,
|
857
|
+
new_matrix rows, column_count
|
585
858
|
end
|
586
859
|
|
587
860
|
#
|
@@ -595,20 +868,20 @@ class Matrix
|
|
595
868
|
when Numeric
|
596
869
|
Matrix.Raise ErrOperationNotDefined, "-", self.class, m.class
|
597
870
|
when Vector
|
598
|
-
m =
|
871
|
+
m = self.class.column_vector(m)
|
599
872
|
when Matrix
|
600
873
|
else
|
601
874
|
return apply_through_coercion(m, __method__)
|
602
875
|
end
|
603
876
|
|
604
|
-
Matrix.Raise ErrDimensionMismatch unless
|
877
|
+
Matrix.Raise ErrDimensionMismatch unless row_count == m.row_count and column_count == m.column_count
|
605
878
|
|
606
|
-
rows = (
|
607
|
-
(
|
879
|
+
rows = Array.new(row_count) {|i|
|
880
|
+
Array.new(column_count) {|j|
|
608
881
|
self[i, j] - m[i, j]
|
609
882
|
}
|
610
883
|
}
|
611
|
-
new_matrix rows,
|
884
|
+
new_matrix rows, column_count
|
612
885
|
end
|
613
886
|
|
614
887
|
#
|
@@ -621,11 +894,9 @@ class Matrix
|
|
621
894
|
case other
|
622
895
|
when Numeric
|
623
896
|
rows = @rows.collect {|row|
|
624
|
-
row.collect {|e|
|
625
|
-
e / other
|
626
|
-
}
|
897
|
+
row.collect {|e| e / other }
|
627
898
|
}
|
628
|
-
return new_matrix rows,
|
899
|
+
return new_matrix rows, column_count
|
629
900
|
when Matrix
|
630
901
|
return self * other.inverse
|
631
902
|
else
|
@@ -641,12 +912,12 @@ class Matrix
|
|
641
912
|
#
|
642
913
|
def inverse
|
643
914
|
Matrix.Raise ErrDimensionMismatch unless square?
|
644
|
-
|
915
|
+
self.class.I(row_count).send(:inverse_from, self)
|
645
916
|
end
|
646
917
|
alias inv inverse
|
647
918
|
|
648
919
|
def inverse_from(src) # :nodoc:
|
649
|
-
last =
|
920
|
+
last = row_count - 1
|
650
921
|
a = src.to_a
|
651
922
|
|
652
923
|
0.upto(last) do |k|
|
@@ -691,8 +962,10 @@ class Matrix
|
|
691
962
|
private :inverse_from
|
692
963
|
|
693
964
|
#
|
694
|
-
# Matrix exponentiation.
|
965
|
+
# Matrix exponentiation.
|
695
966
|
# Equivalent to multiplying the matrix by itself N times.
|
967
|
+
# Non integer exponents will be handled by diagonalizing the matrix.
|
968
|
+
#
|
696
969
|
# Matrix[[7,6], [3,9]] ** 2
|
697
970
|
# => 67 96
|
698
971
|
# 48 99
|
@@ -703,7 +976,7 @@ class Matrix
|
|
703
976
|
x = self
|
704
977
|
if other <= 0
|
705
978
|
x = self.inverse
|
706
|
-
return
|
979
|
+
return self.class.identity(self.column_count) if other == 0
|
707
980
|
other = -other
|
708
981
|
end
|
709
982
|
z = nil
|
@@ -712,6 +985,9 @@ class Matrix
|
|
712
985
|
return z if (other >>= 1).zero?
|
713
986
|
x *= x
|
714
987
|
end
|
988
|
+
when Numeric
|
989
|
+
v, d, v_inv = eigensystem
|
990
|
+
v * self.class.diagonal(*d.each(:diagonal).map{|e| e ** other}) * v_inv
|
715
991
|
else
|
716
992
|
Matrix.Raise ErrOperationNotDefined, "**", self.class, other.class
|
717
993
|
end
|
@@ -734,7 +1010,7 @@ class Matrix
|
|
734
1010
|
def determinant
|
735
1011
|
Matrix.Raise ErrDimensionMismatch unless square?
|
736
1012
|
m = @rows
|
737
|
-
case
|
1013
|
+
case row_count
|
738
1014
|
# Up to 4x4, give result using Laplacian expansion by minors.
|
739
1015
|
# This will typically be faster, as well as giving good results
|
740
1016
|
# in case of Floats
|
@@ -783,7 +1059,7 @@ class Matrix
|
|
783
1059
|
# intermediate results with better precision.
|
784
1060
|
#
|
785
1061
|
def determinant_bareiss
|
786
|
-
size =
|
1062
|
+
size = row_count
|
787
1063
|
last = size - 1
|
788
1064
|
a = to_a
|
789
1065
|
no_pivot = Proc.new{ return 0 }
|
@@ -815,7 +1091,7 @@ class Matrix
|
|
815
1091
|
#
|
816
1092
|
def determinant_e
|
817
1093
|
warn "#{caller(1)[0]}: warning: Matrix#determinant_e is deprecated; use #determinant"
|
818
|
-
|
1094
|
+
determinant
|
819
1095
|
end
|
820
1096
|
alias det_e determinant_e
|
821
1097
|
|
@@ -832,9 +1108,8 @@ class Matrix
|
|
832
1108
|
# We currently use Bareiss' multistep integer-preserving gaussian elimination
|
833
1109
|
# (see comments on determinant)
|
834
1110
|
a = to_a
|
835
|
-
last_column =
|
836
|
-
last_row =
|
837
|
-
rank = 0
|
1111
|
+
last_column = column_count - 1
|
1112
|
+
last_row = row_count - 1
|
838
1113
|
pivot_row = 0
|
839
1114
|
previous_pivot = 1
|
840
1115
|
0.upto(last_column) do |k|
|
@@ -865,6 +1140,12 @@ class Matrix
|
|
865
1140
|
rank
|
866
1141
|
end
|
867
1142
|
|
1143
|
+
# Returns a matrix with entries rounded to the given precision
|
1144
|
+
# (see Float#round)
|
1145
|
+
#
|
1146
|
+
def round(ndigits=0)
|
1147
|
+
map{|e| e.round(ndigits)}
|
1148
|
+
end
|
868
1149
|
|
869
1150
|
#
|
870
1151
|
# Returns the trace (sum of diagonal elements) of the matrix.
|
@@ -873,7 +1154,7 @@ class Matrix
|
|
873
1154
|
#
|
874
1155
|
def trace
|
875
1156
|
Matrix.Raise ErrDimensionMismatch unless square?
|
876
|
-
(0...
|
1157
|
+
(0...column_count).inject(0) do |tr, i|
|
877
1158
|
tr + @rows[i][i]
|
878
1159
|
end
|
879
1160
|
end
|
@@ -890,11 +1171,43 @@ class Matrix
|
|
890
1171
|
# 2 4 6
|
891
1172
|
#
|
892
1173
|
def transpose
|
893
|
-
return
|
894
|
-
new_matrix @rows.transpose,
|
1174
|
+
return self.class.empty(column_count, 0) if row_count.zero?
|
1175
|
+
new_matrix @rows.transpose, row_count
|
895
1176
|
end
|
896
1177
|
alias t transpose
|
897
1178
|
|
1179
|
+
#--
|
1180
|
+
# DECOMPOSITIONS -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
1181
|
+
#++
|
1182
|
+
|
1183
|
+
#
|
1184
|
+
# Returns the Eigensystem of the matrix; see +EigenvalueDecomposition+.
|
1185
|
+
# m = Matrix[[1, 2], [3, 4]]
|
1186
|
+
# v, d, v_inv = m.eigensystem
|
1187
|
+
# d.diagonal? # => true
|
1188
|
+
# v.inv == v_inv # => true
|
1189
|
+
# (v * d * v_inv).round(5) == m # => true
|
1190
|
+
#
|
1191
|
+
def eigensystem
|
1192
|
+
EigenvalueDecomposition.new(self)
|
1193
|
+
end
|
1194
|
+
alias eigen eigensystem
|
1195
|
+
|
1196
|
+
#
|
1197
|
+
# Returns the LUP decomposition of the matrix; see +LUPDecomposition+.
|
1198
|
+
# a = Matrix[[1, 2], [3, 4]]
|
1199
|
+
# l, u, p = a.lup
|
1200
|
+
# l.lower_triangular? # => true
|
1201
|
+
# u.upper_triangular? # => true
|
1202
|
+
# p.permutation? # => true
|
1203
|
+
# l * u == p * a # => true
|
1204
|
+
# a.lup.solve([2, 5]) # => Vector[(1/1), (1/2)]
|
1205
|
+
#
|
1206
|
+
def lup
|
1207
|
+
LUPDecomposition.new(self)
|
1208
|
+
end
|
1209
|
+
alias lup_decomposition lup
|
1210
|
+
|
898
1211
|
#--
|
899
1212
|
# COMPLEX ARITHMETIC -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
900
1213
|
#++
|
@@ -975,7 +1288,7 @@ class Matrix
|
|
975
1288
|
# Returns an array of the row vectors of the matrix. See Vector.
|
976
1289
|
#
|
977
1290
|
def row_vectors
|
978
|
-
(
|
1291
|
+
Array.new(row_count) {|i|
|
979
1292
|
row(i)
|
980
1293
|
}
|
981
1294
|
end
|
@@ -984,7 +1297,7 @@ class Matrix
|
|
984
1297
|
# Returns an array of the column vectors of the matrix. See Vector.
|
985
1298
|
#
|
986
1299
|
def column_vectors
|
987
|
-
(
|
1300
|
+
Array.new(column_count) {|i|
|
988
1301
|
column(i)
|
989
1302
|
}
|
990
1303
|
end
|
@@ -993,7 +1306,7 @@ class Matrix
|
|
993
1306
|
# Returns an array of arrays that describe the rows of the matrix.
|
994
1307
|
#
|
995
1308
|
def to_a
|
996
|
-
@rows.collect
|
1309
|
+
@rows.collect(&:dup)
|
997
1310
|
end
|
998
1311
|
|
999
1312
|
def elements_to_f
|
@@ -1020,9 +1333,9 @@ class Matrix
|
|
1020
1333
|
#
|
1021
1334
|
def to_s
|
1022
1335
|
if empty?
|
1023
|
-
"
|
1336
|
+
"#{self.class}.empty(#{row_count}, #{column_count})"
|
1024
1337
|
else
|
1025
|
-
"
|
1338
|
+
"#{self.class}[" + @rows.collect{|row|
|
1026
1339
|
"[" + row.collect{|e| e.to_s}.join(", ") + "]"
|
1027
1340
|
}.join(", ")+"]"
|
1028
1341
|
end
|
@@ -1033,9 +1346,9 @@ class Matrix
|
|
1033
1346
|
#
|
1034
1347
|
def inspect
|
1035
1348
|
if empty?
|
1036
|
-
"
|
1349
|
+
"#{self.class}.empty(#{row_count}, #{column_count})"
|
1037
1350
|
else
|
1038
|
-
"
|
1351
|
+
"#{self.class}#{@rows.inspect}"
|
1039
1352
|
end
|
1040
1353
|
end
|
1041
1354
|
|
@@ -1189,37 +1502,41 @@ end
|
|
1189
1502
|
# == Method Catalogue
|
1190
1503
|
#
|
1191
1504
|
# To create a Vector:
|
1192
|
-
# *
|
1193
|
-
# *
|
1505
|
+
# * Vector.[](*array)
|
1506
|
+
# * Vector.elements(array, copy = true)
|
1194
1507
|
#
|
1195
1508
|
# To access elements:
|
1196
|
-
# *
|
1509
|
+
# * #[](i)
|
1197
1510
|
#
|
1198
1511
|
# To enumerate the elements:
|
1199
|
-
# *
|
1200
|
-
# *
|
1512
|
+
# * #each2(v)
|
1513
|
+
# * #collect2(v)
|
1201
1514
|
#
|
1202
1515
|
# Vector arithmetic:
|
1203
|
-
# *
|
1204
|
-
# *
|
1205
|
-
# *
|
1516
|
+
# * #*(x) "is matrix or number"
|
1517
|
+
# * #+(v)
|
1518
|
+
# * #-(v)
|
1206
1519
|
#
|
1207
1520
|
# Vector functions:
|
1208
|
-
# *
|
1209
|
-
# *
|
1210
|
-
# *
|
1211
|
-
# *
|
1212
|
-
# *
|
1213
|
-
# *
|
1521
|
+
# * #inner_product(v)
|
1522
|
+
# * #cross_product(v)
|
1523
|
+
# * #collect
|
1524
|
+
# * #magnitude
|
1525
|
+
# * #map
|
1526
|
+
# * #map2(v)
|
1527
|
+
# * #norm
|
1528
|
+
# * #normalize
|
1529
|
+
# * #r
|
1530
|
+
# * #size
|
1214
1531
|
#
|
1215
1532
|
# Conversion to other data types:
|
1216
|
-
# *
|
1217
|
-
# *
|
1218
|
-
# *
|
1533
|
+
# * #covector
|
1534
|
+
# * #to_a
|
1535
|
+
# * #coerce(other)
|
1219
1536
|
#
|
1220
1537
|
# String representations:
|
1221
|
-
# *
|
1222
|
-
# *
|
1538
|
+
# * #to_s
|
1539
|
+
# * #inspect
|
1223
1540
|
#
|
1224
1541
|
class Vector
|
1225
1542
|
include ExceptionForMatrix
|
@@ -1237,7 +1554,7 @@ class Vector
|
|
1237
1554
|
# Vector[7, 4, ...]
|
1238
1555
|
#
|
1239
1556
|
def Vector.[](*array)
|
1240
|
-
new convert_to_array(array,
|
1557
|
+
new convert_to_array(array, false)
|
1241
1558
|
end
|
1242
1559
|
|
1243
1560
|
#
|
@@ -1315,7 +1632,7 @@ class Vector
|
|
1315
1632
|
raise TypeError, "Integer is not like Vector" if v.kind_of?(Integer)
|
1316
1633
|
Vector.Raise ErrDimensionMismatch if size != v.size
|
1317
1634
|
return to_enum(:collect2, v) unless block_given?
|
1318
|
-
size
|
1635
|
+
Array.new(size) do |i|
|
1319
1636
|
yield @elements[i], v[i]
|
1320
1637
|
end
|
1321
1638
|
end
|
@@ -1341,7 +1658,7 @@ class Vector
|
|
1341
1658
|
# Return a copy of the vector.
|
1342
1659
|
#
|
1343
1660
|
def clone
|
1344
|
-
|
1661
|
+
self.class.elements(@elements)
|
1345
1662
|
end
|
1346
1663
|
|
1347
1664
|
#
|
@@ -1362,7 +1679,7 @@ class Vector
|
|
1362
1679
|
case x
|
1363
1680
|
when Numeric
|
1364
1681
|
els = @elements.collect{|e| e * x}
|
1365
|
-
|
1682
|
+
self.class.elements(els, false)
|
1366
1683
|
when Matrix
|
1367
1684
|
Matrix.column_vector(self) * x
|
1368
1685
|
when Vector
|
@@ -1382,7 +1699,7 @@ class Vector
|
|
1382
1699
|
els = collect2(v) {|v1, v2|
|
1383
1700
|
v1 + v2
|
1384
1701
|
}
|
1385
|
-
|
1702
|
+
self.class.elements(els, false)
|
1386
1703
|
when Matrix
|
1387
1704
|
Matrix.column_vector(self) + v
|
1388
1705
|
else
|
@@ -1400,7 +1717,7 @@ class Vector
|
|
1400
1717
|
els = collect2(v) {|v1, v2|
|
1401
1718
|
v1 - v2
|
1402
1719
|
}
|
1403
|
-
|
1720
|
+
self.class.elements(els, false)
|
1404
1721
|
when Matrix
|
1405
1722
|
Matrix.column_vector(self) - v
|
1406
1723
|
else
|
@@ -1415,7 +1732,7 @@ class Vector
|
|
1415
1732
|
case x
|
1416
1733
|
when Numeric
|
1417
1734
|
els = @elements.collect{|e| e / x}
|
1418
|
-
|
1735
|
+
self.class.elements(els, false)
|
1419
1736
|
when Matrix, Vector
|
1420
1737
|
Vector.Raise ErrOperationNotDefined, "/", self.class, x.class
|
1421
1738
|
else
|
@@ -1436,36 +1753,63 @@ class Vector
|
|
1436
1753
|
|
1437
1754
|
p = 0
|
1438
1755
|
each2(v) {|v1, v2|
|
1439
|
-
p += v1 * v2
|
1756
|
+
p += v1 * v2.conj
|
1440
1757
|
}
|
1441
1758
|
p
|
1442
1759
|
end
|
1443
1760
|
|
1761
|
+
#
|
1762
|
+
# Returns the cross product of this vector with the other.
|
1763
|
+
# Vector[1, 0, 0].cross_product Vector[0, 1, 0] => Vector[0, 0, 1]
|
1764
|
+
#
|
1765
|
+
def cross_product(v)
|
1766
|
+
Vector.Raise ErrDimensionMismatch unless size == v.size && v.size == 3
|
1767
|
+
Vector[ v[1]*@elements[2] - v[2]*@elements[1],
|
1768
|
+
v[2]*@elements[0] - v[0]*@elements[2],
|
1769
|
+
v[0]*@elements[1] - v[1]*@elements[0] ]
|
1770
|
+
end
|
1771
|
+
|
1444
1772
|
#
|
1445
1773
|
# Like Array#collect.
|
1446
1774
|
#
|
1447
1775
|
def collect(&block) # :yield: e
|
1448
1776
|
return to_enum(:collect) unless block_given?
|
1449
1777
|
els = @elements.collect(&block)
|
1450
|
-
|
1778
|
+
self.class.elements(els, false)
|
1451
1779
|
end
|
1452
1780
|
alias map collect
|
1453
1781
|
|
1782
|
+
#
|
1783
|
+
# Returns the modulus (Pythagorean distance) of the vector.
|
1784
|
+
# Vector[5,8,2].r => 9.643650761
|
1785
|
+
#
|
1786
|
+
def magnitude
|
1787
|
+
Math.sqrt(@elements.inject(0) {|v, e| v + e.abs2})
|
1788
|
+
end
|
1789
|
+
alias r magnitude
|
1790
|
+
alias norm magnitude
|
1791
|
+
|
1454
1792
|
#
|
1455
1793
|
# Like Vector#collect2, but returns a Vector instead of an Array.
|
1456
1794
|
#
|
1457
1795
|
def map2(v, &block) # :yield: e1, e2
|
1458
1796
|
return to_enum(:map2, v) unless block_given?
|
1459
1797
|
els = collect2(v, &block)
|
1460
|
-
|
1798
|
+
self.class.elements(els, false)
|
1461
1799
|
end
|
1462
1800
|
|
1801
|
+
class ZeroVectorError < StandardError
|
1802
|
+
end
|
1463
1803
|
#
|
1464
|
-
# Returns the
|
1465
|
-
# Vector[5,8,2].
|
1804
|
+
# Returns a new vector with the same direction but with norm 1.
|
1805
|
+
# v = Vector[5,8,2].normalize
|
1806
|
+
# # => Vector[0.5184758473652127, 0.8295613557843402, 0.20739033894608505]
|
1807
|
+
# v.norm => 1.0
|
1466
1808
|
#
|
1467
|
-
def
|
1468
|
-
|
1809
|
+
def normalize
|
1810
|
+
n = magnitude
|
1811
|
+
raise ZeroVectorError, "Zero vectors can not be normalized" if n == 0
|
1812
|
+
self / n
|
1469
1813
|
end
|
1470
1814
|
|
1471
1815
|
#--
|
@@ -1532,6 +1876,6 @@ class Vector
|
|
1532
1876
|
# Overrides Object#inspect
|
1533
1877
|
#
|
1534
1878
|
def inspect
|
1535
|
-
|
1879
|
+
"Vector" + @elements.inspect
|
1536
1880
|
end
|
1537
1881
|
end
|