rubysl-matrix 1.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -32,79 +32,99 @@ end
32
32
  # == Method Catalogue
33
33
  #
34
34
  # To create a matrix:
35
- # * <tt> Matrix[*rows] </tt>
36
- # * <tt> Matrix.[](*rows) </tt>
37
- # * <tt> Matrix.rows(rows, copy = true) </tt>
38
- # * <tt> Matrix.columns(columns) </tt>
39
- # * <tt> Matrix.build(row_size, column_size, &block) </tt>
40
- # * <tt> Matrix.diagonal(*values) </tt>
41
- # * <tt> Matrix.scalar(n, value) </tt>
42
- # * <tt> Matrix.identity(n) </tt>
43
- # * <tt> Matrix.unit(n) </tt>
44
- # * <tt> Matrix.I(n) </tt>
45
- # * <tt> Matrix.zero(n) </tt>
46
- # * <tt> Matrix.row_vector(row) </tt>
47
- # * <tt> Matrix.column_vector(column) </tt>
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
- # * <tt> [](i, j) </tt>
51
- # * <tt> #row_size </tt>
52
- # * <tt> #column_size </tt>
53
- # * <tt> #row(i) </tt>
54
- # * <tt> #column(j) </tt>
55
- # * <tt> #collect </tt>
56
- # * <tt> #map </tt>
57
- # * <tt> #each </tt>
58
- # * <tt> #each_with_index </tt>
59
- # * <tt> #minor(*param) </tt>
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
- # * <tt> #empty? </tt>
63
- # * <tt> #real? </tt>
64
- # * <tt> #regular? </tt>
65
- # * <tt> #singular? </tt>
66
- # * <tt> #square? </tt>
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
- # * <tt> *(m) </tt>
70
- # * <tt> +(m) </tt>
71
- # * <tt> -(m) </tt>
72
- # * <tt> #/(m) </tt>
73
- # * <tt> #inverse </tt>
74
- # * <tt> #inv </tt>
75
- # * <tt> ** </tt>
80
+ # * #*(m)
81
+ # * #+(m)
82
+ # * #-(m)
83
+ # * #/(m)
84
+ # * #inverse
85
+ # * #inv
86
+ # * #**
76
87
  #
77
88
  # Matrix functions:
78
- # * <tt> #determinant </tt>
79
- # * <tt> #det </tt>
80
- # * <tt> #rank </tt>
81
- # * <tt> #trace </tt>
82
- # * <tt> #tr </tt>
83
- # * <tt> #transpose </tt>
84
- # * <tt> #t </tt>
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
- # * <tt> conj </tt>
88
- # * <tt> conjugate </tt>
89
- # * <tt> imag </tt>
90
- # * <tt> imaginary </tt>
91
- # * <tt> real </tt>
92
- # * <tt> rect </tt>
93
- # * <tt> rectangular </tt>
105
+ # * conj
106
+ # * conjugate
107
+ # * imag
108
+ # * imaginary
109
+ # * real
110
+ # * rect
111
+ # * rectangular
94
112
  #
95
113
  # Conversion to other data types:
96
- # * <tt> #coerce(other) </tt>
97
- # * <tt> #row_vectors </tt>
98
- # * <tt> #column_vectors </tt>
99
- # * <tt> #to_a </tt>
114
+ # * #coerce(other)
115
+ # * #row_vectors
116
+ # * #column_vectors
117
+ # * #to_a
100
118
  #
101
119
  # String representations:
102
- # * <tt> #to_s </tt>
103
- # * <tt> #inspect </tt>
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
- Matrix.rows(rows, false)
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
- Matrix.Raise ErrDimensionMismatch, "row size differs (#{row.size} should be #{size})" unless row.size == size
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
- Matrix.rows(columns, false).transpose
171
+ rows(columns, false).transpose
152
172
  end
153
173
 
154
174
  #
