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