155
- # Creates a matrix of size +row_size+ x +column_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(row_size, column_size = row_size)
166
- row_size = CoercionHelper.coerce_to_int(row_size)
167
- column_size = CoercionHelper.coerce_to_int(column_size)
168
- raise ArgumentError if row_size < 0 || column_size < 0
169
- return to_enum :build, row_size, column_size unless block_given?
170
- rows = row_size.times.map do |i|
171
- column_size.times.map do |j|
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, column_size
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 = (0 ... size).collect {|j|
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
- Matrix.diagonal(*Array.new(n, value))
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
- Matrix.scalar(n, 1)
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 an +n+ by +n+ zero matrix.
241
+ # Creates a zero matrix.
222
242
  # Matrix.zero(2)
223
243
  # => 0 0
224
244
  # 0 0
225
245
  #
226
- def Matrix.zero(n)
227
- Matrix.scalar(n, 0)
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 +row_size+ x +column_size+.
256
- # At least one of +row_size+ or +column_size+ must be 0.
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(row_size = 0, column_size = 0)
268
- Matrix.Raise ArgumentError, "One size must be 0" if column_size != 0 && row_size != 0
269
- Matrix.Raise ArgumentError, "Negative size" if column_size < 0 || row_size < 0
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([[]]*row_size, column_size)
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, column_size = rows[0].size)
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
- # column_size must be the size of the first row, if there is one,
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
- @column_size = column_size
303
+ @column_count = column_count
283
304
  end
284
305
 
285
- def new_matrix(rows, column_size = rows[0].size) # :nodoc:
286
- Matrix.send(:new, rows, column_size) # bypass privacy of Matrix.new
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 row_size
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 :column_size
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 >= column_size || j < -column_size
339
- row_size.times do |i|
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 >= column_size || j < -column_size
345
- col = (0 ... row_size).collect {|i|
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, column_size
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
- def each(&block) # :yield: e
373
- return to_enum(:each) unless block_given?
374
- @rows.each do |row|
375
- row.each(&block)
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
- # Yields all elements of the matrix, starting with those of the first row,
382
- # along with the row index and column index,
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
- # # => 1 at 0, 0
388
- # # => 2 at 0, 1
389
- # # => 3 at 1, 0
390
- # # => 4 at 1, 1
391
- #
392
- def each_with_index(&block) # :yield: e, row, column
393
- return to_enum(:each_with_index) unless block_given?
394
- @rows.each_with_index do |row, row_index|
395
- row.each_with_index do |e, col_index|
396
- yield e, row_index, col_index
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 row_size or column_size respectively.
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 += row_size if from_row < 0
564
+ from_row += row_count if from_row < 0
421
565
  to_row = row_range.end
422
- to_row += row_size if to_row < 0
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 += column_size if from_col < 0
571
+ from_col += column_count if from_col < 0
428
572
  to_col = col_range.end
429
- to_col += column_size if to_col < 0
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 += row_size if from_row < 0
436
- from_col += column_size if from_col < 0
579
+ from_row += row_count if from_row < 0
580
+ from_col += column_count if from_col < 0
437
581
  else
438
- Matrix.Raise ArgumentError, param.inspect
582
+ raise ArgumentError, param.inspect
439
583
  end
440
584
 
441
- return nil if from_row > row_size || from_col > column_size || from_row < 0 || from_col < 0
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, column_size - from_col
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
- column_size == 0 || row_size == 0
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
- column_size == row_size
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), column_size
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, column_size
812
+ return new_matrix rows, column_count
540
813
  when Vector
541
- m = Matrix.column_vector(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 column_size != m.row_size
818
+ Matrix.Raise ErrDimensionMismatch if column_count != m.row_count
546
819
 
547
- rows = (0 ... row_size).collect {|i|
548
- (0 ... m.column_size).collect {|j|
549
- (0 ... column_size).inject(0) do |vij, k|
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.column_size
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 = Matrix.column_vector(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 row_size == m.row_size and column_size == m.column_size
850
+ Matrix.Raise ErrDimensionMismatch unless row_count == m.row_count and column_count == m.column_count
578
851
 
579
- rows = (0 ... row_size).collect {|i|
580
- (0 ... column_size).collect {|j|
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, column_size
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 = Matrix.column_vector(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 row_size == m.row_size and column_size == m.column_size
877
+ Matrix.Raise ErrDimensionMismatch unless row_count == m.row_count and column_count == m.column_count
605
878
 
606
- rows = (0 ... row_size).collect {|i|
607
- (0 ... column_size).collect {|j|
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, column_size
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, column_size
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
- Matrix.I(row_size).send(:inverse_from, self)
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 = row_size - 1
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. Currently implemented for integer powers only.
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 Matrix.identity(self.column_size) if other == 0
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 row_size
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 = row_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
- rank
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 = column_size - 1
836
- last_row = row_size - 1
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...column_size).inject(0) do |tr, i|
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 Matrix.empty(column_size, 0) if row_size.zero?
894
- new_matrix @rows.transpose, row_size
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
- (0 ... row_size).collect {|i|
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
- (0 ... column_size).collect {|i|
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{|row| row.dup}
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
- "Matrix.empty(#{row_size}, #{column_size})"
1336
+ "#{self.class}.empty(#{row_count}, #{column_count})"
1024
1337
  else
1025
- "Matrix[" + @rows.collect{|row|
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
- "Matrix.empty(#{row_size}, #{column_size})"
1349
+ "#{self.class}.empty(#{row_count}, #{column_count})"
1037
1350
  else
1038
- "Matrix#{@rows.inspect}"
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
- # * <tt> Vector.[](*array) </tt>
1193
- # * <tt> Vector.elements(array, copy = true) </tt>
1505
+ # * Vector.[](*array)
1506
+ # * Vector.elements(array, copy = true)
1194
1507
  #
1195
1508
  # To access elements:
1196
- # * <tt> [](i) </tt>
1509
+ # * #[](i)
1197
1510
  #
1198
1511
  # To enumerate the elements:
1199
- # * <tt> #each2(v) </tt>
1200
- # * <tt> #collect2(v) </tt>
1512
+ # * #each2(v)
1513
+ # * #collect2(v)
1201
1514
  #
1202
1515
  # Vector arithmetic:
1203
- # * <tt> *(x) "is matrix or number" </tt>
1204
- # * <tt> +(v) </tt>
1205
- # * <tt> -(v) </tt>
1516
+ # * #*(x) "is matrix or number"
1517
+ # * #+(v)
1518
+ # * #-(v)
1206
1519
  #
1207
1520
  # Vector functions:
1208
- # * <tt> #inner_product(v) </tt>
1209
- # * <tt> #collect </tt>
1210
- # * <tt> #map </tt>
1211
- # * <tt> #map2(v) </tt>
1212
- # * <tt> #r </tt>
1213
- # * <tt> #size </tt>
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
- # * <tt> #covector </tt>
1217
- # * <tt> #to_a </tt>
1218
- # * <tt> #coerce(other) </tt>
1533
+ # * #covector
1534
+ # * #to_a
1535
+ # * #coerce(other)
1219
1536
  #
1220
1537
  # String representations:
1221
- # * <tt> #to_s </tt>
1222
- # * <tt> #inspect </tt>
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, copy = false)
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.times.collect do |i|
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
- Vector.elements(@elements)
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
- Vector.elements(els, false)
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
- Vector.elements(els, false)
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
- Vector.elements(els, false)
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
- Vector.elements(els, false)
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
- Vector.elements(els, false)
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
- Vector.elements(els, false)
1798
+ self.class.elements(els, false)
1461
1799
  end
1462
1800
 
1801
+ class ZeroVectorError < StandardError
1802
+ end
1463
1803
  #
1464
- # Returns the modulus (Pythagorean distance) of the vector.
1465
- # Vector[5,8,2].r => 9.643650761
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 r
1468
- Math.sqrt(@elements.inject(0) {|v, e| v + e*e})
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
- str = "Vector"+@elements.inspect
1879
+ "Vector" + @elements.inspect
1536
1880
  end
1537
1881
  end